1use std::fmt::{Debug, Formatter};
2use std::ops::Range;
3
4use rustc_abi::{FieldIdx, VariantIdx};
5use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry};
6use rustc_data_structures::stack::ensure_sufficient_stack;
7use rustc_index::IndexVec;
8use rustc_index::bit_set::DenseBitSet;
9use rustc_middle::mir::visit::{PlaceContext, Visitor};
10use rustc_middle::mir::*;
11use rustc_middle::ty::{self, Ty, TyCtxt};
12use tracing::debug;
13
14use crate::JoinSemiLattice;
15use crate::lattice::{HasBottom, HasTop};
16
17rustc_index::newtype_index!(
18 pub struct PlaceIndex {}
23);
24
25rustc_index::newtype_index!(
26 pub struct ValueIndex {}
28);
29
30#[derive(PartialEq, Eq, Debug)]
32pub struct StateData<V> {
33 bottom: V,
34 map: FxHashMap<ValueIndex, V>,
36}
37
38impl<V: HasBottom> StateData<V> {
39 fn new() -> StateData<V> {
40 StateData { bottom: V::BOTTOM, map: FxHashMap::default() }
41 }
42
43 fn get(&self, idx: ValueIndex) -> &V {
44 self.map.get(&idx).unwrap_or(&self.bottom)
45 }
46
47 fn insert(&mut self, idx: ValueIndex, elem: V) {
48 if elem.is_bottom() {
49 self.map.remove(&idx);
50 } else {
51 self.map.insert(idx, elem);
52 }
53 }
54}
55
56impl<V: Clone> Clone for StateData<V> {
57 fn clone(&self) -> Self {
58 StateData { bottom: self.bottom.clone(), map: self.map.clone() }
59 }
60
61 fn clone_from(&mut self, source: &Self) {
62 self.map.clone_from(&source.map)
63 }
64}
65
66impl<V: JoinSemiLattice + Clone> JoinSemiLattice for StateData<V> {
67 fn join(&mut self, other: &Self) -> bool {
68 let mut changed = false;
69 #[allow(rustc::potential_query_instability)]
70 for (i, v) in other.map.iter() {
71 match self.map.entry(*i) {
72 StdEntry::Vacant(e) => {
73 e.insert(v.clone());
74 changed = true
75 }
76 StdEntry::Occupied(e) => changed |= e.into_mut().join(v),
77 }
78 }
79 changed
80 }
81}
82
83#[derive(PartialEq, Eq, Debug)]
94pub enum State<V> {
95 Unreachable,
96 Reachable(StateData<V>),
97}
98
99impl<V: Clone> Clone for State<V> {
100 fn clone(&self) -> Self {
101 match self {
102 Self::Reachable(x) => Self::Reachable(x.clone()),
103 Self::Unreachable => Self::Unreachable,
104 }
105 }
106
107 fn clone_from(&mut self, source: &Self) {
108 match (&mut *self, source) {
109 (Self::Reachable(x), Self::Reachable(y)) => {
110 x.clone_from(&y);
111 }
112 _ => *self = source.clone(),
113 }
114 }
115}
116
117impl<V: Clone + HasBottom> State<V> {
118 pub fn new_reachable() -> State<V> {
119 State::Reachable(StateData::new())
120 }
121
122 pub fn all_bottom(&self) -> bool {
123 match self {
124 State::Unreachable => false,
125 State::Reachable(values) =>
126 {
127 #[allow(rustc::potential_query_instability)]
128 values.map.values().all(V::is_bottom)
129 }
130 }
131 }
132
133 pub fn is_reachable(&self) -> bool {
134 matches!(self, State::Reachable(_))
135 }
136
137 pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
139 self.flood_with_tail_elem(place, None, map, value)
140 }
141
142 pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
144 where
145 V: HasTop,
146 {
147 self.flood_with(place, map, V::TOP)
148 }
149
150 fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
152 self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value)
153 }
154
155 pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
157 where
158 V: HasTop,
159 {
160 self.flood_discr_with(place, map, V::TOP)
161 }
162
163 pub fn flood_with_tail_elem(
171 &mut self,
172 place: PlaceRef<'_>,
173 tail_elem: Option<TrackElem>,
174 map: &Map<'_>,
175 value: V,
176 ) {
177 let State::Reachable(values) = self else { return };
178 map.for_each_aliasing_place(place, tail_elem, &mut |vi| values.insert(vi, value.clone()));
179 }
180
181 fn insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map<'_>) {
186 match result {
187 ValueOrPlace::Value(value) => self.insert_value_idx(target, value, map),
188 ValueOrPlace::Place(source) => self.insert_place_idx(target, source, map),
189 }
190 }
191
192 pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map<'_>) {
197 let State::Reachable(values) = self else { return };
198 if let Some(value_index) = map.places[target].value_index {
199 values.insert(value_index, value)
200 }
201 }
202
203 pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map<'_>) {
211 let State::Reachable(values) = self else { return };
212 map.for_each_value_pair(target, source, &mut |target, source| {
213 values.insert(target, values.get(source).clone());
214 });
215 }
216
217 pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
219 where
220 V: HasTop,
221 {
222 self.flood(target, map);
223 if let Some(target) = map.find(target) {
224 self.insert_idx(target, result, map);
225 }
226 }
227
228 pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
230 where
231 V: HasTop,
232 {
233 self.flood_discr(target, map);
234 if let Some(target) = map.find_discr(target) {
235 self.insert_idx(target, result, map);
236 }
237 }
238
239 pub fn try_get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
241 let place = map.find(place)?;
242 self.try_get_idx(place, map)
243 }
244
245 pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
247 let place = map.find_discr(place)?;
248 self.try_get_idx(place, map)
249 }
250
251 pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
253 let place = map.find_len(place)?;
254 self.try_get_idx(place, map)
255 }
256
257 pub fn try_get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> Option<V> {
259 match self {
260 State::Reachable(values) => {
261 map.places[place].value_index.map(|v| values.get(v).clone())
262 }
263 State::Unreachable => None,
264 }
265 }
266
267 pub fn get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
271 where
272 V: HasBottom + HasTop,
273 {
274 match self {
275 State::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP),
276 State::Unreachable => V::BOTTOM,
278 }
279 }
280
281 pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
285 where
286 V: HasBottom + HasTop,
287 {
288 match self {
289 State::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP),
290 State::Unreachable => V::BOTTOM,
292 }
293 }
294
295 pub fn get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
299 where
300 V: HasBottom + HasTop,
301 {
302 match self {
303 State::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP),
304 State::Unreachable => V::BOTTOM,
306 }
307 }
308
309 pub fn get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> V
313 where
314 V: HasBottom + HasTop,
315 {
316 match self {
317 State::Reachable(values) => {
318 map.places[place].value_index.map(|v| values.get(v).clone()).unwrap_or(V::TOP)
319 }
320 State::Unreachable => {
321 V::BOTTOM
323 }
324 }
325 }
326}
327
328impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> {
329 fn join(&mut self, other: &Self) -> bool {
330 match (&mut *self, other) {
331 (_, State::Unreachable) => false,
332 (State::Unreachable, _) => {
333 *self = other.clone();
334 true
335 }
336 (State::Reachable(this), State::Reachable(other)) => this.join(other),
337 }
338 }
339}
340
341#[derive(Debug)]
348pub struct Map<'tcx> {
349 locals: IndexVec<Local, Option<PlaceIndex>>,
350 projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
351 places: IndexVec<PlaceIndex, PlaceInfo<'tcx>>,
352 value_count: usize,
353 inner_values: IndexVec<PlaceIndex, Range<usize>>,
355 inner_values_buffer: Vec<ValueIndex>,
356}
357
358impl<'tcx> Map<'tcx> {
359 pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self {
365 let mut map = Self {
366 locals: IndexVec::from_elem(None, &body.local_decls),
367 projections: FxHashMap::default(),
368 places: IndexVec::new(),
369 value_count: 0,
370 inner_values: IndexVec::new(),
371 inner_values_buffer: Vec::new(),
372 };
373 let exclude = excluded_locals(body);
374 map.register(tcx, body, exclude, value_limit);
375 debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len());
376 map
377 }
378
379 #[tracing::instrument(level = "trace", skip(self, tcx, body))]
381 fn register(
382 &mut self,
383 tcx: TyCtxt<'tcx>,
384 body: &Body<'tcx>,
385 exclude: DenseBitSet<Local>,
386 value_limit: Option<usize>,
387 ) {
388 for (local, decl) in body.local_decls.iter_enumerated() {
390 if exclude.contains(local) {
391 continue;
392 }
393 if decl.ty.is_async_drop_in_place_coroutine(tcx) {
394 continue;
395 }
396
397 debug_assert!(self.locals[local].is_none());
399 let place = self.places.push(PlaceInfo::new(decl.ty, None));
400 self.locals[local] = Some(place);
401 }
402
403 let mut collector =
405 PlaceCollector { tcx, body, map: self, assignments: Default::default() };
406 collector.visit_body(body);
407 let PlaceCollector { mut assignments, .. } = collector;
408
409 let mut num_places = 0;
421 while num_places < self.places.len() {
422 num_places = self.places.len();
423
424 for assign in 0.. {
425 let Some(&(lhs, rhs)) = assignments.get_index(assign) else { break };
426
427 let mut child = self.places[lhs].first_child;
429 while let Some(lhs_child) = child {
430 let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[lhs_child];
431 let rhs_child =
432 self.register_place(ty, rhs, proj_elem.expect("child is not a projection"));
433 assignments.insert((lhs_child, rhs_child));
434 child = next_sibling;
435 }
436
437 let mut child = self.places[rhs].first_child;
439 while let Some(rhs_child) = child {
440 let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[rhs_child];
441 let lhs_child =
442 self.register_place(ty, lhs, proj_elem.expect("child is not a projection"));
443 assignments.insert((lhs_child, rhs_child));
444 child = next_sibling;
445 }
446 }
447 }
448 drop(assignments);
449
450 let typing_env = body.typing_env(tcx);
452 for place_info in self.places.iter_mut() {
453 if let Some(value_limit) = value_limit
455 && self.value_count >= value_limit
456 {
457 break;
458 }
459
460 if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, place_info.ty) {
461 place_info.ty = ty;
462 }
463
464 assert!(place_info.value_index.is_none());
466 if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(place_info.ty))
467 && layout.backend_repr.is_scalar()
468 {
469 place_info.value_index = Some(self.value_count.into());
470 self.value_count += 1;
471 }
472 }
473
474 self.inner_values_buffer = Vec::with_capacity(self.value_count);
478 self.inner_values = IndexVec::from_elem(0..0, &self.places);
479 for local in body.local_decls.indices() {
480 if let Some(place) = self.locals[local] {
481 self.cache_preorder_invoke(place);
482 }
483 }
484
485 for opt_place in self.locals.iter_mut() {
487 if let Some(place) = *opt_place
488 && self.inner_values[place].is_empty()
489 {
490 *opt_place = None;
491 }
492 }
493 #[allow(rustc::potential_query_instability)]
494 self.projections.retain(|_, child| !self.inner_values[*child].is_empty());
495 }
496
497 #[tracing::instrument(level = "trace", skip(self), ret)]
498 fn register_place(&mut self, ty: Ty<'tcx>, base: PlaceIndex, elem: TrackElem) -> PlaceIndex {
499 *self.projections.entry((base, elem)).or_insert_with(|| {
500 let next = self.places.push(PlaceInfo::new(ty, Some(elem)));
501 self.places[next].next_sibling = self.places[base].first_child;
502 self.places[base].first_child = Some(next);
503 next
504 })
505 }
506
507 fn cache_preorder_invoke(&mut self, root: PlaceIndex) {
510 let start = self.inner_values_buffer.len();
511 if let Some(vi) = self.places[root].value_index {
512 self.inner_values_buffer.push(vi);
513 }
514
515 let mut next_child = self.places[root].first_child;
517 while let Some(child) = next_child {
518 ensure_sufficient_stack(|| self.cache_preorder_invoke(child));
519 next_child = self.places[child].next_sibling;
520 }
521
522 let end = self.inner_values_buffer.len();
523 self.inner_values[root] = start..end;
524 }
525}
526
527struct PlaceCollector<'a, 'tcx> {
528 tcx: TyCtxt<'tcx>,
529 body: &'a Body<'tcx>,
530 map: &'a mut Map<'tcx>,
531 assignments: FxIndexSet<(PlaceIndex, PlaceIndex)>,
532}
533
534impl<'tcx> PlaceCollector<'_, 'tcx> {
535 #[tracing::instrument(level = "trace", skip(self))]
536 fn register_place(&mut self, place: Place<'tcx>) -> Option<PlaceIndex> {
537 let mut place_index = self.map.locals[place.local]?;
539 let mut ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty);
540 tracing::trace!(?place_index, ?ty);
541
542 if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
543 && let ty::Slice(..) = ref_ty.kind()
544 {
545 self.map.register_place(self.tcx.types.usize, place_index, TrackElem::DerefLen);
546 } else if ty.ty.is_enum() {
547 let discriminant_ty = ty.ty.discriminant_ty(self.tcx);
548 self.map.register_place(discriminant_ty, place_index, TrackElem::Discriminant);
549 }
550
551 for proj in place.projection {
552 let track_elem = proj.try_into().ok()?;
553 ty = ty.projection_ty(self.tcx, proj);
554 place_index = self.map.register_place(ty.ty, place_index, track_elem);
555 tracing::trace!(?proj, ?place_index, ?ty);
556
557 if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
558 && let ty::Slice(..) = ref_ty.kind()
559 {
560 self.map.register_place(self.tcx.types.usize, place_index, TrackElem::DerefLen);
561 } else if ty.ty.is_enum() {
562 let discriminant_ty = ty.ty.discriminant_ty(self.tcx);
563 self.map.register_place(discriminant_ty, place_index, TrackElem::Discriminant);
564 }
565 }
566
567 Some(place_index)
568 }
569}
570
571impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> {
572 #[tracing::instrument(level = "trace", skip(self))]
573 fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, _: Location) {
574 if !ctxt.is_use() {
575 return;
576 }
577
578 self.register_place(*place);
579 }
580
581 fn visit_assign(&mut self, lhs: &Place<'tcx>, rhs: &Rvalue<'tcx>, location: Location) {
582 self.super_assign(lhs, rhs, location);
583
584 match rhs {
585 Rvalue::Use(Operand::Move(rhs) | Operand::Copy(rhs)) | Rvalue::CopyForDeref(rhs) => {
586 let Some(lhs) = self.register_place(*lhs) else { return };
587 let Some(rhs) = self.register_place(*rhs) else { return };
588 self.assignments.insert((lhs, rhs));
589 }
590 Rvalue::Aggregate(kind, fields) => {
591 let Some(mut lhs) = self.register_place(*lhs) else { return };
592 match **kind {
593 AggregateKind::Adt(_, _, _, _, Some(_)) => return,
595 AggregateKind::Adt(_, variant, _, _, None) => {
596 let ty = self.map.places[lhs].ty;
597 if ty.is_enum() {
598 lhs = self.map.register_place(ty, lhs, TrackElem::Variant(variant));
599 }
600 }
601 AggregateKind::RawPtr(..)
602 | AggregateKind::Array(_)
603 | AggregateKind::Tuple
604 | AggregateKind::Closure(..)
605 | AggregateKind::Coroutine(..)
606 | AggregateKind::CoroutineClosure(..) => {}
607 }
608 for (index, field) in fields.iter_enumerated() {
609 if let Some(rhs) = field.place()
610 && let Some(rhs) = self.register_place(rhs)
611 {
612 let lhs = self.map.register_place(
613 self.map.places[rhs].ty,
614 lhs,
615 TrackElem::Field(index),
616 );
617 self.assignments.insert((lhs, rhs));
618 }
619 }
620 }
621 _ => {}
622 }
623 }
624}
625
626impl<'tcx> Map<'tcx> {
627 pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
629 self.projections.get(&(place, elem)).copied()
630 }
631
632 fn find_extra(
634 &self,
635 place: PlaceRef<'_>,
636 extra: impl IntoIterator<Item = TrackElem>,
637 ) -> Option<PlaceIndex> {
638 let mut index = *self.locals[place.local].as_ref()?;
639
640 for &elem in place.projection {
641 index = self.apply(index, elem.try_into().ok()?)?;
642 }
643 for elem in extra {
644 index = self.apply(index, elem)?;
645 }
646
647 Some(index)
648 }
649
650 pub fn find(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
652 self.find_extra(place, [])
653 }
654
655 pub fn find_discr(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
657 self.find_extra(place, [TrackElem::Discriminant])
658 }
659
660 pub fn find_len(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
662 self.find_extra(place, [TrackElem::DerefLen])
663 }
664
665 pub fn value(&self, place: PlaceIndex) -> Option<ValueIndex> {
667 self.places[place].value_index
668 }
669
670 pub fn find_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
672 self.value(self.find(place)?)
673 }
674
675 pub fn find_discr_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
677 self.value(self.find_discr(place)?)
678 }
679
680 pub fn find_len_value(&self, place: PlaceRef<'_>) -> Option<ValueIndex> {
682 self.value(self.find_len(place)?)
683 }
684
685 fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> {
687 Children::new(self, parent)
688 }
689
690 pub fn for_each_aliasing_place(
698 &self,
699 place: PlaceRef<'_>,
700 tail_elem: Option<TrackElem>,
701 f: &mut impl FnMut(ValueIndex),
702 ) {
703 if place.is_indirect_first_projection() {
704 return;
706 }
707 let Some(mut index) = self.locals[place.local] else {
708 return;
710 };
711 let elems = place.projection.iter().map(|&elem| elem.try_into()).chain(tail_elem.map(Ok));
712 for elem in elems {
713 if let Some(vi) = self.places[index].value_index {
715 f(vi);
716 }
717
718 let Ok(elem) = elem else { return };
719 let sub = self.apply(index, elem);
720 if let TrackElem::Variant(..) | TrackElem::Discriminant = elem {
721 self.for_each_variant_sibling(index, sub, f);
723 }
724 if let Some(sub) = sub {
725 index = sub
726 } else {
727 return;
728 }
729 }
730 self.for_each_value_inside(index, f);
731 }
732
733 fn for_each_variant_sibling(
735 &self,
736 parent: PlaceIndex,
737 preserved_child: Option<PlaceIndex>,
738 f: &mut impl FnMut(ValueIndex),
739 ) {
740 for sibling in self.children(parent) {
741 let elem = self.places[sibling].proj_elem;
742 if let Some(TrackElem::Variant(..) | TrackElem::Discriminant) = elem
745 && Some(sibling) != preserved_child
747 {
748 self.for_each_value_inside(sibling, f);
749 }
750 }
751 }
752
753 pub fn values_inside(&self, root: PlaceIndex) -> &[ValueIndex] {
755 let range = self.inner_values[root].clone();
756 &self.inner_values_buffer[range]
757 }
758
759 fn for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex)) {
761 for &v in self.values_inside(root) {
762 f(v)
763 }
764 }
765
766 pub fn for_each_projection_value<O>(
768 &self,
769 root: PlaceIndex,
770 value: O,
771 project: &mut impl FnMut(TrackElem, &O) -> Option<O>,
772 f: &mut impl FnMut(PlaceIndex, &O),
773 ) {
774 if self.inner_values[root].is_empty() {
776 return;
777 }
778
779 if self.places[root].value_index.is_some() {
780 f(root, &value)
781 }
782
783 for child in self.children(root) {
784 let elem = self.places[child].proj_elem.unwrap();
785 if let Some(value) = project(elem, &value) {
786 self.for_each_projection_value(child, value, project, f);
787 }
788 }
789 }
790
791 pub fn for_each_value_pair(
794 &self,
795 target: PlaceIndex,
796 source: PlaceIndex,
797 f: &mut impl FnMut(ValueIndex, ValueIndex),
798 ) {
799 if let Some(target_value) = self.places[target].value_index
803 && let Some(source_value) = self.places[source].value_index
804 {
805 f(target_value, source_value)
806 }
807 for target_child in self.children(target) {
808 let projection = self.places[target_child].proj_elem.unwrap();
810 if let Some(source_child) = self.projections.get(&(source, projection)) {
811 self.for_each_value_pair(target_child, *source_child, f);
812 }
813 }
814 }
815}
816
817#[derive(Debug)]
822struct PlaceInfo<'tcx> {
823 ty: Ty<'tcx>,
825
826 value_index: Option<ValueIndex>,
828
829 proj_elem: Option<TrackElem>,
831
832 first_child: Option<PlaceIndex>,
834
835 next_sibling: Option<PlaceIndex>,
837}
838
839impl<'tcx> PlaceInfo<'tcx> {
840 fn new(ty: Ty<'tcx>, proj_elem: Option<TrackElem>) -> Self {
841 Self { ty, next_sibling: None, first_child: None, proj_elem, value_index: None }
842 }
843}
844
845struct Children<'a, 'tcx> {
846 map: &'a Map<'tcx>,
847 next: Option<PlaceIndex>,
848}
849
850impl<'a, 'tcx> Children<'a, 'tcx> {
851 fn new(map: &'a Map<'tcx>, parent: PlaceIndex) -> Self {
852 Self { map, next: map.places[parent].first_child }
853 }
854}
855
856impl Iterator for Children<'_, '_> {
857 type Item = PlaceIndex;
858
859 fn next(&mut self) -> Option<Self::Item> {
860 match self.next {
861 Some(child) => {
862 self.next = self.map.places[child].next_sibling;
863 Some(child)
864 }
865 None => None,
866 }
867 }
868}
869
870#[derive(Debug)]
872pub enum ValueOrPlace<V> {
873 Value(V),
874 Place(PlaceIndex),
875}
876
877impl<V: HasTop> ValueOrPlace<V> {
878 pub const TOP: Self = ValueOrPlace::Value(V::TOP);
879}
880
881#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
885pub enum TrackElem {
886 Field(FieldIdx),
887 Variant(VariantIdx),
888 Discriminant,
889 DerefLen,
891}
892
893impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
894 type Error = ();
895
896 fn try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error> {
897 match value {
898 ProjectionElem::Field(field, _) => Ok(TrackElem::Field(field)),
899 ProjectionElem::Downcast(_, idx) => Ok(TrackElem::Variant(idx)),
900 _ => Err(()),
901 }
902 }
903}
904
905pub fn iter_fields<'tcx>(
907 ty: Ty<'tcx>,
908 tcx: TyCtxt<'tcx>,
909 typing_env: ty::TypingEnv<'tcx>,
910 mut f: impl FnMut(Option<VariantIdx>, FieldIdx, Ty<'tcx>),
911) {
912 match ty.kind() {
913 ty::Tuple(list) => {
914 for (field, ty) in list.iter().enumerate() {
915 f(None, field.into(), ty);
916 }
917 }
918 ty::Adt(def, args) => {
919 if def.is_union() {
920 return;
921 }
922 for (v_index, v_def) in def.variants().iter_enumerated() {
923 let variant = if def.is_struct() { None } else { Some(v_index) };
924 for (f_index, f_def) in v_def.fields.iter().enumerate() {
925 let field_ty = f_def.ty(tcx, args);
926 let field_ty = tcx
927 .try_normalize_erasing_regions(typing_env, field_ty)
928 .unwrap_or_else(|_| tcx.erase_and_anonymize_regions(field_ty));
929 f(variant, f_index.into(), field_ty);
930 }
931 }
932 }
933 ty::Closure(_, args) => {
934 iter_fields(args.as_closure().tupled_upvars_ty(), tcx, typing_env, f);
935 }
936 ty::Coroutine(_, args) => {
937 iter_fields(args.as_coroutine().tupled_upvars_ty(), tcx, typing_env, f);
938 }
939 ty::CoroutineClosure(_, args) => {
940 iter_fields(args.as_coroutine_closure().tupled_upvars_ty(), tcx, typing_env, f);
941 }
942 _ => (),
943 }
944}
945
946pub fn excluded_locals(body: &Body<'_>) -> DenseBitSet<Local> {
948 struct Collector {
949 result: DenseBitSet<Local>,
950 }
951
952 impl<'tcx> Visitor<'tcx> for Collector {
953 fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
954 if context.may_observe_address() && !place.is_indirect() {
955 self.result.insert(place.local);
958 }
959 }
960 }
961
962 let mut collector = Collector { result: DenseBitSet::new_empty(body.local_decls.len()) };
963 collector.visit_body(body);
964 collector.result
965}
966
967fn debug_with_context_rec<V: Debug + Eq + HasBottom>(
968 place: PlaceIndex,
969 place_str: &str,
970 new: &StateData<V>,
971 old: Option<&StateData<V>>,
972 map: &Map<'_>,
973 f: &mut Formatter<'_>,
974) -> std::fmt::Result {
975 if let Some(value) = map.places[place].value_index {
976 match old {
977 None => writeln!(f, "{}: {:?}", place_str, new.get(value))?,
978 Some(old) => {
979 if new.get(value) != old.get(value) {
980 writeln!(f, "\u{001f}-{}: {:?}", place_str, old.get(value))?;
981 writeln!(f, "\u{001f}+{}: {:?}", place_str, new.get(value))?;
982 }
983 }
984 }
985 }
986
987 for child in map.children(place) {
988 let info_elem = map.places[child].proj_elem.unwrap();
989 let child_place_str = match info_elem {
990 TrackElem::Discriminant => {
991 format!("discriminant({place_str})")
992 }
993 TrackElem::Variant(idx) => {
994 format!("({place_str} as {idx:?})")
995 }
996 TrackElem::Field(field) => {
997 if place_str.starts_with('*') {
998 format!("({}).{}", place_str, field.index())
999 } else {
1000 format!("{}.{}", place_str, field.index())
1001 }
1002 }
1003 TrackElem::DerefLen => {
1004 format!("Len(*{})", place_str)
1005 }
1006 };
1007 debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
1008 }
1009
1010 Ok(())
1011}
1012
1013pub fn debug_with_context<V: Debug + Eq + HasBottom>(
1014 new: &StateData<V>,
1015 old: Option<&StateData<V>>,
1016 map: &Map<'_>,
1017 f: &mut Formatter<'_>,
1018) -> std::fmt::Result {
1019 for (local, place) in map.locals.iter_enumerated() {
1020 if let Some(place) = place {
1021 debug_with_context_rec(*place, &format!("{local:?}"), new, old, map, f)?;
1022 }
1023 }
1024 Ok(())
1025}