@@ -13,7 +13,7 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
1313
1414use super :: {
1515 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 ,
1717 Provenance , Scalar ,
1818} ;
1919
@@ -240,37 +240,69 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
240240 let int = self . to_scalar ( ) . assert_int ( ) ;
241241 ConstInt :: new ( int, self . layout . ty . is_signed ( ) , self . layout . ty . is_ptr_sized_integral ( ) )
242242 }
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+ }
243287}
244288
245289impl < ' 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 ) ;
253296 }
254297 // There are no unsized immediates.
255- self . assert_mem_place ( ) . len ( cx )
298+ self . assert_mem_place ( ) . meta
256299 } 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+ } )
262302 }
263303
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)
274306 }
275307
276308 /// Offset the operand in memory (if possible) and change its metadata.
@@ -286,13 +318,9 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
286318 match self . as_mplace_or_imm ( ) {
287319 Left ( mplace) => Ok ( mplace. offset_with_meta ( offset, meta, layout, cx) ?. into ( ) ) ,
288320 Right ( imm) => {
289- assert ! (
290- matches!( * imm, Immediate :: Uninit ) ,
291- "Scalar/ScalarPair cannot be offset into"
292- ) ;
293321 assert ! ( !meta. has_meta( ) ) ; // no place to store metadata here
294322 // Every part of an uninit is uninit.
295- Ok ( ImmTy :: uninit ( layout) . into ( ) )
323+ Ok ( imm . offset ( offset , layout, cx ) . into ( ) )
296324 }
297325 }
298326 }
@@ -502,13 +530,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
502530 & self ,
503531 place : & PlaceTy < ' tcx , M :: Provenance > ,
504532 ) -> 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)
509549 }
510- } ;
511- Ok ( OpTy { op, layout : place. layout , align : Some ( place. align ) } )
550+ }
512551 }
513552
514553 /// Evaluate a place with the goal of reading from it. This lets us sometimes
0 commit comments