]>
Commit | Line | Data |
---|---|---|
064997fb | 1 | use crate::deref_separator::deref_finder; |
c295e0f8 | 2 | use crate::MirPass; |
e74abb32 | 3 | use rustc_index::bit_set::BitSet; |
49aad941 | 4 | use rustc_index::IndexVec; |
c295e0f8 | 5 | use rustc_middle::mir::patch::MirPatch; |
ba9703b0 XL |
6 | use rustc_middle::mir::*; |
7 | use rustc_middle::ty::{self, TyCtxt}; | |
c295e0f8 XL |
8 | use rustc_mir_dataflow::elaborate_drops::{elaborate_drop, DropFlagState, Unwind}; |
9 | use rustc_mir_dataflow::elaborate_drops::{DropElaborator, DropFlagMode, DropStyle}; | |
10 | use rustc_mir_dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; | |
11 | use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; | |
12 | use rustc_mir_dataflow::on_lookup_result_bits; | |
064997fb | 13 | use rustc_mir_dataflow::un_derefer::UnDerefer; |
c295e0f8 XL |
14 | use rustc_mir_dataflow::MoveDataParamEnv; |
15 | use rustc_mir_dataflow::{on_all_children_bits, on_all_drop_children_bits}; | |
16 | use rustc_mir_dataflow::{Analysis, ResultsCursor}; | |
49aad941 | 17 | use rustc_span::Span; |
353b0b11 | 18 | use rustc_target::abi::{FieldIdx, VariantIdx}; |
0bf4aa26 | 19 | use std::fmt; |
3157f602 | 20 | |
353b0b11 | 21 | /// During MIR building, Drop terminators are inserted in every place where a drop may occur. |
9ffffee4 FG |
22 | /// However, in this phase, the presence of these terminators does not guarantee that a destructor will run, |
23 | /// as the target of the drop may be uninitialized. | |
24 | /// In general, the compiler cannot determine at compile time whether a destructor will run or not. | |
25 | /// | |
353b0b11 | 26 | /// At a high level, this pass refines Drop to only run the destructor if the |
49aad941 | 27 | /// target is initialized. The way this is achieved is by inserting drop flags for every variable |
9ffffee4 | 28 | /// that may be dropped, and then using those flags to determine whether a destructor should run. |
9ffffee4 FG |
29 | /// Once this is complete, Drop terminators in the MIR correspond to a call to the "drop glue" or |
30 | /// "drop shim" for the type of the dropped place. | |
31 | /// | |
32 | /// This pass relies on dropped places having an associated move path, which is then used to determine | |
33 | /// the initialization status of the place and its descendants. | |
34 | /// It's worth noting that a MIR containing a Drop without an associated move path is probably ill formed, | |
35 | /// as it would allow running a destructor on a place behind a reference: | |
36 | /// | |
37 | /// ```text | |
38 | // fn drop_term<T>(t: &mut T) { | |
39 | // mir!( | |
40 | // { | |
41 | // Drop(*t, exit) | |
42 | // } | |
43 | // exit = { | |
44 | // Return() | |
45 | // } | |
46 | // ) | |
47 | // } | |
48 | /// ``` | |
3157f602 XL |
49 | pub struct ElaborateDrops; |
50 | ||
e1599b0c | 51 | impl<'tcx> MirPass<'tcx> for ElaborateDrops { |
29967ef6 XL |
52 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
53 | debug!("elaborate_drops({:?} @ {:?})", body.source, body.span); | |
abe05a73 | 54 | |
29967ef6 XL |
55 | let def_id = body.source.def_id(); |
56 | let param_env = tcx.param_env_reveal_all_normalized(def_id); | |
064997fb | 57 | let (side_table, move_data) = match MoveData::gather_moves(body, tcx, param_env) { |
8faf50e0 | 58 | Ok(move_data) => move_data, |
dfeec247 XL |
59 | Err((move_data, _)) => { |
60 | tcx.sess.delay_span_bug( | |
61 | body.span, | |
62 | "No `move_errors` should be allowed in MIR borrowck", | |
63 | ); | |
064997fb | 64 | (Default::default(), move_data) |
dfeec247 | 65 | } |
8faf50e0 | 66 | }; |
064997fb | 67 | let un_derefer = UnDerefer { tcx: tcx, derefer_sidetable: side_table }; |
3157f602 | 68 | let elaborate_patch = { |
dfeec247 | 69 | let env = MoveDataParamEnv { move_data, param_env }; |
9ffffee4 | 70 | remove_dead_unwinds(tcx, body, &env, &un_derefer); |
74b04a01 XL |
71 | |
72 | let inits = MaybeInitializedPlaces::new(tcx, body, &env) | |
29967ef6 | 73 | .into_engine(tcx, body) |
1b1a35ee | 74 | .pass_name("elaborate_drops") |
74b04a01 XL |
75 | .iterate_to_fixpoint() |
76 | .into_results_cursor(body); | |
77 | ||
78 | let uninits = MaybeUninitializedPlaces::new(tcx, body, &env) | |
f035d41b | 79 | .mark_inactive_variants_as_uninit() |
29967ef6 | 80 | .into_engine(tcx, body) |
1b1a35ee | 81 | .pass_name("elaborate_drops") |
74b04a01 XL |
82 | .iterate_to_fixpoint() |
83 | .into_results_cursor(body); | |
3157f602 | 84 | |
9ffffee4 FG |
85 | let reachable = traversal::reachable_as_bitset(body); |
86 | ||
49aad941 | 87 | let drop_flags = IndexVec::from_elem(None, &env.move_data.move_paths); |
3157f602 | 88 | ElaborateDropsCtxt { |
3b2f2976 | 89 | tcx, |
dc9dc135 | 90 | body, |
3157f602 | 91 | env: &env, |
74b04a01 | 92 | init_data: InitializationData { inits, uninits }, |
49aad941 | 93 | drop_flags, |
dc9dc135 | 94 | patch: MirPatch::new(body), |
064997fb | 95 | un_derefer: un_derefer, |
9ffffee4 | 96 | reachable, |
dfeec247 XL |
97 | } |
98 | .elaborate() | |
3157f602 | 99 | }; |
dc9dc135 | 100 | elaborate_patch.apply(body); |
064997fb | 101 | deref_finder(tcx, body); |
3157f602 XL |
102 | } |
103 | } | |
104 | ||
9ffffee4 | 105 | /// Removes unwind edges which are known to be unreachable, because they are in `drop` terminators |
cc61c64b | 106 | /// that can't drop anything. |
9ffffee4 | 107 | fn remove_dead_unwinds<'tcx>( |
dc9dc135 | 108 | tcx: TyCtxt<'tcx>, |
9ffffee4 | 109 | body: &mut Body<'tcx>, |
dc9dc135 | 110 | env: &MoveDataParamEnv<'tcx>, |
064997fb | 111 | und: &UnDerefer<'tcx>, |
9ffffee4 FG |
112 | ) { |
113 | debug!("remove_dead_unwinds({:?})", body.span); | |
cc61c64b XL |
114 | // We only need to do this pass once, because unwind edges can only |
115 | // reach cleanup blocks, which can't have unwind edges themselves. | |
9ffffee4 | 116 | let mut dead_unwinds = Vec::new(); |
74b04a01 | 117 | let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env) |
29967ef6 | 118 | .into_engine(tcx, body) |
9ffffee4 | 119 | .pass_name("remove_dead_unwinds") |
74b04a01 XL |
120 | .iterate_to_fixpoint() |
121 | .into_results_cursor(body); | |
f2b60f7d | 122 | for (bb, bb_data) in body.basic_blocks.iter_enumerated() { |
f035d41b | 123 | let place = match bb_data.terminator().kind { |
353b0b11 | 124 | TerminatorKind::Drop { ref place, unwind: UnwindAction::Cleanup(_), .. } => { |
064997fb FG |
125 | und.derefer(place.as_ref(), body).unwrap_or(*place) |
126 | } | |
ea8adc8c XL |
127 | _ => continue, |
128 | }; | |
cc61c64b | 129 | |
9ffffee4 | 130 | debug!("remove_dead_unwinds @ {:?}: {:?}", bb, bb_data); |
cc61c64b | 131 | |
5e7ed085 | 132 | let LookupResult::Exact(path) = env.move_data.rev_lookup.find(place.as_ref()) else { |
9ffffee4 | 133 | debug!("remove_dead_unwinds: has parent; skipping"); |
5e7ed085 | 134 | continue; |
ea8adc8c | 135 | }; |
cc61c64b | 136 | |
f9f354fc | 137 | flow_inits.seek_before_primary_effect(body.terminator_loc(bb)); |
74b04a01 | 138 | debug!( |
9ffffee4 | 139 | "remove_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}", |
74b04a01 | 140 | bb, |
f035d41b | 141 | place, |
74b04a01 XL |
142 | path, |
143 | flow_inits.get() | |
144 | ); | |
cc61c64b | 145 | |
ea8adc8c | 146 | let mut maybe_live = false; |
dc9dc135 | 147 | on_all_drop_children_bits(tcx, body, &env, path, |child| { |
74b04a01 | 148 | maybe_live |= flow_inits.contains(child); |
ea8adc8c XL |
149 | }); |
150 | ||
9ffffee4 | 151 | debug!("remove_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live); |
ea8adc8c | 152 | if !maybe_live { |
9ffffee4 | 153 | dead_unwinds.push(bb); |
cc61c64b XL |
154 | } |
155 | } | |
156 | ||
9ffffee4 FG |
157 | if dead_unwinds.is_empty() { |
158 | return; | |
159 | } | |
160 | ||
161 | let basic_blocks = body.basic_blocks.as_mut(); | |
162 | for &bb in dead_unwinds.iter() { | |
163 | if let Some(unwind) = basic_blocks[bb].terminator_mut().unwind_mut() { | |
353b0b11 | 164 | *unwind = UnwindAction::Unreachable; |
9ffffee4 FG |
165 | } |
166 | } | |
cc61c64b XL |
167 | } |
168 | ||
74b04a01 XL |
169 | struct InitializationData<'mir, 'tcx> { |
170 | inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, | |
171 | uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, | |
3157f602 XL |
172 | } |
173 | ||
74b04a01 XL |
174 | impl InitializationData<'_, '_> { |
175 | fn seek_before(&mut self, loc: Location) { | |
f9f354fc XL |
176 | self.inits.seek_before_primary_effect(loc); |
177 | self.uninits.seek_before_primary_effect(loc); | |
3157f602 XL |
178 | } |
179 | ||
74b04a01 XL |
180 | fn maybe_live_dead(&self, path: MovePathIndex) -> (bool, bool) { |
181 | (self.inits.contains(path), self.uninits.contains(path)) | |
3157f602 XL |
182 | } |
183 | } | |
184 | ||
dc9dc135 | 185 | struct Elaborator<'a, 'b, 'tcx> { |
cc61c64b XL |
186 | ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>, |
187 | } | |
188 | ||
a2a8927a | 189 | impl fmt::Debug for Elaborator<'_, '_, '_> { |
9fa01778 | 190 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { |
3157f602 XL |
191 | Ok(()) |
192 | } | |
193 | } | |
194 | ||
a2a8927a | 195 | impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> { |
cc61c64b XL |
196 | type Path = MovePathIndex; |
197 | ||
198 | fn patch(&mut self) -> &mut MirPatch<'tcx> { | |
199 | &mut self.ctxt.patch | |
200 | } | |
201 | ||
dc9dc135 XL |
202 | fn body(&self) -> &'a Body<'tcx> { |
203 | self.ctxt.body | |
cc61c64b XL |
204 | } |
205 | ||
dc9dc135 | 206 | fn tcx(&self) -> TyCtxt<'tcx> { |
cc61c64b XL |
207 | self.ctxt.tcx |
208 | } | |
209 | ||
7cac9316 | 210 | fn param_env(&self) -> ty::ParamEnv<'tcx> { |
cc61c64b XL |
211 | self.ctxt.param_env() |
212 | } | |
213 | ||
214 | fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { | |
215 | let ((maybe_live, maybe_dead), multipart) = match mode { | |
74b04a01 | 216 | DropFlagMode::Shallow => (self.ctxt.init_data.maybe_live_dead(path), false), |
cc61c64b XL |
217 | DropFlagMode::Deep => { |
218 | let mut some_live = false; | |
219 | let mut some_dead = false; | |
220 | let mut children_count = 0; | |
dfeec247 | 221 | on_all_drop_children_bits(self.tcx(), self.body(), self.ctxt.env, path, |child| { |
74b04a01 | 222 | let (live, dead) = self.ctxt.init_data.maybe_live_dead(child); |
dfeec247 XL |
223 | debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead)); |
224 | some_live |= live; | |
225 | some_dead |= dead; | |
226 | children_count += 1; | |
227 | }); | |
cc61c64b XL |
228 | ((some_live, some_dead), children_count != 1) |
229 | } | |
230 | }; | |
231 | match (maybe_live, maybe_dead, multipart) { | |
232 | (false, _, _) => DropStyle::Dead, | |
233 | (true, false, _) => DropStyle::Static, | |
234 | (true, true, false) => DropStyle::Conditional, | |
235 | (true, true, true) => DropStyle::Open, | |
236 | } | |
237 | } | |
238 | ||
239 | fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) { | |
240 | match mode { | |
241 | DropFlagMode::Shallow => { | |
242 | self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent); | |
243 | } | |
244 | DropFlagMode::Deep => { | |
245 | on_all_children_bits( | |
dfeec247 XL |
246 | self.tcx(), |
247 | self.body(), | |
248 | self.ctxt.move_data(), | |
249 | path, | |
250 | |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent), | |
251 | ); | |
cc61c64b XL |
252 | } |
253 | } | |
254 | } | |
255 | ||
353b0b11 | 256 | fn field_subpath(&self, path: Self::Path, field: FieldIdx) -> Option<Self::Path> { |
c295e0f8 | 257 | rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { |
f9f354fc | 258 | ProjectionElem::Field(idx, _) => idx == field, |
e1599b0c | 259 | _ => false, |
cc61c64b XL |
260 | }) |
261 | } | |
262 | ||
1b1a35ee | 263 | fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option<Self::Path> { |
c295e0f8 | 264 | rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { |
60c5eb7d | 265 | ProjectionElem::ConstantIndex { offset, min_length, from_end } => { |
f9f354fc | 266 | debug_assert!(size == min_length, "min_length should be exact for arrays"); |
60c5eb7d | 267 | assert!(!from_end, "from_end should not be used for array element ConstantIndex"); |
f9f354fc | 268 | offset == index |
e1599b0c | 269 | } |
e1599b0c | 270 | _ => false, |
ff7c6d11 XL |
271 | }) |
272 | } | |
273 | ||
cc61c64b | 274 | fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path> { |
c295e0f8 | 275 | rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| { |
f9f354fc | 276 | e == ProjectionElem::Deref |
cc61c64b XL |
277 | }) |
278 | } | |
279 | ||
a1dfa0c6 | 280 | fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option<Self::Path> { |
c295e0f8 | 281 | rustc_mir_dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { |
f9f354fc | 282 | ProjectionElem::Downcast(_, idx) => idx == variant, |
dfeec247 | 283 | _ => false, |
cc61c64b XL |
284 | }) |
285 | } | |
286 | ||
287 | fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>> { | |
ff7c6d11 | 288 | self.ctxt.drop_flag(path).map(Operand::Copy) |
cc61c64b XL |
289 | } |
290 | } | |
291 | ||
dc9dc135 XL |
292 | struct ElaborateDropsCtxt<'a, 'tcx> { |
293 | tcx: TyCtxt<'tcx>, | |
294 | body: &'a Body<'tcx>, | |
295 | env: &'a MoveDataParamEnv<'tcx>, | |
74b04a01 | 296 | init_data: InitializationData<'a, 'tcx>, |
49aad941 | 297 | drop_flags: IndexVec<MovePathIndex, Option<Local>>, |
3157f602 | 298 | patch: MirPatch<'tcx>, |
064997fb | 299 | un_derefer: UnDerefer<'tcx>, |
9ffffee4 | 300 | reachable: BitSet<BasicBlock>, |
3157f602 XL |
301 | } |
302 | ||
3157f602 | 303 | impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { |
dfeec247 XL |
304 | fn move_data(&self) -> &'b MoveData<'tcx> { |
305 | &self.env.move_data | |
306 | } | |
7cac9316 XL |
307 | |
308 | fn param_env(&self) -> ty::ParamEnv<'tcx> { | |
309 | self.env.param_env | |
3157f602 XL |
310 | } |
311 | ||
cc61c64b | 312 | fn create_drop_flag(&mut self, index: MovePathIndex, span: Span) { |
3157f602 XL |
313 | let tcx = self.tcx; |
314 | let patch = &mut self.patch; | |
dc9dc135 | 315 | debug!("create_drop_flag({:?})", self.body.span); |
49aad941 | 316 | self.drop_flags[index].get_or_insert_with(|| patch.new_internal(tcx.types.bool, span)); |
3157f602 XL |
317 | } |
318 | ||
ff7c6d11 | 319 | fn drop_flag(&mut self, index: MovePathIndex) -> Option<Place<'tcx>> { |
49aad941 | 320 | self.drop_flags[index].map(Place::from) |
3157f602 XL |
321 | } |
322 | ||
323 | /// create a patch that elaborates all drops in the input | |
324 | /// MIR. | |
dfeec247 | 325 | fn elaborate(mut self) -> MirPatch<'tcx> { |
3157f602 XL |
326 | self.collect_drop_flags(); |
327 | ||
328 | self.elaborate_drops(); | |
329 | ||
330 | self.drop_flags_on_init(); | |
331 | self.drop_flags_for_fn_rets(); | |
332 | self.drop_flags_for_args(); | |
333 | self.drop_flags_for_locs(); | |
334 | ||
335 | self.patch | |
336 | } | |
337 | ||
dfeec247 | 338 | fn collect_drop_flags(&mut self) { |
f2b60f7d | 339 | for (bb, data) in self.body.basic_blocks.iter_enumerated() { |
9ffffee4 FG |
340 | if !self.reachable.contains(bb) { |
341 | continue; | |
342 | } | |
3157f602 | 343 | let terminator = data.terminator(); |
f035d41b | 344 | let place = match terminator.kind { |
353b0b11 | 345 | TerminatorKind::Drop { ref place, .. } => { |
064997fb FG |
346 | self.un_derefer.derefer(place.as_ref(), self.body).unwrap_or(*place) |
347 | } | |
dfeec247 | 348 | _ => continue, |
3157f602 XL |
349 | }; |
350 | ||
74b04a01 | 351 | self.init_data.seek_before(self.body.terminator_loc(bb)); |
3157f602 | 352 | |
f035d41b XL |
353 | let path = self.move_data().rev_lookup.find(place.as_ref()); |
354 | debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, place, path); | |
3157f602 | 355 | |
9e0c209e SL |
356 | let path = match path { |
357 | LookupResult::Exact(e) => e, | |
358 | LookupResult::Parent(None) => continue, | |
359 | LookupResult::Parent(Some(parent)) => { | |
74b04a01 | 360 | let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent); |
064997fb FG |
361 | |
362 | if self.body.local_decls[place.local].is_deref_temp() { | |
363 | continue; | |
364 | } | |
365 | ||
9e0c209e | 366 | if maybe_dead { |
a2a8927a | 367 | self.tcx.sess.delay_span_bug( |
dfeec247 | 368 | terminator.source_info.span, |
49aad941 | 369 | format!( |
a2a8927a | 370 | "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", |
064997fb | 371 | bb, place, path |
a2a8927a | 372 | ), |
dfeec247 | 373 | ); |
9e0c209e | 374 | } |
dfeec247 | 375 | continue; |
9e0c209e SL |
376 | } |
377 | }; | |
378 | ||
dc9dc135 | 379 | on_all_drop_children_bits(self.tcx, self.body, self.env, path, |child| { |
74b04a01 | 380 | let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child); |
dfeec247 XL |
381 | debug!( |
382 | "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", | |
383 | child, | |
f035d41b | 384 | place, |
dfeec247 XL |
385 | path, |
386 | (maybe_live, maybe_dead) | |
387 | ); | |
cc61c64b XL |
388 | if maybe_live && maybe_dead { |
389 | self.create_drop_flag(child, terminator.source_info.span) | |
3157f602 XL |
390 | } |
391 | }); | |
392 | } | |
393 | } | |
394 | ||
dfeec247 | 395 | fn elaborate_drops(&mut self) { |
f2b60f7d | 396 | for (bb, data) in self.body.basic_blocks.iter_enumerated() { |
9ffffee4 FG |
397 | if !self.reachable.contains(bb) { |
398 | continue; | |
399 | } | |
9e0c209e | 400 | let loc = Location { block: bb, statement_index: data.statements.len() }; |
3157f602 XL |
401 | let terminator = data.terminator(); |
402 | ||
3157f602 | 403 | match terminator.kind { |
49aad941 | 404 | TerminatorKind::Drop { mut place, target, unwind, replace } => { |
064997fb FG |
405 | if let Some(new_place) = self.un_derefer.derefer(place.as_ref(), self.body) { |
406 | place = new_place; | |
407 | } | |
408 | ||
74b04a01 | 409 | self.init_data.seek_before(loc); |
f035d41b | 410 | match self.move_data().rev_lookup.find(place.as_ref()) { |
353b0b11 FG |
411 | LookupResult::Exact(path) => { |
412 | let unwind = if data.is_cleanup { | |
dfeec247 XL |
413 | Unwind::InCleanup |
414 | } else { | |
353b0b11 FG |
415 | match unwind { |
416 | UnwindAction::Cleanup(cleanup) => Unwind::To(cleanup), | |
417 | UnwindAction::Continue => Unwind::To(self.patch.resume_block()), | |
418 | UnwindAction::Unreachable => { | |
419 | Unwind::To(self.patch.unreachable_cleanup_block()) | |
420 | } | |
421 | UnwindAction::Terminate => { | |
422 | Unwind::To(self.patch.terminate_block()) | |
423 | } | |
424 | } | |
425 | }; | |
426 | elaborate_drop( | |
427 | &mut Elaborator { ctxt: self }, | |
428 | terminator.source_info, | |
429 | place, | |
430 | path, | |
431 | target, | |
432 | unwind, | |
433 | bb, | |
434 | ) | |
435 | } | |
9e0c209e | 436 | LookupResult::Parent(..) => { |
49aad941 | 437 | if !replace { |
353b0b11 FG |
438 | self.tcx.sess.delay_span_bug( |
439 | terminator.source_info.span, | |
49aad941 | 440 | format!("drop of untracked value {:?}", bb), |
353b0b11 FG |
441 | ); |
442 | } | |
443 | // A drop and replace behind a pointer/array/whatever. | |
444 | // The borrow checker requires that these locations are initialized before the assignment, | |
445 | // so we just leave an unconditional drop. | |
446 | assert!(!data.is_cleanup); | |
9e0c209e SL |
447 | } |
448 | } | |
3157f602 | 449 | } |
dfeec247 | 450 | _ => continue, |
3157f602 XL |
451 | } |
452 | } | |
453 | } | |
454 | ||
3157f602 | 455 | fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { |
cc61c64b | 456 | Rvalue::Use(Operand::Constant(Box::new(Constant { |
3b2f2976 | 457 | span, |
b7449926 | 458 | user_ty: None, |
923072b8 | 459 | literal: ConstantKind::from_bool(self.tcx, val), |
cc61c64b | 460 | }))) |
3157f602 XL |
461 | } |
462 | ||
463 | fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) { | |
49aad941 | 464 | if let Some(flag) = self.drop_flags[path] { |
dc9dc135 | 465 | let span = self.patch.source_info_for_location(self.body, loc).span; |
3157f602 | 466 | let val = self.constant_bool(span, val.value()); |
dc9dc135 | 467 | self.patch.add_assign(loc, Place::from(flag), val); |
3157f602 XL |
468 | } |
469 | } | |
470 | ||
471 | fn drop_flags_on_init(&mut self) { | |
83c7162d | 472 | let loc = Location::START; |
dc9dc135 | 473 | let span = self.patch.source_info_for_location(self.body, loc).span; |
3157f602 | 474 | let false_ = self.constant_bool(span, false); |
49aad941 | 475 | for flag in self.drop_flags.iter().flatten() { |
dc9dc135 | 476 | self.patch.add_assign(loc, Place::from(*flag), false_.clone()); |
3157f602 XL |
477 | } |
478 | } | |
479 | ||
480 | fn drop_flags_for_fn_rets(&mut self) { | |
f2b60f7d | 481 | for (bb, data) in self.body.basic_blocks.iter_enumerated() { |
9ffffee4 FG |
482 | if !self.reachable.contains(bb) { |
483 | continue; | |
484 | } | |
3157f602 | 485 | if let TerminatorKind::Call { |
353b0b11 FG |
486 | destination, |
487 | target: Some(tgt), | |
488 | unwind: UnwindAction::Cleanup(_), | |
489 | .. | |
dfeec247 XL |
490 | } = data.terminator().kind |
491 | { | |
3157f602 XL |
492 | assert!(!self.patch.is_patched(bb)); |
493 | ||
9e0c209e | 494 | let loc = Location { block: tgt, statement_index: 0 }; |
923072b8 | 495 | let path = self.move_data().rev_lookup.find(destination.as_ref()); |
dfeec247 XL |
496 | on_lookup_result_bits(self.tcx, self.body, self.move_data(), path, |child| { |
497 | self.set_drop_flag(loc, child, DropFlagState::Present) | |
498 | }); | |
3157f602 XL |
499 | } |
500 | } | |
501 | } | |
502 | ||
503 | fn drop_flags_for_args(&mut self) { | |
83c7162d | 504 | let loc = Location::START; |
c295e0f8 XL |
505 | rustc_mir_dataflow::drop_flag_effects_for_function_entry( |
506 | self.tcx, | |
507 | self.body, | |
508 | self.env, | |
509 | |path, ds| { | |
510 | self.set_drop_flag(loc, path, ds); | |
511 | }, | |
512 | ) | |
3157f602 XL |
513 | } |
514 | ||
515 | fn drop_flags_for_locs(&mut self) { | |
516 | // We intentionally iterate only over the *old* basic blocks. | |
517 | // | |
518 | // Basic blocks created by drop elaboration update their | |
519 | // drop flags by themselves, to avoid the drop flags being | |
520 | // clobbered before they are read. | |
521 | ||
f2b60f7d | 522 | for (bb, data) in self.body.basic_blocks.iter_enumerated() { |
9ffffee4 FG |
523 | if !self.reachable.contains(bb) { |
524 | continue; | |
525 | } | |
3157f602 | 526 | debug!("drop_flags_for_locs({:?})", data); |
dfeec247 | 527 | for i in 0..(data.statements.len() + 1) { |
3157f602 | 528 | debug!("drop_flag_for_locs: stmt {}", i); |
3157f602 XL |
529 | if i == data.statements.len() { |
530 | match data.terminator().kind { | |
531 | TerminatorKind::Drop { .. } => { | |
532 | // drop elaboration should handle that by itself | |
dfeec247 | 533 | continue; |
3157f602 | 534 | } |
041b39d2 XL |
535 | TerminatorKind::Resume => { |
536 | // It is possible for `Resume` to be patched | |
537 | // (in particular it can be patched to be replaced with | |
538 | // a Goto; see `MirPatch::new`). | |
539 | } | |
3157f602 XL |
540 | _ => { |
541 | assert!(!self.patch.is_patched(bb)); | |
542 | } | |
543 | } | |
544 | } | |
9e0c209e | 545 | let loc = Location { block: bb, statement_index: i }; |
c295e0f8 | 546 | rustc_mir_dataflow::drop_flag_effects_for_location( |
dfeec247 XL |
547 | self.tcx, |
548 | self.body, | |
549 | self.env, | |
550 | loc, | |
353b0b11 | 551 | |path, ds| self.set_drop_flag(loc, path, ds), |
3157f602 XL |
552 | ) |
553 | } | |
554 | ||
555 | // There may be a critical edge after this call, | |
556 | // so mark the return as initialized *before* the | |
557 | // call. | |
353b0b11 FG |
558 | if let TerminatorKind::Call { |
559 | destination, | |
560 | target: Some(_), | |
561 | unwind: UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate, | |
562 | .. | |
563 | } = data.terminator().kind | |
dfeec247 | 564 | { |
3157f602 XL |
565 | assert!(!self.patch.is_patched(bb)); |
566 | ||
9e0c209e | 567 | let loc = Location { block: bb, statement_index: data.statements.len() }; |
923072b8 | 568 | let path = self.move_data().rev_lookup.find(destination.as_ref()); |
dfeec247 XL |
569 | on_lookup_result_bits(self.tcx, self.body, self.move_data(), path, |child| { |
570 | self.set_drop_flag(loc, child, DropFlagState::Present) | |
571 | }); | |
3157f602 XL |
572 | } |
573 | } | |
574 | } | |
3157f602 | 575 | } |