@@ -187,11 +187,13 @@ pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
187
187
/// where in the local this place is located; if it is `None`, no projection has been applied.
188
188
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
189
189
/// (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`.
192
194
///
193
195
/// 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 } ,
195
197
}
196
198
197
199
/// An evaluated place, together with its type.
@@ -233,10 +235,10 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
233
235
#[ inline( always) ]
234
236
pub fn as_mplace_or_local (
235
237
& self ,
236
- ) -> Either < MPlaceTy < ' tcx , Prov > , ( usize , mir:: Local , Option < Size > ) > {
238
+ ) -> Either < MPlaceTy < ' tcx , Prov > , ( mir:: Local , Option < Size > , usize ) > {
237
239
match self . place {
238
240
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 ) ) ,
240
242
}
241
243
}
242
244
@@ -279,7 +281,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
279
281
) -> InterpResult < ' tcx , Self > {
280
282
Ok ( match self . as_mplace_or_local ( ) {
281
283
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 ) ) => {
283
285
debug_assert ! ( layout. is_sized( ) , "unsized locals should live in memory" ) ;
284
286
assert_matches ! ( meta, MemPlaceMeta :: None ) ; // we couldn't store it anyway...
285
287
// `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> {
292
294
. offset ( old_offset. unwrap_or ( Size :: ZERO ) . bytes ( ) , offset. bytes ( ) ) ?,
293
295
) ;
294
296
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
+ }
296
301
}
297
302
} )
298
303
}
@@ -331,7 +336,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
331
336
pub trait Writeable < ' tcx , Prov : Provenance > : Projectable < ' tcx , Prov > {
332
337
fn as_mplace_or_local (
333
338
& 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 > ) > ;
335
340
336
341
fn force_mplace < ' mir , M : Machine < ' mir , ' tcx , Provenance = Prov > > (
337
342
& self ,
@@ -343,9 +348,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
343
348
#[ inline( always) ]
344
349
fn as_mplace_or_local (
345
350
& 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 > ) > {
347
352
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 ) )
349
354
}
350
355
351
356
#[ inline( always) ]
@@ -361,7 +366,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
361
366
#[ inline( always) ]
362
367
fn as_mplace_or_local (
363
368
& 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 > ) > {
365
370
Left ( self . clone ( ) )
366
371
}
367
372
@@ -501,21 +506,21 @@ where
501
506
Ok ( ( mplace, len) )
502
507
}
503
508
509
+ /// Turn a local in the current frame into a place.
504
510
pub fn local_to_place (
505
511
& self ,
506
- frame : usize ,
507
512
local : mir:: Local ,
508
513
) -> InterpResult < ' tcx , PlaceTy < ' tcx , M :: Provenance > > {
509
514
// Other parts of the system rely on `Place::Local` never being unsized.
510
515
// 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 ) ?;
513
518
let place = if layout. is_sized ( ) {
514
519
// 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 ( ) }
516
521
} else {
517
522
// Unsized `Local` isn't okay (we cannot store the metadata).
518
- match frame_ref . locals [ local] . access ( ) ? {
523
+ match frame . locals [ local] . access ( ) ? {
519
524
Operand :: Immediate ( _) => bug ! ( ) ,
520
525
Operand :: Indirect ( mplace) => Place :: Ptr ( * mplace) ,
521
526
}
@@ -530,7 +535,7 @@ where
530
535
& self ,
531
536
mir_place : mir:: Place < ' tcx > ,
532
537
) -> 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 ) ?;
534
539
// Using `try_fold` turned out to be bad for performance, hence the loop.
535
540
for elem in mir_place. projection . iter ( ) {
536
541
place = self . project ( & place, elem) ?
@@ -611,23 +616,23 @@ where
611
616
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
612
617
// but not factored as a separate function.
613
618
let mplace = match dest. as_mplace_or_local ( ) {
614
- Right ( ( frame , local, offset, layout) ) => {
619
+ Right ( ( local, offset, locals_addr , layout) ) => {
615
620
if offset. is_some ( ) {
616
621
// This has been projected to a part of this local. We could have complicated
617
622
// logic to still keep this local as an `Operand`... but it's much easier to
618
623
// just fall back to the indirect path.
619
624
dest. force_mplace ( self ) ?
620
625
} 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 ( ) ? {
623
628
Operand :: Immediate ( local_val) => {
624
629
// Local can be updated in-place.
625
630
* local_val = src;
626
631
// Double-check that the value we are storing and the local fit to each other.
627
632
// (*After* doing the update for borrow checker reasons.)
628
633
if cfg ! ( debug_assertions) {
629
634
let local_layout =
630
- self . layout_of_local ( & self . stack ( ) [ frame ] , local, None ) ?;
635
+ self . layout_of_local ( & self . frame ( ) , local, None ) ?;
631
636
match ( src, local_layout. abi ) {
632
637
( Immediate :: Scalar ( scalar) , Abi :: Scalar ( s) ) => {
633
638
assert_eq ! ( scalar. size( ) , s. size( self ) )
@@ -725,16 +730,16 @@ where
725
730
) -> InterpResult < ' tcx > {
726
731
let mplace = match dest. as_mplace_or_local ( ) {
727
732
Left ( mplace) => mplace,
728
- Right ( ( frame , local, offset, layout) ) => {
733
+ Right ( ( local, offset, locals_addr , layout) ) => {
729
734
if offset. is_some ( ) {
730
735
// This has been projected to a part of this local. We could have complicated
731
736
// logic to still keep this local as an `Operand`... but it's much easier to
732
737
// just fall back to the indirect path.
733
738
// FIXME: share the logic with `write_immediate_no_validate`.
734
739
dest. force_mplace ( self ) ?
735
740
} 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 ( ) ? {
738
743
Operand :: Immediate ( local) => {
739
744
* local = Immediate :: Uninit ;
740
745
return Ok ( ( ) ) ;
@@ -912,17 +917,16 @@ where
912
917
place : & PlaceTy < ' tcx , M :: Provenance > ,
913
918
) -> InterpResult < ' tcx , MPlaceTy < ' tcx , M :: Provenance > > {
914
919
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 ( ) ? {
918
923
& mut Operand :: Immediate ( local_val) => {
919
924
// We need to make an allocation.
920
925
921
926
// We need the layout of the local. We can NOT use the layout we got,
922
927
// that might e.g., be an inner field of a struct with `Scalar` layout,
923
928
// 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 ) ?;
926
930
assert ! ( local_layout. is_sized( ) , "unsized locals cannot be immediate" ) ;
927
931
let mplace = self . allocate ( local_layout, MemoryKind :: Stack ) ?;
928
932
// Preserve old value. (As an optimization, we can skip this if it was uninit.)
@@ -936,11 +940,11 @@ where
936
940
mplace. mplace ,
937
941
) ?;
938
942
}
939
- M :: after_local_allocated ( self , frame , local, & mplace) ?;
943
+ M :: after_local_allocated ( self , local, & mplace) ?;
940
944
// Now we can call `access_mut` again, asserting it goes well, and actually
941
945
// overwrite things. This points to the entire allocation, not just the part
942
946
// 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 ( ) =
944
948
Operand :: Indirect ( mplace. mplace ) ;
945
949
mplace. mplace
946
950
}
0 commit comments