1 use crate::elaborate_drops::DropFlagState;
2 use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind};
3 use rustc_middle::ty::{self, TyCtxt};
4 use rustc_target::abi::VariantIdx;
5
6 use super::indexes::MovePathIndex;
7 use super::move_paths::{InitKind, LookupResult, MoveData};
8 use super::MoveDataParamEnv;
9
move_path_children_matching<'tcx, F>( move_data: &MoveData<'tcx>, path: MovePathIndex, mut cond: F, ) -> Option<MovePathIndex> where F: FnMut(mir::PlaceElem<'tcx>) -> bool,10 pub fn move_path_children_matching<'tcx, F>(
11 move_data: &MoveData<'tcx>,
12 path: MovePathIndex,
13 mut cond: F,
14 ) -> Option<MovePathIndex>
15 where
16 F: FnMut(mir::PlaceElem<'tcx>) -> bool,
17 {
18 let mut next_child = move_data.move_paths[path].first_child;
19 while let Some(child_index) = next_child {
20 let move_path_children = &move_data.move_paths[child_index];
21 if let Some(&elem) = move_path_children.place.projection.last() {
22 if cond(elem) {
23 return Some(child_index);
24 }
25 }
26 next_child = move_path_children.next_sibling;
27 }
28
29 None
30 }
31
on_lookup_result_bits<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, move_data: &MoveData<'tcx>, lookup_result: LookupResult, each_child: F, ) where F: FnMut(MovePathIndex),32 pub fn on_lookup_result_bits<'tcx, F>(
33 tcx: TyCtxt<'tcx>,
34 body: &Body<'tcx>,
35 move_data: &MoveData<'tcx>,
36 lookup_result: LookupResult,
37 each_child: F,
38 ) where
39 F: FnMut(MovePathIndex),
40 {
41 match lookup_result {
42 LookupResult::Parent(..) => {
43 // access to untracked value - do not touch children
44 }
45 LookupResult::Exact(e) => on_all_children_bits(tcx, body, move_data, e, each_child),
46 }
47 }
48
on_all_children_bits<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, move_data: &MoveData<'tcx>, move_path_index: MovePathIndex, mut each_child: F, ) where F: FnMut(MovePathIndex),49 pub fn on_all_children_bits<'tcx, F>(
50 tcx: TyCtxt<'tcx>,
51 body: &Body<'tcx>,
52 move_data: &MoveData<'tcx>,
53 move_path_index: MovePathIndex,
54 mut each_child: F,
55 ) where
56 F: FnMut(MovePathIndex),
57 {
58 #[inline]
59 fn is_terminal_path<'tcx>(
60 tcx: TyCtxt<'tcx>,
61 body: &Body<'tcx>,
62 move_data: &MoveData<'tcx>,
63 path: MovePathIndex,
64 ) -> bool {
65 let place = move_data.move_paths[path].place;
66
67 // When enumerating the child fragments of a path, don't recurse into
68 // paths (1.) past arrays, slices, and pointers, nor (2.) into a type
69 // that implements `Drop`.
70 //
71 // Places behind references or arrays are not tracked by elaboration
72 // and are always assumed to be initialized when accessible. As
73 // references and indexes can be reseated, trying to track them can
74 // only lead to trouble.
75 //
76 // Places behind ADT's with a Drop impl are not tracked by
77 // elaboration since they can never have a drop-flag state that
78 // differs from that of the parent with the Drop impl.
79 //
80 // In both cases, the contents can only be accessed if and only if
81 // their parents are initialized. This implies for example that there
82 // is no need to maintain separate drop flags to track such state.
83 //
84 // FIXME: we have to do something for moving slice patterns.
85 let ty = place.ty(body, tcx).ty;
86 match ty.kind() {
87 ty::Adt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => {
88 debug!(
89 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true",
90 place, ty
91 );
92 true
93 }
94 ty::Array(..) => {
95 debug!(
96 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false",
97 place, ty
98 );
99 false
100 }
101 ty::Slice(..) | ty::Ref(..) | ty::RawPtr(..) => {
102 debug!(
103 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true",
104 place, ty
105 );
106 true
107 }
108 _ => false,
109 }
110 }
111
112 fn on_all_children_bits<'tcx, F>(
113 tcx: TyCtxt<'tcx>,
114 body: &Body<'tcx>,
115 move_data: &MoveData<'tcx>,
116 move_path_index: MovePathIndex,
117 each_child: &mut F,
118 ) where
119 F: FnMut(MovePathIndex),
120 {
121 each_child(move_path_index);
122
123 if is_terminal_path(tcx, body, move_data, move_path_index) {
124 return;
125 }
126
127 let mut next_child_index = move_data.move_paths[move_path_index].first_child;
128 while let Some(child_index) = next_child_index {
129 on_all_children_bits(tcx, body, move_data, child_index, each_child);
130 next_child_index = move_data.move_paths[child_index].next_sibling;
131 }
132 }
133 on_all_children_bits(tcx, body, move_data, move_path_index, &mut each_child);
134 }
135
on_all_drop_children_bits<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, ctxt: &MoveDataParamEnv<'tcx>, path: MovePathIndex, mut each_child: F, ) where F: FnMut(MovePathIndex),136 pub fn on_all_drop_children_bits<'tcx, F>(
137 tcx: TyCtxt<'tcx>,
138 body: &Body<'tcx>,
139 ctxt: &MoveDataParamEnv<'tcx>,
140 path: MovePathIndex,
141 mut each_child: F,
142 ) where
143 F: FnMut(MovePathIndex),
144 {
145 on_all_children_bits(tcx, body, &ctxt.move_data, path, |child| {
146 let place = &ctxt.move_data.move_paths[path].place;
147 let ty = place.ty(body, tcx).ty;
148 debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, place, ty);
149
150 let erased_ty = tcx.erase_regions(ty);
151 if erased_ty.needs_drop(tcx, ctxt.param_env) {
152 each_child(child);
153 } else {
154 debug!("on_all_drop_children_bits - skipping")
155 }
156 })
157 }
158
drop_flag_effects_for_function_entry<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, ctxt: &MoveDataParamEnv<'tcx>, mut callback: F, ) where F: FnMut(MovePathIndex, DropFlagState),159 pub fn drop_flag_effects_for_function_entry<'tcx, F>(
160 tcx: TyCtxt<'tcx>,
161 body: &Body<'tcx>,
162 ctxt: &MoveDataParamEnv<'tcx>,
163 mut callback: F,
164 ) where
165 F: FnMut(MovePathIndex, DropFlagState),
166 {
167 let move_data = &ctxt.move_data;
168 for arg in body.args_iter() {
169 let place = mir::Place::from(arg);
170 let lookup_result = move_data.rev_lookup.find(place.as_ref());
171 on_lookup_result_bits(tcx, body, move_data, lookup_result, |mpi| {
172 callback(mpi, DropFlagState::Present)
173 });
174 }
175 }
176
drop_flag_effects_for_location<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, ctxt: &MoveDataParamEnv<'tcx>, loc: Location, mut callback: F, ) where F: FnMut(MovePathIndex, DropFlagState),177 pub fn drop_flag_effects_for_location<'tcx, F>(
178 tcx: TyCtxt<'tcx>,
179 body: &Body<'tcx>,
180 ctxt: &MoveDataParamEnv<'tcx>,
181 loc: Location,
182 mut callback: F,
183 ) where
184 F: FnMut(MovePathIndex, DropFlagState),
185 {
186 let move_data = &ctxt.move_data;
187 debug!("drop_flag_effects_for_location({:?})", loc);
188
189 // first, move out of the RHS
190 for mi in &move_data.loc_map[loc] {
191 let path = mi.move_path_index(move_data);
192 debug!("moving out of path {:?}", move_data.move_paths[path]);
193
194 on_all_children_bits(tcx, body, move_data, path, |mpi| callback(mpi, DropFlagState::Absent))
195 }
196
197 // Drop does not count as a move but we should still consider the variable uninitialized.
198 if let Some(Terminator { kind: TerminatorKind::Drop { place, .. }, .. }) =
199 body.stmt_at(loc).right()
200 {
201 if let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) {
202 on_all_children_bits(tcx, body, move_data, mpi, |mpi| {
203 callback(mpi, DropFlagState::Absent)
204 })
205 }
206 }
207
208 debug!("drop_flag_effects: assignment for location({:?})", loc);
209
210 for_location_inits(tcx, body, move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
211 }
212
for_location_inits<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, move_data: &MoveData<'tcx>, loc: Location, mut callback: F, ) where F: FnMut(MovePathIndex),213 pub fn for_location_inits<'tcx, F>(
214 tcx: TyCtxt<'tcx>,
215 body: &Body<'tcx>,
216 move_data: &MoveData<'tcx>,
217 loc: Location,
218 mut callback: F,
219 ) where
220 F: FnMut(MovePathIndex),
221 {
222 for ii in &move_data.init_loc_map[loc] {
223 let init = move_data.inits[*ii];
224 match init.kind {
225 InitKind::Deep => {
226 let path = init.path;
227
228 on_all_children_bits(tcx, body, move_data, path, &mut callback)
229 }
230 InitKind::Shallow => {
231 let mpi = init.path;
232 callback(mpi);
233 }
234 InitKind::NonPanicPathOnly => (),
235 }
236 }
237 }
238
239 /// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a
240 /// `Downcast` to a variant besides the `active_variant`.
241 ///
242 /// NOTE: If there are no move paths corresponding to an inactive variant,
243 /// `handle_inactive_variant` will not be called for that variant.
on_all_inactive_variants<'tcx>( tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, move_data: &MoveData<'tcx>, enum_place: mir::Place<'tcx>, active_variant: VariantIdx, mut handle_inactive_variant: impl FnMut(MovePathIndex), )244 pub(crate) fn on_all_inactive_variants<'tcx>(
245 tcx: TyCtxt<'tcx>,
246 body: &mir::Body<'tcx>,
247 move_data: &MoveData<'tcx>,
248 enum_place: mir::Place<'tcx>,
249 active_variant: VariantIdx,
250 mut handle_inactive_variant: impl FnMut(MovePathIndex),
251 ) {
252 let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
253 return;
254 };
255
256 let enum_path = &move_data.move_paths[enum_mpi];
257 for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
258 // Because of the way we build the `MoveData` tree, each child should have exactly one more
259 // projection than `enum_place`. This additional projection must be a downcast since the
260 // base is an enum.
261 let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
262 assert_eq!(enum_place.projection.len(), base_proj.len());
263
264 let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
265 unreachable!();
266 };
267
268 if variant_idx != active_variant {
269 on_all_children_bits(tcx, body, move_data, variant_mpi, |mpi| {
270 handle_inactive_variant(mpi)
271 });
272 }
273 }
274 }
275