]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_dataflow/src/drop_flag_effects.rs
Rollup merge of #103104 - SUPERCILEX:sep-ref, r=dtolnay
[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 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
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
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
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
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     debug!("drop_flag_effects: assignment for location({:?})", loc);
198
199     for_location_inits(tcx, body, move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
200 }
201
202 pub fn for_location_inits<'tcx, F>(
203     tcx: TyCtxt<'tcx>,
204     body: &Body<'tcx>,
205     move_data: &MoveData<'tcx>,
206     loc: Location,
207     mut callback: F,
208 ) where
209     F: FnMut(MovePathIndex),
210 {
211     for ii in &move_data.init_loc_map[loc] {
212         let init = move_data.inits[*ii];
213         match init.kind {
214             InitKind::Deep => {
215                 let path = init.path;
216
217                 on_all_children_bits(tcx, body, move_data, path, &mut callback)
218             }
219             InitKind::Shallow => {
220                 let mpi = init.path;
221                 callback(mpi);
222             }
223             InitKind::NonPanicPathOnly => (),
224         }
225     }
226 }
227
228 /// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a
229 /// `Downcast` to a variant besides the `active_variant`.
230 ///
231 /// NOTE: If there are no move paths corresponding to an inactive variant,
232 /// `handle_inactive_variant` will not be called for that variant.
233 pub(crate) fn on_all_inactive_variants<'tcx>(
234     tcx: TyCtxt<'tcx>,
235     body: &mir::Body<'tcx>,
236     move_data: &MoveData<'tcx>,
237     enum_place: mir::Place<'tcx>,
238     active_variant: VariantIdx,
239     mut handle_inactive_variant: impl FnMut(MovePathIndex),
240 ) {
241     let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
242         return;
243     };
244
245     let enum_path = &move_data.move_paths[enum_mpi];
246     for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
247         // Because of the way we build the `MoveData` tree, each child should have exactly one more
248         // projection than `enum_place`. This additional projection must be a downcast since the
249         // base is an enum.
250         let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
251         assert_eq!(enum_place.projection.len(), base_proj.len());
252
253         let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
254             unreachable!();
255         };
256
257         if variant_idx != active_variant {
258             on_all_children_bits(tcx, body, move_data, variant_mpi, |mpi| {
259                 handle_inactive_variant(mpi)
260             });
261         }
262     }
263 }