@@ -108,34 +108,51 @@ pub enum StackPopCleanup {
108
108
/// State of a local variable including a memoized layout
109
109
#[ derive( Clone , PartialEq , Eq ) ]
110
110
pub struct LocalState < ' tcx , Tag =( ) , Id =AllocId > {
111
- pub state : LocalValue < Tag , Id > ,
111
+ pub value : LocalValue < Tag , Id > ,
112
112
/// Don't modify if `Some`, this is only used to prevent computing the layout twice
113
113
pub layout : Cell < Option < TyLayout < ' tcx > > > ,
114
114
}
115
115
116
- /// State of a local variable
117
- #[ derive( Copy , Clone , PartialEq , Eq , Hash ) ]
116
+ /// Current value of a local variable
117
+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , Debug ) ]
118
118
pub enum LocalValue < Tag =( ) , Id =AllocId > {
119
+ /// This local is not currently alive, and cannot be used at all.
119
120
Dead ,
120
- // Mostly for convenience, we re-use the `Operand` type here.
121
- // This is an optimization over just always having a pointer here;
122
- // we can thus avoid doing an allocation when the local just stores
123
- // immediate values *and* never has its address taken.
121
+ /// This local is alive but not yet initialized. It can be written to
122
+ /// but not read from or its address taken. Locals get initialized on
123
+ /// first write because for unsized locals, we do not know their size
124
+ /// before that.
125
+ Uninitialized ,
126
+ /// A normal, live local.
127
+ /// Mostly for convenience, we re-use the `Operand` type here.
128
+ /// This is an optimization over just always having a pointer here;
129
+ /// we can thus avoid doing an allocation when the local just stores
130
+ /// immediate values *and* never has its address taken.
124
131
Live ( Operand < Tag , Id > ) ,
125
132
}
126
133
127
- impl < ' tcx , Tag > LocalState < ' tcx , Tag > {
128
- pub fn access ( & self ) -> EvalResult < ' tcx , & Operand < Tag > > {
129
- match self . state {
134
+ impl < ' tcx , Tag : Copy + ' static > LocalState < ' tcx , Tag > {
135
+ pub fn access ( & self ) -> EvalResult < ' tcx , Operand < Tag > > {
136
+ match self . value {
130
137
LocalValue :: Dead => err ! ( DeadLocal ) ,
131
- LocalValue :: Live ( ref val) => Ok ( val) ,
138
+ LocalValue :: Uninitialized =>
139
+ bug ! ( "The type checker should prevent reading from a never-written local" ) ,
140
+ LocalValue :: Live ( val) => Ok ( val) ,
132
141
}
133
142
}
134
143
135
- pub fn access_mut ( & mut self ) -> EvalResult < ' tcx , & mut Operand < Tag > > {
136
- match self . state {
144
+ /// Overwrite the local. If the local can be overwritten in place, return a reference
145
+ /// to do so; otherwise return the `MemPlace` to consult instead.
146
+ pub fn access_mut (
147
+ & mut self ,
148
+ ) -> EvalResult < ' tcx , Result < & mut LocalValue < Tag > , MemPlace < Tag > > > {
149
+ match self . value {
137
150
LocalValue :: Dead => err ! ( DeadLocal ) ,
138
- LocalValue :: Live ( ref mut val) => Ok ( val) ,
151
+ LocalValue :: Live ( Operand :: Indirect ( mplace) ) => Ok ( Err ( mplace) ) ,
152
+ ref mut local @ LocalValue :: Live ( Operand :: Immediate ( _) ) |
153
+ ref mut local @ LocalValue :: Uninitialized => {
154
+ Ok ( Ok ( local) )
155
+ }
139
156
}
140
157
}
141
158
}
@@ -327,6 +344,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
327
344
let local_ty = self . monomorphize_with_substs ( local_ty, frame. instance . substs ) ;
328
345
self . layout_of ( local_ty)
329
346
} ) ?;
347
+ // Layouts of locals are requested a lot, so we cache them.
330
348
frame. locals [ local] . layout . set ( Some ( layout) ) ;
331
349
Ok ( layout)
332
350
}
@@ -473,19 +491,15 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
473
491
474
492
// don't allocate at all for trivial constants
475
493
if mir. local_decls . len ( ) > 1 {
476
- // We put some marker immediate into the locals that we later want to initialize.
477
- // This can be anything except for LocalValue::Dead -- because *that* is the
478
- // value we use for things that we know are initially dead.
494
+ // Locals are initially uninitialized.
479
495
let dummy = LocalState {
480
- state : LocalValue :: Live ( Operand :: Immediate ( Immediate :: Scalar (
481
- ScalarMaybeUndef :: Undef ,
482
- ) ) ) ,
496
+ value : LocalValue :: Uninitialized ,
483
497
layout : Cell :: new ( None ) ,
484
498
} ;
485
499
let mut locals = IndexVec :: from_elem ( dummy, & mir. local_decls ) ;
486
500
// Return place is handled specially by the `eval_place` functions, and the
487
501
// entry in `locals` should never be used. Make it dead, to be sure.
488
- locals[ mir:: RETURN_PLACE ] . state = LocalValue :: Dead ;
502
+ locals[ mir:: RETURN_PLACE ] . value = LocalValue :: Dead ;
489
503
// Now mark those locals as dead that we do not want to initialize
490
504
match self . tcx . describe_def ( instance. def_id ( ) ) {
491
505
// statics and constants don't have `Storage*` statements, no need to look for them
@@ -498,29 +512,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
498
512
match stmt. kind {
499
513
StorageLive ( local) |
500
514
StorageDead ( local) => {
501
- locals[ local] . state = LocalValue :: Dead ;
515
+ locals[ local] . value = LocalValue :: Dead ;
502
516
}
503
517
_ => { }
504
518
}
505
519
}
506
520
}
507
521
} ,
508
522
}
509
- // Finally, properly initialize all those that still have the dummy value
510
- for ( idx, local) in locals. iter_enumerated_mut ( ) {
511
- match local. state {
512
- LocalValue :: Live ( _) => {
513
- // This needs to be properly initialized.
514
- let ty = self . monomorphize ( mir. local_decls [ idx] . ty ) ?;
515
- let layout = self . layout_of ( ty) ?;
516
- local. state = LocalValue :: Live ( self . uninit_operand ( layout) ?) ;
517
- local. layout = Cell :: new ( Some ( layout) ) ;
518
- }
519
- LocalValue :: Dead => {
520
- // Nothing to do
521
- }
522
- }
523
- }
524
523
// done
525
524
self . frame_mut ( ) . locals = locals;
526
525
}
@@ -555,7 +554,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
555
554
}
556
555
// Deallocate all locals that are backed by an allocation.
557
556
for local in frame. locals {
558
- self . deallocate_local ( local. state ) ?;
557
+ self . deallocate_local ( local. value ) ?;
559
558
}
560
559
// Validate the return value. Do this after deallocating so that we catch dangling
561
560
// references.
@@ -603,10 +602,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
603
602
assert ! ( local != mir:: RETURN_PLACE , "Cannot make return place live" ) ;
604
603
trace ! ( "{:?} is now live" , local) ;
605
604
606
- let layout = self . layout_of_local ( self . frame ( ) , local, None ) ?;
607
- let init = LocalValue :: Live ( self . uninit_operand ( layout) ?) ;
605
+ let local_val = LocalValue :: Uninitialized ;
608
606
// StorageLive *always* kills the value that's currently stored
609
- Ok ( mem:: replace ( & mut self . frame_mut ( ) . locals [ local] . state , init ) )
607
+ Ok ( mem:: replace ( & mut self . frame_mut ( ) . locals [ local] . value , local_val ) )
610
608
}
611
609
612
610
/// Returns the old value of the local.
@@ -615,7 +613,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
615
613
assert ! ( local != mir:: RETURN_PLACE , "Cannot make return place dead" ) ;
616
614
trace ! ( "{:?} is now dead" , local) ;
617
615
618
- mem:: replace ( & mut self . frame_mut ( ) . locals [ local] . state , LocalValue :: Dead )
616
+ mem:: replace ( & mut self . frame_mut ( ) . locals [ local] . value , LocalValue :: Dead )
619
617
}
620
618
621
619
pub ( super ) fn deallocate_local (
@@ -668,31 +666,31 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tc
668
666
}
669
667
write ! ( msg, ":" ) . unwrap ( ) ;
670
668
671
- match self . stack [ frame] . locals [ local] . access ( ) {
672
- Err ( err) => {
673
- if let InterpError :: DeadLocal = err. kind {
674
- write ! ( msg, " is dead" ) . unwrap ( ) ;
675
- } else {
676
- panic ! ( "Failed to access local: {:?}" , err) ;
677
- }
678
- }
679
- Ok ( Operand :: Indirect ( mplace) ) => {
680
- let ( ptr, align) = mplace. to_scalar_ptr_align ( ) ;
681
- match ptr {
669
+ match self . stack [ frame] . locals [ local] . value {
670
+ LocalValue :: Dead => write ! ( msg, " is dead" ) . unwrap ( ) ,
671
+ LocalValue :: Uninitialized => write ! ( msg, " is uninitialized" ) . unwrap ( ) ,
672
+ LocalValue :: Live ( Operand :: Indirect ( mplace) ) => {
673
+ match mplace. ptr {
682
674
Scalar :: Ptr ( ptr) => {
683
- write ! ( msg, " by align({}) ref:" , align. bytes( ) ) . unwrap ( ) ;
675
+ write ! ( msg, " by align({}){} ref:" ,
676
+ mplace. align. bytes( ) ,
677
+ match mplace. meta {
678
+ Some ( meta) => format!( " meta({:?})" , meta) ,
679
+ None => String :: new( )
680
+ }
681
+ ) . unwrap ( ) ;
684
682
allocs. push ( ptr. alloc_id ) ;
685
683
}
686
684
ptr => write ! ( msg, " by integral ref: {:?}" , ptr) . unwrap ( ) ,
687
685
}
688
686
}
689
- Ok ( Operand :: Immediate ( Immediate :: Scalar ( val) ) ) => {
687
+ LocalValue :: Live ( Operand :: Immediate ( Immediate :: Scalar ( val) ) ) => {
690
688
write ! ( msg, " {:?}" , val) . unwrap ( ) ;
691
689
if let ScalarMaybeUndef :: Scalar ( Scalar :: Ptr ( ptr) ) = val {
692
690
allocs. push ( ptr. alloc_id ) ;
693
691
}
694
692
}
695
- Ok ( Operand :: Immediate ( Immediate :: ScalarPair ( val1, val2) ) ) => {
693
+ LocalValue :: Live ( Operand :: Immediate ( Immediate :: ScalarPair ( val1, val2) ) ) => {
696
694
write ! ( msg, " ({:?}, {:?})" , val1, val2) . unwrap ( ) ;
697
695
if let ScalarMaybeUndef :: Scalar ( Scalar :: Ptr ( ptr) ) = val1 {
698
696
allocs. push ( ptr. alloc_id ) ;
0 commit comments