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,33 @@ 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 stroage 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 :: with_capacity ( num_locals) ;
479+ for i in 0 ..num_locals {
480+ let local = mir:: Local :: new ( i+1 ) ;
481+ locals. push ( if annotated_locals. contains ( & local) { None } else { Some ( Value :: ByVal ( PrimVal :: Undef ) ) } ) ;
482+ }
459483
460484 self . stack . push ( Frame {
461485 mir,
@@ -509,21 +533,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
509533 }
510534 // deallocate all locals that are backed by an allocation
511535 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- }
536+ self . deallocate_local ( local) ?;
522537 }
523538
524539 Ok ( ( ) )
525540 }
526541
542+ pub fn deallocate_local ( & mut self , local : Option < Value > ) -> EvalResult < ' tcx > {
543+ if let Some ( Value :: ByRef ( ptr) ) = local {
544+ trace ! ( "deallocating local" ) ;
545+ self . memory . dump_alloc ( ptr. alloc_id ) ;
546+ match self . memory . deallocate ( ptr) {
547+ // We could alternatively check whether the alloc_id is static before calling
548+ // deallocate, but this is much simpler and is probably the rare case.
549+ Ok ( ( ) ) | Err ( EvalError :: DeallocatedStaticMemory ) => { } ,
550+ other => return other,
551+ }
552+ } ;
553+ Ok ( ( ) )
554+ }
555+
527556 pub fn assign_discr_and_fields <
528557 V : IntoValTyPair < ' tcx > ,
529558 J : IntoIterator < Item = V > ,
@@ -1047,16 +1076,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10471076 Lvalue :: Local { frame, local, field } => {
10481077 // -1 since we don't store the return value
10491078 match self . stack [ frame] . locals [ local. index ( ) - 1 ] {
1050- Value :: ByRef ( ptr) => {
1079+ None => return Err ( EvalError :: DeadLocal ) ,
1080+ Some ( Value :: ByRef ( ptr) ) => {
10511081 assert ! ( field. is_none( ) ) ;
10521082 Lvalue :: from_ptr ( ptr)
10531083 } ,
1054- val => {
1084+ Some ( val) => {
10551085 let ty = self . stack [ frame] . mir . local_decls [ local] . ty ;
10561086 let ty = self . monomorphize ( ty, self . stack [ frame] . instance . substs ) ;
10571087 let substs = self . stack [ frame] . instance . substs ;
10581088 let ptr = self . alloc_ptr_with_substs ( ty, substs) ?;
1059- self . stack [ frame] . locals [ local. index ( ) - 1 ] = Value :: ByRef ( ptr) ;
1089+ self . stack [ frame] . locals [ local. index ( ) - 1 ] = Some ( Value :: ByRef ( ptr) ) ; // it stays live
10601090 self . write_value_to_ptr ( val, ptr, ty) ?;
10611091 let lval = Lvalue :: from_ptr ( ptr) ;
10621092 if let Some ( ( field, field_ty) ) = field {
@@ -1139,7 +1169,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11391169 * this. globals . get_mut ( & cid) . expect ( "already checked" ) = Global {
11401170 value : val,
11411171 ..dest
1142- }
1172+ } ;
1173+ Ok ( ( ) )
11431174 } ;
11441175 self . write_value_possibly_by_val ( src_val, write_dest, dest. value , dest_ty)
11451176 } ,
@@ -1150,7 +1181,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11501181 }
11511182
11521183 Lvalue :: Local { frame, local, field } => {
1153- let dest = self . stack [ frame] . get_local ( local, field. map ( |( i, _) | i) ) ;
1184+ let dest = self . stack [ frame] . get_local ( local, field. map ( |( i, _) | i) ) ? ;
11541185 self . write_value_possibly_by_val (
11551186 src_val,
11561187 |this, val| this. stack [ frame] . set_local ( local, field. map ( |( i, _) | i) , val) ,
@@ -1162,7 +1193,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11621193 }
11631194
11641195 // The cases here can be a bit subtle. Read carefully!
1165- fn write_value_possibly_by_val < F : FnOnce ( & mut Self , Value ) > (
1196+ fn write_value_possibly_by_val < F : FnOnce ( & mut Self , Value ) -> EvalResult < ' tcx > > (
11661197 & mut self ,
11671198 src_val : Value ,
11681199 write_dest : F ,
@@ -1192,17 +1223,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11921223 // source and write that into the destination without making an allocation, so
11931224 // we do so here.
11941225 if let Ok ( Some ( src_val) ) = self . try_read_value ( src_ptr, dest_ty) {
1195- write_dest ( self , src_val) ;
1226+ write_dest ( self , src_val) ? ;
11961227 } else {
11971228 let dest_ptr = self . alloc_ptr ( dest_ty) ?;
11981229 self . copy ( src_ptr, dest_ptr, dest_ty) ?;
1199- write_dest ( self , Value :: ByRef ( dest_ptr) ) ;
1230+ write_dest ( self , Value :: ByRef ( dest_ptr) ) ? ;
12001231 }
12011232
12021233 } else {
12031234 // Finally, we have the simple case where neither source nor destination are
12041235 // `ByRef`. We may simply copy the source value over the the destintion.
1205- write_dest ( self , src_val) ;
1236+ write_dest ( self , src_val) ? ;
12061237 }
12071238 Ok ( ( ) )
12081239 }
@@ -1572,14 +1603,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
15721603 write ! ( msg, ":" ) . unwrap ( ) ;
15731604
15741605 match self . stack [ frame] . get_local ( local, field. map ( |( i, _) | i) ) {
1575- Value :: ByRef ( ptr) => {
1606+ Err ( EvalError :: DeadLocal ) => {
1607+ write ! ( msg, " is dead" ) . unwrap ( ) ;
1608+ }
1609+ Err ( err) => {
1610+ panic ! ( "Failed to access local: {:?}" , err) ;
1611+ }
1612+ Ok ( Value :: ByRef ( ptr) ) => {
15761613 allocs. push ( ptr. alloc_id ) ;
15771614 }
1578- Value :: ByVal ( val) => {
1615+ Ok ( Value :: ByVal ( val) ) => {
15791616 write ! ( msg, " {:?}" , val) . unwrap ( ) ;
15801617 if let PrimVal :: Ptr ( ptr) = val { allocs. push ( ptr. alloc_id ) ; }
15811618 }
1582- Value :: ByValPair ( val1, val2) => {
1619+ Ok ( Value :: ByValPair ( val1, val2) ) => {
15831620 write ! ( msg, " ({:?}, {:?})" , val1, val2) . unwrap ( ) ;
15841621 if let PrimVal :: Ptr ( ptr) = val1 { allocs. push ( ptr. alloc_id ) ; }
15851622 if let PrimVal :: Ptr ( ptr) = val2 { allocs. push ( ptr. alloc_id ) ; }
@@ -1614,9 +1651,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
16141651 ) -> EvalResult < ' tcx >
16151652 where F : FnOnce ( & mut Self , Value ) -> EvalResult < ' tcx , Value > ,
16161653 {
1617- let val = self . stack [ frame] . get_local ( local, field) ;
1654+ let val = self . stack [ frame] . get_local ( local, field) ? ;
16181655 let new_val = f ( self , val) ?;
1619- self . stack [ frame] . set_local ( local, field, new_val) ;
1656+ self . stack [ frame] . set_local ( local, field, new_val) ? ;
16201657 // FIXME(solson): Run this when setting to Undef? (See previous version of this code.)
16211658 // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) {
16221659 // self.memory.deallocate(ptr)?;
@@ -1626,53 +1663,79 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
16261663}
16271664
16281665impl < ' tcx > Frame < ' tcx > {
1629- pub fn get_local ( & self , local : mir:: Local , field : Option < usize > ) -> Value {
1666+ pub fn get_local ( & self , local : mir:: Local , field : Option < usize > ) -> EvalResult < ' tcx , Value > {
16301667 // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
16311668 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 ( _) => {
1669+ Ok ( match self . locals [ local. index ( ) - 1 ] {
1670+ None => return Err ( EvalError :: DeadLocal ) ,
1671+ Some ( Value :: ByRef ( _) ) => bug ! ( "can't have lvalue fields for ByRef" ) ,
1672+ Some ( val @ Value :: ByVal ( _) ) => {
16351673 assert_eq ! ( field, 0 ) ;
16361674 val
16371675 } ,
1638- Value :: ByValPair ( a, b) => {
1676+ Some ( Value :: ByValPair ( a, b) ) => {
16391677 match field {
16401678 0 => Value :: ByVal ( a) ,
16411679 1 => Value :: ByVal ( b) ,
16421680 _ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
16431681 }
16441682 } ,
1645- }
1683+ } )
16461684 } else {
1647- self . locals [ local. index ( ) - 1 ]
1685+ self . locals [ local. index ( ) - 1 ] . ok_or ( EvalError :: DeadLocal )
16481686 }
16491687 }
16501688
1651- fn set_local ( & mut self , local : mir:: Local , field : Option < usize > , value : Value ) {
1689+ fn set_local ( & mut self , local : mir:: Local , field : Option < usize > , value : Value ) -> EvalResult < ' tcx > {
16521690 // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
16531691 if let Some ( field) = field {
16541692 match self . locals [ local. index ( ) - 1 ] {
1655- Value :: ByRef ( _) => bug ! ( "can't have lvalue fields for ByRef" ) ,
1656- Value :: ByVal ( _) => {
1693+ None => return Err ( EvalError :: DeadLocal ) ,
1694+ Some ( Value :: ByRef ( _) ) => bug ! ( "can't have lvalue fields for ByRef" ) ,
1695+ Some ( Value :: ByVal ( _) ) => {
16571696 assert_eq ! ( field, 0 ) ;
1658- self . set_local ( local, None , value) ;
1697+ self . set_local ( local, None , value) ? ;
16591698 } ,
1660- Value :: ByValPair ( a, b) => {
1699+ Some ( Value :: ByValPair ( a, b) ) => {
16611700 let prim = match value {
16621701 Value :: ByRef ( _) => bug ! ( "can't set ValPair field to ByRef" ) ,
16631702 Value :: ByVal ( val) => val,
16641703 Value :: ByValPair ( _, _) => bug ! ( "can't set ValPair field to ValPair" ) ,
16651704 } ;
16661705 match field {
1667- 0 => self . set_local ( local, None , Value :: ByValPair ( prim, b) ) ,
1668- 1 => self . set_local ( local, None , Value :: ByValPair ( a, prim) ) ,
1706+ 0 => self . set_local ( local, None , Value :: ByValPair ( prim, b) ) ? ,
1707+ 1 => self . set_local ( local, None , Value :: ByValPair ( a, prim) ) ? ,
16691708 _ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
16701709 }
16711710 } ,
16721711 }
16731712 } else {
1674- self . locals [ local. index ( ) - 1 ] = value;
1713+ match self . locals [ local. index ( ) - 1 ] {
1714+ None => return Err ( EvalError :: DeadLocal ) ,
1715+ Some ( ref mut local) => { * local = value; }
1716+ }
1717+ }
1718+ return Ok ( ( ) ) ;
1719+ }
1720+
1721+ pub fn storage_live ( & mut self , local : mir:: Local ) -> EvalResult < ' tcx > {
1722+ trace ! ( "{:?} is now live" , local) ;
1723+ if self . locals [ local. index ( ) - 1 ] . is_some ( ) {
1724+ // The variables comes live now, but was already accessed previously, when it was still dead
1725+ return Err ( EvalError :: DeadLocal ) ;
1726+ } else {
1727+ self . locals [ local. index ( ) - 1 ] = Some ( Value :: ByVal ( PrimVal :: Undef ) ) ;
16751728 }
1729+ return Ok ( ( ) ) ;
1730+ }
1731+
1732+ /// Returns the old value of the local
1733+ pub fn storage_dead ( & mut self , local : mir:: Local ) -> EvalResult < ' tcx , Option < Value > > {
1734+ trace ! ( "{:?} is now dead" , local) ;
1735+
1736+ let old = self . locals [ local. index ( ) - 1 ] ;
1737+ self . locals [ local. index ( ) - 1 ] = None ;
1738+ return Ok ( old) ;
16761739 }
16771740}
16781741
0 commit comments