1 use crate::elaborate_drops::DropFlagState;
2 use rustc_middle::mir::{self, Body, Location};
3 use rustc_middle::ty::{self, TyCtxt};
4 use rustc_target::abi::VariantIdx;
6 use super::indexes::MovePathIndex;
7 use super::move_paths::{InitKind, LookupResult, MoveData};
8 use super::MoveDataParamEnv;
10 pub fn move_path_children_matching<'tcx, F>(
11 move_data: &MoveData<'tcx>,
14 ) -> Option<MovePathIndex>
16 F: FnMut(mir::PlaceElem<'tcx>) -> bool,
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() {
23 return Some(child_index);
26 next_child = move_path_children.next_sibling;
32 /// When enumerating the child fragments of a path, don't recurse into
33 /// paths (1.) past arrays, slices, and pointers, nor (2.) into a type
34 /// that implements `Drop`.
36 /// Places behind references or arrays are not tracked by elaboration
37 /// and are always assumed to be initialized when accessible. As
38 /// references and indexes can be reseated, trying to track them can
39 /// only lead to trouble.
41 /// Places behind ADT's with a Drop impl are not tracked by
42 /// elaboration since they can never have a drop-flag state that
43 /// differs from that of the parent with the Drop impl.
45 /// In both cases, the contents can only be accessed if and only if
46 /// their parents are initialized. This implies for example that there
47 /// is no need to maintain separate drop flags to track such state.
49 // FIXME: we have to do something for moving slice patterns.
50 fn place_contents_drop_state_cannot_differ<'tcx>(
53 place: mir::Place<'tcx>,
55 let ty = place.ty(body, tcx).ty;
59 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false",
64 ty::Slice(..) | ty::Ref(..) | ty::RawPtr(..) => {
66 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true",
71 ty::Adt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => {
73 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true",
82 pub fn on_lookup_result_bits<'tcx, F>(
85 move_data: &MoveData<'tcx>,
86 lookup_result: LookupResult,
89 F: FnMut(MovePathIndex),
92 LookupResult::Parent(..) => {
93 // access to untracked value - do not touch children
95 LookupResult::Exact(e) => on_all_children_bits(tcx, body, move_data, e, each_child),
99 pub fn on_all_children_bits<'tcx, F>(
102 move_data: &MoveData<'tcx>,
103 move_path_index: MovePathIndex,
106 F: FnMut(MovePathIndex),
108 fn is_terminal_path<'tcx>(
111 move_data: &MoveData<'tcx>,
114 place_contents_drop_state_cannot_differ(tcx, body, move_data.move_paths[path].place)
117 fn on_all_children_bits<'tcx, F>(
120 move_data: &MoveData<'tcx>,
121 move_path_index: MovePathIndex,
124 F: FnMut(MovePathIndex),
126 each_child(move_path_index);
128 if is_terminal_path(tcx, body, move_data, move_path_index) {
132 let mut next_child_index = move_data.move_paths[move_path_index].first_child;
133 while let Some(child_index) = next_child_index {
134 on_all_children_bits(tcx, body, move_data, child_index, each_child);
135 next_child_index = move_data.move_paths[child_index].next_sibling;
138 on_all_children_bits(tcx, body, move_data, move_path_index, &mut each_child);
141 pub fn on_all_drop_children_bits<'tcx, F>(
144 ctxt: &MoveDataParamEnv<'tcx>,
148 F: FnMut(MovePathIndex),
150 on_all_children_bits(tcx, body, &ctxt.move_data, path, |child| {
151 let place = &ctxt.move_data.move_paths[path].place;
152 let ty = place.ty(body, tcx).ty;
153 debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, place, ty);
155 let erased_ty = tcx.erase_regions(ty);
156 if erased_ty.needs_drop(tcx, ctxt.param_env) {
159 debug!("on_all_drop_children_bits - skipping")
164 pub fn drop_flag_effects_for_function_entry<'tcx, F>(
167 ctxt: &MoveDataParamEnv<'tcx>,
170 F: FnMut(MovePathIndex, DropFlagState),
172 let move_data = &ctxt.move_data;
173 for arg in body.args_iter() {
174 let place = mir::Place::from(arg);
175 let lookup_result = move_data.rev_lookup.find(place.as_ref());
176 on_lookup_result_bits(tcx, body, move_data, lookup_result, |mpi| {
177 callback(mpi, DropFlagState::Present)
182 pub fn drop_flag_effects_for_location<'tcx, F>(
185 ctxt: &MoveDataParamEnv<'tcx>,
189 F: FnMut(MovePathIndex, DropFlagState),
191 let move_data = &ctxt.move_data;
192 debug!("drop_flag_effects_for_location({:?})", loc);
194 // first, move out of the RHS
195 for mi in &move_data.loc_map[loc] {
196 let path = mi.move_path_index(move_data);
197 debug!("moving out of path {:?}", move_data.move_paths[path]);
199 on_all_children_bits(tcx, body, move_data, path, |mpi| callback(mpi, DropFlagState::Absent))
202 debug!("drop_flag_effects: assignment for location({:?})", loc);
204 for_location_inits(tcx, body, move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
207 pub fn for_location_inits<'tcx, F>(
210 move_data: &MoveData<'tcx>,
214 F: FnMut(MovePathIndex),
216 for ii in &move_data.init_loc_map[loc] {
217 let init = move_data.inits[*ii];
220 let path = init.path;
222 on_all_children_bits(tcx, body, move_data, path, &mut callback)
224 InitKind::Shallow => {
228 InitKind::NonPanicPathOnly => (),
233 /// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a
234 /// `Downcast` to a variant besides the `active_variant`.
236 /// NOTE: If there are no move paths corresponding to an inactive variant,
237 /// `handle_inactive_variant` will not be called for that variant.
238 pub(crate) fn on_all_inactive_variants<'tcx>(
240 body: &mir::Body<'tcx>,
241 move_data: &MoveData<'tcx>,
242 enum_place: mir::Place<'tcx>,
243 active_variant: VariantIdx,
244 mut handle_inactive_variant: impl FnMut(MovePathIndex),
246 let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
250 let enum_path = &move_data.move_paths[enum_mpi];
251 for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
252 // Because of the way we build the `MoveData` tree, each child should have exactly one more
253 // projection than `enum_place`. This additional projection must be a downcast since the
255 let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
256 assert_eq!(enum_place.projection.len(), base_proj.len());
258 let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
262 if variant_idx != active_variant {
263 on_all_children_bits(tcx, body, move_data, variant_mpi, |mpi| {
264 handle_inactive_variant(mpi)