@@ -13,7 +13,7 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
13
13
14
14
use super :: {
15
15
alloc_range, from_known_layout, mir_assign_valid_types, AllocId , ConstValue , Frame , GlobalId ,
16
- InterpCx , InterpResult , MPlaceTy , Machine , MemPlace , MemPlaceMeta , Place , PlaceTy , Pointer ,
16
+ InterpCx , InterpResult , MPlaceTy , Machine , MemPlace , MemPlaceMeta , PlaceTy , Pointer ,
17
17
Provenance , Scalar ,
18
18
} ;
19
19
@@ -240,37 +240,69 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
240
240
let int = self . to_scalar ( ) . assert_int ( ) ;
241
241
ConstInt :: new ( int, self . layout . ty . is_signed ( ) , self . layout . ty . is_ptr_sized_integral ( ) )
242
242
}
243
+
244
+ /// Compute the "sub-immediate" that is located within the `base` at the given offset with the
245
+ /// given layout.
246
+ pub ( super ) fn offset (
247
+ & self ,
248
+ offset : Size ,
249
+ layout : TyAndLayout < ' tcx > ,
250
+ cx : & impl HasDataLayout ,
251
+ ) -> Self {
252
+ // This makes several assumptions about what layouts we will encounter; we match what
253
+ // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
254
+ let inner_val: Immediate < _ > = match ( * * self , self . layout . abi ) {
255
+ // if the entire value is uninit, then so is the field (can happen in ConstProp)
256
+ ( Immediate :: Uninit , _) => Immediate :: Uninit ,
257
+ // the field contains no information, can be left uninit
258
+ _ if layout. is_zst ( ) => Immediate :: Uninit ,
259
+ // the field covers the entire type
260
+ _ if layout. size == self . layout . size => {
261
+ assert ! ( match ( self . layout. abi, layout. abi) {
262
+ ( Abi :: Scalar ( ..) , Abi :: Scalar ( ..) ) => true ,
263
+ ( Abi :: ScalarPair ( ..) , Abi :: ScalarPair ( ..) ) => true ,
264
+ _ => false ,
265
+ } ) ;
266
+ assert ! ( offset. bytes( ) == 0 ) ;
267
+ * * self
268
+ }
269
+ // extract fields from types with `ScalarPair` ABI
270
+ ( Immediate :: ScalarPair ( a_val, b_val) , Abi :: ScalarPair ( a, b) ) => {
271
+ assert ! ( matches!( layout. abi, Abi :: Scalar ( ..) ) ) ;
272
+ Immediate :: from ( if offset. bytes ( ) == 0 {
273
+ debug_assert_eq ! ( layout. size, a. size( cx) ) ;
274
+ a_val
275
+ } else {
276
+ debug_assert_eq ! ( offset, a. size( cx) . align_to( b. align( cx) . abi) ) ;
277
+ debug_assert_eq ! ( layout. size, b. size( cx) ) ;
278
+ b_val
279
+ } )
280
+ }
281
+ // everything else is a bug
282
+ _ => bug ! ( "invalid field access on immediate {}, layout {:#?}" , self , self . layout) ,
283
+ } ;
284
+
285
+ ImmTy :: from_immediate ( inner_val, layout)
286
+ }
243
287
}
244
288
245
289
impl < ' tcx , Prov : Provenance > OpTy < ' tcx , Prov > {
246
- pub fn len ( & self , cx : & impl HasDataLayout ) -> InterpResult < ' tcx , u64 > {
247
- if self . layout . is_unsized ( ) {
248
- if matches ! ( self . op, Operand :: Immediate ( Immediate :: Uninit ) ) {
249
- // Uninit unsized places shouldn't occur. In the interpreter we have them
250
- // temporarily for unsized arguments before their value is put in; in ConstProp they
251
- // remain uninit and this code can actually be reached.
252
- throw_inval ! ( UninitUnsizedLocal ) ;
290
+ pub ( super ) fn meta ( & self ) -> InterpResult < ' tcx , MemPlaceMeta < Prov > > {
291
+ Ok ( if self . layout . is_unsized ( ) {
292
+ if matches ! ( self . op, Operand :: Immediate ( _) ) {
293
+ // Unsized immediate OpTy cannot occur. We create a MemPlace for all unsized locals during argument passing.
294
+ // However, ConstProp doesn't do that, so we can run into this nonsense situation.
295
+ throw_inval ! ( ConstPropNonsense ) ;
253
296
}
254
297
// There are no unsized immediates.
255
- self . assert_mem_place ( ) . len ( cx )
298
+ self . assert_mem_place ( ) . meta
256
299
} else {
257
- match self . layout . fields {
258
- abi:: FieldsShape :: Array { count, .. } => Ok ( count) ,
259
- _ => bug ! ( "len not supported on sized type {:?}" , self . layout. ty) ,
260
- }
261
- }
300
+ MemPlaceMeta :: None
301
+ } )
262
302
}
263
303
264
- /// Replace the layout of this operand. There's basically no sanity check that this makes sense,
265
- /// you better know what you are doing! If this is an immediate, applying the wrong layout can
266
- /// not just lead to invalid data, it can actually *shift the data around* since the offsets of
267
- /// a ScalarPair are entirely determined by the layout, not the data.
268
- pub fn transmute ( & self , layout : TyAndLayout < ' tcx > ) -> Self {
269
- assert_eq ! (
270
- self . layout. size, layout. size,
271
- "transmuting with a size change, that doesn't seem right"
272
- ) ;
273
- OpTy { layout, ..* self }
304
+ pub fn len ( & self , cx : & impl HasDataLayout ) -> InterpResult < ' tcx , u64 > {
305
+ self . meta ( ) ?. len ( self . layout , cx)
274
306
}
275
307
276
308
/// Offset the operand in memory (if possible) and change its metadata.
@@ -286,13 +318,9 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
286
318
match self . as_mplace_or_imm ( ) {
287
319
Left ( mplace) => Ok ( mplace. offset_with_meta ( offset, meta, layout, cx) ?. into ( ) ) ,
288
320
Right ( imm) => {
289
- assert ! (
290
- matches!( * imm, Immediate :: Uninit ) ,
291
- "Scalar/ScalarPair cannot be offset into"
292
- ) ;
293
321
assert ! ( !meta. has_meta( ) ) ; // no place to store metadata here
294
322
// Every part of an uninit is uninit.
295
- Ok ( ImmTy :: uninit ( layout) . into ( ) )
323
+ Ok ( imm . offset ( offset , layout, cx ) . into ( ) )
296
324
}
297
325
}
298
326
}
@@ -502,13 +530,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
502
530
& self ,
503
531
place : & PlaceTy < ' tcx , M :: Provenance > ,
504
532
) -> InterpResult < ' tcx , OpTy < ' tcx , M :: Provenance > > {
505
- let op = match * * place {
506
- Place :: Ptr ( mplace) => Operand :: Indirect ( mplace) ,
507
- Place :: Local { frame, local } => {
508
- * self . local_to_op ( & self . stack ( ) [ frame] , local, None ) ?
533
+ match place. as_mplace_or_local ( ) {
534
+ Left ( mplace) => Ok ( mplace. into ( ) ) ,
535
+ Right ( ( frame, local, offset) ) => {
536
+ let base = self . local_to_op ( & self . stack ( ) [ frame] , local, None ) ?;
537
+ let mut field = if let Some ( offset) = offset {
538
+ // This got offset. We can be sure that the field is sized.
539
+ base. offset ( offset, place. layout , self ) ?
540
+ } else {
541
+ assert_eq ! ( place. layout, base. layout) ;
542
+ // Unsized cases are possible here since an unsized local will be a
543
+ // `Place::Local` until the first projection calls `place_to_op` to extract the
544
+ // underlying mplace.
545
+ base
546
+ } ;
547
+ field. align = Some ( place. align ) ;
548
+ Ok ( field)
509
549
}
510
- } ;
511
- Ok ( OpTy { op, layout : place. layout , align : Some ( place. align ) } )
550
+ }
512
551
}
513
552
514
553
/// Evaluate a place with the goal of reading from it. This lets us sometimes
0 commit comments