1- use std:: collections:: HashMap ;
1+ use std:: collections:: { HashMap , HashSet } ;
22use std:: fmt:: Write ;
33
44use rustc:: hir:: def_id:: DefId ;
@@ -74,11 +74,12 @@ pub struct Frame<'tcx> {
7474 pub return_lvalue : Lvalue < ' tcx > ,
7575
7676 /// The list of locals for this stack frame, stored in order as
77- /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which
77+ /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option<Value>`s.
78+ /// `None` represents a local that is currently dead, while a live local
7879 /// can either directly contain `PrimVal` or refer to some part of an `Allocation`.
7980 ///
80- /// Before being initialized, all locals are `Value::ByVal(PrimVal::Undef)`.
81- pub locals : Vec < Value > ,
81+ /// Before being initialized, arguments are `Value::ByVal(PrimVal::Undef)` and other locals are `None `.
82+ pub locals : Vec < Option < Value > > ,
8283
8384 ////////////////////////////////////////////////////////////////////////////////
8485 // Current position within the function
@@ -452,10 +453,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
452453 ) -> EvalResult < ' tcx > {
453454 :: log_settings:: settings ( ) . indentation += 1 ;
454455
456+ /// Return the set of locals that have a storage annotation anywhere
457+ fn collect_storage_annotations < ' tcx > ( mir : & ' tcx mir:: Mir < ' tcx > ) -> HashSet < mir:: Local > {
458+ use rustc:: mir:: StatementKind :: * ;
459+
460+ let mut set = HashSet :: new ( ) ;
461+ for block in mir. basic_blocks ( ) {
462+ for stmt in block. statements . iter ( ) {
463+ match stmt. kind {
464+ StorageLive ( mir:: Lvalue :: Local ( local) ) | StorageDead ( mir:: Lvalue :: Local ( local) ) => {
465+ set. insert ( local) ;
466+ }
467+ _ => { }
468+ }
469+ }
470+ } ;
471+ set
472+ }
473+
455474 // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local
456475 // `Value` for that.
476+ let annotated_locals = collect_storage_annotations ( mir) ;
457477 let num_locals = mir. local_decls . len ( ) - 1 ;
458- let locals = vec ! [ Value :: ByVal ( PrimVal :: Undef ) ; num_locals] ;
478+ let mut locals = vec ! [ None ; num_locals] ;
479+ for i in 0 ..num_locals {
480+ let local = mir:: Local :: new ( i+1 ) ;
481+ if !annotated_locals. contains ( & local) {
482+ locals[ i] = Some ( Value :: ByVal ( PrimVal :: Undef ) ) ;
483+ }
484+ }
459485
460486 self . stack . push ( Frame {
461487 mir,
@@ -509,21 +535,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
509535 }
510536 // deallocate all locals that are backed by an allocation
511537 for local in frame. locals {
512- if let Value :: ByRef ( ptr) = local {
513- trace ! ( "deallocating local" ) ;
514- self . memory . dump_alloc ( ptr. alloc_id ) ;
515- match self . memory . deallocate ( ptr) {
516- // We could alternatively check whether the alloc_id is static before calling
517- // deallocate, but this is much simpler and is probably the rare case.
518- Ok ( ( ) ) | Err ( EvalError :: DeallocatedStaticMemory ) => { } ,
519- other => return other,
520- }
521- }
538+ self . deallocate_local ( local) ?;
522539 }
523540
524541 Ok ( ( ) )
525542 }
526543
544+ pub fn deallocate_local ( & mut self , local : Option < Value > ) -> EvalResult < ' tcx > {
545+ if let Some ( Value :: ByRef ( ptr) ) = local {
546+ trace ! ( "deallocating local" ) ;
547+ self . memory . dump_alloc ( ptr. alloc_id ) ;
548+ match self . memory . deallocate ( ptr) {
549+ // We could alternatively check whether the alloc_id is static before calling
550+ // deallocate, but this is much simpler and is probably the rare case.
551+ Ok ( ( ) ) | Err ( EvalError :: DeallocatedStaticMemory ) => { } ,
552+ other => return other,
553+ }
554+ } ;
555+ Ok ( ( ) )
556+ }
557+
527558 pub fn assign_discr_and_fields <
528559 V : IntoValTyPair < ' tcx > ,
529560 J : IntoIterator < Item = V > ,
@@ -1047,16 +1078,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10471078 Lvalue :: Local { frame, local, field } => {
10481079 // -1 since we don't store the return value
10491080 match self . stack [ frame] . locals [ local. index ( ) - 1 ] {
1050- Value :: ByRef ( ptr) => {
1081+ None => return Err ( EvalError :: DeadLocal ) ,
1082+ Some ( Value :: ByRef ( ptr) ) => {
10511083 assert ! ( field. is_none( ) ) ;
10521084 Lvalue :: from_ptr ( ptr)
10531085 } ,
1054- val => {
1086+ Some ( val) => {
10551087 let ty = self . stack [ frame] . mir . local_decls [ local] . ty ;
10561088 let ty = self . monomorphize ( ty, self . stack [ frame] . instance . substs ) ;
10571089 let substs = self . stack [ frame] . instance . substs ;
10581090 let ptr = self . alloc_ptr_with_substs ( ty, substs) ?;
1059- self . stack [ frame] . locals [ local. index ( ) - 1 ] = Value :: ByRef ( ptr) ;
1091+ self . stack [ frame] . locals [ local. index ( ) - 1 ] = Some ( Value :: ByRef ( ptr) ) ; // it stays live
10601092 self . write_value_to_ptr ( val, ptr, ty) ?;
10611093 let lval = Lvalue :: from_ptr ( ptr) ;
10621094 if let Some ( ( field, field_ty) ) = field {
@@ -1139,7 +1171,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11391171 * this. globals . get_mut ( & cid) . expect ( "already checked" ) = Global {
11401172 value : val,
11411173 ..dest
1142- }
1174+ } ;
1175+ Ok ( ( ) )
11431176 } ;
11441177 self . write_value_possibly_by_val ( src_val, write_dest, dest. value , dest_ty)
11451178 } ,
@@ -1150,7 +1183,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11501183 }
11511184
11521185 Lvalue :: Local { frame, local, field } => {
1153- let dest = self . stack [ frame] . get_local ( local, field. map ( |( i, _) | i) ) ;
1186+ let dest = self . stack [ frame] . get_local ( local, field. map ( |( i, _) | i) ) ? ;
11541187 self . write_value_possibly_by_val (
11551188 src_val,
11561189 |this, val| this. stack [ frame] . set_local ( local, field. map ( |( i, _) | i) , val) ,
@@ -1162,7 +1195,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11621195 }
11631196
11641197 // The cases here can be a bit subtle. Read carefully!
1165- fn write_value_possibly_by_val < F : FnOnce ( & mut Self , Value ) > (
1198+ fn write_value_possibly_by_val < F : FnOnce ( & mut Self , Value ) -> EvalResult < ' tcx > > (
11661199 & mut self ,
11671200 src_val : Value ,
11681201 write_dest : F ,
@@ -1192,17 +1225,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11921225 // source and write that into the destination without making an allocation, so
11931226 // we do so here.
11941227 if let Ok ( Some ( src_val) ) = self . try_read_value ( src_ptr, dest_ty) {
1195- write_dest ( self , src_val) ;
1228+ write_dest ( self , src_val) ? ;
11961229 } else {
11971230 let dest_ptr = self . alloc_ptr ( dest_ty) ?;
11981231 self . copy ( src_ptr, dest_ptr, dest_ty) ?;
1199- write_dest ( self , Value :: ByRef ( dest_ptr) ) ;
1232+ write_dest ( self , Value :: ByRef ( dest_ptr) ) ? ;
12001233 }
12011234
12021235 } else {
12031236 // Finally, we have the simple case where neither source nor destination are
12041237 // `ByRef`. We may simply copy the source value over the the destintion.
1205- write_dest ( self , src_val) ;
1238+ write_dest ( self , src_val) ? ;
12061239 }
12071240 Ok ( ( ) )
12081241 }
@@ -1572,14 +1605,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
15721605 write ! ( msg, ":" ) . unwrap ( ) ;
15731606
15741607 match self . stack [ frame] . get_local ( local, field. map ( |( i, _) | i) ) {
1575- Value :: ByRef ( ptr) => {
1608+ Err ( EvalError :: DeadLocal ) => {
1609+ write ! ( msg, " is dead" ) . unwrap ( ) ;
1610+ }
1611+ Err ( err) => {
1612+ panic ! ( "Failed to access local: {:?}" , err) ;
1613+ }
1614+ Ok ( Value :: ByRef ( ptr) ) => {
15761615 allocs. push ( ptr. alloc_id ) ;
15771616 }
1578- Value :: ByVal ( val) => {
1617+ Ok ( Value :: ByVal ( val) ) => {
15791618 write ! ( msg, " {:?}" , val) . unwrap ( ) ;
15801619 if let PrimVal :: Ptr ( ptr) = val { allocs. push ( ptr. alloc_id ) ; }
15811620 }
1582- Value :: ByValPair ( val1, val2) => {
1621+ Ok ( Value :: ByValPair ( val1, val2) ) => {
15831622 write ! ( msg, " ({:?}, {:?})" , val1, val2) . unwrap ( ) ;
15841623 if let PrimVal :: Ptr ( ptr) = val1 { allocs. push ( ptr. alloc_id ) ; }
15851624 if let PrimVal :: Ptr ( ptr) = val2 { allocs. push ( ptr. alloc_id ) ; }
@@ -1614,9 +1653,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
16141653 ) -> EvalResult < ' tcx >
16151654 where F : FnOnce ( & mut Self , Value ) -> EvalResult < ' tcx , Value > ,
16161655 {
1617- let val = self . stack [ frame] . get_local ( local, field) ;
1656+ let val = self . stack [ frame] . get_local ( local, field) ? ;
16181657 let new_val = f ( self , val) ?;
1619- self . stack [ frame] . set_local ( local, field, new_val) ;
1658+ self . stack [ frame] . set_local ( local, field, new_val) ? ;
16201659 // FIXME(solson): Run this when setting to Undef? (See previous version of this code.)
16211660 // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) {
16221661 // self.memory.deallocate(ptr)?;
@@ -1626,53 +1665,76 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
16261665}
16271666
16281667impl < ' tcx > Frame < ' tcx > {
1629- pub fn get_local ( & self , local : mir:: Local , field : Option < usize > ) -> Value {
1668+ pub fn get_local ( & self , local : mir:: Local , field : Option < usize > ) -> EvalResult < ' tcx , Value > {
16301669 // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
16311670 if let Some ( field) = field {
1632- match self . locals [ local. index ( ) - 1 ] {
1633- Value :: ByRef ( _) => bug ! ( "can't have lvalue fields for ByRef" ) ,
1634- val @ Value :: ByVal ( _) => {
1671+ Ok ( match self . locals [ local. index ( ) - 1 ] {
1672+ None => return Err ( EvalError :: DeadLocal ) ,
1673+ Some ( Value :: ByRef ( _) ) => bug ! ( "can't have lvalue fields for ByRef" ) ,
1674+ Some ( val @ Value :: ByVal ( _) ) => {
16351675 assert_eq ! ( field, 0 ) ;
16361676 val
16371677 } ,
1638- Value :: ByValPair ( a, b) => {
1678+ Some ( Value :: ByValPair ( a, b) ) => {
16391679 match field {
16401680 0 => Value :: ByVal ( a) ,
16411681 1 => Value :: ByVal ( b) ,
16421682 _ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
16431683 }
16441684 } ,
1645- }
1685+ } )
16461686 } else {
1647- self . locals [ local. index ( ) - 1 ]
1687+ self . locals [ local. index ( ) - 1 ] . ok_or ( EvalError :: DeadLocal )
16481688 }
16491689 }
16501690
1651- fn set_local ( & mut self , local : mir:: Local , field : Option < usize > , value : Value ) {
1691+ fn set_local ( & mut self , local : mir:: Local , field : Option < usize > , value : Value ) -> EvalResult < ' tcx > {
16521692 // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
16531693 if let Some ( field) = field {
16541694 match self . locals [ local. index ( ) - 1 ] {
1655- Value :: ByRef ( _) => bug ! ( "can't have lvalue fields for ByRef" ) ,
1656- Value :: ByVal ( _) => {
1695+ None => return Err ( EvalError :: DeadLocal ) ,
1696+ Some ( Value :: ByRef ( _) ) => bug ! ( "can't have lvalue fields for ByRef" ) ,
1697+ Some ( Value :: ByVal ( _) ) => {
16571698 assert_eq ! ( field, 0 ) ;
1658- self . set_local ( local, None , value) ;
1699+ self . set_local ( local, None , value) ? ;
16591700 } ,
1660- Value :: ByValPair ( a, b) => {
1701+ Some ( Value :: ByValPair ( a, b) ) => {
16611702 let prim = match value {
16621703 Value :: ByRef ( _) => bug ! ( "can't set ValPair field to ByRef" ) ,
16631704 Value :: ByVal ( val) => val,
16641705 Value :: ByValPair ( _, _) => bug ! ( "can't set ValPair field to ValPair" ) ,
16651706 } ;
16661707 match field {
1667- 0 => self . set_local ( local, None , Value :: ByValPair ( prim, b) ) ,
1668- 1 => self . set_local ( local, None , Value :: ByValPair ( a, prim) ) ,
1708+ 0 => self . set_local ( local, None , Value :: ByValPair ( prim, b) ) ? ,
1709+ 1 => self . set_local ( local, None , Value :: ByValPair ( a, prim) ) ? ,
16691710 _ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
16701711 }
16711712 } ,
16721713 }
16731714 } else {
1674- self . locals [ local. index ( ) - 1 ] = value;
1715+ match self . locals [ local. index ( ) - 1 ] {
1716+ None => return Err ( EvalError :: DeadLocal ) ,
1717+ Some ( ref mut local) => { * local = value; }
1718+ }
16751719 }
1720+ return Ok ( ( ) ) ;
1721+ }
1722+
1723+ pub fn storage_live ( & mut self , local : mir:: Local ) -> EvalResult < ' tcx , Option < Value > > {
1724+ trace ! ( "{:?} is now live" , local) ;
1725+
1726+ let old = self . locals [ local. index ( ) - 1 ] ;
1727+ self . locals [ local. index ( ) - 1 ] = Some ( Value :: ByVal ( PrimVal :: Undef ) ) ; // StorageLive *always* kills the value that's currently stored
1728+ return Ok ( old) ;
1729+ }
1730+
1731+ /// Returns the old value of the local
1732+ pub fn storage_dead ( & mut self , local : mir:: Local ) -> EvalResult < ' tcx , Option < Value > > {
1733+ trace ! ( "{:?} is now dead" , local) ;
1734+
1735+ let old = self . locals [ local. index ( ) - 1 ] ;
1736+ self . locals [ local. index ( ) - 1 ] = None ;
1737+ return Ok ( old) ;
16761738 }
16771739}
16781740
0 commit comments