1 //! This module provides a framework on top of the normal MIR dataflow framework to simplify the
2 //! implementation of analyses that track information about the values stored in certain places.
3 //! We are using the term "place" here to refer to a `mir::Place` (a place expression) instead of
4 //! an `interpret::Place` (a memory location).
6 //! The default methods of [`ValueAnalysis`] (prefixed with `super_` instead of `handle_`)
7 //! provide some behavior that should be valid for all abstract domains that are based only on the
8 //! value stored in a certain place. On top of these default rules, an implementation should
9 //! override some of the `handle_` methods. For an example, see `ConstAnalysis`.
11 //! An implementation must also provide a [`Map`]. Before the analysis begins, all places that
12 //! should be tracked during the analysis must be registered. During the analysis, no new places
13 //! can be registered. The [`State`] can be queried to retrieve the abstract value stored for a
14 //! certain place by passing the map.
16 //! This framework is currently experimental. Originally, it supported shared references and enum
17 //! variants. However, it was discovered that both of these were unsound, and especially references
18 //! had subtle but serious issues. In the future, they could be added back in, but we should clarify
19 //! the rules for optimizations that rely on the aliasing model first.
24 //! - The bottom state denotes uninitialized memory. Because we are only doing a sound approximation
25 //! of the actual execution, we can also use this state for places where access would be UB.
27 //! - The assignment logic in `State::insert_place_idx` assumes that the places are non-overlapping,
28 //! or identical. Note that this refers to place expressions, not memory locations.
30 //! - Currently, places that have their reference taken cannot be tracked. Although this would be
31 //! possible, it has to rely on some aliasing model, which we are not ready to commit to yet.
32 //! Because of that, we can assume that the only way to change the value behind a tracked place is
33 //! by direct assignment.
35 use std
::fmt
::{Debug, Formatter}
;
37 use rustc_data_structures
::fx
::FxHashMap
;
38 use rustc_index
::bit_set
::BitSet
;
39 use rustc_index
::vec
::IndexVec
;
40 use rustc_middle
::mir
::visit
::{MutatingUseContext, PlaceContext, Visitor}
;
41 use rustc_middle
::mir
::*;
42 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
43 use rustc_target
::abi
::VariantIdx
;
45 use crate::lattice
::{HasBottom, HasTop}
;
47 fmt
::DebugWithContext
, Analysis
, AnalysisDomain
, CallReturnPlaces
, JoinSemiLattice
,
51 pub trait ValueAnalysis
<'tcx
> {
52 /// For each place of interest, the analysis tracks a value of the given type.
53 type Value
: Clone
+ JoinSemiLattice
+ HasBottom
+ HasTop
;
55 const NAME
: &'
static str;
57 fn map(&self) -> &Map
;
59 fn handle_statement(&self, statement
: &Statement
<'tcx
>, state
: &mut State
<Self::Value
>) {
60 self.super_statement(statement
, state
)
63 fn super_statement(&self, statement
: &Statement
<'tcx
>, state
: &mut State
<Self::Value
>) {
64 match &statement
.kind
{
65 StatementKind
::Assign(box (place
, rvalue
)) => {
66 self.handle_assign(*place
, rvalue
, state
);
68 StatementKind
::SetDiscriminant { box ref place, .. }
=> {
69 state
.flood_discr(place
.as_ref(), self.map());
71 StatementKind
::Intrinsic(box intrinsic
) => {
72 self.handle_intrinsic(intrinsic
, state
);
74 StatementKind
::StorageLive(local
) | StatementKind
::StorageDead(local
) => {
75 // StorageLive leaves the local in an uninitialized state.
76 // StorageDead makes it UB to access the local afterwards.
77 state
.flood_with(Place
::from(*local
).as_ref(), self.map(), Self::Value
::bottom());
79 StatementKind
::Deinit(box place
) => {
80 // Deinit makes the place uninitialized.
81 state
.flood_with(place
.as_ref(), self.map(), Self::Value
::bottom());
83 StatementKind
::Retag(..) => {
84 // We don't track references.
86 StatementKind
::ConstEvalCounter
88 | StatementKind
::FakeRead(..)
89 | StatementKind
::Coverage(..)
90 | StatementKind
::AscribeUserType(..) => (),
96 intrinsic
: &NonDivergingIntrinsic
<'tcx
>,
97 state
: &mut State
<Self::Value
>,
99 self.super_intrinsic(intrinsic
, state
);
104 intrinsic
: &NonDivergingIntrinsic
<'tcx
>,
105 state
: &mut State
<Self::Value
>,
108 NonDivergingIntrinsic
::Assume(..) => {
109 // Could use this, but ignoring it is sound.
111 NonDivergingIntrinsic
::CopyNonOverlapping(CopyNonOverlapping { dst, .. }
) => {
112 if let Some(place
) = dst
.place() {
113 state
.flood(place
.as_ref(), self.map());
122 rvalue
: &Rvalue
<'tcx
>,
123 state
: &mut State
<Self::Value
>,
125 self.super_assign(target
, rvalue
, state
)
131 rvalue
: &Rvalue
<'tcx
>,
132 state
: &mut State
<Self::Value
>,
134 let result
= self.handle_rvalue(rvalue
, state
);
135 state
.assign(target
.as_ref(), result
, self.map());
140 rvalue
: &Rvalue
<'tcx
>,
141 state
: &mut State
<Self::Value
>,
142 ) -> ValueOrPlace
<Self::Value
> {
143 self.super_rvalue(rvalue
, state
)
148 rvalue
: &Rvalue
<'tcx
>,
149 state
: &mut State
<Self::Value
>,
150 ) -> ValueOrPlace
<Self::Value
> {
152 Rvalue
::Use(operand
) => self.handle_operand(operand
, state
),
153 Rvalue
::CopyForDeref(place
) => self.handle_operand(&Operand
::Copy(*place
), state
),
154 Rvalue
::Ref(..) | Rvalue
::AddressOf(..) => {
155 // We don't track such places.
159 | Rvalue
::ThreadLocalRef(..)
162 | Rvalue
::BinaryOp(..)
163 | Rvalue
::CheckedBinaryOp(..)
164 | Rvalue
::NullaryOp(..)
165 | Rvalue
::UnaryOp(..)
166 | Rvalue
::Discriminant(..)
167 | Rvalue
::Aggregate(..)
168 | Rvalue
::ShallowInitBox(..) => {
169 // No modification is possible through these r-values.
177 operand
: &Operand
<'tcx
>,
178 state
: &mut State
<Self::Value
>,
179 ) -> ValueOrPlace
<Self::Value
> {
180 self.super_operand(operand
, state
)
185 operand
: &Operand
<'tcx
>,
186 state
: &mut State
<Self::Value
>,
187 ) -> ValueOrPlace
<Self::Value
> {
189 Operand
::Constant(box constant
) => {
190 ValueOrPlace
::Value(self.handle_constant(constant
, state
))
192 Operand
::Copy(place
) | Operand
::Move(place
) => {
193 // On move, we would ideally flood the place with bottom. But with the current
194 // framework this is not possible (similar to `InterpCx::eval_operand`).
196 .find(place
.as_ref())
197 .map(ValueOrPlace
::Place
)
198 .unwrap_or(ValueOrPlace
::top())
205 constant
: &Constant
<'tcx
>,
206 state
: &mut State
<Self::Value
>,
208 self.super_constant(constant
, state
)
213 _constant
: &Constant
<'tcx
>,
214 _state
: &mut State
<Self::Value
>,
219 /// The effect of a successful function call return should not be
220 /// applied here, see [`Analysis::apply_terminator_effect`].
221 fn handle_terminator(&self, terminator
: &Terminator
<'tcx
>, state
: &mut State
<Self::Value
>) {
222 self.super_terminator(terminator
, state
)
225 fn super_terminator(&self, terminator
: &Terminator
<'tcx
>, state
: &mut State
<Self::Value
>) {
226 match &terminator
.kind
{
227 TerminatorKind
::Call { .. }
| TerminatorKind
::InlineAsm { .. }
=> {
228 // Effect is applied by `handle_call_return`.
230 TerminatorKind
::Drop { place, .. }
=> {
231 state
.flood_with(place
.as_ref(), self.map(), Self::Value
::bottom());
233 TerminatorKind
::DropAndReplace { .. }
| TerminatorKind
::Yield { .. }
=> {
234 // They would have an effect, but are not allowed in this phase.
235 bug
!("encountered disallowed terminator");
237 TerminatorKind
::Goto { .. }
238 | TerminatorKind
::SwitchInt { .. }
239 | TerminatorKind
::Resume
240 | TerminatorKind
::Abort
241 | TerminatorKind
::Return
242 | TerminatorKind
::Unreachable
243 | TerminatorKind
::Assert { .. }
244 | TerminatorKind
::GeneratorDrop
245 | TerminatorKind
::FalseEdge { .. }
246 | TerminatorKind
::FalseUnwind { .. }
=> {
247 // These terminators have no effect on the analysis.
252 fn handle_call_return(
254 return_places
: CallReturnPlaces
<'_
, 'tcx
>,
255 state
: &mut State
<Self::Value
>,
257 self.super_call_return(return_places
, state
)
260 fn super_call_return(
262 return_places
: CallReturnPlaces
<'_
, 'tcx
>,
263 state
: &mut State
<Self::Value
>,
265 return_places
.for_each(|place
| {
266 state
.flood(place
.as_ref(), self.map());
270 fn handle_switch_int(
272 discr
: &Operand
<'tcx
>,
273 apply_edge_effects
: &mut impl SwitchIntEdgeEffects
<State
<Self::Value
>>,
275 self.super_switch_int(discr
, apply_edge_effects
)
280 _discr
: &Operand
<'tcx
>,
281 _apply_edge_effects
: &mut impl SwitchIntEdgeEffects
<State
<Self::Value
>>,
285 fn wrap(self) -> ValueAnalysisWrapper
<Self>
289 ValueAnalysisWrapper(self)
293 pub struct ValueAnalysisWrapper
<T
>(pub T
);
295 impl<'tcx
, T
: ValueAnalysis
<'tcx
>> AnalysisDomain
<'tcx
> for ValueAnalysisWrapper
<T
> {
296 type Domain
= State
<T
::Value
>;
298 type Direction
= crate::Forward
;
300 const NAME
: &'
static str = T
::NAME
;
302 fn bottom_value(&self, _body
: &Body
<'tcx
>) -> Self::Domain
{
303 State(StateData
::Unreachable
)
306 fn initialize_start_block(&self, body
: &Body
<'tcx
>, state
: &mut Self::Domain
) {
307 // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥.
308 assert
!(matches
!(state
.0, StateData
::Unreachable
));
309 let values
= IndexVec
::from_elem_n(T
::Value
::bottom(), self.0.map().value_count
);
310 *state
= State(StateData
::Reachable(values
));
311 for arg
in body
.args_iter() {
312 state
.flood(PlaceRef { local: arg, projection: &[] }
, self.0.map());
317 impl<'tcx
, T
> Analysis
<'tcx
> for ValueAnalysisWrapper
<T
>
319 T
: ValueAnalysis
<'tcx
>,
321 fn apply_statement_effect(
323 state
: &mut Self::Domain
,
324 statement
: &Statement
<'tcx
>,
327 if state
.is_reachable() {
328 self.0.handle_statement(statement
, state
);
332 fn apply_terminator_effect(
334 state
: &mut Self::Domain
,
335 terminator
: &Terminator
<'tcx
>,
338 if state
.is_reachable() {
339 self.0.handle_terminator(terminator
, state
);
343 fn apply_call_return_effect(
345 state
: &mut Self::Domain
,
347 return_places
: crate::CallReturnPlaces
<'_
, 'tcx
>,
349 if state
.is_reachable() {
350 self.0.handle_call_return(return_places
, state
)
354 fn apply_switch_int_edge_effects(
357 discr
: &Operand
<'tcx
>,
358 apply_edge_effects
: &mut impl SwitchIntEdgeEffects
<Self::Domain
>,
360 // FIXME: Dataflow framework provides no access to current state here.
361 self.0.handle_switch_int(discr
, apply_edge_effects
)
365 rustc_index
::newtype_index
!(
366 /// This index uniquely identifies a place.
368 /// Not every place has a `PlaceIndex`, and not every `PlaceIndex` correspondends to a tracked
369 /// place. However, every tracked place and all places along its projection have a `PlaceIndex`.
370 pub struct PlaceIndex {}
373 rustc_index
::newtype_index
!(
374 /// This index uniquely identifies a tracked place and therefore a slot in [`State`].
376 /// It is an implementation detail of this module.
381 #[derive(PartialEq, Eq, Debug)]
383 Reachable(IndexVec
<ValueIndex
, V
>),
387 impl<V
: Clone
> Clone
for StateData
<V
> {
388 fn clone(&self) -> Self {
390 Self::Reachable(x
) => Self::Reachable(x
.clone()),
391 Self::Unreachable
=> Self::Unreachable
,
395 fn clone_from(&mut self, source
: &Self) {
396 match (&mut *self, source
) {
397 (Self::Reachable(x
), Self::Reachable(y
)) => {
398 // We go through `raw` here, because `IndexVec` currently has a naive `clone_from`.
399 x
.raw
.clone_from(&y
.raw
);
401 _
=> *self = source
.clone(),
406 /// The dataflow state for an instance of [`ValueAnalysis`].
408 /// Every instance specifies a lattice that represents the possible values of a single tracked
409 /// place. If we call this lattice `V` and set of tracked places `P`, then a [`State`] is an
410 /// element of `{unreachable} ∪ (P -> V)`. This again forms a lattice, where the bottom element is
411 /// `unreachable` and the top element is the mapping `p ↦ ⊤`. Note that the mapping `p ↦ ⊥` is not
412 /// the bottom element (because joining an unreachable and any other reachable state yields a
413 /// reachable state). All operations on unreachable states are ignored.
415 /// Flooding means assigning a value (by default `⊤`) to all tracked projections of a given place.
416 #[derive(PartialEq, Eq, Debug)]
417 pub struct State
<V
>(StateData
<V
>);
419 impl<V
: Clone
> Clone
for State
<V
> {
420 fn clone(&self) -> Self {
424 fn clone_from(&mut self, source
: &Self) {
425 self.0.clone_from(&source
.0);
429 impl<V
: Clone
+ HasTop
+ HasBottom
> State
<V
> {
430 pub fn is_reachable(&self) -> bool
{
431 matches
!(&self.0, StateData
::Reachable(_
))
434 pub fn mark_unreachable(&mut self) {
435 self.0 = StateData
::Unreachable
;
438 pub fn flood_all(&mut self) {
439 self.flood_all_with(V
::top())
442 pub fn flood_all_with(&mut self, value
: V
) {
443 let StateData
::Reachable(values
) = &mut self.0 else { return }
;
444 values
.raw
.fill(value
);
447 pub fn flood_with(&mut self, place
: PlaceRef
<'_
>, map
: &Map
, value
: V
) {
448 let StateData
::Reachable(values
) = &mut self.0 else { return }
;
449 map
.for_each_aliasing_place(place
, None
, &mut |place
| {
450 if let Some(vi
) = map
.places
[place
].value_index
{
451 values
[vi
] = value
.clone();
456 pub fn flood(&mut self, place
: PlaceRef
<'_
>, map
: &Map
) {
457 self.flood_with(place
, map
, V
::top())
460 pub fn flood_discr_with(&mut self, place
: PlaceRef
<'_
>, map
: &Map
, value
: V
) {
461 let StateData
::Reachable(values
) = &mut self.0 else { return }
;
462 map
.for_each_aliasing_place(place
, Some(TrackElem
::Discriminant
), &mut |place
| {
463 if let Some(vi
) = map
.places
[place
].value_index
{
464 values
[vi
] = value
.clone();
469 pub fn flood_discr(&mut self, place
: PlaceRef
<'_
>, map
: &Map
) {
470 self.flood_discr_with(place
, map
, V
::top())
473 /// Low-level method that assigns to a place.
474 /// This does nothing if the place is not tracked.
476 /// The target place must have been flooded before calling this method.
477 pub fn insert_idx(&mut self, target
: PlaceIndex
, result
: ValueOrPlace
<V
>, map
: &Map
) {
479 ValueOrPlace
::Value(value
) => self.insert_value_idx(target
, value
, map
),
480 ValueOrPlace
::Place(source
) => self.insert_place_idx(target
, source
, map
),
484 /// Low-level method that assigns a value to a place.
485 /// This does nothing if the place is not tracked.
487 /// The target place must have been flooded before calling this method.
488 pub fn insert_value_idx(&mut self, target
: PlaceIndex
, value
: V
, map
: &Map
) {
489 let StateData
::Reachable(values
) = &mut self.0 else { return }
;
490 if let Some(value_index
) = map
.places
[target
].value_index
{
491 values
[value_index
] = value
;
495 /// Copies `source` to `target`, including all tracked places beneath.
497 /// If `target` contains a place that is not contained in `source`, it will be overwritten with
498 /// Top. Also, because this will copy all entries one after another, it may only be used for
499 /// places that are non-overlapping or identical.
501 /// The target place must have been flooded before calling this method.
502 fn insert_place_idx(&mut self, target
: PlaceIndex
, source
: PlaceIndex
, map
: &Map
) {
503 let StateData
::Reachable(values
) = &mut self.0 else { return }
;
505 // If both places are tracked, we copy the value to the target.
506 // If the target is tracked, but the source is not, we do nothing, as invalidation has
507 // already been performed.
508 if let Some(target_value
) = map
.places
[target
].value_index
{
509 if let Some(source_value
) = map
.places
[source
].value_index
{
510 values
[target_value
] = values
[source_value
].clone();
513 for target_child
in map
.children(target
) {
514 // Try to find corresponding child and recurse. Reasoning is similar as above.
515 let projection
= map
.places
[target_child
].proj_elem
.unwrap();
516 if let Some(source_child
) = map
.projections
.get(&(source
, projection
)) {
517 self.insert_place_idx(target_child
, *source_child
, map
);
522 /// Helper method to interpret `target = result`.
523 pub fn assign(&mut self, target
: PlaceRef
<'_
>, result
: ValueOrPlace
<V
>, map
: &Map
) {
524 self.flood(target
, map
);
525 if let Some(target
) = map
.find(target
) {
526 self.insert_idx(target
, result
, map
);
530 /// Helper method for assignments to a discriminant.
531 pub fn assign_discr(&mut self, target
: PlaceRef
<'_
>, result
: ValueOrPlace
<V
>, map
: &Map
) {
532 self.flood_discr(target
, map
);
533 if let Some(target
) = map
.find_discr(target
) {
534 self.insert_idx(target
, result
, map
);
538 /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
539 pub fn get(&self, place
: PlaceRef
<'_
>, map
: &Map
) -> V
{
540 map
.find(place
).map(|place
| self.get_idx(place
, map
)).unwrap_or(V
::top())
543 /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
544 pub fn get_discr(&self, place
: PlaceRef
<'_
>, map
: &Map
) -> V
{
545 match map
.find_discr(place
) {
546 Some(place
) => self.get_idx(place
, map
),
551 /// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
552 pub fn get_idx(&self, place
: PlaceIndex
, map
: &Map
) -> V
{
554 StateData
::Reachable(values
) => {
555 map
.places
[place
].value_index
.map(|v
| values
[v
].clone()).unwrap_or(V
::top())
557 StateData
::Unreachable
=> {
558 // Because this is unreachable, we can return any value we want.
565 impl<V
: JoinSemiLattice
+ Clone
> JoinSemiLattice
for State
<V
> {
566 fn join(&mut self, other
: &Self) -> bool
{
567 match (&mut self.0, &other
.0) {
568 (_
, StateData
::Unreachable
) => false,
569 (StateData
::Unreachable
, _
) => {
570 *self = other
.clone();
573 (StateData
::Reachable(this
), StateData
::Reachable(other
)) => this
.join(other
),
578 /// Partial mapping from [`Place`] to [`PlaceIndex`], where some places also have a [`ValueIndex`].
580 /// This data structure essentially maintains a tree of places and their projections. Some
581 /// additional bookkeeping is done, to speed up traversal over this tree:
582 /// - For iteration, every [`PlaceInfo`] contains an intrusive linked list of its children.
583 /// - To directly get the child for a specific projection, there is a `projections` map.
586 locals
: IndexVec
<Local
, Option
<PlaceIndex
>>,
587 projections
: FxHashMap
<(PlaceIndex
, TrackElem
), PlaceIndex
>,
588 places
: IndexVec
<PlaceIndex
, PlaceInfo
>,
595 locals
: IndexVec
::new(),
596 projections
: FxHashMap
::default(),
597 places
: IndexVec
::new(),
602 /// Returns a map that only tracks places whose type passes the filter.
604 /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
605 /// chosen is an implementation detail and may not be relied upon (other than that their type
606 /// passes the filter).
607 pub fn from_filter
<'tcx
>(
610 filter
: impl FnMut(Ty
<'tcx
>) -> bool
,
611 place_limit
: Option
<usize>,
613 let mut map
= Self::new();
614 let exclude
= excluded_locals(body
);
615 map
.register_with_filter(tcx
, body
, filter
, exclude
, place_limit
);
616 debug
!("registered {} places ({} nodes in total)", map
.value_count
, map
.places
.len());
620 /// Register all non-excluded places that pass the filter.
621 fn register_with_filter
<'tcx
>(
625 mut filter
: impl FnMut(Ty
<'tcx
>) -> bool
,
626 exclude
: BitSet
<Local
>,
627 place_limit
: Option
<usize>,
629 // We use this vector as stack, pushing and popping projections.
630 let mut projection
= Vec
::new();
631 for (local
, decl
) in body
.local_decls
.iter_enumerated() {
632 if !exclude
.contains(local
) {
633 self.register_with_filter_rec(
645 /// Potentially register the (local, projection) place and its fields, recursively.
647 /// Invariant: The projection must only contain trackable elements.
648 fn register_with_filter_rec
<'tcx
>(
652 projection
: &mut Vec
<PlaceElem
<'tcx
>>,
654 filter
: &mut impl FnMut(Ty
<'tcx
>) -> bool
,
655 place_limit
: Option
<usize>,
657 if let Some(place_limit
) = place_limit
&& self.value_count
>= place_limit
{
661 // We know that the projection only contains trackable elements.
662 let place
= self.make_place(local
, projection
).unwrap();
664 // Allocate a value slot if it doesn't have one, and the user requested one.
665 if self.places
[place
].value_index
.is_none() && filter(ty
) {
666 self.places
[place
].value_index
= Some(self.value_count
.into());
667 self.value_count
+= 1;
671 let discr_ty
= ty
.discriminant_ty(tcx
);
672 if filter(discr_ty
) {
675 .entry((place
, TrackElem
::Discriminant
))
677 // Prepend new child to the linked list.
678 let next
= self.places
.push(PlaceInfo
::new(Some(TrackElem
::Discriminant
)));
679 self.places
[next
].next_sibling
= self.places
[place
].first_child
;
680 self.places
[place
].first_child
= Some(next
);
684 // Allocate a value slot if it doesn't have one.
685 if self.places
[discr
].value_index
.is_none() {
686 self.places
[discr
].value_index
= Some(self.value_count
.into());
687 self.value_count
+= 1;
692 // Recurse with all fields of this place.
693 iter_fields(ty
, tcx
, |variant
, field
, ty
| {
694 if let Some(variant
) = variant
{
695 projection
.push(PlaceElem
::Downcast(None
, variant
));
696 let _
= self.make_place(local
, projection
);
697 projection
.push(PlaceElem
::Field(field
, ty
));
698 self.register_with_filter_rec(tcx
, local
, projection
, ty
, filter
, place_limit
);
703 projection
.push(PlaceElem
::Field(field
, ty
));
704 self.register_with_filter_rec(tcx
, local
, projection
, ty
, filter
, place_limit
);
709 /// Tries to add the place to the map, without allocating a value slot.
711 /// Can fail if the projection contains non-trackable elements.
715 projection
: &[PlaceElem
<'tcx
>],
716 ) -> Result
<PlaceIndex
, ()> {
717 // Get the base index of the local.
719 *self.locals
.get_or_insert_with(local
, || self.places
.push(PlaceInfo
::new(None
)));
721 // Apply the projection.
722 for &elem
in projection
{
723 let elem
= elem
.try_into()?
;
724 index
= *self.projections
.entry((index
, elem
)).or_insert_with(|| {
725 // Prepend new child to the linked list.
726 let next
= self.places
.push(PlaceInfo
::new(Some(elem
)));
727 self.places
[next
].next_sibling
= self.places
[index
].first_child
;
728 self.places
[index
].first_child
= Some(next
);
736 /// Returns the number of tracked places, i.e., those for which a value can be stored.
737 pub fn tracked_places(&self) -> usize {
741 /// Applies a single projection element, yielding the corresponding child.
742 pub fn apply(&self, place
: PlaceIndex
, elem
: TrackElem
) -> Option
<PlaceIndex
> {
743 self.projections
.get(&(place
, elem
)).copied()
746 /// Locates the given place, if it exists in the tree.
750 extra
: impl IntoIterator
<Item
= TrackElem
>,
751 ) -> Option
<PlaceIndex
> {
752 let mut index
= *self.locals
.get(place
.local
)?
.as_ref()?
;
754 for &elem
in place
.projection
{
755 index
= self.apply(index
, elem
.try_into().ok()?
)?
;
758 index
= self.apply(index
, elem
)?
;
764 /// Locates the given place, if it exists in the tree.
765 pub fn find(&self, place
: PlaceRef
<'_
>) -> Option
<PlaceIndex
> {
766 self.find_extra(place
, [])
769 /// Locates the given place and applies `Discriminant`, if it exists in the tree.
770 pub fn find_discr(&self, place
: PlaceRef
<'_
>) -> Option
<PlaceIndex
> {
771 self.find_extra(place
, [TrackElem
::Discriminant
])
774 /// Iterate over all direct children.
775 pub fn children(&self, parent
: PlaceIndex
) -> impl Iterator
<Item
= PlaceIndex
> + '_
{
776 Children
::new(self, parent
)
779 /// Invoke a function on the given place and all places that may alias it.
781 /// In particular, when the given place has a variant downcast, we invoke the function on all
782 /// the other variants.
784 /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
786 pub fn for_each_aliasing_place(
789 tail_elem
: Option
<TrackElem
>,
790 f
: &mut impl FnMut(PlaceIndex
),
792 if place
.is_indirect() {
793 // We do not track indirect places.
796 let Some(&Some(mut index
)) = self.locals
.get(place
.local
) else {
797 // The local is not tracked at all, so it does not alias anything.
803 .map(|&elem
| elem
.try_into())
804 .chain(tail_elem
.map(Ok
).into_iter());
806 // A field aliases the parent place.
809 let Ok(elem
) = elem
else { return }
;
810 let sub
= self.apply(index
, elem
);
811 if let TrackElem
::Variant(..) | TrackElem
::Discriminant
= elem
{
812 // Enum variant fields and enum discriminants alias each another.
813 self.for_each_variant_sibling(index
, sub
, f
);
815 if let Some(sub
) = sub
{
821 self.preorder_invoke(index
, f
);
824 /// Invoke the given function on all the descendants of the given place, except one branch.
825 fn for_each_variant_sibling(
828 preserved_child
: Option
<PlaceIndex
>,
829 f
: &mut impl FnMut(PlaceIndex
),
831 for sibling
in self.children(parent
) {
832 let elem
= self.places
[sibling
].proj_elem
;
833 // Only invalidate variants and discriminant. Fields (for generators) are not
834 // invalidated by assignment to a variant.
835 if let Some(TrackElem
::Variant(..) | TrackElem
::Discriminant
) = elem
836 // Only invalidate the other variants, the current one is fine.
837 && Some(sibling
) != preserved_child
839 self.preorder_invoke(sibling
, f
);
844 /// Invoke a function on the given place and all descendants.
845 fn preorder_invoke(&self, root
: PlaceIndex
, f
: &mut impl FnMut(PlaceIndex
)) {
847 for child
in self.children(root
) {
848 self.preorder_invoke(child
, f
);
853 /// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`].
855 /// Together, `first_child` and `next_sibling` form an intrusive linked list, which is used to
856 /// model a tree structure (a replacement for a member like `children: Vec<PlaceIndex>`).
859 /// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
860 value_index
: Option
<ValueIndex
>,
862 /// The projection used to go from parent to this node (only None for root).
863 proj_elem
: Option
<TrackElem
>,
865 /// The left-most child.
866 first_child
: Option
<PlaceIndex
>,
868 /// Index of the sibling to the right of this node.
869 next_sibling
: Option
<PlaceIndex
>,
873 fn new(proj_elem
: Option
<TrackElem
>) -> Self {
874 Self { next_sibling: None, first_child: None, proj_elem, value_index: None }
878 struct Children
<'a
> {
880 next
: Option
<PlaceIndex
>,
883 impl<'a
> Children
<'a
> {
884 fn new(map
: &'a Map
, parent
: PlaceIndex
) -> Self {
885 Self { map, next: map.places[parent].first_child }
889 impl<'a
> Iterator
for Children
<'a
> {
890 type Item
= PlaceIndex
;
892 fn next(&mut self) -> Option
<Self::Item
> {
895 self.next
= self.map
.places
[child
].next_sibling
;
903 /// Used as the result of an operand or r-value.
905 pub enum ValueOrPlace
<V
> {
910 impl<V
: HasTop
> ValueOrPlace
<V
> {
911 pub fn top() -> Self {
912 ValueOrPlace
::Value(V
::top())
916 /// The set of projection elements that can be used by a tracked place.
918 /// Although only field projections are currently allowed, this could change in the future.
919 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
926 impl<V
, T
> TryFrom
<ProjectionElem
<V
, T
>> for TrackElem
{
929 fn try_from(value
: ProjectionElem
<V
, T
>) -> Result
<Self, Self::Error
> {
931 ProjectionElem
::Field(field
, _
) => Ok(TrackElem
::Field(field
)),
932 ProjectionElem
::Downcast(_
, idx
) => Ok(TrackElem
::Variant(idx
)),
938 /// Invokes `f` on all direct fields of `ty`.
939 pub fn iter_fields
<'tcx
>(
942 mut f
: impl FnMut(Option
<VariantIdx
>, Field
, Ty
<'tcx
>),
946 for (field
, ty
) in list
.iter().enumerate() {
947 f(None
, field
.into(), ty
);
950 ty
::Adt(def
, substs
) => {
954 for (v_index
, v_def
) in def
.variants().iter_enumerated() {
955 let variant
= if def
.is_struct() { None }
else { Some(v_index) }
;
956 for (f_index
, f_def
) in v_def
.fields
.iter().enumerate() {
957 let field_ty
= f_def
.ty(tcx
, substs
);
959 .try_normalize_erasing_regions(ty
::ParamEnv
::reveal_all(), field_ty
)
960 .unwrap_or(field_ty
);
961 f(variant
, f_index
.into(), field_ty
);
965 ty
::Closure(_
, substs
) => {
966 iter_fields(substs
.as_closure().tupled_upvars_ty(), tcx
, f
);
972 /// Returns all locals with projections that have their reference or address taken.
973 pub fn excluded_locals(body
: &Body
<'_
>) -> BitSet
<Local
> {
975 result
: BitSet
<Local
>,
978 impl<'tcx
> Visitor
<'tcx
> for Collector
{
979 fn visit_place(&mut self, place
: &Place
<'tcx
>, context
: PlaceContext
, _location
: Location
) {
980 if (context
.is_borrow()
981 || context
.is_address_of()
983 || context
== PlaceContext
::MutatingUse(MutatingUseContext
::AsmOutput
))
984 && !place
.is_indirect()
986 // A pointer to a place could be used to access other places with the same local,
987 // hence we have to exclude the local completely.
988 self.result
.insert(place
.local
);
993 let mut collector
= Collector { result: BitSet::new_empty(body.local_decls.len()) }
;
994 collector
.visit_body(body
);
998 /// This is used to visualize the dataflow analysis.
999 impl<'tcx
, T
> DebugWithContext
<ValueAnalysisWrapper
<T
>> for State
<T
::Value
>
1001 T
: ValueAnalysis
<'tcx
>,
1004 fn fmt_with(&self, ctxt
: &ValueAnalysisWrapper
<T
>, f
: &mut Formatter
<'_
>) -> std
::fmt
::Result
{
1006 StateData
::Reachable(values
) => debug_with_context(values
, None
, ctxt
.0.map(), f
),
1007 StateData
::Unreachable
=> write
!(f
, "unreachable"),
1014 ctxt
: &ValueAnalysisWrapper
<T
>,
1015 f
: &mut Formatter
<'_
>,
1016 ) -> std
::fmt
::Result
{
1017 match (&self.0, &old
.0) {
1018 (StateData
::Reachable(this
), StateData
::Reachable(old
)) => {
1019 debug_with_context(this
, Some(old
), ctxt
.0.map(), f
)
1021 _
=> Ok(()), // Consider printing something here.
1026 fn debug_with_context_rec
<V
: Debug
+ Eq
>(
1029 new
: &IndexVec
<ValueIndex
, V
>,
1030 old
: Option
<&IndexVec
<ValueIndex
, V
>>,
1032 f
: &mut Formatter
<'_
>,
1033 ) -> std
::fmt
::Result
{
1034 if let Some(value
) = map
.places
[place
].value_index
{
1036 None
=> writeln
!(f
, "{}: {:?}", place_str
, new
[value
])?
,
1038 if new
[value
] != old
[value
] {
1039 writeln
!(f
, "\u{001f}-{}: {:?}", place_str
, old
[value
])?
;
1040 writeln
!(f
, "\u{001f}+{}: {:?}", place_str
, new
[value
])?
;
1046 for child
in map
.children(place
) {
1047 let info_elem
= map
.places
[child
].proj_elem
.unwrap();
1048 let child_place_str
= match info_elem
{
1049 TrackElem
::Discriminant
=> {
1050 format
!("discriminant({})", place_str
)
1052 TrackElem
::Variant(idx
) => {
1053 format
!("({} as {:?})", place_str
, idx
)
1055 TrackElem
::Field(field
) => {
1056 if place_str
.starts_with('
*'
) {
1057 format
!("({}).{}", place_str
, field
.index())
1059 format
!("{}.{}", place_str
, field
.index())
1063 debug_with_context_rec(child
, &child_place_str
, new
, old
, map
, f
)?
;
1069 fn debug_with_context
<V
: Debug
+ Eq
>(
1070 new
: &IndexVec
<ValueIndex
, V
>,
1071 old
: Option
<&IndexVec
<ValueIndex
, V
>>,
1073 f
: &mut Formatter
<'_
>,
1074 ) -> std
::fmt
::Result
{
1075 for (local
, place
) in map
.locals
.iter_enumerated() {
1076 if let Some(place
) = place
{
1077 debug_with_context_rec(*place
, &format
!("{local:?}"), new
, old
, map
, f
)?
;