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,33 @@ 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 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
+
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 :: 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
+ }
459
483
460
484
self . stack . push ( Frame {
461
485
mir,
@@ -509,21 +533,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
509
533
}
510
534
// deallocate all locals that are backed by an allocation
511
535
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) ?;
522
537
}
523
538
524
539
Ok ( ( ) )
525
540
}
526
541
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
+
527
556
pub fn assign_discr_and_fields <
528
557
V : IntoValTyPair < ' tcx > ,
529
558
J : IntoIterator < Item = V > ,
@@ -1047,16 +1076,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1047
1076
Lvalue :: Local { frame, local, field } => {
1048
1077
// -1 since we don't store the return value
1049
1078
match self . stack [ frame] . locals [ local. index ( ) - 1 ] {
1050
- Value :: ByRef ( ptr) => {
1079
+ None => return Err ( EvalError :: DeadLocal ) ,
1080
+ Some ( Value :: ByRef ( ptr) ) => {
1051
1081
assert ! ( field. is_none( ) ) ;
1052
1082
Lvalue :: from_ptr ( ptr)
1053
1083
} ,
1054
- val => {
1084
+ Some ( val) => {
1055
1085
let ty = self . stack [ frame] . mir . local_decls [ local] . ty ;
1056
1086
let ty = self . monomorphize ( ty, self . stack [ frame] . instance . substs ) ;
1057
1087
let substs = self . stack [ frame] . instance . substs ;
1058
1088
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
1060
1090
self . write_value_to_ptr ( val, ptr, ty) ?;
1061
1091
let lval = Lvalue :: from_ptr ( ptr) ;
1062
1092
if let Some ( ( field, field_ty) ) = field {
@@ -1139,7 +1169,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1139
1169
* this. globals . get_mut ( & cid) . expect ( "already checked" ) = Global {
1140
1170
value : val,
1141
1171
..dest
1142
- }
1172
+ } ;
1173
+ Ok ( ( ) )
1143
1174
} ;
1144
1175
self . write_value_possibly_by_val ( src_val, write_dest, dest. value , dest_ty)
1145
1176
} ,
@@ -1150,7 +1181,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1150
1181
}
1151
1182
1152
1183
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) ) ? ;
1154
1185
self . write_value_possibly_by_val (
1155
1186
src_val,
1156
1187
|this, val| this. stack [ frame] . set_local ( local, field. map ( |( i, _) | i) , val) ,
@@ -1162,7 +1193,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1162
1193
}
1163
1194
1164
1195
// 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 > > (
1166
1197
& mut self ,
1167
1198
src_val : Value ,
1168
1199
write_dest : F ,
@@ -1192,17 +1223,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1192
1223
// source and write that into the destination without making an allocation, so
1193
1224
// we do so here.
1194
1225
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) ? ;
1196
1227
} else {
1197
1228
let dest_ptr = self . alloc_ptr ( dest_ty) ?;
1198
1229
self . copy ( src_ptr, dest_ptr, dest_ty) ?;
1199
- write_dest ( self , Value :: ByRef ( dest_ptr) ) ;
1230
+ write_dest ( self , Value :: ByRef ( dest_ptr) ) ? ;
1200
1231
}
1201
1232
1202
1233
} else {
1203
1234
// Finally, we have the simple case where neither source nor destination are
1204
1235
// `ByRef`. We may simply copy the source value over the the destintion.
1205
- write_dest ( self , src_val) ;
1236
+ write_dest ( self , src_val) ? ;
1206
1237
}
1207
1238
Ok ( ( ) )
1208
1239
}
@@ -1572,14 +1603,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1572
1603
write ! ( msg, ":" ) . unwrap ( ) ;
1573
1604
1574
1605
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) ) => {
1576
1613
allocs. push ( ptr. alloc_id ) ;
1577
1614
}
1578
- Value :: ByVal ( val) => {
1615
+ Ok ( Value :: ByVal ( val) ) => {
1579
1616
write ! ( msg, " {:?}" , val) . unwrap ( ) ;
1580
1617
if let PrimVal :: Ptr ( ptr) = val { allocs. push ( ptr. alloc_id ) ; }
1581
1618
}
1582
- Value :: ByValPair ( val1, val2) => {
1619
+ Ok ( Value :: ByValPair ( val1, val2) ) => {
1583
1620
write ! ( msg, " ({:?}, {:?})" , val1, val2) . unwrap ( ) ;
1584
1621
if let PrimVal :: Ptr ( ptr) = val1 { allocs. push ( ptr. alloc_id ) ; }
1585
1622
if let PrimVal :: Ptr ( ptr) = val2 { allocs. push ( ptr. alloc_id ) ; }
@@ -1614,9 +1651,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1614
1651
) -> EvalResult < ' tcx >
1615
1652
where F : FnOnce ( & mut Self , Value ) -> EvalResult < ' tcx , Value > ,
1616
1653
{
1617
- let val = self . stack [ frame] . get_local ( local, field) ;
1654
+ let val = self . stack [ frame] . get_local ( local, field) ? ;
1618
1655
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) ? ;
1620
1657
// FIXME(solson): Run this when setting to Undef? (See previous version of this code.)
1621
1658
// if let Value::ByRef(ptr) = self.stack[frame].get_local(local) {
1622
1659
// self.memory.deallocate(ptr)?;
@@ -1626,53 +1663,79 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1626
1663
}
1627
1664
1628
1665
impl < ' 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 > {
1630
1667
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
1631
1668
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 ( _) ) => {
1635
1673
assert_eq ! ( field, 0 ) ;
1636
1674
val
1637
1675
} ,
1638
- Value :: ByValPair ( a, b) => {
1676
+ Some ( Value :: ByValPair ( a, b) ) => {
1639
1677
match field {
1640
1678
0 => Value :: ByVal ( a) ,
1641
1679
1 => Value :: ByVal ( b) ,
1642
1680
_ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
1643
1681
}
1644
1682
} ,
1645
- }
1683
+ } )
1646
1684
} else {
1647
- self . locals [ local. index ( ) - 1 ]
1685
+ self . locals [ local. index ( ) - 1 ] . ok_or ( EvalError :: DeadLocal )
1648
1686
}
1649
1687
}
1650
1688
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 > {
1652
1690
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
1653
1691
if let Some ( field) = field {
1654
1692
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 ( _) ) => {
1657
1696
assert_eq ! ( field, 0 ) ;
1658
- self . set_local ( local, None , value) ;
1697
+ self . set_local ( local, None , value) ? ;
1659
1698
} ,
1660
- Value :: ByValPair ( a, b) => {
1699
+ Some ( Value :: ByValPair ( a, b) ) => {
1661
1700
let prim = match value {
1662
1701
Value :: ByRef ( _) => bug ! ( "can't set ValPair field to ByRef" ) ,
1663
1702
Value :: ByVal ( val) => val,
1664
1703
Value :: ByValPair ( _, _) => bug ! ( "can't set ValPair field to ValPair" ) ,
1665
1704
} ;
1666
1705
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) ) ? ,
1669
1708
_ => bug ! ( "ByValPair has only two fields, tried to access {}" , field) ,
1670
1709
}
1671
1710
} ,
1672
1711
}
1673
1712
} 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 ) ) ;
1675
1728
}
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) ;
1676
1739
}
1677
1740
}
1678
1741
0 commit comments