]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir_dataflow/src/value_analysis.rs
401db890a9810d1f811d63390c9f082f8beb57a9
[rustc.git] / compiler / rustc_mir_dataflow / src / value_analysis.rs
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).
5 //!
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`.
10 //!
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.
15 //!
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.
20 //!
21 //!
22 //! # Notes
23 //!
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.
26 //!
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.
29 //!
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.
34
35 use std::fmt::{Debug, Formatter};
36
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;
44
45 use crate::lattice::{HasBottom, HasTop};
46 use crate::{
47 fmt::DebugWithContext, Analysis, AnalysisDomain, CallReturnPlaces, JoinSemiLattice,
48 SwitchIntEdgeEffects,
49 };
50
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;
54
55 const NAME: &'static str;
56
57 fn map(&self) -> &Map;
58
59 fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
60 self.super_statement(statement, state)
61 }
62
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);
67 }
68 StatementKind::SetDiscriminant { box ref place, .. } => {
69 state.flood_discr(place.as_ref(), self.map());
70 }
71 StatementKind::Intrinsic(box intrinsic) => {
72 self.handle_intrinsic(intrinsic, state);
73 }
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());
78 }
79 StatementKind::Deinit(box place) => {
80 // Deinit makes the place uninitialized.
81 state.flood_with(place.as_ref(), self.map(), Self::Value::bottom());
82 }
83 StatementKind::Retag(..) => {
84 // We don't track references.
85 }
86 StatementKind::ConstEvalCounter
87 | StatementKind::Nop
88 | StatementKind::FakeRead(..)
89 | StatementKind::Coverage(..)
90 | StatementKind::AscribeUserType(..) => (),
91 }
92 }
93
94 fn handle_intrinsic(
95 &self,
96 intrinsic: &NonDivergingIntrinsic<'tcx>,
97 state: &mut State<Self::Value>,
98 ) {
99 self.super_intrinsic(intrinsic, state);
100 }
101
102 fn super_intrinsic(
103 &self,
104 intrinsic: &NonDivergingIntrinsic<'tcx>,
105 state: &mut State<Self::Value>,
106 ) {
107 match intrinsic {
108 NonDivergingIntrinsic::Assume(..) => {
109 // Could use this, but ignoring it is sound.
110 }
111 NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { dst, .. }) => {
112 if let Some(place) = dst.place() {
113 state.flood(place.as_ref(), self.map());
114 }
115 }
116 }
117 }
118
119 fn handle_assign(
120 &self,
121 target: Place<'tcx>,
122 rvalue: &Rvalue<'tcx>,
123 state: &mut State<Self::Value>,
124 ) {
125 self.super_assign(target, rvalue, state)
126 }
127
128 fn super_assign(
129 &self,
130 target: Place<'tcx>,
131 rvalue: &Rvalue<'tcx>,
132 state: &mut State<Self::Value>,
133 ) {
134 let result = self.handle_rvalue(rvalue, state);
135 state.assign(target.as_ref(), result, self.map());
136 }
137
138 fn handle_rvalue(
139 &self,
140 rvalue: &Rvalue<'tcx>,
141 state: &mut State<Self::Value>,
142 ) -> ValueOrPlace<Self::Value> {
143 self.super_rvalue(rvalue, state)
144 }
145
146 fn super_rvalue(
147 &self,
148 rvalue: &Rvalue<'tcx>,
149 state: &mut State<Self::Value>,
150 ) -> ValueOrPlace<Self::Value> {
151 match rvalue {
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.
156 ValueOrPlace::top()
157 }
158 Rvalue::Repeat(..)
159 | Rvalue::ThreadLocalRef(..)
160 | Rvalue::Len(..)
161 | Rvalue::Cast(..)
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.
170 ValueOrPlace::top()
171 }
172 }
173 }
174
175 fn handle_operand(
176 &self,
177 operand: &Operand<'tcx>,
178 state: &mut State<Self::Value>,
179 ) -> ValueOrPlace<Self::Value> {
180 self.super_operand(operand, state)
181 }
182
183 fn super_operand(
184 &self,
185 operand: &Operand<'tcx>,
186 state: &mut State<Self::Value>,
187 ) -> ValueOrPlace<Self::Value> {
188 match operand {
189 Operand::Constant(box constant) => {
190 ValueOrPlace::Value(self.handle_constant(constant, state))
191 }
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`).
195 self.map()
196 .find(place.as_ref())
197 .map(ValueOrPlace::Place)
198 .unwrap_or(ValueOrPlace::top())
199 }
200 }
201 }
202
203 fn handle_constant(
204 &self,
205 constant: &Constant<'tcx>,
206 state: &mut State<Self::Value>,
207 ) -> Self::Value {
208 self.super_constant(constant, state)
209 }
210
211 fn super_constant(
212 &self,
213 _constant: &Constant<'tcx>,
214 _state: &mut State<Self::Value>,
215 ) -> Self::Value {
216 Self::Value::top()
217 }
218
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)
223 }
224
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`.
229 }
230 TerminatorKind::Drop { place, .. } => {
231 state.flood_with(place.as_ref(), self.map(), Self::Value::bottom());
232 }
233 TerminatorKind::DropAndReplace { .. } | TerminatorKind::Yield { .. } => {
234 // They would have an effect, but are not allowed in this phase.
235 bug!("encountered disallowed terminator");
236 }
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.
248 }
249 }
250 }
251
252 fn handle_call_return(
253 &self,
254 return_places: CallReturnPlaces<'_, 'tcx>,
255 state: &mut State<Self::Value>,
256 ) {
257 self.super_call_return(return_places, state)
258 }
259
260 fn super_call_return(
261 &self,
262 return_places: CallReturnPlaces<'_, 'tcx>,
263 state: &mut State<Self::Value>,
264 ) {
265 return_places.for_each(|place| {
266 state.flood(place.as_ref(), self.map());
267 })
268 }
269
270 fn handle_switch_int(
271 &self,
272 discr: &Operand<'tcx>,
273 apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
274 ) {
275 self.super_switch_int(discr, apply_edge_effects)
276 }
277
278 fn super_switch_int(
279 &self,
280 _discr: &Operand<'tcx>,
281 _apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
282 ) {
283 }
284
285 fn wrap(self) -> ValueAnalysisWrapper<Self>
286 where
287 Self: Sized,
288 {
289 ValueAnalysisWrapper(self)
290 }
291 }
292
293 pub struct ValueAnalysisWrapper<T>(pub T);
294
295 impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper<T> {
296 type Domain = State<T::Value>;
297
298 type Direction = crate::Forward;
299
300 const NAME: &'static str = T::NAME;
301
302 fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
303 State(StateData::Unreachable)
304 }
305
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());
313 }
314 }
315 }
316
317 impl<'tcx, T> Analysis<'tcx> for ValueAnalysisWrapper<T>
318 where
319 T: ValueAnalysis<'tcx>,
320 {
321 fn apply_statement_effect(
322 &self,
323 state: &mut Self::Domain,
324 statement: &Statement<'tcx>,
325 _location: Location,
326 ) {
327 if state.is_reachable() {
328 self.0.handle_statement(statement, state);
329 }
330 }
331
332 fn apply_terminator_effect(
333 &self,
334 state: &mut Self::Domain,
335 terminator: &Terminator<'tcx>,
336 _location: Location,
337 ) {
338 if state.is_reachable() {
339 self.0.handle_terminator(terminator, state);
340 }
341 }
342
343 fn apply_call_return_effect(
344 &self,
345 state: &mut Self::Domain,
346 _block: BasicBlock,
347 return_places: crate::CallReturnPlaces<'_, 'tcx>,
348 ) {
349 if state.is_reachable() {
350 self.0.handle_call_return(return_places, state)
351 }
352 }
353
354 fn apply_switch_int_edge_effects(
355 &self,
356 _block: BasicBlock,
357 discr: &Operand<'tcx>,
358 apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
359 ) {
360 // FIXME: Dataflow framework provides no access to current state here.
361 self.0.handle_switch_int(discr, apply_edge_effects)
362 }
363 }
364
365 rustc_index::newtype_index!(
366 /// This index uniquely identifies a place.
367 ///
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 {}
371 );
372
373 rustc_index::newtype_index!(
374 /// This index uniquely identifies a tracked place and therefore a slot in [`State`].
375 ///
376 /// It is an implementation detail of this module.
377 struct ValueIndex {}
378 );
379
380 /// See [`State`].
381 #[derive(PartialEq, Eq, Debug)]
382 enum StateData<V> {
383 Reachable(IndexVec<ValueIndex, V>),
384 Unreachable,
385 }
386
387 impl<V: Clone> Clone for StateData<V> {
388 fn clone(&self) -> Self {
389 match self {
390 Self::Reachable(x) => Self::Reachable(x.clone()),
391 Self::Unreachable => Self::Unreachable,
392 }
393 }
394
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);
400 }
401 _ => *self = source.clone(),
402 }
403 }
404 }
405
406 /// The dataflow state for an instance of [`ValueAnalysis`].
407 ///
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.
414 ///
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>);
418
419 impl<V: Clone> Clone for State<V> {
420 fn clone(&self) -> Self {
421 Self(self.0.clone())
422 }
423
424 fn clone_from(&mut self, source: &Self) {
425 self.0.clone_from(&source.0);
426 }
427 }
428
429 impl<V: Clone + HasTop + HasBottom> State<V> {
430 pub fn is_reachable(&self) -> bool {
431 matches!(&self.0, StateData::Reachable(_))
432 }
433
434 pub fn mark_unreachable(&mut self) {
435 self.0 = StateData::Unreachable;
436 }
437
438 pub fn flood_all(&mut self) {
439 self.flood_all_with(V::top())
440 }
441
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);
445 }
446
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();
452 }
453 });
454 }
455
456 pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) {
457 self.flood_with(place, map, V::top())
458 }
459
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();
465 }
466 });
467 }
468
469 pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) {
470 self.flood_discr_with(place, map, V::top())
471 }
472
473 /// Low-level method that assigns to a place.
474 /// This does nothing if the place is not tracked.
475 ///
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) {
478 match result {
479 ValueOrPlace::Value(value) => self.insert_value_idx(target, value, map),
480 ValueOrPlace::Place(source) => self.insert_place_idx(target, source, map),
481 }
482 }
483
484 /// Low-level method that assigns a value to a place.
485 /// This does nothing if the place is not tracked.
486 ///
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;
492 }
493 }
494
495 /// Copies `source` to `target`, including all tracked places beneath.
496 ///
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.
500 ///
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 };
504
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();
511 }
512 }
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);
518 }
519 }
520 }
521
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);
527 }
528 }
529
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);
535 }
536 }
537
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())
541 }
542
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),
547 None => V::top(),
548 }
549 }
550
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 {
553 match &self.0 {
554 StateData::Reachable(values) => {
555 map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::top())
556 }
557 StateData::Unreachable => {
558 // Because this is unreachable, we can return any value we want.
559 V::bottom()
560 }
561 }
562 }
563 }
564
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();
571 true
572 }
573 (StateData::Reachable(this), StateData::Reachable(other)) => this.join(other),
574 }
575 }
576 }
577
578 /// Partial mapping from [`Place`] to [`PlaceIndex`], where some places also have a [`ValueIndex`].
579 ///
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.
584 #[derive(Debug)]
585 pub struct Map {
586 locals: IndexVec<Local, Option<PlaceIndex>>,
587 projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
588 places: IndexVec<PlaceIndex, PlaceInfo>,
589 value_count: usize,
590 }
591
592 impl Map {
593 fn new() -> Self {
594 Self {
595 locals: IndexVec::new(),
596 projections: FxHashMap::default(),
597 places: IndexVec::new(),
598 value_count: 0,
599 }
600 }
601
602 /// Returns a map that only tracks places whose type passes the filter.
603 ///
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>(
608 tcx: TyCtxt<'tcx>,
609 body: &Body<'tcx>,
610 filter: impl FnMut(Ty<'tcx>) -> bool,
611 place_limit: Option<usize>,
612 ) -> Self {
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());
617 map
618 }
619
620 /// Register all non-excluded places that pass the filter.
621 fn register_with_filter<'tcx>(
622 &mut self,
623 tcx: TyCtxt<'tcx>,
624 body: &Body<'tcx>,
625 mut filter: impl FnMut(Ty<'tcx>) -> bool,
626 exclude: BitSet<Local>,
627 place_limit: Option<usize>,
628 ) {
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(
634 tcx,
635 local,
636 &mut projection,
637 decl.ty,
638 &mut filter,
639 place_limit,
640 );
641 }
642 }
643 }
644
645 /// Potentially register the (local, projection) place and its fields, recursively.
646 ///
647 /// Invariant: The projection must only contain trackable elements.
648 fn register_with_filter_rec<'tcx>(
649 &mut self,
650 tcx: TyCtxt<'tcx>,
651 local: Local,
652 projection: &mut Vec<PlaceElem<'tcx>>,
653 ty: Ty<'tcx>,
654 filter: &mut impl FnMut(Ty<'tcx>) -> bool,
655 place_limit: Option<usize>,
656 ) {
657 if let Some(place_limit) = place_limit && self.value_count >= place_limit {
658 return
659 }
660
661 // We know that the projection only contains trackable elements.
662 let place = self.make_place(local, projection).unwrap();
663
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;
668 }
669
670 if ty.is_enum() {
671 let discr_ty = ty.discriminant_ty(tcx);
672 if filter(discr_ty) {
673 let discr = *self
674 .projections
675 .entry((place, TrackElem::Discriminant))
676 .or_insert_with(|| {
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);
681 next
682 });
683
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;
688 }
689 }
690 }
691
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);
699 projection.pop();
700 projection.pop();
701 return;
702 }
703 projection.push(PlaceElem::Field(field, ty));
704 self.register_with_filter_rec(tcx, local, projection, ty, filter, place_limit);
705 projection.pop();
706 });
707 }
708
709 /// Tries to add the place to the map, without allocating a value slot.
710 ///
711 /// Can fail if the projection contains non-trackable elements.
712 fn make_place<'tcx>(
713 &mut self,
714 local: Local,
715 projection: &[PlaceElem<'tcx>],
716 ) -> Result<PlaceIndex, ()> {
717 // Get the base index of the local.
718 let mut index =
719 *self.locals.get_or_insert_with(local, || self.places.push(PlaceInfo::new(None)));
720
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);
729 next
730 });
731 }
732
733 Ok(index)
734 }
735
736 /// Returns the number of tracked places, i.e., those for which a value can be stored.
737 pub fn tracked_places(&self) -> usize {
738 self.value_count
739 }
740
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()
744 }
745
746 /// Locates the given place, if it exists in the tree.
747 pub fn find_extra(
748 &self,
749 place: PlaceRef<'_>,
750 extra: impl IntoIterator<Item = TrackElem>,
751 ) -> Option<PlaceIndex> {
752 let mut index = *self.locals.get(place.local)?.as_ref()?;
753
754 for &elem in place.projection {
755 index = self.apply(index, elem.try_into().ok()?)?;
756 }
757 for elem in extra {
758 index = self.apply(index, elem)?;
759 }
760
761 Some(index)
762 }
763
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, [])
767 }
768
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])
772 }
773
774 /// Iterate over all direct children.
775 pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
776 Children::new(self, parent)
777 }
778
779 /// Invoke a function on the given place and all places that may alias it.
780 ///
781 /// In particular, when the given place has a variant downcast, we invoke the function on all
782 /// the other variants.
783 ///
784 /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
785 /// as such.
786 pub fn for_each_aliasing_place(
787 &self,
788 place: PlaceRef<'_>,
789 tail_elem: Option<TrackElem>,
790 f: &mut impl FnMut(PlaceIndex),
791 ) {
792 if place.is_indirect() {
793 // We do not track indirect places.
794 return;
795 }
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.
798 return;
799 };
800 let elems = place
801 .projection
802 .iter()
803 .map(|&elem| elem.try_into())
804 .chain(tail_elem.map(Ok).into_iter());
805 for elem in elems {
806 // A field aliases the parent place.
807 f(index);
808
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);
814 }
815 if let Some(sub) = sub {
816 index = sub
817 } else {
818 return;
819 }
820 }
821 self.preorder_invoke(index, f);
822 }
823
824 /// Invoke the given function on all the descendants of the given place, except one branch.
825 fn for_each_variant_sibling(
826 &self,
827 parent: PlaceIndex,
828 preserved_child: Option<PlaceIndex>,
829 f: &mut impl FnMut(PlaceIndex),
830 ) {
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
838 {
839 self.preorder_invoke(sibling, f);
840 }
841 }
842 }
843
844 /// Invoke a function on the given place and all descendants.
845 fn preorder_invoke(&self, root: PlaceIndex, f: &mut impl FnMut(PlaceIndex)) {
846 f(root);
847 for child in self.children(root) {
848 self.preorder_invoke(child, f);
849 }
850 }
851 }
852
853 /// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`].
854 ///
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>`).
857 #[derive(Debug)]
858 struct PlaceInfo {
859 /// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
860 value_index: Option<ValueIndex>,
861
862 /// The projection used to go from parent to this node (only None for root).
863 proj_elem: Option<TrackElem>,
864
865 /// The left-most child.
866 first_child: Option<PlaceIndex>,
867
868 /// Index of the sibling to the right of this node.
869 next_sibling: Option<PlaceIndex>,
870 }
871
872 impl PlaceInfo {
873 fn new(proj_elem: Option<TrackElem>) -> Self {
874 Self { next_sibling: None, first_child: None, proj_elem, value_index: None }
875 }
876 }
877
878 struct Children<'a> {
879 map: &'a Map,
880 next: Option<PlaceIndex>,
881 }
882
883 impl<'a> Children<'a> {
884 fn new(map: &'a Map, parent: PlaceIndex) -> Self {
885 Self { map, next: map.places[parent].first_child }
886 }
887 }
888
889 impl<'a> Iterator for Children<'a> {
890 type Item = PlaceIndex;
891
892 fn next(&mut self) -> Option<Self::Item> {
893 match self.next {
894 Some(child) => {
895 self.next = self.map.places[child].next_sibling;
896 Some(child)
897 }
898 None => None,
899 }
900 }
901 }
902
903 /// Used as the result of an operand or r-value.
904 #[derive(Debug)]
905 pub enum ValueOrPlace<V> {
906 Value(V),
907 Place(PlaceIndex),
908 }
909
910 impl<V: HasTop> ValueOrPlace<V> {
911 pub fn top() -> Self {
912 ValueOrPlace::Value(V::top())
913 }
914 }
915
916 /// The set of projection elements that can be used by a tracked place.
917 ///
918 /// Although only field projections are currently allowed, this could change in the future.
919 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
920 pub enum TrackElem {
921 Field(Field),
922 Variant(VariantIdx),
923 Discriminant,
924 }
925
926 impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
927 type Error = ();
928
929 fn try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error> {
930 match value {
931 ProjectionElem::Field(field, _) => Ok(TrackElem::Field(field)),
932 ProjectionElem::Downcast(_, idx) => Ok(TrackElem::Variant(idx)),
933 _ => Err(()),
934 }
935 }
936 }
937
938 /// Invokes `f` on all direct fields of `ty`.
939 pub fn iter_fields<'tcx>(
940 ty: Ty<'tcx>,
941 tcx: TyCtxt<'tcx>,
942 mut f: impl FnMut(Option<VariantIdx>, Field, Ty<'tcx>),
943 ) {
944 match ty.kind() {
945 ty::Tuple(list) => {
946 for (field, ty) in list.iter().enumerate() {
947 f(None, field.into(), ty);
948 }
949 }
950 ty::Adt(def, substs) => {
951 if def.is_union() {
952 return;
953 }
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);
958 let field_ty = tcx
959 .try_normalize_erasing_regions(ty::ParamEnv::reveal_all(), field_ty)
960 .unwrap_or(field_ty);
961 f(variant, f_index.into(), field_ty);
962 }
963 }
964 }
965 ty::Closure(_, substs) => {
966 iter_fields(substs.as_closure().tupled_upvars_ty(), tcx, f);
967 }
968 _ => (),
969 }
970 }
971
972 /// Returns all locals with projections that have their reference or address taken.
973 pub fn excluded_locals(body: &Body<'_>) -> BitSet<Local> {
974 struct Collector {
975 result: BitSet<Local>,
976 }
977
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()
982 || context.is_drop()
983 || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput))
984 && !place.is_indirect()
985 {
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);
989 }
990 }
991 }
992
993 let mut collector = Collector { result: BitSet::new_empty(body.local_decls.len()) };
994 collector.visit_body(body);
995 collector.result
996 }
997
998 /// This is used to visualize the dataflow analysis.
999 impl<'tcx, T> DebugWithContext<ValueAnalysisWrapper<T>> for State<T::Value>
1000 where
1001 T: ValueAnalysis<'tcx>,
1002 T::Value: Debug,
1003 {
1004 fn fmt_with(&self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>) -> std::fmt::Result {
1005 match &self.0 {
1006 StateData::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f),
1007 StateData::Unreachable => write!(f, "unreachable"),
1008 }
1009 }
1010
1011 fn fmt_diff_with(
1012 &self,
1013 old: &Self,
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)
1020 }
1021 _ => Ok(()), // Consider printing something here.
1022 }
1023 }
1024 }
1025
1026 fn debug_with_context_rec<V: Debug + Eq>(
1027 place: PlaceIndex,
1028 place_str: &str,
1029 new: &IndexVec<ValueIndex, V>,
1030 old: Option<&IndexVec<ValueIndex, V>>,
1031 map: &Map,
1032 f: &mut Formatter<'_>,
1033 ) -> std::fmt::Result {
1034 if let Some(value) = map.places[place].value_index {
1035 match old {
1036 None => writeln!(f, "{}: {:?}", place_str, new[value])?,
1037 Some(old) => {
1038 if new[value] != old[value] {
1039 writeln!(f, "\u{001f}-{}: {:?}", place_str, old[value])?;
1040 writeln!(f, "\u{001f}+{}: {:?}", place_str, new[value])?;
1041 }
1042 }
1043 }
1044 }
1045
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)
1051 }
1052 TrackElem::Variant(idx) => {
1053 format!("({} as {:?})", place_str, idx)
1054 }
1055 TrackElem::Field(field) => {
1056 if place_str.starts_with('*') {
1057 format!("({}).{}", place_str, field.index())
1058 } else {
1059 format!("{}.{}", place_str, field.index())
1060 }
1061 }
1062 };
1063 debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
1064 }
1065
1066 Ok(())
1067 }
1068
1069 fn debug_with_context<V: Debug + Eq>(
1070 new: &IndexVec<ValueIndex, V>,
1071 old: Option<&IndexVec<ValueIndex, V>>,
1072 map: &Map,
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)?;
1078 }
1079 }
1080 Ok(())
1081 }