@@ -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
@@ -512,7 +517,7 @@ where
512517 let layout = self . layout_of_local ( frame_ref, 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_ref . locals_addr ( ) }
516521 } else {
517522 // Unsized `Local` isn't okay (we cannot store the metadata).
518523 match frame_ref. locals [ local] . access ( ) ? {
@@ -611,23 +616,24 @@ 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+ M :: before_access_local_mut ( self , self . frame_idx ( ) , local) ?;
628+ match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
623629 Operand :: Immediate ( local_val) => {
624630 // Local can be updated in-place.
625631 * local_val = src;
626632 // Double-check that the value we are storing and the local fit to each other.
627633 // (*After* doing the update for borrow checker reasons.)
628634 if cfg ! ( debug_assertions) {
629635 let local_layout =
630- self . layout_of_local ( & self . stack ( ) [ frame ] , local, None ) ?;
636+ self . layout_of_local ( & self . frame ( ) , local, None ) ?;
631637 match ( src, local_layout. abi ) {
632638 ( Immediate :: Scalar ( scalar) , Abi :: Scalar ( s) ) => {
633639 assert_eq ! ( scalar. size( ) , s. size( self ) )
@@ -725,16 +731,17 @@ where
725731 ) -> InterpResult < ' tcx > {
726732 let mplace = match dest. as_mplace_or_local ( ) {
727733 Left ( mplace) => mplace,
728- Right ( ( frame , local, offset, layout) ) => {
734+ Right ( ( local, offset, locals_addr , layout) ) => {
729735 if offset. is_some ( ) {
730736 // This has been projected to a part of this local. We could have complicated
731737 // logic to still keep this local as an `Operand`... but it's much easier to
732738 // just fall back to the indirect path.
733739 // FIXME: share the logic with `write_immediate_no_validate`.
734740 dest. force_mplace ( self ) ?
735741 } else {
736- M :: before_access_local_mut ( self , frame, local) ?;
737- match self . stack_mut ( ) [ frame] . locals [ local] . access_mut ( ) ? {
742+ debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
743+ M :: before_access_local_mut ( self , self . frame_idx ( ) , local) ?;
744+ match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
738745 Operand :: Immediate ( local) => {
739746 * local = Immediate :: Uninit ;
740747 return Ok ( ( ) ) ;
@@ -912,17 +919,17 @@ where
912919 place : & PlaceTy < ' tcx , M :: Provenance > ,
913920 ) -> InterpResult < ' tcx , MPlaceTy < ' tcx , M :: Provenance > > {
914921 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 ( ) ? {
922+ Place :: Local { local, offset, locals_addr } => {
923+ debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
924+ M :: before_access_local_mut ( self , self . frame_idx ( ) , local) ?;
925+ let whole_local = match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
918926 & mut Operand :: Immediate ( local_val) => {
919927 // We need to make an allocation.
920928
921929 // We need the layout of the local. We can NOT use the layout we got,
922930 // that might e.g., be an inner field of a struct with `Scalar` layout,
923931 // that has different alignment than the outer field.
924- let local_layout =
925- self . layout_of_local ( & self . stack ( ) [ frame] , local, None ) ?;
932+ let local_layout = self . layout_of_local ( & self . frame ( ) , local, None ) ?;
926933 assert ! ( local_layout. is_sized( ) , "unsized locals cannot be immediate" ) ;
927934 let mplace = self . allocate ( local_layout, MemoryKind :: Stack ) ?;
928935 // Preserve old value. (As an optimization, we can skip this if it was uninit.)
@@ -936,11 +943,11 @@ where
936943 mplace. mplace ,
937944 ) ?;
938945 }
939- M :: after_local_allocated ( self , frame , local, & mplace) ?;
946+ M :: after_local_allocated ( self , self . frame_idx ( ) , local, & mplace) ?;
940947 // Now we can call `access_mut` again, asserting it goes well, and actually
941948 // overwrite things. This points to the entire allocation, not just the part
942949 // the place refers to, i.e. we do this before we apply `offset`.
943- * self . stack_mut ( ) [ frame ] . locals [ local] . access_mut ( ) . unwrap ( ) =
950+ * self . frame_mut ( ) . locals [ local] . access_mut ( ) . unwrap ( ) =
944951 Operand :: Indirect ( mplace. mplace ) ;
945952 mplace. mplace
946953 }
0 commit comments