1
- use std:: collections:: HashMap ;
1
+ use std:: collections:: { HashMap , HashSet } ;
2
2
use std:: fmt:: Write ;
3
3
4
4
use rustc:: hir:: def_id:: DefId ;
@@ -74,11 +74,12 @@ pub struct Frame<'tcx> {
74
74
pub return_lvalue : Lvalue < ' tcx > ,
75
75
76
76
/// 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
78
79
/// can either directly contain `PrimVal` or refer to some part of an `Allocation`.
79
80
///
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 > > ,
82
83
83
84
////////////////////////////////////////////////////////////////////////////////
84
85
// Current position within the function
@@ -452,10 +453,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
452
453
) -> EvalResult < ' tcx > {
453
454
:: log_settings:: settings ( ) . indentation += 1 ;
454
455
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
+
455
474
// Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local
456
475
// `Value` for that.
476
+ let annotated_locals = collect_storage_annotations ( mir) ;
457
477
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
+ }
459
485
460
486
self . stack . push ( Frame {
461
487
mir,
@@ -509,21 +535,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
509
535
}
510
536
// deallocate all locals that are backed by an allocation
511
537
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) ?;
522
539
}
523
540
524
541
Ok ( ( ) )
525
542
}
526
543
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
+
527
558
pub fn assign_discr_and_fields <
528
559
V : IntoValTyPair < ' tcx > ,
529
560
J : IntoIterator < Item = V > ,
@@ -1047,16 +1078,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1047
1078
Lvalue :: Local { frame, local, field } => {
1048
1079
// -1 since we don't store the return value
1049
1080
match self . stack [ frame] . locals [ local. index ( ) - 1 ] {
1050
- Value :: ByRef ( ptr) => {
1081
+ None => return Err ( EvalError :: DeadLocal ) ,
1082
+ Some ( Value :: ByRef ( ptr) ) => {
1051
1083
assert ! ( field. is_none( ) ) ;
1052
1084
Lvalue :: from_ptr ( ptr)
1053
1085
} ,
1054
- val => {
1086
+ Some ( val) => {
1055
1087
let ty = self . stack [ frame] . mir . local_decls [ local] . ty ;
1056
1088
let ty = self . monomorphize ( ty, self . stack [ frame] . instance . substs ) ;
1057
1089
let substs = self . stack [ frame] . instance . substs ;
1058
1090
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
1060
1092
self . write_value_to_ptr ( val, ptr, ty) ?;
1061
1093
let lval = Lvalue :: from_ptr ( ptr) ;
1062
1094
if let Some ( ( field, field_ty) ) = field {
@@ -1139,7 +1171,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1139
1171
* this. globals . get_mut ( & cid) . expect ( "already checked" ) = Global {
1140
1172
value : val,
1141
1173
..dest
1142
- }
1174
+ } ;
1175
+ Ok ( ( ) )
1143
1176
} ;
1144
1177
self . write_value_possibly_by_val ( src_val, write_dest, dest. value , dest_ty)
1145
1178
} ,
@@ -1150,7 +1183,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1150
1183
}
1151
1184
1152
1185
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) ) ? ;
1154
1187
self . write_value_possibly_by_val (
1155
1188
src_val,
1156
1189
|this, val| this. stack [ frame] . set_local ( local, field. map ( |( i, _) | i) , val) ,
@@ -1162,7 +1195,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1162
1195
}
1163
1196
1164
1197
// 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 > > (
1166
1199
& mut self ,
1167
1200
src_val : Value ,
1168
1201
write_dest : F ,
@@ -1192,17 +1225,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1192
1225
// source and write that into the destination without making an allocation, so
1193
1226
// we do so here.
1194
1227
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) ? ;
1196
1229
} else {
1197
1230
let dest_ptr = self . alloc_ptr ( dest_ty) ?;
1198
1231
self . copy ( src_ptr, dest_ptr, dest_ty) ?;
1199
- write_dest ( self , Value :: ByRef ( dest_ptr) ) ;
1232
+ write_dest ( self , Value :: ByRef ( dest_ptr) ) ? ;
1200
1233
}
1201
1234
1202
1235
} else {
1203
1236
// Finally, we have the simple case where neither source nor destination are
1204
1237
// `ByRef`. We may simply copy the source value over the the destintion.
1205
- write_dest ( self , src_val) ;
1238
+ write_dest ( self , src_val) ? ;
1206
1239
}
1207
1240
Ok ( ( ) )
1208
1241
}
@@ -1572,14 +1605,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1572
1605
write ! ( msg, ":" ) . unwrap ( ) ;
1573
1606
1574
1607
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) ) => {
1576
1615
allocs. push ( ptr. alloc_id ) ;
1577
1616
}
1578
- Value :: ByVal ( val) => {
1617
+ Ok ( Value :: ByVal ( val) ) => {
1579
1618
write ! ( msg, " {:?}" , val) . unwrap ( ) ;
1580
1619
if let PrimVal :: Ptr ( ptr) = val { allocs. push ( ptr. alloc_id ) ; }
1581
1620
}
1582
- Value :: ByValPair ( val1, val2) => {
1621
+ Ok ( Value :: ByValPair ( val1, val2) ) => {
1583
1622
write ! ( msg, " ({:?}, {:?})" , val1, val2) . unwrap ( ) ;
1584
1623
if let PrimVal :: Ptr ( ptr) = val1 { allocs. push ( ptr. alloc_id ) ; }
1585
1624
if let PrimVal :: Ptr ( ptr) = val2 { allocs. push ( ptr. alloc_id ) ; }
@@ -1614,9 +1653,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1614
1653
) -> EvalResult < ' tcx >
1615
1654
where F : FnOnce ( & mut Self , Value ) -> EvalResult < ' tcx , Value > ,
1616
1655
{
1617
- let val = self . stack [ frame] . get_local ( local, field) ;
1656
+ let val = self . stack [ frame] . get_local ( local, field) ? ;
1618
1657
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) ? ;
1620
1659
// FIXME(solson): Run this when setting to Undef? (See previous version of this code.)
1621
1660
// if let Value::ByRef(ptr) = self.stack[frame].get_local(local) {
1622
1661
// self.memory.deallocate(ptr)?;
@@ -1626,53 +1665,76 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1626
1665
}
1627
1666
1628
1667
impl < ' 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 > {
1630
1669
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
1631
1670
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 ( _) ) => {
1635
1675
assert_eq ! ( field, 0 ) ;
1636
1676
val
1637
1677
} ,
1638
- Value :: ByValPair ( a, b) => {
1678
+ Some ( Value :: ByValPair ( a, b) ) => {
1639
1679
match field {
1640
1680
0 => Value :: ByVal ( a) ,
1641
1681
1 => Value :: ByVal ( b) ,
1642
1682
_ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
1643
1683
}
1644
1684
} ,
1645
- }
1685
+ } )
1646
1686
} else {
1647
- self . locals [ local. index ( ) - 1 ]
1687
+ self . locals [ local. index ( ) - 1 ] . ok_or ( EvalError :: DeadLocal )
1648
1688
}
1649
1689
}
1650
1690
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 > {
1652
1692
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
1653
1693
if let Some ( field) = field {
1654
1694
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 ( _) ) => {
1657
1698
assert_eq ! ( field, 0 ) ;
1658
- self . set_local ( local, None , value) ;
1699
+ self . set_local ( local, None , value) ? ;
1659
1700
} ,
1660
- Value :: ByValPair ( a, b) => {
1701
+ Some ( Value :: ByValPair ( a, b) ) => {
1661
1702
let prim = match value {
1662
1703
Value :: ByRef ( _) => bug ! ( "can't set ValPair field to ByRef" ) ,
1663
1704
Value :: ByVal ( val) => val,
1664
1705
Value :: ByValPair ( _, _) => bug ! ( "can't set ValPair field to ValPair" ) ,
1665
1706
} ;
1666
1707
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) ) ? ,
1669
1710
_ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
1670
1711
}
1671
1712
} ,
1672
1713
}
1673
1714
} 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
+ }
1675
1719
}
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) ;
1676
1738
}
1677
1739
}
1678
1740
0 commit comments