rustc_mir_dataflow/
value_analysis.rs

1use std::assert_matches::debug_assert_matches;
2use std::fmt::{Debug, Formatter};
3use std::ops::Range;
4
5use rustc_abi::{FieldIdx, VariantIdx};
6use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry};
7use rustc_data_structures::stack::ensure_sufficient_stack;
8use rustc_index::IndexVec;
9use rustc_index::bit_set::DenseBitSet;
10use rustc_middle::mir::visit::{PlaceContext, Visitor};
11use rustc_middle::mir::*;
12use rustc_middle::ty::{self, Ty, TyCtxt};
13use tracing::debug;
14
15use crate::JoinSemiLattice;
16use crate::lattice::{HasBottom, HasTop};
17
18rustc_index::newtype_index!(
19    /// This index uniquely identifies a place.
20    ///
21    /// Not every place has a `PlaceIndex`, and not every `PlaceIndex` corresponds to a tracked
22    /// place. However, every tracked place and all places along its projection have a `PlaceIndex`.
23    pub struct PlaceIndex {}
24);
25
26rustc_index::newtype_index!(
27    /// This index uniquely identifies a tracked place and therefore a slot in [`State`].
28    pub struct ValueIndex {}
29);
30
31/// See [`State`].
32#[derive(PartialEq, Eq, Debug)]
33pub struct StateData<V> {
34    bottom: V,
35    /// This map only contains values that are not `⊥`.
36    map: FxHashMap<ValueIndex, V>,
37}
38
39impl<V: HasBottom> StateData<V> {
40    fn new() -> StateData<V> {
41        StateData { bottom: V::BOTTOM, map: FxHashMap::default() }
42    }
43
44    fn get(&self, idx: ValueIndex) -> &V {
45        self.map.get(&idx).unwrap_or(&self.bottom)
46    }
47
48    fn insert(&mut self, idx: ValueIndex, elem: V) {
49        if elem.is_bottom() {
50            self.map.remove(&idx);
51        } else {
52            self.map.insert(idx, elem);
53        }
54    }
55}
56
57impl<V: Clone> Clone for StateData<V> {
58    fn clone(&self) -> Self {
59        StateData { bottom: self.bottom.clone(), map: self.map.clone() }
60    }
61
62    fn clone_from(&mut self, source: &Self) {
63        self.map.clone_from(&source.map)
64    }
65}
66
67impl<V: JoinSemiLattice + Clone> JoinSemiLattice for StateData<V> {
68    fn join(&mut self, other: &Self) -> bool {
69        let mut changed = false;
70        #[allow(rustc::potential_query_instability)]
71        for (i, v) in other.map.iter() {
72            match self.map.entry(*i) {
73                StdEntry::Vacant(e) => {
74                    e.insert(v.clone());
75                    changed = true
76                }
77                StdEntry::Occupied(e) => changed |= e.into_mut().join(v),
78            }
79        }
80        changed
81    }
82}
83
84/// Dataflow state.
85///
86/// Every instance specifies a lattice that represents the possible values of a single tracked
87/// place. If we call this lattice `V` and set of tracked places `P`, then a [`State`] is an
88/// element of `{unreachable} ∪ (P -> V)`. This again forms a lattice, where the bottom element is
89/// `unreachable` and the top element is the mapping `p ↦ ⊤`. Note that the mapping `p ↦ ⊥` is not
90/// the bottom element (because joining an unreachable and any other reachable state yields a
91/// reachable state). All operations on unreachable states are ignored.
92///
93/// Flooding means assigning a value (by default `⊤`) to all tracked projections of a given place.
94#[derive(PartialEq, Eq, Debug)]
95pub enum State<V> {
96    Unreachable,
97    Reachable(StateData<V>),
98}
99
100impl<V: Clone> Clone for State<V> {
101    fn clone(&self) -> Self {
102        match self {
103            Self::Reachable(x) => Self::Reachable(x.clone()),
104            Self::Unreachable => Self::Unreachable,
105        }
106    }
107
108    fn clone_from(&mut self, source: &Self) {
109        match (&mut *self, source) {
110            (Self::Reachable(x), Self::Reachable(y)) => {
111                x.clone_from(&y);
112            }
113            _ => *self = source.clone(),
114        }
115    }
116}
117
118impl<V: Clone + HasBottom> State<V> {
119    pub fn new_reachable() -> State<V> {
120        State::Reachable(StateData::new())
121    }
122
123    pub fn all_bottom(&self) -> bool {
124        match self {
125            State::Unreachable => false,
126            State::Reachable(values) =>
127            {
128                #[allow(rustc::potential_query_instability)]
129                values.map.values().all(V::is_bottom)
130            }
131        }
132    }
133
134    pub fn is_reachable(&self) -> bool {
135        matches!(self, State::Reachable(_))
136    }
137
138    /// Assign `value` to all places that are contained in `place` or may alias one.
139    pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
140        self.flood_with_tail_elem(place, None, map, value)
141    }
142
143    /// Assign `TOP` to all places that are contained in `place` or may alias one.
144    pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
145    where
146        V: HasTop,
147    {
148        self.flood_with(place, map, V::TOP)
149    }
150
151    /// Assign `value` to the discriminant of `place` and all places that may alias it.
152    fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
153        self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value)
154    }
155
156    /// Assign `TOP` to the discriminant of `place` and all places that may alias it.
157    pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
158    where
159        V: HasTop,
160    {
161        self.flood_discr_with(place, map, V::TOP)
162    }
163
164    /// This method is the most general version of the `flood_*` method.
165    ///
166    /// Assign `value` on the given place and all places that may alias it. In particular, when
167    /// the given place has a variant downcast, we invoke the function on all the other variants.
168    ///
169    /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
170    /// as such.
171    pub fn flood_with_tail_elem(
172        &mut self,
173        place: PlaceRef<'_>,
174        tail_elem: Option<TrackElem>,
175        map: &Map<'_>,
176        value: V,
177    ) {
178        let State::Reachable(values) = self else { return };
179        map.for_each_aliasing_place(place, tail_elem, &mut |vi| values.insert(vi, value.clone()));
180    }
181
182    /// Low-level method that assigns to a place.
183    /// This does nothing if the place is not tracked.
184    ///
185    /// The target place must have been flooded before calling this method.
186    fn insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map<'_>) {
187        match result {
188            ValueOrPlace::Value(value) => self.insert_value_idx(target, value, map),
189            ValueOrPlace::Place(source) => self.insert_place_idx(target, source, map),
190        }
191    }
192
193    /// Low-level method that assigns a value to a place.
194    /// This does nothing if the place is not tracked.
195    ///
196    /// The target place must have been flooded before calling this method.
197    pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map<'_>) {
198        let State::Reachable(values) = self else { return };
199        if let Some(value_index) = map.places[target].value_index {
200            values.insert(value_index, value)
201        }
202    }
203
204    /// Copies `source` to `target`, including all tracked places beneath.
205    ///
206    /// If `target` contains a place that is not contained in `source`, it will be overwritten with
207    /// Top. Also, because this will copy all entries one after another, it may only be used for
208    /// places that are non-overlapping or identical.
209    ///
210    /// The target place must have been flooded before calling this method.
211    pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map<'_>) {
212        let State::Reachable(values) = self else { return };
213        map.for_each_value_pair(target, source, &mut |target, source| {
214            values.insert(target, values.get(source).clone());
215        });
216    }
217
218    /// Helper method to interpret `target = result`.
219    pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
220    where
221        V: HasTop,
222    {
223        self.flood(target, map);
224        if let Some(target) = map.find(target) {
225            self.insert_idx(target, result, map);
226        }
227    }
228
229    /// Helper method for assignments to a discriminant.
230    pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
231    where
232        V: HasTop,
233    {
234        self.flood_discr(target, map);
235        if let Some(target) = map.find_discr(target) {
236            self.insert_idx(target, result, map);
237        }
238    }
239
240    /// Retrieve the value stored for a place, or `None` if it is not tracked.
241    pub fn try_get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
242        let place = map.find(place)?;
243        self.try_get_idx(place, map)
244    }
245
246    /// Retrieve the discriminant stored for a place, or `None` if it is not tracked.
247    pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
248        let place = map.find_discr(place)?;
249        self.try_get_idx(place, map)
250    }
251
252    /// Retrieve the slice length stored for a place, or `None` if it is not tracked.
253    pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
254        let place = map.find_len(place)?;
255        self.try_get_idx(place, map)
256    }
257
258    /// Retrieve the value stored for a place index, or `None` if it is not tracked.
259    pub fn try_get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> Option<V> {
260        match self {
261            State::Reachable(values) => {
262                map.places[place].value_index.map(|v| values.get(v).clone())
263            }
264            State::Unreachable => None,
265        }
266    }
267
268    /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
269    ///
270    /// This method returns ⊥ if the place is tracked and the state is unreachable.
271    pub fn get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
272    where
273        V: HasBottom + HasTop,
274    {
275        match self {
276            State::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP),
277            // Because this is unreachable, we can return any value we want.
278            State::Unreachable => V::BOTTOM,
279        }
280    }
281
282    /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
283    ///
284    /// This method returns ⊥ the current state is unreachable.
285    pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
286    where
287        V: HasBottom + HasTop,
288    {
289        match self {
290            State::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP),
291            // Because this is unreachable, we can return any value we want.
292            State::Unreachable => V::BOTTOM,
293        }
294    }
295
296    /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
297    ///
298    /// This method returns ⊥ the current state is unreachable.
299    pub fn get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
300    where
301        V: HasBottom + HasTop,
302    {
303        match self {
304            State::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP),
305            // Because this is unreachable, we can return any value we want.
306            State::Unreachable => V::BOTTOM,
307        }
308    }
309
310    /// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
311    ///
312    /// This method returns ⊥ the current state is unreachable.
313    pub fn get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> V
314    where
315        V: HasBottom + HasTop,
316    {
317        match self {
318            State::Reachable(values) => {
319                map.places[place].value_index.map(|v| values.get(v).clone()).unwrap_or(V::TOP)
320            }
321            State::Unreachable => {
322                // Because this is unreachable, we can return any value we want.
323                V::BOTTOM
324            }
325        }
326    }
327}
328
329impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> {
330    fn join(&mut self, other: &Self) -> bool {
331        match (&mut *self, other) {
332            (_, State::Unreachable) => false,
333            (State::Unreachable, _) => {
334                *self = other.clone();
335                true
336            }
337            (State::Reachable(this), State::Reachable(other)) => this.join(other),
338        }
339    }
340}
341
342/// Partial mapping from [`Place`] to [`PlaceIndex`], where some places also have a [`ValueIndex`].
343///
344/// This data structure essentially maintains a tree of places and their projections. Some
345/// additional bookkeeping is done, to speed up traversal over this tree:
346/// - For iteration, every [`PlaceInfo`] contains an intrusive linked list of its children.
347/// - To directly get the child for a specific projection, there is a `projections` map.
348#[derive(Debug)]
349pub struct Map<'tcx> {
350    locals: IndexVec<Local, Option<PlaceIndex>>,
351    projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
352    places: IndexVec<PlaceIndex, PlaceInfo<'tcx>>,
353    value_count: usize,
354    mode: PlaceCollectionMode,
355    // The Range corresponds to a slice into `inner_values_buffer`.
356    inner_values: IndexVec<PlaceIndex, Range<usize>>,
357    inner_values_buffer: Vec<ValueIndex>,
358}
359
360#[derive(Copy, Clone, Debug)]
361pub enum PlaceCollectionMode {
362    Full { value_limit: Option<usize> },
363    OnDemand,
364}
365
366impl<'tcx> Map<'tcx> {
367    /// Returns a map that only tracks places whose type has scalar layout.
368    ///
369    /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
370    /// chosen is an implementation detail and may not be relied upon (other than that their type
371    /// are scalars).
372    #[tracing::instrument(level = "trace", skip(tcx, body))]
373    pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, mode: PlaceCollectionMode) -> Self {
374        tracing::trace!(def_id=?body.source.def_id());
375        let capacity = 4 * body.local_decls.len();
376        let mut map = Self {
377            locals: IndexVec::from_elem(None, &body.local_decls),
378            projections: FxHashMap::default(),
379            places: IndexVec::with_capacity(capacity),
380            value_count: 0,
381            mode,
382            inner_values: IndexVec::new(),
383            inner_values_buffer: Vec::new(),
384        };
385        map.register_locals(tcx, body);
386        match mode {
387            PlaceCollectionMode::Full { value_limit } => {
388                map.collect_places(tcx, body);
389                map.propagate_assignments(tcx, body);
390                map.create_values(tcx, body, value_limit);
391                map.trim_useless_places();
392            }
393            PlaceCollectionMode::OnDemand => {}
394        }
395        debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len());
396        map
397    }
398
399    /// Register all non-excluded places that have scalar layout.
400    #[tracing::instrument(level = "trace", skip(self, tcx, body))]
401    fn register_locals(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
402        let exclude = excluded_locals(body);
403
404        // Start by constructing the places for each bare local.
405        for (local, decl) in body.local_decls.iter_enumerated() {
406            if exclude.contains(local) {
407                continue;
408            }
409            if decl.ty.is_async_drop_in_place_coroutine(tcx) {
410                continue;
411            }
412
413            // Create a place for the local.
414            debug_assert!(self.locals[local].is_none());
415            let place = self.places.push(PlaceInfo::new(decl.ty, None));
416            self.locals[local] = Some(place);
417        }
418    }
419
420    /// Collect syntactic places from body, and create `PlaceIndex` for them.
421    #[tracing::instrument(level = "trace", skip(self, tcx, body))]
422    fn collect_places(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
423        let mut collector = PlaceCollector { tcx, body, map: self };
424        collector.visit_body(body);
425    }
426
427    /// Just collecting syntactic places is not enough. We may need to propagate this pattern:
428    ///      _1 = (const 5u32, const 13i64);
429    ///      _2 = _1;
430    ///      _3 = (_2.0 as u32);
431    ///
432    /// `_1.0` does not appear, but we still need to track it. This is achieved by propagating
433    /// projections from assignments. We recorded an assignment between `_2` and `_1`, so we
434    /// want `_1` and `_2` to have the same sub-places.
435    #[tracing::instrument(level = "trace", skip(self, tcx, body))]
436    fn propagate_assignments(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
437        // Collect syntactic places and assignments between them.
438        let mut assignments = FxIndexSet::default();
439
440        for bbdata in body.basic_blocks.iter() {
441            for stmt in bbdata.statements.iter() {
442                let Some((lhs, rhs)) = stmt.kind.as_assign() else { continue };
443                match rhs {
444                    Rvalue::Use(Operand::Move(rhs) | Operand::Copy(rhs))
445                    | Rvalue::CopyForDeref(rhs) => {
446                        let Some(lhs) = self.register_place_and_discr(tcx, body, *lhs) else {
447                            continue;
448                        };
449                        let Some(rhs) = self.register_place_and_discr(tcx, body, *rhs) else {
450                            continue;
451                        };
452                        assignments.insert((lhs, rhs));
453                    }
454                    Rvalue::Aggregate(kind, fields) => {
455                        let Some(mut lhs) = self.register_place_and_discr(tcx, body, *lhs) else {
456                            continue;
457                        };
458                        match **kind {
459                            // Do not propagate unions.
460                            AggregateKind::Adt(_, _, _, _, Some(_)) => continue,
461                            AggregateKind::Adt(_, variant, _, _, None) => {
462                                let ty = self.places[lhs].ty;
463                                if ty.is_enum() {
464                                    lhs = self.register_place_index(
465                                        ty,
466                                        lhs,
467                                        TrackElem::Variant(variant),
468                                    );
469                                }
470                            }
471                            AggregateKind::RawPtr(..)
472                            | AggregateKind::Array(_)
473                            | AggregateKind::Tuple
474                            | AggregateKind::Closure(..)
475                            | AggregateKind::Coroutine(..)
476                            | AggregateKind::CoroutineClosure(..) => {}
477                        }
478                        for (index, field) in fields.iter_enumerated() {
479                            if let Some(rhs) = field.place()
480                                && let Some(rhs) = self.register_place_and_discr(tcx, body, rhs)
481                            {
482                                let lhs = self.register_place_index(
483                                    self.places[rhs].ty,
484                                    lhs,
485                                    TrackElem::Field(index),
486                                );
487                                assignments.insert((lhs, rhs));
488                            }
489                        }
490                    }
491                    _ => {}
492                }
493            }
494        }
495
496        // This is a fixpoint loop does. While we are still creating places, run through
497        // all the assignments, and register places for children.
498        let mut num_places = 0;
499        while num_places < self.places.len() {
500            num_places = self.places.len();
501
502            for assign in 0.. {
503                let Some(&(lhs, rhs)) = assignments.get_index(assign) else { break };
504
505                // Mirror children from `lhs` in `rhs`.
506                let mut child = self.places[lhs].first_child;
507                while let Some(lhs_child) = child {
508                    let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[lhs_child];
509                    let rhs_child = self.register_place_index(
510                        ty,
511                        rhs,
512                        proj_elem.expect("child is not a projection"),
513                    );
514                    assignments.insert((lhs_child, rhs_child));
515                    child = next_sibling;
516                }
517
518                // Conversely, mirror children from `rhs` in `lhs`.
519                let mut child = self.places[rhs].first_child;
520                while let Some(rhs_child) = child {
521                    let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[rhs_child];
522                    let lhs_child = self.register_place_index(
523                        ty,
524                        lhs,
525                        proj_elem.expect("child is not a projection"),
526                    );
527                    assignments.insert((lhs_child, rhs_child));
528                    child = next_sibling;
529                }
530            }
531        }
532    }
533
534    /// Create values for places whose type have scalar layout.
535    #[tracing::instrument(level = "trace", skip(self, tcx, body))]
536    fn create_values(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) {
537        debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. });
538        let typing_env = body.typing_env(tcx);
539        for place_info in self.places.iter_mut() {
540            // The user requires a bound on the number of created values.
541            if let Some(value_limit) = value_limit
542                && self.value_count >= value_limit
543            {
544                break;
545            }
546
547            if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, place_info.ty) {
548                place_info.ty = ty;
549            }
550
551            // Allocate a value slot if it doesn't have one, and the user requested one.
552            assert!(place_info.value_index.is_none());
553            if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(place_info.ty))
554                && layout.backend_repr.is_scalar()
555            {
556                place_info.value_index = Some(self.value_count.into());
557                self.value_count += 1;
558            }
559        }
560
561        // Pre-compute the tree of ValueIndex nested in each PlaceIndex.
562        // `inner_values_buffer[inner_values[place]]` is the set of all the values
563        // reachable by projecting `place`.
564        self.inner_values_buffer = Vec::with_capacity(self.value_count);
565        self.inner_values = IndexVec::from_elem(0..0, &self.places);
566        for local in body.local_decls.indices() {
567            if let Some(place) = self.locals[local] {
568                self.cache_preorder_invoke(place);
569            }
570        }
571    }
572
573    /// Trim useless places.
574    #[tracing::instrument(level = "trace", skip(self))]
575    fn trim_useless_places(&mut self) {
576        debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. });
577        for opt_place in self.locals.iter_mut() {
578            if let Some(place) = *opt_place
579                && self.inner_values[place].is_empty()
580            {
581                *opt_place = None;
582            }
583        }
584        #[allow(rustc::potential_query_instability)]
585        self.projections.retain(|_, child| !self.inner_values[*child].is_empty());
586    }
587
588    #[tracing::instrument(level = "trace", skip(self), ret)]
589    pub fn register_place_index(
590        &mut self,
591        ty: Ty<'tcx>,
592        base: PlaceIndex,
593        elem: TrackElem,
594    ) -> PlaceIndex {
595        *self.projections.entry((base, elem)).or_insert_with(|| {
596            let next = self.places.push(PlaceInfo::new(ty, Some(elem)));
597            self.places[next].next_sibling = self.places[base].first_child;
598            self.places[base].first_child = Some(next);
599            next
600        })
601    }
602
603    #[tracing::instrument(level = "trace", skip(self, tcx, body), ret)]
604    pub fn register_place(
605        &mut self,
606        tcx: TyCtxt<'tcx>,
607        body: &Body<'tcx>,
608        place: Place<'tcx>,
609        tail: Option<TrackElem>,
610    ) -> Option<PlaceIndex> {
611        // Create a place for this projection.
612        let mut place_index = self.locals[place.local]?;
613        let mut ty = PlaceTy::from_ty(body.local_decls[place.local].ty);
614        tracing::trace!(?place_index, ?ty);
615
616        for proj in place.projection {
617            let track_elem = proj.try_into().ok()?;
618            ty = ty.projection_ty(tcx, proj);
619            place_index = self.register_place_index(ty.ty, place_index, track_elem);
620            tracing::trace!(?proj, ?place_index, ?ty);
621        }
622
623        if let Some(tail) = tail {
624            let ty = match tail {
625                TrackElem::Discriminant => ty.ty.discriminant_ty(tcx),
626                TrackElem::Variant(..) | TrackElem::Field(..) => todo!(),
627                TrackElem::DerefLen => tcx.types.usize,
628            };
629            place_index = self.register_place_index(ty, place_index, tail);
630        }
631
632        Some(place_index)
633    }
634
635    #[tracing::instrument(level = "trace", skip(self, tcx, body), ret)]
636    fn register_place_and_discr(
637        &mut self,
638        tcx: TyCtxt<'tcx>,
639        body: &Body<'tcx>,
640        place: Place<'tcx>,
641    ) -> Option<PlaceIndex> {
642        let place = self.register_place(tcx, body, place, None)?;
643        let ty = self.places[place].ty;
644
645        if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind()
646            && let ty::Slice(..) = ref_ty.kind()
647        {
648            self.register_place_index(tcx.types.usize, place, TrackElem::DerefLen);
649        } else if ty.is_enum() {
650            let discriminant_ty = ty.discriminant_ty(tcx);
651            self.register_place_index(discriminant_ty, place, TrackElem::Discriminant);
652        }
653
654        Some(place)
655    }
656
657    #[tracing::instrument(level = "trace", skip(self, tcx, typing_env), ret)]
658    pub fn register_value(
659        &mut self,
660        tcx: TyCtxt<'tcx>,
661        typing_env: ty::TypingEnv<'tcx>,
662        place: PlaceIndex,
663    ) -> Option<ValueIndex> {
664        let place_info = &mut self.places[place];
665        if let Some(value) = place_info.value_index {
666            return Some(value);
667        }
668
669        if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, place_info.ty) {
670            place_info.ty = ty;
671        }
672
673        // Allocate a value slot if it doesn't have one, and the user requested one.
674        if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(place_info.ty))
675            && layout.backend_repr.is_scalar()
676        {
677            place_info.value_index = Some(self.value_count.into());
678            self.value_count += 1;
679        }
680
681        place_info.value_index
682    }
683
684    #[tracing::instrument(level = "trace", skip(self, f))]
685    pub fn register_copy_tree(
686        &mut self,
687        // Tree to copy.
688        source: PlaceIndex,
689        // Tree to build.
690        target: PlaceIndex,
691        f: &mut impl FnMut(ValueIndex, ValueIndex),
692    ) {
693        if let Some(source_value) = self.places[source].value_index {
694            let target_value = *self.places[target].value_index.get_or_insert_with(|| {
695                let value_index = self.value_count.into();
696                self.value_count += 1;
697                value_index
698            });
699            f(source_value, target_value)
700        }
701
702        // Iterate over `source` children and recurse.
703        let mut source_child_iter = self.places[source].first_child;
704        while let Some(source_child) = source_child_iter {
705            source_child_iter = self.places[source_child].next_sibling;
706
707            // Try to find corresponding child and recurse. Reasoning is similar as above.
708            let source_info = &self.places[source_child];
709            let source_ty = source_info.ty;
710            let source_elem = source_info.proj_elem.unwrap();
711            let target_child = self.register_place_index(source_ty, target, source_elem);
712            self.register_copy_tree(source_child, target_child, f);
713        }
714    }
715
716    /// Precompute the list of values inside `root` and store it inside
717    /// as a slice within `inner_values_buffer`.
718    #[tracing::instrument(level = "trace", skip(self))]
719    fn cache_preorder_invoke(&mut self, root: PlaceIndex) {
720        debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. });
721        let start = self.inner_values_buffer.len();
722        if let Some(vi) = self.places[root].value_index {
723            self.inner_values_buffer.push(vi);
724        }
725
726        // We manually iterate instead of using `children` as we need to mutate `self`.
727        let mut next_child = self.places[root].first_child;
728        while let Some(child) = next_child {
729            ensure_sufficient_stack(|| self.cache_preorder_invoke(child));
730            next_child = self.places[child].next_sibling;
731        }
732
733        let end = self.inner_values_buffer.len();
734        self.inner_values[root] = start..end;
735    }
736}
737
738struct PlaceCollector<'a, 'tcx> {
739    tcx: TyCtxt<'tcx>,
740    body: &'a Body<'tcx>,
741    map: &'a mut Map<'tcx>,
742}
743
744impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> {
745    #[tracing::instrument(level = "trace", skip(self))]
746    fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, _: Location) {
747        if !ctxt.is_use() {
748            return;
749        }
750
751        self.map.register_place_and_discr(self.tcx, self.body, *place);
752    }
753}
754
755impl<'tcx> Map<'tcx> {
756    /// Applies a single projection element, yielding the corresponding child.
757    pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
758        self.projections.get(&(place, elem)).copied()
759    }
760
761    /// Locates the given place, if it exists in the tree.
762    fn find_extra(
763        &self,
764        place: PlaceRef<'_>,
765        extra: impl IntoIterator<Item = TrackElem>,
766    ) -> Option<PlaceIndex> {
767        let mut index = *self.locals[place.local].as_ref()?;
768
769        for &elem in place.projection {
770            index = self.apply(index, elem.try_into().ok()?)?;
771        }
772        for elem in extra {
773            index = self.apply(index, elem)?;
774        }
775
776        Some(index)
777    }
778
779    /// Locates the given place, if it exists in the tree.
780    pub fn find(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
781        self.find_extra(place, [])
782    }
783
784    /// Locates the given place and applies `Discriminant`, if it exists in the tree.
785    pub fn find_discr(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
786        self.find_extra(place, [TrackElem::Discriminant])
787    }
788
789    /// Locates the given place and applies `DerefLen`, if it exists in the tree.
790    pub fn find_len(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
791        self.find_extra(place, [TrackElem::DerefLen])
792    }
793
794    /// Locates the value corresponding to the given place.
795    pub fn value(&self, place: PlaceIndex) -> Option<ValueIndex> {
796        self.places[place].value_index
797    }
798
799    /// Locates the value corresponding to the given place.
800    pub fn find_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
801        self.value(self.find(place)?)
802    }
803
804    /// Locates the value corresponding to the given discriminant.
805    pub fn find_discr_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
806        self.value(self.find_discr(place)?)
807    }
808
809    /// Locates the value corresponding to the given length.
810    pub fn find_len_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
811        self.value(self.find_len(place)?)
812    }
813
814    /// Iterate over all direct children.
815    fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> {
816        Children::new(self, parent)
817    }
818
819    /// Invoke a function on the given place and all places that may alias it.
820    ///
821    /// In particular, when the given place has a variant downcast, we invoke the function on all
822    /// the other variants.
823    ///
824    /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
825    /// as such.
826    #[tracing::instrument(level = "trace", skip(self, f))]
827    pub fn for_each_aliasing_place(
828        &self,
829        place: PlaceRef<'_>,
830        tail_elem: Option<TrackElem>,
831        f: &mut impl FnMut(ValueIndex),
832    ) {
833        if place.is_indirect_first_projection() {
834            // We do not track indirect places.
835            return;
836        }
837        let Some(mut index) = self.locals[place.local] else {
838            // The local is not tracked at all, so it does not alias anything.
839            return;
840        };
841        let elems = place.projection.iter().map(|&elem| elem.try_into()).chain(tail_elem.map(Ok));
842        for elem in elems {
843            // A field aliases the parent place.
844            if let Some(vi) = self.places[index].value_index {
845                f(vi);
846            }
847
848            let Ok(elem) = elem else { return };
849            let sub = self.apply(index, elem);
850            if let TrackElem::Variant(..) | TrackElem::Discriminant = elem {
851                // Enum variant fields and enum discriminants alias each another.
852                self.for_each_variant_sibling(index, sub, f);
853            }
854            let Some(sub) = sub else { return };
855            index = sub;
856        }
857        self.for_each_value_inside(index, f);
858    }
859
860    /// Invoke the given function on all the descendants of the given place, except one branch.
861    #[tracing::instrument(level = "trace", skip(self, f))]
862    fn for_each_variant_sibling(
863        &self,
864        parent: PlaceIndex,
865        preserved_child: Option<PlaceIndex>,
866        f: &mut impl FnMut(ValueIndex),
867    ) {
868        for sibling in self.children(parent) {
869            let elem = self.places[sibling].proj_elem;
870            // Only invalidate variants and discriminant. Fields (for coroutines) are not
871            // invalidated by assignment to a variant.
872            if let Some(TrackElem::Variant(..) | TrackElem::Discriminant) = elem
873                // Only invalidate the other variants, the current one is fine.
874                && Some(sibling) != preserved_child
875            {
876                self.for_each_value_inside(sibling, f);
877            }
878        }
879    }
880
881    /// Return the range of value indices inside this place.
882    pub fn values_inside(&self, root: PlaceIndex) -> &[ValueIndex] {
883        let range = self.inner_values[root].clone();
884        &self.inner_values_buffer[range]
885    }
886
887    /// Invoke a function on each value in the given place and all descendants.
888    #[tracing::instrument(level = "trace", skip(self, f))]
889    fn for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex)) {
890        if let Some(range) = self.inner_values.get(root) {
891            // Optimized path: we have cached the inner values.
892            let values = &self.inner_values_buffer[range.clone()];
893            for &v in values {
894                f(v)
895            }
896        } else {
897            if let Some(root) = self.places[root].value_index {
898                f(root)
899            }
900
901            for child in self.children(root) {
902                self.for_each_value_inside(child, f);
903            }
904        }
905    }
906
907    /// Invoke a function on each value in the given place and all descendants.
908    pub fn for_each_projection_value<O>(
909        &self,
910        root: PlaceIndex,
911        value: O,
912        project: &mut impl FnMut(TrackElem, &O) -> Option<O>,
913        f: &mut impl FnMut(PlaceIndex, &O),
914    ) {
915        // Fast path is there is nothing to do.
916        if let Some(value_range) = self.inner_values.get(root)
917            && value_range.is_empty()
918        {
919            return;
920        }
921
922        if self.places[root].value_index.is_some() {
923            f(root, &value)
924        }
925
926        for child in self.children(root) {
927            let elem = self.places[child].proj_elem.unwrap();
928            if let Some(value) = project(elem, &value) {
929                self.for_each_projection_value(child, value, project, f);
930            }
931        }
932    }
933
934    /// Recursively iterates on each value contained in `target`, paired with matching projection
935    /// inside `source`.
936    pub fn for_each_value_pair(
937        &self,
938        target: PlaceIndex,
939        source: PlaceIndex,
940        f: &mut impl FnMut(ValueIndex, ValueIndex),
941    ) {
942        // If both places are tracked, we copy the value to the target.
943        // If the target is tracked, but the source is not, we do nothing, as invalidation has
944        // already been performed.
945        if let Some(target_value) = self.places[target].value_index
946            && let Some(source_value) = self.places[source].value_index
947        {
948            f(target_value, source_value)
949        }
950        for target_child in self.children(target) {
951            // Try to find corresponding child and recurse. Reasoning is similar as above.
952            let projection = self.places[target_child].proj_elem.unwrap();
953            if let Some(source_child) = self.projections.get(&(source, projection)) {
954                self.for_each_value_pair(target_child, *source_child, f);
955            }
956        }
957    }
958}
959
960/// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`].
961///
962/// Together, `first_child` and `next_sibling` form an intrusive linked list, which is used to
963/// model a tree structure (a replacement for a member like `children: Vec<PlaceIndex>`).
964#[derive(Debug)]
965struct PlaceInfo<'tcx> {
966    /// Type of the referenced place.
967    ty: Ty<'tcx>,
968
969    /// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
970    value_index: Option<ValueIndex>,
971
972    /// The projection used to go from parent to this node (only None for root).
973    proj_elem: Option<TrackElem>,
974
975    /// The leftmost child.
976    first_child: Option<PlaceIndex>,
977
978    /// Index of the sibling to the right of this node.
979    next_sibling: Option<PlaceIndex>,
980}
981
982impl<'tcx> PlaceInfo<'tcx> {
983    fn new(ty: Ty<'tcx>, proj_elem: Option<TrackElem>) -> Self {
984        Self { ty, next_sibling: None, first_child: None, proj_elem, value_index: None }
985    }
986}
987
988struct Children<'a, 'tcx> {
989    map: &'a Map<'tcx>,
990    next: Option<PlaceIndex>,
991}
992
993impl<'a, 'tcx> Children<'a, 'tcx> {
994    fn new(map: &'a Map<'tcx>, parent: PlaceIndex) -> Self {
995        Self { map, next: map.places[parent].first_child }
996    }
997}
998
999impl Iterator for Children<'_, '_> {
1000    type Item = PlaceIndex;
1001
1002    fn next(&mut self) -> Option<Self::Item> {
1003        match self.next {
1004            Some(child) => {
1005                self.next = self.map.places[child].next_sibling;
1006                Some(child)
1007            }
1008            None => None,
1009        }
1010    }
1011}
1012
1013/// Used as the result of an operand or r-value.
1014#[derive(Debug)]
1015pub enum ValueOrPlace<V> {
1016    Value(V),
1017    Place(PlaceIndex),
1018}
1019
1020impl<V: HasTop> ValueOrPlace<V> {
1021    pub const TOP: Self = ValueOrPlace::Value(V::TOP);
1022}
1023
1024/// The set of projection elements that can be used by a tracked place.
1025///
1026/// Although only field projections are currently allowed, this could change in the future.
1027#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1028pub enum TrackElem {
1029    Field(FieldIdx),
1030    Variant(VariantIdx),
1031    Discriminant,
1032    // Length of a slice.
1033    DerefLen,
1034}
1035
1036impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
1037    type Error = ();
1038
1039    fn try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error> {
1040        match value {
1041            ProjectionElem::Field(field, _) => Ok(TrackElem::Field(field)),
1042            ProjectionElem::Downcast(_, idx) => Ok(TrackElem::Variant(idx)),
1043            _ => Err(()),
1044        }
1045    }
1046}
1047
1048/// Invokes `f` on all direct fields of `ty`.
1049pub fn iter_fields<'tcx>(
1050    ty: Ty<'tcx>,
1051    tcx: TyCtxt<'tcx>,
1052    typing_env: ty::TypingEnv<'tcx>,
1053    mut f: impl FnMut(Option<VariantIdx>, FieldIdx, Ty<'tcx>),
1054) {
1055    match ty.kind() {
1056        ty::Tuple(list) => {
1057            for (field, ty) in list.iter().enumerate() {
1058                f(None, field.into(), ty);
1059            }
1060        }
1061        ty::Adt(def, args) => {
1062            if def.is_union() {
1063                return;
1064            }
1065            for (v_index, v_def) in def.variants().iter_enumerated() {
1066                let variant = if def.is_struct() { None } else { Some(v_index) };
1067                for (f_index, f_def) in v_def.fields.iter().enumerate() {
1068                    let field_ty = f_def.ty(tcx, args);
1069                    let field_ty = tcx
1070                        .try_normalize_erasing_regions(typing_env, field_ty)
1071                        .unwrap_or_else(|_| tcx.erase_and_anonymize_regions(field_ty));
1072                    f(variant, f_index.into(), field_ty);
1073                }
1074            }
1075        }
1076        ty::Closure(_, args) => {
1077            iter_fields(args.as_closure().tupled_upvars_ty(), tcx, typing_env, f);
1078        }
1079        ty::Coroutine(_, args) => {
1080            iter_fields(args.as_coroutine().tupled_upvars_ty(), tcx, typing_env, f);
1081        }
1082        ty::CoroutineClosure(_, args) => {
1083            iter_fields(args.as_coroutine_closure().tupled_upvars_ty(), tcx, typing_env, f);
1084        }
1085        _ => (),
1086    }
1087}
1088
1089/// Returns all locals with projections that have their reference or address taken.
1090pub fn excluded_locals(body: &Body<'_>) -> DenseBitSet<Local> {
1091    struct Collector {
1092        result: DenseBitSet<Local>,
1093    }
1094
1095    impl<'tcx> Visitor<'tcx> for Collector {
1096        fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
1097            if context.may_observe_address() && !place.is_indirect() {
1098                // A pointer to a place could be used to access other places with the same local,
1099                // hence we have to exclude the local completely.
1100                self.result.insert(place.local);
1101            }
1102        }
1103    }
1104
1105    let mut collector = Collector { result: DenseBitSet::new_empty(body.local_decls.len()) };
1106    collector.visit_body(body);
1107    collector.result
1108}
1109
1110fn debug_with_context_rec<V: Debug + Eq + HasBottom>(
1111    place: PlaceIndex,
1112    place_str: &str,
1113    new: &StateData<V>,
1114    old: Option<&StateData<V>>,
1115    map: &Map<'_>,
1116    f: &mut Formatter<'_>,
1117) -> std::fmt::Result {
1118    if let Some(value) = map.places[place].value_index {
1119        match old {
1120            None => writeln!(f, "{}: {:?}", place_str, new.get(value))?,
1121            Some(old) => {
1122                if new.get(value) != old.get(value) {
1123                    writeln!(f, "\u{001f}-{}: {:?}", place_str, old.get(value))?;
1124                    writeln!(f, "\u{001f}+{}: {:?}", place_str, new.get(value))?;
1125                }
1126            }
1127        }
1128    }
1129
1130    for child in map.children(place) {
1131        let info_elem = map.places[child].proj_elem.unwrap();
1132        let child_place_str = match info_elem {
1133            TrackElem::Discriminant => {
1134                format!("discriminant({place_str})")
1135            }
1136            TrackElem::Variant(idx) => {
1137                format!("({place_str} as {idx:?})")
1138            }
1139            TrackElem::Field(field) => {
1140                if place_str.starts_with('*') {
1141                    format!("({}).{}", place_str, field.index())
1142                } else {
1143                    format!("{}.{}", place_str, field.index())
1144                }
1145            }
1146            TrackElem::DerefLen => {
1147                format!("Len(*{})", place_str)
1148            }
1149        };
1150        debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
1151    }
1152
1153    Ok(())
1154}
1155
1156pub fn debug_with_context<V: Debug + Eq + HasBottom>(
1157    new: &StateData<V>,
1158    old: Option<&StateData<V>>,
1159    map: &Map<'_>,
1160    f: &mut Formatter<'_>,
1161) -> std::fmt::Result {
1162    for (local, place) in map.locals.iter_enumerated() {
1163        if let Some(place) = place {
1164            debug_with_context_rec(*place, &format!("{local:?}"), new, old, map, f)?;
1165        }
1166    }
1167    Ok(())
1168}