Skip to content

Commit b5063ab

Browse files
committed
Make more assumptions explicit
1 parent 2f66e94 commit b5063ab

File tree

1 file changed

+26
-7
lines changed

1 file changed

+26
-7
lines changed

compiler/rustc_mir_dataflow/src/value_analysis.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@
5050
//! Borrows.
5151
//!
5252
//! 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.
5367
5468
use std::fmt::{Debug, Formatter};
5569

@@ -91,12 +105,11 @@ pub trait ValueAnalysis<'tcx> {
91105
self.handle_intrinsic(intrinsic, state);
92106
}
93107
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)
96109
state.flood_with(Place::from(*local).as_ref(), self.map(), Self::Value::bottom());
97110
}
98111
StatementKind::Deinit(box place) => {
99-
// The bottom states denotes uninitialized values.
112+
// (A2)
100113
state.flood_with(place.as_ref(), self.map(), Self::Value::bottom());
101114
}
102115
StatementKind::Nop
@@ -200,7 +213,8 @@ pub trait ValueAnalysis<'tcx> {
200213
ValueOrPlace::Value(self.handle_constant(constant, state))
201214
}
202215
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`).
204218
self.map()
205219
.find(place.as_ref())
206220
.map(ValueOrPlace::Place)
@@ -306,7 +320,6 @@ impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper
306320

307321
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
308322
// 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.
310323
assert!(matches!(state.0, StateData::Unreachable));
311324
let values = IndexVec::from_elem_n(T::Value::bottom(), self.0.map().value_count);
312325
*state = State(StateData::Reachable(values));
@@ -440,10 +453,10 @@ impl<V: Clone + HasTop> State<V> {
440453
self.flood_idx_with(place, map, V::top())
441454
}
442455

443-
/// This method assumes that the given places are not overlapping, and that we can therefore
444-
/// copy all entries one after another.
445456
pub fn assign_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
457+
// We use (A3) and copy all entries one after another.
446458
let StateData::Reachable(values) = &mut self.0 else { return };
459+
447460
// If both places are tracked, we copy the value to the target. If the target is tracked,
448461
// but the source is not, we have to invalidate the value in target. If the target is not
449462
// tracked, then we don't have to do anything.
@@ -490,6 +503,10 @@ impl<V: Clone + HasTop> State<V> {
490503
if let Some(value_index) = map.places[target].value_index {
491504
values[value_index] = V::top();
492505
}
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).
493510
if let Some(target_deref) = map.apply_elem(target, ProjElem::Deref) {
494511
self.assign_place_idx(target_deref, source, map);
495512
}
@@ -657,6 +674,8 @@ impl Map {
657674
return Err(());
658675
}
659676

677+
// FIXME: Check that the place is `Freeze`.
678+
660679
let place = self.make_place(local, projection)?;
661680

662681
// Allocate a value slot if it doesn't have one.

0 commit comments

Comments
 (0)