|
50 | 50 | //! Borrows.
|
51 | 51 | //!
|
52 | 52 | //! To be continued...
|
| 53 | +//! |
| 54 | +//! The bottom state denotes uninitalized memory. |
| 55 | +//! |
| 56 | +//! |
| 57 | +//! # Assumptions |
| 58 | +//! |
| 59 | +//! - (A1) Assignment to any tracked place invalidates all pointers that could be used to change |
| 60 | +//! the underlying value. |
| 61 | +//! - (A2) `StorageLive`, `StorageDead` and `Deinit` make the underlying memory at least |
| 62 | +//! uninitialized (at least in the sense that declaring access UB is also fine). |
| 63 | +//! - (A3) An assignment with `State::assign_place_idx` either involves non-overlapping places, or |
| 64 | +//! the places are the same. |
| 65 | +//! - (A4) If the value behind a reference to a `Freeze` place is changed, dereferencing the |
| 66 | +//! reference is UB. |
53 | 67 |
|
54 | 68 | use std::fmt::{Debug, Formatter};
|
55 | 69 |
|
@@ -91,12 +105,11 @@ pub trait ValueAnalysis<'tcx> {
|
91 | 105 | self.handle_intrinsic(intrinsic, state);
|
92 | 106 | }
|
93 | 107 | StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
|
94 |
| - // We can flood with bottom here, because `StorageLive` makes the local |
95 |
| - // uninitialized, and `StorageDead` makes it UB to access. |
| 108 | + // (A2) |
96 | 109 | state.flood_with(Place::from(*local).as_ref(), self.map(), Self::Value::bottom());
|
97 | 110 | }
|
98 | 111 | StatementKind::Deinit(box place) => {
|
99 |
| - // The bottom states denotes uninitialized values. |
| 112 | + // (A2) |
100 | 113 | state.flood_with(place.as_ref(), self.map(), Self::Value::bottom());
|
101 | 114 | }
|
102 | 115 | StatementKind::Nop
|
@@ -200,7 +213,8 @@ pub trait ValueAnalysis<'tcx> {
|
200 | 213 | ValueOrPlace::Value(self.handle_constant(constant, state))
|
201 | 214 | }
|
202 | 215 | Operand::Copy(place) | Operand::Move(place) => {
|
203 |
| - // Do we want to handle moves differently? Could flood place with bottom. |
| 216 | + // On move, we would ideally flood the place with bottom. But with the current |
| 217 | + // framework this is not possible (similar to `InterpCx::eval_operand`). |
204 | 218 | self.map()
|
205 | 219 | .find(place.as_ref())
|
206 | 220 | .map(ValueOrPlace::Place)
|
@@ -306,7 +320,6 @@ impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper
|
306 | 320 |
|
307 | 321 | fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
|
308 | 322 | // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥.
|
309 |
| - // This utilizes that reading from an uninitialized place is UB. |
310 | 323 | assert!(matches!(state.0, StateData::Unreachable));
|
311 | 324 | let values = IndexVec::from_elem_n(T::Value::bottom(), self.0.map().value_count);
|
312 | 325 | *state = State(StateData::Reachable(values));
|
@@ -440,10 +453,10 @@ impl<V: Clone + HasTop> State<V> {
|
440 | 453 | self.flood_idx_with(place, map, V::top())
|
441 | 454 | }
|
442 | 455 |
|
443 |
| - /// This method assumes that the given places are not overlapping, and that we can therefore |
444 |
| - /// copy all entries one after another. |
445 | 456 | pub fn assign_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
|
| 457 | + // We use (A3) and copy all entries one after another. |
446 | 458 | let StateData::Reachable(values) = &mut self.0 else { return };
|
| 459 | + |
447 | 460 | // If both places are tracked, we copy the value to the target. If the target is tracked,
|
448 | 461 | // but the source is not, we have to invalidate the value in target. If the target is not
|
449 | 462 | // tracked, then we don't have to do anything.
|
@@ -490,6 +503,10 @@ impl<V: Clone + HasTop> State<V> {
|
490 | 503 | if let Some(value_index) = map.places[target].value_index {
|
491 | 504 | values[value_index] = V::top();
|
492 | 505 | }
|
| 506 | + // Instead of tracking of *where* a reference points to (as in, which place), we |
| 507 | + // track *what* it points to (as in, what do we know about the target). For an |
| 508 | + // assignment `x = &y`, we thus copy the info we have for `y` to `*x`. This is |
| 509 | + // sound because we only track places that are `Freeze`, and (A4). |
493 | 510 | if let Some(target_deref) = map.apply_elem(target, ProjElem::Deref) {
|
494 | 511 | self.assign_place_idx(target_deref, source, map);
|
495 | 512 | }
|
@@ -657,6 +674,8 @@ impl Map {
|
657 | 674 | return Err(());
|
658 | 675 | }
|
659 | 676 |
|
| 677 | + // FIXME: Check that the place is `Freeze`. |
| 678 | + |
660 | 679 | let place = self.make_place(local, projection)?;
|
661 | 680 |
|
662 | 681 | // Allocate a value slot if it doesn't have one.
|
|
0 commit comments