]>
Commit | Line | Data |
---|---|---|
3157f602 XL |
1 | // Copyright 2016 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
041b39d2 | 11 | use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult}; |
2c00a5a8 | 12 | use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; |
041b39d2 XL |
13 | use dataflow::{DataflowResults}; |
14 | use dataflow::{on_all_children_bits, on_all_drop_children_bits}; | |
15 | use dataflow::{drop_flag_effects_for_location, on_lookup_result_bits}; | |
16 | use dataflow::MoveDataParamEnv; | |
ff7c6d11 | 17 | use dataflow::{self, do_dataflow, DebugFormatted}; |
cc61c64b | 18 | use rustc::ty::{self, TyCtxt}; |
c30ab7b3 | 19 | use rustc::mir::*; |
476ff2be | 20 | use rustc::util::nodemap::FxHashMap; |
c30ab7b3 | 21 | use rustc_data_structures::indexed_set::IdxSetBuf; |
3157f602 | 22 | use rustc_data_structures::indexed_vec::Idx; |
abe05a73 | 23 | use transform::{MirPass, MirSource}; |
041b39d2 XL |
24 | use util::patch::MirPatch; |
25 | use util::elaborate_drops::{DropFlagState, Unwind, elaborate_drop}; | |
26 | use util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode}; | |
cc61c64b | 27 | use syntax::ast; |
3157f602 XL |
28 | use syntax_pos::Span; |
29 | ||
30 | use std::fmt; | |
3157f602 XL |
31 | |
32 | pub struct ElaborateDrops; | |
33 | ||
7cac9316 XL |
34 | impl MirPass for ElaborateDrops { |
35 | fn run_pass<'a, 'tcx>(&self, | |
36 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
37 | src: MirSource, | |
38 | mir: &mut Mir<'tcx>) | |
3157f602 XL |
39 | { |
40 | debug!("elaborate_drops({:?} @ {:?})", src, mir.span); | |
abe05a73 | 41 | |
abe05a73 | 42 | let id = tcx.hir.as_local_node_id(src.def_id).unwrap(); |
0531ce1d | 43 | let param_env = tcx.param_env(src.def_id).with_reveal_all(); |
8faf50e0 XL |
44 | let move_data = match MoveData::gather_moves(mir, tcx) { |
45 | Ok(move_data) => move_data, | |
46 | Err((move_data, _move_errors)) => { | |
47 | // The only way we should be allowing any move_errors | |
48 | // in here is if we are in the migration path for the | |
49 | // NLL-based MIR-borrowck. | |
50 | // | |
51 | // If we are in the migration path, we have already | |
52 | // reported these errors as warnings to the user. So | |
53 | // we will just ignore them here. | |
54 | assert!(tcx.migrate_borrowck()); | |
55 | move_data | |
56 | } | |
57 | }; | |
3157f602 XL |
58 | let elaborate_patch = { |
59 | let mir = &*mir; | |
60 | let env = MoveDataParamEnv { | |
3b2f2976 XL |
61 | move_data, |
62 | param_env, | |
3157f602 | 63 | }; |
cc61c64b | 64 | let dead_unwinds = find_dead_unwinds(tcx, mir, id, &env); |
3157f602 | 65 | let flow_inits = |
ff7c6d11 | 66 | do_dataflow(tcx, mir, id, &[], &dead_unwinds, |
2c00a5a8 | 67 | MaybeInitializedPlaces::new(tcx, mir, &env), |
ff7c6d11 | 68 | |bd, p| DebugFormatted::new(&bd.move_data().move_paths[p])); |
3157f602 | 69 | let flow_uninits = |
ff7c6d11 | 70 | do_dataflow(tcx, mir, id, &[], &dead_unwinds, |
2c00a5a8 | 71 | MaybeUninitializedPlaces::new(tcx, mir, &env), |
ff7c6d11 | 72 | |bd, p| DebugFormatted::new(&bd.move_data().move_paths[p])); |
3157f602 XL |
73 | |
74 | ElaborateDropsCtxt { | |
3b2f2976 XL |
75 | tcx, |
76 | mir, | |
3157f602 | 77 | env: &env, |
3b2f2976 XL |
78 | flow_inits, |
79 | flow_uninits, | |
476ff2be | 80 | drop_flags: FxHashMap(), |
3157f602 XL |
81 | patch: MirPatch::new(mir), |
82 | }.elaborate() | |
83 | }; | |
84 | elaborate_patch.apply(mir); | |
85 | } | |
86 | } | |
87 | ||
cc61c64b XL |
88 | /// Return the set of basic blocks whose unwind edges are known |
89 | /// to not be reachable, because they are `drop` terminators | |
90 | /// that can't drop anything. | |
91 | fn find_dead_unwinds<'a, 'tcx>( | |
92 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
93 | mir: &Mir<'tcx>, | |
94 | id: ast::NodeId, | |
abe05a73 | 95 | env: &MoveDataParamEnv<'tcx, 'tcx>) |
cc61c64b XL |
96 | -> IdxSetBuf<BasicBlock> |
97 | { | |
98 | debug!("find_dead_unwinds({:?})", mir.span); | |
99 | // We only need to do this pass once, because unwind edges can only | |
100 | // reach cleanup blocks, which can't have unwind edges themselves. | |
101 | let mut dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); | |
102 | let flow_inits = | |
ff7c6d11 | 103 | do_dataflow(tcx, mir, id, &[], &dead_unwinds, |
2c00a5a8 | 104 | MaybeInitializedPlaces::new(tcx, mir, &env), |
ff7c6d11 | 105 | |bd, p| DebugFormatted::new(&bd.move_data().move_paths[p])); |
cc61c64b | 106 | for (bb, bb_data) in mir.basic_blocks().iter_enumerated() { |
ea8adc8c | 107 | let location = match bb_data.terminator().kind { |
cc61c64b | 108 | TerminatorKind::Drop { ref location, unwind: Some(_), .. } | |
ea8adc8c XL |
109 | TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => location, |
110 | _ => continue, | |
111 | }; | |
cc61c64b | 112 | |
ea8adc8c XL |
113 | let mut init_data = InitializationData { |
114 | live: flow_inits.sets().on_entry_set_for(bb.index()).to_owned(), | |
115 | dead: IdxSetBuf::new_empty(env.move_data.move_paths.len()), | |
116 | }; | |
117 | debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}", | |
118 | bb, bb_data, init_data.live); | |
119 | for stmt in 0..bb_data.statements.len() { | |
120 | let loc = Location { block: bb, statement_index: stmt }; | |
121 | init_data.apply_location(tcx, mir, env, loc); | |
122 | } | |
cc61c64b | 123 | |
ea8adc8c XL |
124 | let path = match env.move_data.rev_lookup.find(location) { |
125 | LookupResult::Exact(e) => e, | |
126 | LookupResult::Parent(..) => { | |
127 | debug!("find_dead_unwinds: has parent; skipping"); | |
128 | continue | |
129 | } | |
130 | }; | |
cc61c64b | 131 | |
ea8adc8c | 132 | debug!("find_dead_unwinds @ {:?}: path({:?})={:?}", bb, location, path); |
cc61c64b | 133 | |
ea8adc8c XL |
134 | let mut maybe_live = false; |
135 | on_all_drop_children_bits(tcx, mir, &env, path, |child| { | |
136 | let (child_maybe_live, _) = init_data.state(child); | |
137 | maybe_live |= child_maybe_live; | |
138 | }); | |
139 | ||
140 | debug!("find_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live); | |
141 | if !maybe_live { | |
142 | dead_unwinds.add(&bb); | |
cc61c64b XL |
143 | } |
144 | } | |
145 | ||
146 | dead_unwinds | |
147 | } | |
148 | ||
3157f602 XL |
149 | struct InitializationData { |
150 | live: IdxSetBuf<MovePathIndex>, | |
151 | dead: IdxSetBuf<MovePathIndex> | |
152 | } | |
153 | ||
154 | impl InitializationData { | |
155 | fn apply_location<'a,'tcx>(&mut self, | |
156 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
157 | mir: &Mir<'tcx>, | |
abe05a73 | 158 | env: &MoveDataParamEnv<'tcx, 'tcx>, |
3157f602 XL |
159 | loc: Location) |
160 | { | |
161 | drop_flag_effects_for_location(tcx, mir, env, loc, |path, df| { | |
162 | debug!("at location {:?}: setting {:?} to {:?}", | |
163 | loc, path, df); | |
164 | match df { | |
165 | DropFlagState::Present => { | |
166 | self.live.add(&path); | |
167 | self.dead.remove(&path); | |
168 | } | |
169 | DropFlagState::Absent => { | |
170 | self.dead.add(&path); | |
171 | self.live.remove(&path); | |
172 | } | |
173 | } | |
174 | }); | |
175 | } | |
176 | ||
177 | fn state(&self, path: MovePathIndex) -> (bool, bool) { | |
178 | (self.live.contains(&path), self.dead.contains(&path)) | |
179 | } | |
180 | } | |
181 | ||
cc61c64b XL |
182 | struct Elaborator<'a, 'b: 'a, 'tcx: 'b> { |
183 | init_data: &'a InitializationData, | |
184 | ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>, | |
185 | } | |
186 | ||
187 | impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> { | |
94b46f34 | 188 | fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { |
3157f602 XL |
189 | Ok(()) |
190 | } | |
191 | } | |
192 | ||
cc61c64b XL |
193 | impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { |
194 | type Path = MovePathIndex; | |
195 | ||
196 | fn patch(&mut self) -> &mut MirPatch<'tcx> { | |
197 | &mut self.ctxt.patch | |
198 | } | |
199 | ||
200 | fn mir(&self) -> &'a Mir<'tcx> { | |
201 | self.ctxt.mir | |
202 | } | |
203 | ||
ea8adc8c | 204 | fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { |
cc61c64b XL |
205 | self.ctxt.tcx |
206 | } | |
207 | ||
7cac9316 | 208 | fn param_env(&self) -> ty::ParamEnv<'tcx> { |
cc61c64b XL |
209 | self.ctxt.param_env() |
210 | } | |
211 | ||
212 | fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { | |
213 | let ((maybe_live, maybe_dead), multipart) = match mode { | |
214 | DropFlagMode::Shallow => (self.init_data.state(path), false), | |
215 | DropFlagMode::Deep => { | |
216 | let mut some_live = false; | |
217 | let mut some_dead = false; | |
218 | let mut children_count = 0; | |
219 | on_all_drop_children_bits( | |
220 | self.tcx(), self.mir(), self.ctxt.env, path, |child| { | |
221 | let (live, dead) = self.init_data.state(child); | |
222 | debug!("elaborate_drop: state({:?}) = {:?}", | |
223 | child, (live, dead)); | |
224 | some_live |= live; | |
225 | some_dead |= dead; | |
226 | children_count += 1; | |
227 | }); | |
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( | |
246 | self.tcx(), self.mir(), self.ctxt.move_data(), path, | |
247 | |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent) | |
248 | ); | |
249 | } | |
250 | } | |
251 | } | |
252 | ||
253 | fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path> { | |
041b39d2 | 254 | dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { |
cc61c64b XL |
255 | match p { |
256 | &Projection { | |
257 | elem: ProjectionElem::Field(idx, _), .. | |
258 | } => idx == field, | |
259 | _ => false | |
260 | } | |
261 | }) | |
262 | } | |
263 | ||
ff7c6d11 XL |
264 | fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option<Self::Path> { |
265 | dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { | |
266 | match p { | |
267 | &Projection { | |
268 | elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: false}, .. | |
269 | } => offset == index, | |
270 | &Projection { | |
271 | elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true}, .. | |
272 | } => size - offset == index, | |
273 | _ => false | |
274 | } | |
275 | }) | |
276 | } | |
277 | ||
cc61c64b | 278 | fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path> { |
041b39d2 | 279 | dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { |
cc61c64b XL |
280 | match p { |
281 | &Projection { elem: ProjectionElem::Deref, .. } => true, | |
282 | _ => false | |
283 | } | |
284 | }) | |
285 | } | |
286 | ||
287 | fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option<Self::Path> { | |
041b39d2 | 288 | dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { |
cc61c64b XL |
289 | match p { |
290 | &Projection { | |
291 | elem: ProjectionElem::Downcast(_, idx), .. | |
292 | } => idx == variant, | |
293 | _ => false | |
294 | } | |
295 | }) | |
296 | } | |
297 | ||
298 | fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>> { | |
ff7c6d11 | 299 | self.ctxt.drop_flag(path).map(Operand::Copy) |
cc61c64b XL |
300 | } |
301 | } | |
302 | ||
3157f602 XL |
303 | struct ElaborateDropsCtxt<'a, 'tcx: 'a> { |
304 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
305 | mir: &'a Mir<'tcx>, | |
abe05a73 | 306 | env: &'a MoveDataParamEnv<'tcx, 'tcx>, |
2c00a5a8 XL |
307 | flow_inits: DataflowResults<MaybeInitializedPlaces<'a, 'tcx, 'tcx>>, |
308 | flow_uninits: DataflowResults<MaybeUninitializedPlaces<'a, 'tcx, 'tcx>>, | |
476ff2be | 309 | drop_flags: FxHashMap<MovePathIndex, Local>, |
3157f602 XL |
310 | patch: MirPatch<'tcx>, |
311 | } | |
312 | ||
3157f602 XL |
313 | impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { |
314 | fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data } | |
7cac9316 XL |
315 | |
316 | fn param_env(&self) -> ty::ParamEnv<'tcx> { | |
317 | self.env.param_env | |
3157f602 XL |
318 | } |
319 | ||
320 | fn initialization_data_at(&self, loc: Location) -> InitializationData { | |
321 | let mut data = InitializationData { | |
322 | live: self.flow_inits.sets().on_entry_set_for(loc.block.index()) | |
323 | .to_owned(), | |
324 | dead: self.flow_uninits.sets().on_entry_set_for(loc.block.index()) | |
325 | .to_owned(), | |
326 | }; | |
9e0c209e | 327 | for stmt in 0..loc.statement_index { |
3157f602 | 328 | data.apply_location(self.tcx, self.mir, self.env, |
9e0c209e | 329 | Location { block: loc.block, statement_index: stmt }); |
3157f602 XL |
330 | } |
331 | data | |
332 | } | |
333 | ||
cc61c64b | 334 | fn create_drop_flag(&mut self, index: MovePathIndex, span: Span) { |
3157f602 XL |
335 | let tcx = self.tcx; |
336 | let patch = &mut self.patch; | |
8bb4bdeb | 337 | debug!("create_drop_flag({:?})", self.mir.span); |
3157f602 | 338 | self.drop_flags.entry(index).or_insert_with(|| { |
ea8adc8c | 339 | patch.new_internal(tcx.types.bool, span) |
3157f602 XL |
340 | }); |
341 | } | |
342 | ||
ff7c6d11 XL |
343 | fn drop_flag(&mut self, index: MovePathIndex) -> Option<Place<'tcx>> { |
344 | self.drop_flags.get(&index).map(|t| Place::Local(*t)) | |
3157f602 XL |
345 | } |
346 | ||
347 | /// create a patch that elaborates all drops in the input | |
348 | /// MIR. | |
349 | fn elaborate(mut self) -> MirPatch<'tcx> | |
350 | { | |
351 | self.collect_drop_flags(); | |
352 | ||
353 | self.elaborate_drops(); | |
354 | ||
355 | self.drop_flags_on_init(); | |
356 | self.drop_flags_for_fn_rets(); | |
357 | self.drop_flags_for_args(); | |
358 | self.drop_flags_for_locs(); | |
359 | ||
360 | self.patch | |
361 | } | |
362 | ||
3157f602 XL |
363 | fn collect_drop_flags(&mut self) |
364 | { | |
365 | for (bb, data) in self.mir.basic_blocks().iter_enumerated() { | |
366 | let terminator = data.terminator(); | |
367 | let location = match terminator.kind { | |
368 | TerminatorKind::Drop { ref location, .. } | | |
369 | TerminatorKind::DropAndReplace { ref location, .. } => location, | |
370 | _ => continue | |
371 | }; | |
372 | ||
3157f602 XL |
373 | let init_data = self.initialization_data_at(Location { |
374 | block: bb, | |
9e0c209e | 375 | statement_index: data.statements.len() |
3157f602 XL |
376 | }); |
377 | ||
378 | let path = self.move_data().rev_lookup.find(location); | |
ff7c6d11 | 379 | debug!("collect_drop_flags: {:?}, place {:?} ({:?})", |
3157f602 XL |
380 | bb, location, path); |
381 | ||
9e0c209e SL |
382 | let path = match path { |
383 | LookupResult::Exact(e) => e, | |
384 | LookupResult::Parent(None) => continue, | |
385 | LookupResult::Parent(Some(parent)) => { | |
386 | let (_maybe_live, maybe_dead) = init_data.state(parent); | |
387 | if maybe_dead { | |
388 | span_bug!(terminator.source_info.span, | |
ff7c6d11 | 389 | "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", |
9e0c209e SL |
390 | bb, location, path); |
391 | } | |
392 | continue | |
393 | } | |
394 | }; | |
395 | ||
cc61c64b XL |
396 | on_all_drop_children_bits(self.tcx, self.mir, self.env, path, |child| { |
397 | let (maybe_live, maybe_dead) = init_data.state(child); | |
398 | debug!("collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", | |
399 | child, location, path, (maybe_live, maybe_dead)); | |
400 | if maybe_live && maybe_dead { | |
401 | self.create_drop_flag(child, terminator.source_info.span) | |
3157f602 XL |
402 | } |
403 | }); | |
404 | } | |
405 | } | |
406 | ||
407 | fn elaborate_drops(&mut self) | |
408 | { | |
409 | for (bb, data) in self.mir.basic_blocks().iter_enumerated() { | |
9e0c209e | 410 | let loc = Location { block: bb, statement_index: data.statements.len() }; |
3157f602 XL |
411 | let terminator = data.terminator(); |
412 | ||
413 | let resume_block = self.patch.resume_block(); | |
414 | match terminator.kind { | |
415 | TerminatorKind::Drop { ref location, target, unwind } => { | |
416 | let init_data = self.initialization_data_at(loc); | |
9e0c209e SL |
417 | match self.move_data().rev_lookup.find(location) { |
418 | LookupResult::Exact(path) => { | |
cc61c64b XL |
419 | elaborate_drop( |
420 | &mut Elaborator { | |
421 | init_data: &init_data, | |
422 | ctxt: self | |
423 | }, | |
424 | terminator.source_info, | |
cc61c64b XL |
425 | location, |
426 | path, | |
427 | target, | |
428 | if data.is_cleanup { | |
7cac9316 | 429 | Unwind::InCleanup |
9e0c209e | 430 | } else { |
7cac9316 | 431 | Unwind::To(Option::unwrap_or(unwind, resume_block)) |
cc61c64b XL |
432 | }, |
433 | bb) | |
3157f602 | 434 | } |
9e0c209e SL |
435 | LookupResult::Parent(..) => { |
436 | span_bug!(terminator.source_info.span, | |
437 | "drop of untracked value {:?}", bb); | |
438 | } | |
439 | } | |
3157f602 XL |
440 | } |
441 | TerminatorKind::DropAndReplace { ref location, ref value, | |
442 | target, unwind } => | |
443 | { | |
444 | assert!(!data.is_cleanup); | |
445 | ||
446 | self.elaborate_replace( | |
447 | loc, | |
448 | location, value, | |
449 | target, unwind | |
450 | ); | |
451 | } | |
452 | _ => continue | |
453 | } | |
454 | } | |
455 | } | |
456 | ||
457 | /// Elaborate a MIR `replace` terminator. This instruction | |
94b46f34 | 458 | /// is not directly handled by codegen, and therefore |
3157f602 XL |
459 | /// must be desugared. |
460 | /// | |
461 | /// The desugaring drops the location if needed, and then writes | |
462 | /// the value (including setting the drop flag) over it in *both* arms. | |
463 | /// | |
ff7c6d11 | 464 | /// The `replace` terminator can also be called on places that |
3157f602 XL |
465 | /// are not tracked by elaboration (for example, |
466 | /// `replace x[i] <- tmp0`). The borrow checker requires that | |
467 | /// these locations are initialized before the assignment, | |
468 | /// so we just generate an unconditional drop. | |
469 | fn elaborate_replace( | |
470 | &mut self, | |
471 | loc: Location, | |
ff7c6d11 | 472 | location: &Place<'tcx>, |
3157f602 XL |
473 | value: &Operand<'tcx>, |
474 | target: BasicBlock, | |
475 | unwind: Option<BasicBlock>) | |
476 | { | |
477 | let bb = loc.block; | |
478 | let data = &self.mir[bb]; | |
479 | let terminator = data.terminator(); | |
7cac9316 | 480 | assert!(!data.is_cleanup, "DropAndReplace in unwind path not supported"); |
3157f602 XL |
481 | |
482 | let assign = Statement { | |
483 | kind: StatementKind::Assign(location.clone(), Rvalue::Use(value.clone())), | |
484 | source_info: terminator.source_info | |
485 | }; | |
486 | ||
487 | let unwind = unwind.unwrap_or(self.patch.resume_block()); | |
488 | let unwind = self.patch.new_block(BasicBlockData { | |
489 | statements: vec![assign.clone()], | |
490 | terminator: Some(Terminator { | |
491 | kind: TerminatorKind::Goto { target: unwind }, | |
492 | ..*terminator | |
493 | }), | |
494 | is_cleanup: true | |
495 | }); | |
496 | ||
497 | let target = self.patch.new_block(BasicBlockData { | |
498 | statements: vec![assign], | |
499 | terminator: Some(Terminator { | |
500 | kind: TerminatorKind::Goto { target: target }, | |
501 | ..*terminator | |
502 | }), | |
7cac9316 | 503 | is_cleanup: false, |
3157f602 XL |
504 | }); |
505 | ||
9e0c209e SL |
506 | match self.move_data().rev_lookup.find(location) { |
507 | LookupResult::Exact(path) => { | |
508 | debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); | |
509 | let init_data = self.initialization_data_at(loc); | |
510 | ||
cc61c64b XL |
511 | elaborate_drop( |
512 | &mut Elaborator { | |
513 | init_data: &init_data, | |
514 | ctxt: self | |
515 | }, | |
516 | terminator.source_info, | |
cc61c64b XL |
517 | location, |
518 | path, | |
519 | target, | |
7cac9316 | 520 | Unwind::To(unwind), |
cc61c64b | 521 | bb); |
9e0c209e SL |
522 | on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { |
523 | self.set_drop_flag(Location { block: target, statement_index: 0 }, | |
524 | child, DropFlagState::Present); | |
525 | self.set_drop_flag(Location { block: unwind, statement_index: 0 }, | |
526 | child, DropFlagState::Present); | |
527 | }); | |
528 | } | |
529 | LookupResult::Parent(parent) => { | |
530 | // drop and replace behind a pointer/array/whatever. The location | |
531 | // must be initialized. | |
532 | debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent); | |
533 | self.patch.patch_terminator(bb, TerminatorKind::Drop { | |
534 | location: location.clone(), | |
3b2f2976 | 535 | target, |
9e0c209e SL |
536 | unwind: Some(unwind) |
537 | }); | |
538 | } | |
3157f602 XL |
539 | } |
540 | } | |
541 | ||
3157f602 | 542 | fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { |
cc61c64b | 543 | Rvalue::Use(Operand::Constant(Box::new(Constant { |
3b2f2976 | 544 | span, |
3157f602 | 545 | ty: self.tcx.types.bool, |
8faf50e0 | 546 | literal: ty::Const::from_bool(self.tcx, val), |
cc61c64b | 547 | }))) |
3157f602 XL |
548 | } |
549 | ||
550 | fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) { | |
551 | if let Some(&flag) = self.drop_flags.get(&path) { | |
552 | let span = self.patch.source_info_for_location(self.mir, loc).span; | |
553 | let val = self.constant_bool(span, val.value()); | |
ff7c6d11 | 554 | self.patch.add_assign(loc, Place::Local(flag), val); |
3157f602 XL |
555 | } |
556 | } | |
557 | ||
558 | fn drop_flags_on_init(&mut self) { | |
83c7162d | 559 | let loc = Location::START; |
3157f602 XL |
560 | let span = self.patch.source_info_for_location(self.mir, loc).span; |
561 | let false_ = self.constant_bool(span, false); | |
562 | for flag in self.drop_flags.values() { | |
ff7c6d11 | 563 | self.patch.add_assign(loc, Place::Local(*flag), false_.clone()); |
3157f602 XL |
564 | } |
565 | } | |
566 | ||
567 | fn drop_flags_for_fn_rets(&mut self) { | |
568 | for (bb, data) in self.mir.basic_blocks().iter_enumerated() { | |
569 | if let TerminatorKind::Call { | |
ff7c6d11 | 570 | destination: Some((ref place, tgt)), cleanup: Some(_), .. |
3157f602 XL |
571 | } = data.terminator().kind { |
572 | assert!(!self.patch.is_patched(bb)); | |
573 | ||
9e0c209e | 574 | let loc = Location { block: tgt, statement_index: 0 }; |
ff7c6d11 | 575 | let path = self.move_data().rev_lookup.find(place); |
9e0c209e | 576 | on_lookup_result_bits( |
3157f602 XL |
577 | self.tcx, self.mir, self.move_data(), path, |
578 | |child| self.set_drop_flag(loc, child, DropFlagState::Present) | |
579 | ); | |
580 | } | |
581 | } | |
582 | } | |
583 | ||
584 | fn drop_flags_for_args(&mut self) { | |
83c7162d | 585 | let loc = Location::START; |
041b39d2 | 586 | dataflow::drop_flag_effects_for_function_entry( |
3157f602 XL |
587 | self.tcx, self.mir, self.env, |path, ds| { |
588 | self.set_drop_flag(loc, path, ds); | |
589 | } | |
590 | ) | |
591 | } | |
592 | ||
593 | fn drop_flags_for_locs(&mut self) { | |
594 | // We intentionally iterate only over the *old* basic blocks. | |
595 | // | |
596 | // Basic blocks created by drop elaboration update their | |
597 | // drop flags by themselves, to avoid the drop flags being | |
598 | // clobbered before they are read. | |
599 | ||
600 | for (bb, data) in self.mir.basic_blocks().iter_enumerated() { | |
601 | debug!("drop_flags_for_locs({:?})", data); | |
602 | for i in 0..(data.statements.len()+1) { | |
603 | debug!("drop_flag_for_locs: stmt {}", i); | |
604 | let mut allow_initializations = true; | |
605 | if i == data.statements.len() { | |
606 | match data.terminator().kind { | |
607 | TerminatorKind::Drop { .. } => { | |
608 | // drop elaboration should handle that by itself | |
609 | continue | |
610 | } | |
611 | TerminatorKind::DropAndReplace { .. } => { | |
612 | // this contains the move of the source and | |
613 | // the initialization of the destination. We | |
614 | // only want the former - the latter is handled | |
615 | // by the elaboration code and must be done | |
616 | // *after* the destination is dropped. | |
617 | assert!(self.patch.is_patched(bb)); | |
618 | allow_initializations = false; | |
619 | } | |
041b39d2 XL |
620 | TerminatorKind::Resume => { |
621 | // It is possible for `Resume` to be patched | |
622 | // (in particular it can be patched to be replaced with | |
623 | // a Goto; see `MirPatch::new`). | |
624 | } | |
3157f602 XL |
625 | _ => { |
626 | assert!(!self.patch.is_patched(bb)); | |
627 | } | |
628 | } | |
629 | } | |
9e0c209e | 630 | let loc = Location { block: bb, statement_index: i }; |
041b39d2 | 631 | dataflow::drop_flag_effects_for_location( |
3157f602 XL |
632 | self.tcx, self.mir, self.env, loc, |path, ds| { |
633 | if ds == DropFlagState::Absent || allow_initializations { | |
634 | self.set_drop_flag(loc, path, ds) | |
635 | } | |
636 | } | |
637 | ) | |
638 | } | |
639 | ||
640 | // There may be a critical edge after this call, | |
641 | // so mark the return as initialized *before* the | |
642 | // call. | |
643 | if let TerminatorKind::Call { | |
ff7c6d11 | 644 | destination: Some((ref place, _)), cleanup: None, .. |
3157f602 XL |
645 | } = data.terminator().kind { |
646 | assert!(!self.patch.is_patched(bb)); | |
647 | ||
9e0c209e | 648 | let loc = Location { block: bb, statement_index: data.statements.len() }; |
ff7c6d11 | 649 | let path = self.move_data().rev_lookup.find(place); |
9e0c209e | 650 | on_lookup_result_bits( |
3157f602 XL |
651 | self.tcx, self.mir, self.move_data(), path, |
652 | |child| self.set_drop_flag(loc, child, DropFlagState::Present) | |
653 | ); | |
654 | } | |
655 | } | |
656 | } | |
3157f602 | 657 | } |