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 pub struct PlaceIndex {}
24);
25
26rustc_index::newtype_index!(
27 pub struct ValueIndex {}
29);
30
31#[derive(PartialEq, Eq, Debug)]
33pub struct StateData<V> {
34 bottom: V,
35 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#[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 State::Unreachable => V::BOTTOM,
279 }
280 }
281
282 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 State::Unreachable => V::BOTTOM,
293 }
294 }
295
296 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 State::Unreachable => V::BOTTOM,
307 }
308 }
309
310 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 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#[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 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 #[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 #[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 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 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 #[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 #[tracing::instrument(level = "trace", skip(self, tcx, body))]
436 fn propagate_assignments(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
437 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 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 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 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 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 #[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 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 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 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 #[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 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 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 source: PlaceIndex,
689 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 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 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 #[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 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 pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
758 self.projections.get(&(place, elem)).copied()
759 }
760
761 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 pub fn find(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
781 self.find_extra(place, [])
782 }
783
784 pub fn find_discr(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
786 self.find_extra(place, [TrackElem::Discriminant])
787 }
788
789 pub fn find_len(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
791 self.find_extra(place, [TrackElem::DerefLen])
792 }
793
794 pub fn value(&self, place: PlaceIndex) -> Option<ValueIndex> {
796 self.places[place].value_index
797 }
798
799 pub fn find_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
801 self.value(self.find(place)?)
802 }
803
804 pub fn find_discr_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
806 self.value(self.find_discr(place)?)
807 }
808
809 pub fn find_len_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
811 self.value(self.find_len(place)?)
812 }
813
814 fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> {
816 Children::new(self, parent)
817 }
818
819 #[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 return;
836 }
837 let Some(mut index) = self.locals[place.local] else {
838 return;
840 };
841 let elems = place.projection.iter().map(|&elem| elem.try_into()).chain(tail_elem.map(Ok));
842 for elem in elems {
843 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 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 #[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 if let Some(TrackElem::Variant(..) | TrackElem::Discriminant) = elem
873 && Some(sibling) != preserved_child
875 {
876 self.for_each_value_inside(sibling, f);
877 }
878 }
879 }
880
881 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 #[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 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 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 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 pub fn for_each_value_pair(
937 &self,
938 target: PlaceIndex,
939 source: PlaceIndex,
940 f: &mut impl FnMut(ValueIndex, ValueIndex),
941 ) {
942 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 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#[derive(Debug)]
965struct PlaceInfo<'tcx> {
966 ty: Ty<'tcx>,
968
969 value_index: Option<ValueIndex>,
971
972 proj_elem: Option<TrackElem>,
974
975 first_child: Option<PlaceIndex>,
977
978 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#[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#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1028pub enum TrackElem {
1029 Field(FieldIdx),
1030 Variant(VariantIdx),
1031 Discriminant,
1032 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
1048pub 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
1089pub 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 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}