]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_dataflow/src/drop_flag_effects.rs
Rollup merge of #101826 - andrewpollack:fix-joined-without-noop, r=Mark-Simulacrum
[rust.git] / compiler / rustc_mir_dataflow / src / drop_flag_effects.rs
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;
5
6 use super::indexes::MovePathIndex;
7 use super::move_paths::{InitKind, LookupResult, MoveData};
8 use super::MoveDataParamEnv;
9
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
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`.
35 ///
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.
40 ///
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.
44 ///
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.
48 //
49 // FIXME: we have to do something for moving slice patterns.
50 fn place_contents_drop_state_cannot_differ<'tcx>(
51     tcx: TyCtxt<'tcx>,
52     body: &Body<'tcx>,
53     place: mir::Place<'tcx>,
54 ) -> bool {
55     let ty = place.ty(body, tcx).ty;
56     match ty.kind() {
57         ty::Array(..) => {
58             debug!(
59                 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false",
60                 place, ty
61             );
62             false
63         }
64         ty::Slice(..) | ty::Ref(..) | ty::RawPtr(..) => {
65             debug!(
66                 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true",
67                 place, ty
68             );
69             true
70         }
71         ty::Adt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => {
72             debug!(
73                 "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true",
74                 place, ty
75             );
76             true
77         }
78         _ => false,
79     }
80 }
81
82 pub fn on_lookup_result_bits<'tcx, F>(
83     tcx: TyCtxt<'tcx>,
84     body: &Body<'tcx>,
85     move_data: &MoveData<'tcx>,
86     lookup_result: LookupResult,
87     each_child: F,
88 ) where
89     F: FnMut(MovePathIndex),
90 {
91     match lookup_result {
92         LookupResult::Parent(..) => {
93             // access to untracked value - do not touch children
94         }
95         LookupResult::Exact(e) => on_all_children_bits(tcx, body, move_data, e, each_child),
96     }
97 }
98
99 pub fn on_all_children_bits<'tcx, F>(
100     tcx: TyCtxt<'tcx>,
101     body: &Body<'tcx>,
102     move_data: &MoveData<'tcx>,
103     move_path_index: MovePathIndex,
104     mut each_child: F,
105 ) where
106     F: FnMut(MovePathIndex),
107 {
108     fn is_terminal_path<'tcx>(
109         tcx: TyCtxt<'tcx>,
110         body: &Body<'tcx>,
111         move_data: &MoveData<'tcx>,
112         path: MovePathIndex,
113     ) -> bool {
114         place_contents_drop_state_cannot_differ(tcx, body, move_data.move_paths[path].place)
115     }
116
117     fn on_all_children_bits<'tcx, F>(
118         tcx: TyCtxt<'tcx>,
119         body: &Body<'tcx>,
120         move_data: &MoveData<'tcx>,
121         move_path_index: MovePathIndex,
122         each_child: &mut F,
123     ) where
124         F: FnMut(MovePathIndex),
125     {
126         each_child(move_path_index);
127
128         if is_terminal_path(tcx, body, move_data, move_path_index) {
129             return;
130         }
131
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;
136         }
137     }
138     on_all_children_bits(tcx, body, move_data, move_path_index, &mut each_child);
139 }
140
141 pub fn on_all_drop_children_bits<'tcx, F>(
142     tcx: TyCtxt<'tcx>,
143     body: &Body<'tcx>,
144     ctxt: &MoveDataParamEnv<'tcx>,
145     path: MovePathIndex,
146     mut each_child: F,
147 ) where
148     F: FnMut(MovePathIndex),
149 {
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);
154
155         let erased_ty = tcx.erase_regions(ty);
156         if erased_ty.needs_drop(tcx, ctxt.param_env) {
157             each_child(child);
158         } else {
159             debug!("on_all_drop_children_bits - skipping")
160         }
161     })
162 }
163
164 pub fn drop_flag_effects_for_function_entry<'tcx, F>(
165     tcx: TyCtxt<'tcx>,
166     body: &Body<'tcx>,
167     ctxt: &MoveDataParamEnv<'tcx>,
168     mut callback: F,
169 ) where
170     F: FnMut(MovePathIndex, DropFlagState),
171 {
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)
178         });
179     }
180 }
181
182 pub fn drop_flag_effects_for_location<'tcx, F>(
183     tcx: TyCtxt<'tcx>,
184     body: &Body<'tcx>,
185     ctxt: &MoveDataParamEnv<'tcx>,
186     loc: Location,
187     mut callback: F,
188 ) where
189     F: FnMut(MovePathIndex, DropFlagState),
190 {
191     let move_data = &ctxt.move_data;
192     debug!("drop_flag_effects_for_location({:?})", loc);
193
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]);
198
199         on_all_children_bits(tcx, body, move_data, path, |mpi| callback(mpi, DropFlagState::Absent))
200     }
201
202     debug!("drop_flag_effects: assignment for location({:?})", loc);
203
204     for_location_inits(tcx, body, move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
205 }
206
207 pub fn for_location_inits<'tcx, F>(
208     tcx: TyCtxt<'tcx>,
209     body: &Body<'tcx>,
210     move_data: &MoveData<'tcx>,
211     loc: Location,
212     mut callback: F,
213 ) where
214     F: FnMut(MovePathIndex),
215 {
216     for ii in &move_data.init_loc_map[loc] {
217         let init = move_data.inits[*ii];
218         match init.kind {
219             InitKind::Deep => {
220                 let path = init.path;
221
222                 on_all_children_bits(tcx, body, move_data, path, &mut callback)
223             }
224             InitKind::Shallow => {
225                 let mpi = init.path;
226                 callback(mpi);
227             }
228             InitKind::NonPanicPathOnly => (),
229         }
230     }
231 }
232
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`.
235 ///
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>(
239     tcx: TyCtxt<'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),
245 ) {
246     let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
247         return;
248     };
249
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
254         // base is an enum.
255         let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
256         assert_eq!(enum_place.projection.len(), base_proj.len());
257
258         let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
259             unreachable!();
260         };
261
262         if variant_idx != active_variant {
263             on_all_children_bits(tcx, body, move_data, variant_mpi, |mpi| {
264                 handle_inactive_variant(mpi)
265             });
266         }
267     }
268 }