@@ -187,11 +187,13 @@ pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
187187 /// where in the local this place is located; if it is `None`, no projection has been applied.
188188 /// Such projections are meaningful even if the offset is 0, since they can change layouts.
189189 /// (Without that optimization, we'd just always be a `MemPlace`.)
190- /// Note that this only stores the frame index, not the thread this frame belongs to -- that is
191- /// implicit. This means a `Place` must never be moved across interpreter thread boundaries!
190+ /// `Local` places always refer to the current stack frame, so they are unstable under
191+ /// function calls/returns and switching betweens stacks of different threads!
192+ /// We carry around the address of the `locals` buffer of the correct stack frame as a sanity
193+ /// chec to be able to catch some cases of using a dangling `Place`.
192194 ///
193195 /// This variant shall not be used for unsized types -- those must always live in memory.
194- Local { frame : usize , local : mir:: Local , offset : Option < Size > } ,
196+ Local { local : mir:: Local , offset : Option < Size > , locals_addr : usize } ,
195197}
196198
197199/// An evaluated place, together with its type.
@@ -233,10 +235,10 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
233235 #[ inline( always) ]
234236 pub fn as_mplace_or_local (
235237 & self ,
236- ) -> Either < MPlaceTy < ' tcx , Prov > , ( usize , mir:: Local , Option < Size > ) > {
238+ ) -> Either < MPlaceTy < ' tcx , Prov > , ( mir:: Local , Option < Size > , usize ) > {
237239 match self . place {
238240 Place :: Ptr ( mplace) => Left ( MPlaceTy { mplace, layout : self . layout } ) ,
239- Place :: Local { frame , local, offset } => Right ( ( frame , local, offset) ) ,
241+ Place :: Local { local, offset, locals_addr } => Right ( ( local, offset, locals_addr ) ) ,
240242 }
241243 }
242244
@@ -279,7 +281,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
279281 ) -> InterpResult < ' tcx , Self > {
280282 Ok ( match self . as_mplace_or_local ( ) {
281283 Left ( mplace) => mplace. offset_with_meta ( offset, mode, meta, layout, ecx) ?. into ( ) ,
282- Right ( ( frame , local, old_offset) ) => {
284+ Right ( ( local, old_offset, locals_addr ) ) => {
283285 debug_assert ! ( layout. is_sized( ) , "unsized locals should live in memory" ) ;
284286 assert_matches ! ( meta, MemPlaceMeta :: None ) ; // we couldn't store it anyway...
285287 // `Place::Local` are always in-bounds of their surrounding local, so we can just
@@ -292,7 +294,10 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
292294 . offset ( old_offset. unwrap_or ( Size :: ZERO ) . bytes ( ) , offset. bytes ( ) ) ?,
293295 ) ;
294296
295- PlaceTy { place : Place :: Local { frame, local, offset : Some ( new_offset) } , layout }
297+ PlaceTy {
298+ place : Place :: Local { local, offset : Some ( new_offset) , locals_addr } ,
299+ layout,
300+ }
296301 }
297302 } )
298303 }
@@ -331,7 +336,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
331336pub trait Writeable < ' tcx , Prov : Provenance > : Projectable < ' tcx , Prov > {
332337 fn as_mplace_or_local (
333338 & self ,
334- ) -> Either < MPlaceTy < ' tcx , Prov > , ( usize , mir:: Local , Option < Size > , TyAndLayout < ' tcx > ) > ;
339+ ) -> Either < MPlaceTy < ' tcx , Prov > , ( mir:: Local , Option < Size > , usize , TyAndLayout < ' tcx > ) > ;
335340
336341 fn force_mplace < ' mir , M : Machine < ' mir , ' tcx , Provenance = Prov > > (
337342 & self ,
@@ -343,9 +348,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
343348 #[ inline( always) ]
344349 fn as_mplace_or_local (
345350 & self ,
346- ) -> Either < MPlaceTy < ' tcx , Prov > , ( usize , mir:: Local , Option < Size > , TyAndLayout < ' tcx > ) > {
351+ ) -> Either < MPlaceTy < ' tcx , Prov > , ( mir:: Local , Option < Size > , usize , TyAndLayout < ' tcx > ) > {
347352 self . as_mplace_or_local ( )
348- . map_right ( |( frame , local, offset) | ( frame , local, offset, self . layout ) )
353+ . map_right ( |( local, offset, locals_addr ) | ( local, offset, locals_addr , self . layout ) )
349354 }
350355
351356 #[ inline( always) ]
@@ -361,7 +366,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
361366 #[ inline( always) ]
362367 fn as_mplace_or_local (
363368 & self ,
364- ) -> Either < MPlaceTy < ' tcx , Prov > , ( usize , mir:: Local , Option < Size > , TyAndLayout < ' tcx > ) > {
369+ ) -> Either < MPlaceTy < ' tcx , Prov > , ( mir:: Local , Option < Size > , usize , TyAndLayout < ' tcx > ) > {
365370 Left ( self . clone ( ) )
366371 }
367372
@@ -501,21 +506,21 @@ where
501506 Ok ( ( mplace, len) )
502507 }
503508
509+ /// Turn a local in the current frame into a place.
504510 pub fn local_to_place (
505511 & self ,
506- frame : usize ,
507512 local : mir:: Local ,
508513 ) -> InterpResult < ' tcx , PlaceTy < ' tcx , M :: Provenance > > {
509514 // Other parts of the system rely on `Place::Local` never being unsized.
510515 // So we eagerly check here if this local has an MPlace, and if yes we use it.
511- let frame_ref = & self . stack ( ) [ frame ] ;
512- let layout = self . layout_of_local ( frame_ref , local, None ) ?;
516+ let frame = self . frame ( ) ;
517+ let layout = self . layout_of_local ( frame , local, None ) ?;
513518 let place = if layout. is_sized ( ) {
514519 // We can just always use the `Local` for sized values.
515- Place :: Local { frame , local, offset : None }
520+ Place :: Local { local, offset : None , locals_addr : frame . locals_addr ( ) }
516521 } else {
517522 // Unsized `Local` isn't okay (we cannot store the metadata).
518- match frame_ref . locals [ local] . access ( ) ? {
523+ match frame . locals [ local] . access ( ) ? {
519524 Operand :: Immediate ( _) => bug ! ( ) ,
520525 Operand :: Indirect ( mplace) => Place :: Ptr ( * mplace) ,
521526 }
@@ -530,7 +535,7 @@ where
530535 & self ,
531536 mir_place : mir:: Place < ' tcx > ,
532537 ) -> InterpResult < ' tcx , PlaceTy < ' tcx , M :: Provenance > > {
533- let mut place = self . local_to_place ( self . frame_idx ( ) , mir_place. local ) ?;
538+ let mut place = self . local_to_place ( mir_place. local ) ?;
534539 // Using `try_fold` turned out to be bad for performance, hence the loop.
535540 for elem in mir_place. projection . iter ( ) {
536541 place = self . project ( & place, elem) ?
@@ -611,23 +616,23 @@ where
611616 // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
612617 // but not factored as a separate function.
613618 let mplace = match dest. as_mplace_or_local ( ) {
614- Right ( ( frame , local, offset, layout) ) => {
619+ Right ( ( local, offset, locals_addr , layout) ) => {
615620 if offset. is_some ( ) {
616621 // This has been projected to a part of this local. We could have complicated
617622 // logic to still keep this local as an `Operand`... but it's much easier to
618623 // just fall back to the indirect path.
619624 dest. force_mplace ( self ) ?
620625 } else {
621- M :: before_access_local_mut ( self , frame, local ) ? ;
622- match self . stack_mut ( ) [ frame ] . locals [ local] . access_mut ( ) ? {
626+ debug_assert_eq ! ( locals_addr , self . frame( ) . locals_addr ( ) ) ;
627+ match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
623628 Operand :: Immediate ( local_val) => {
624629 // Local can be updated in-place.
625630 * local_val = src;
626631 // Double-check that the value we are storing and the local fit to each other.
627632 // (*After* doing the update for borrow checker reasons.)
628633 if cfg ! ( debug_assertions) {
629634 let local_layout =
630- self . layout_of_local ( & self . stack ( ) [ frame ] , local, None ) ?;
635+ self . layout_of_local ( & self . frame ( ) , local, None ) ?;
631636 match ( src, local_layout. abi ) {
632637 ( Immediate :: Scalar ( scalar) , Abi :: Scalar ( s) ) => {
633638 assert_eq ! ( scalar. size( ) , s. size( self ) )
@@ -725,16 +730,16 @@ where
725730 ) -> InterpResult < ' tcx > {
726731 let mplace = match dest. as_mplace_or_local ( ) {
727732 Left ( mplace) => mplace,
728- Right ( ( frame , local, offset, layout) ) => {
733+ Right ( ( local, offset, locals_addr , layout) ) => {
729734 if offset. is_some ( ) {
730735 // This has been projected to a part of this local. We could have complicated
731736 // logic to still keep this local as an `Operand`... but it's much easier to
732737 // just fall back to the indirect path.
733738 // FIXME: share the logic with `write_immediate_no_validate`.
734739 dest. force_mplace ( self ) ?
735740 } else {
736- M :: before_access_local_mut ( self , frame, local ) ? ;
737- match self . stack_mut ( ) [ frame ] . locals [ local] . access_mut ( ) ? {
741+ debug_assert_eq ! ( locals_addr , self . frame( ) . locals_addr ( ) ) ;
742+ match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
738743 Operand :: Immediate ( local) => {
739744 * local = Immediate :: Uninit ;
740745 return Ok ( ( ) ) ;
@@ -912,17 +917,16 @@ where
912917 place : & PlaceTy < ' tcx , M :: Provenance > ,
913918 ) -> InterpResult < ' tcx , MPlaceTy < ' tcx , M :: Provenance > > {
914919 let mplace = match place. place {
915- Place :: Local { frame , local, offset } => {
916- M :: before_access_local_mut ( self , frame, local ) ? ;
917- let whole_local = match self . stack_mut ( ) [ frame ] . locals [ local] . access_mut ( ) ? {
920+ Place :: Local { local, offset, locals_addr } => {
921+ debug_assert_eq ! ( locals_addr , self . frame( ) . locals_addr ( ) ) ;
922+ let whole_local = match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
918923 & mut Operand :: Immediate ( local_val) => {
919924 // We need to make an allocation.
920925
921926 // We need the layout of the local. We can NOT use the layout we got,
922927 // that might e.g., be an inner field of a struct with `Scalar` layout,
923928 // that has different alignment than the outer field.
924- let local_layout =
925- self . layout_of_local ( & self . stack ( ) [ frame] , local, None ) ?;
929+ let local_layout = self . layout_of_local ( & self . frame ( ) , local, None ) ?;
926930 assert ! ( local_layout. is_sized( ) , "unsized locals cannot be immediate" ) ;
927931 let mplace = self . allocate ( local_layout, MemoryKind :: Stack ) ?;
928932 // Preserve old value. (As an optimization, we can skip this if it was uninit.)
@@ -936,11 +940,11 @@ where
936940 mplace. mplace ,
937941 ) ?;
938942 }
939- M :: after_local_allocated ( self , frame , local, & mplace) ?;
943+ M :: after_local_allocated ( self , local, & mplace) ?;
940944 // Now we can call `access_mut` again, asserting it goes well, and actually
941945 // overwrite things. This points to the entire allocation, not just the part
942946 // the place refers to, i.e. we do this before we apply `offset`.
943- * self . stack_mut ( ) [ frame ] . locals [ local] . access_mut ( ) . unwrap ( ) =
947+ * self . frame_mut ( ) . locals [ local] . access_mut ( ) . unwrap ( ) =
944948 Operand :: Indirect ( mplace. mplace ) ;
945949 mplace. mplace
946950 }
0 commit comments