1use rustc_abi::VariantIdx;
2use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind};
3use smallvec::SmallVec;
4use tracing::debug;
5
6use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex};
7
8#[derive(Debug, PartialEq, Eq, Copy, Clone)]
10pub enum DropFlagState {
11 Present,
13
14 Absent,
17}
18
19impl DropFlagState {
20 pub fn value(self) -> bool {
21 match self {
22 DropFlagState::Present => true,
23 DropFlagState::Absent => false,
24 }
25 }
26}
27
28pub fn move_path_children_matching<'tcx, F>(
29 move_data: &MoveData<'tcx>,
30 path: MovePathIndex,
31 mut cond: F,
32) -> Option<MovePathIndex>
33where
34 F: FnMut(mir::PlaceElem<'tcx>) -> bool,
35{
36 let mut next_child = move_data.move_paths[path].first_child;
37 while let Some(child_index) = next_child {
38 let move_path_children = &move_data.move_paths[child_index];
39 if let Some(&elem) = move_path_children.place.projection.last() {
40 if cond(elem) {
41 return Some(child_index);
42 }
43 }
44 next_child = move_path_children.next_sibling;
45 }
46
47 None
48}
49
50pub fn on_lookup_result_bits<'tcx, F>(
51 move_data: &MoveData<'tcx>,
52 lookup_result: LookupResult,
53 each_child: F,
54) where
55 F: FnMut(MovePathIndex),
56{
57 match lookup_result {
58 LookupResult::Parent(..) => {
59 }
61 LookupResult::Exact(e) => on_all_children_bits(move_data, e, each_child),
62 }
63}
64
65pub fn on_all_children_bits<'tcx, F>(
66 move_data: &MoveData<'tcx>,
67 move_path_index: MovePathIndex,
68 mut each_child: F,
69) where
70 F: FnMut(MovePathIndex),
71{
72 fn on_all_children_bits<'tcx, F>(
73 move_data: &MoveData<'tcx>,
74 move_path_index: MovePathIndex,
75 each_child: &mut F,
76 ) where
77 F: FnMut(MovePathIndex),
78 {
79 each_child(move_path_index);
80
81 let mut next_child_index = move_data.move_paths[move_path_index].first_child;
82 while let Some(child_index) = next_child_index {
83 on_all_children_bits(move_data, child_index, each_child);
84 next_child_index = move_data.move_paths[child_index].next_sibling;
85 }
86 }
87 on_all_children_bits(move_data, move_path_index, &mut each_child);
88}
89
90pub fn drop_flag_effects_for_function_entry<'tcx, F>(
91 body: &Body<'tcx>,
92 move_data: &MoveData<'tcx>,
93 mut callback: F,
94) where
95 F: FnMut(MovePathIndex, DropFlagState),
96{
97 for arg in body.args_iter() {
98 let place = mir::Place::from(arg);
99 let lookup_result = move_data.rev_lookup.find(place.as_ref());
100 on_lookup_result_bits(move_data, lookup_result, |mpi| {
101 callback(mpi, DropFlagState::Present)
102 });
103 }
104}
105
106pub fn drop_flag_effects_for_location<'tcx, F>(
107 body: &Body<'tcx>,
108 move_data: &MoveData<'tcx>,
109 loc: Location,
110 mut callback: F,
111) where
112 F: FnMut(MovePathIndex, DropFlagState),
113{
114 debug!("drop_flag_effects_for_location({:?})", loc);
115
116 for mi in &move_data.loc_map[loc] {
118 let path = mi.move_path_index(move_data);
119 debug!("moving out of path {:?}", move_data.move_paths[path]);
120
121 on_all_children_bits(move_data, path, |mpi| callback(mpi, DropFlagState::Absent))
122 }
123
124 if let Some(Terminator { kind: TerminatorKind::Drop { place, .. }, .. }) =
126 body.stmt_at(loc).right()
127 {
128 if let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) {
129 on_all_children_bits(move_data, mpi, |mpi| callback(mpi, DropFlagState::Absent))
130 }
131 }
132
133 debug!("drop_flag_effects: assignment for location({:?})", loc);
134
135 for_location_inits(move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
136}
137
138fn for_location_inits<'tcx, F>(move_data: &MoveData<'tcx>, loc: Location, mut callback: F)
139where
140 F: FnMut(MovePathIndex),
141{
142 for ii in &move_data.init_loc_map[loc] {
143 let init = move_data.inits[*ii];
144 match init.kind {
145 InitKind::Deep => {
146 let path = init.path;
147
148 on_all_children_bits(move_data, path, &mut callback)
149 }
150 InitKind::Shallow => {
151 let mpi = init.path;
152 callback(mpi);
153 }
154 InitKind::NonPanicPathOnly => (),
155 }
156 }
157}
158
159pub(crate) enum InactiveVariants {
162 Inactives(SmallVec<[VariantIdx; 4]>),
163 Active(VariantIdx),
164}
165
166impl InactiveVariants {
167 fn contains(&self, variant_idx: VariantIdx) -> bool {
168 match self {
169 InactiveVariants::Inactives(inactives) => inactives.contains(&variant_idx),
170 InactiveVariants::Active(active) => variant_idx != *active,
171 }
172 }
173}
174
175pub(crate) fn on_all_inactive_variants<'tcx>(
178 move_data: &MoveData<'tcx>,
179 enum_place: mir::Place<'tcx>,
180 inactive_variants: &InactiveVariants,
181 mut handle_inactive_variant: impl FnMut(MovePathIndex),
182) {
183 let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
184 return;
185 };
186
187 let enum_path = &move_data.move_paths[enum_mpi];
188 for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
189 let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
193 assert_eq!(enum_place.projection.len(), base_proj.len());
194
195 let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
196 unreachable!();
197 };
198
199 if inactive_variants.contains(variant_idx) {
200 on_all_children_bits(move_data, variant_mpi, |mpi| handle_inactive_variant(mpi));
201 }
202 }
203}