]>
Commit | Line | Data |
---|---|---|
5099ac24 FG |
1 | use super::{ |
2 | for_each_consumable, record_consumed_borrow::ConsumedAndBorrowedPlaces, DropRangesBuilder, | |
3 | NodeInfo, PostOrderId, TrackedValue, TrackedValueIndex, | |
4 | }; | |
5 | use hir::{ | |
6 | intravisit::{self, Visitor}, | |
7 | Body, Expr, ExprKind, Guard, HirId, LoopIdError, | |
8 | }; | |
064997fb | 9 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
5099ac24 FG |
10 | use rustc_hir as hir; |
11 | use rustc_index::vec::IndexVec; | |
12 | use rustc_middle::{ | |
13 | hir::map::Map, | |
14 | ty::{TyCtxt, TypeckResults}, | |
15 | }; | |
16 | use std::mem::swap; | |
17 | ||
18 | /// Traverses the body to find the control flow graph and locations for the | |
19 | /// relevant places are dropped or reinitialized. | |
20 | /// | |
21 | /// The resulting structure still needs to be iterated to a fixed point, which | |
22 | /// can be done with propagate_to_fixpoint in cfg_propagate. | |
23 | pub(super) fn build_control_flow_graph<'tcx>( | |
24 | hir: Map<'tcx>, | |
25 | tcx: TyCtxt<'tcx>, | |
26 | typeck_results: &TypeckResults<'tcx>, | |
27 | consumed_borrowed_places: ConsumedAndBorrowedPlaces, | |
28 | body: &'tcx Body<'tcx>, | |
29 | num_exprs: usize, | |
5e7ed085 | 30 | ) -> (DropRangesBuilder, FxHashSet<HirId>) { |
5099ac24 FG |
31 | let mut drop_range_visitor = |
32 | DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs); | |
33 | intravisit::walk_body(&mut drop_range_visitor, body); | |
34 | ||
35 | drop_range_visitor.drop_ranges.process_deferred_edges(); | |
064997fb FG |
36 | if let Some(filename) = &tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg { |
37 | super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx); | |
38 | } | |
5099ac24 | 39 | |
5e7ed085 | 40 | (drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries) |
5099ac24 FG |
41 | } |
42 | ||
43 | /// This struct is used to gather the information for `DropRanges` to determine the regions of the | |
44 | /// HIR tree for which a value is dropped. | |
45 | /// | |
46 | /// We are interested in points where a variables is dropped or initialized, and the control flow | |
47 | /// of the code. We identify locations in code by their post-order traversal index, so it is | |
48 | /// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`. | |
49 | /// | |
50 | /// We make several simplifying assumptions, with the goal of being more conservative than | |
51 | /// necessary rather than less conservative (since being less conservative is unsound, but more | |
52 | /// conservative is still safe). These assumptions are: | |
53 | /// | |
54 | /// 1. Moving a variable `a` counts as a move of the whole variable. | |
55 | /// 2. Moving a partial path like `a.b.c` is ignored. | |
5e7ed085 | 56 | /// 3. Reinitializing through a field (e.g. `a.b.c = 5`) counts as a reinitialization of all of |
5099ac24 FG |
57 | /// `a`. |
58 | /// | |
59 | /// Some examples: | |
60 | /// | |
61 | /// Rule 1: | |
62 | /// ```rust | |
63 | /// let mut a = (vec![0], vec![0]); | |
64 | /// drop(a); | |
65 | /// // `a` is not considered initialized. | |
66 | /// ``` | |
67 | /// | |
68 | /// Rule 2: | |
69 | /// ```rust | |
70 | /// let mut a = (vec![0], vec![0]); | |
71 | /// drop(a.0); | |
72 | /// drop(a.1); | |
73 | /// // `a` is still considered initialized. | |
74 | /// ``` | |
75 | /// | |
76 | /// Rule 3: | |
04454e1e | 77 | /// ```compile_fail,E0382 |
5099ac24 FG |
78 | /// let mut a = (vec![0], vec![0]); |
79 | /// drop(a); | |
80 | /// a.1 = vec![1]; | |
81 | /// // all of `a` is considered initialized | |
82 | /// ``` | |
83 | ||
84 | struct DropRangeVisitor<'a, 'tcx> { | |
85 | hir: Map<'tcx>, | |
86 | places: ConsumedAndBorrowedPlaces, | |
87 | drop_ranges: DropRangesBuilder, | |
88 | expr_index: PostOrderId, | |
89 | tcx: TyCtxt<'tcx>, | |
90 | typeck_results: &'a TypeckResults<'tcx>, | |
91 | label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>, | |
92 | } | |
93 | ||
94 | impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | |
95 | fn new( | |
96 | hir: Map<'tcx>, | |
97 | tcx: TyCtxt<'tcx>, | |
98 | typeck_results: &'a TypeckResults<'tcx>, | |
99 | places: ConsumedAndBorrowedPlaces, | |
100 | num_exprs: usize, | |
101 | ) -> Self { | |
102 | debug!("consumed_places: {:?}", places.consumed); | |
103 | let drop_ranges = DropRangesBuilder::new( | |
104 | places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()), | |
105 | hir, | |
106 | num_exprs, | |
107 | ); | |
108 | Self { | |
109 | hir, | |
110 | places, | |
111 | drop_ranges, | |
112 | expr_index: PostOrderId::from_u32(0), | |
113 | typeck_results, | |
114 | tcx, | |
115 | label_stack: vec![], | |
116 | } | |
117 | } | |
118 | ||
119 | fn record_drop(&mut self, value: TrackedValue) { | |
120 | if self.places.borrowed.contains(&value) { | |
121 | debug!("not marking {:?} as dropped because it is borrowed at some point", value); | |
122 | } else { | |
123 | debug!("marking {:?} as dropped at {:?}", value, self.expr_index); | |
124 | let count = self.expr_index; | |
125 | self.drop_ranges.drop_at(value, count); | |
126 | } | |
127 | } | |
128 | ||
129 | /// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all | |
130 | /// expressions. This method consumes a little deeper into the expression when needed. | |
131 | fn consume_expr(&mut self, expr: &hir::Expr<'_>) { | |
064997fb | 132 | debug!("consuming expr {:?}, count={:?}", expr.kind, self.expr_index); |
5099ac24 FG |
133 | let places = self |
134 | .places | |
135 | .consumed | |
136 | .get(&expr.hir_id) | |
137 | .map_or(vec![], |places| places.iter().cloned().collect()); | |
138 | for place in places { | |
064997fb | 139 | trace!(?place, "consuming place"); |
5099ac24 FG |
140 | for_each_consumable(self.hir, place, |value| self.record_drop(value)); |
141 | } | |
142 | } | |
143 | ||
144 | /// Marks an expression as being reinitialized. | |
145 | /// | |
146 | /// Note that we always approximated on the side of things being more | |
147 | /// initialized than they actually are, as opposed to less. In cases such | |
148 | /// as `x.y = ...`, we would consider all of `x` as being initialized | |
149 | /// instead of just the `y` field. | |
150 | /// | |
151 | /// This is because it is always safe to consider something initialized | |
152 | /// even when it is not, but the other way around will cause problems. | |
153 | /// | |
154 | /// In the future, we will hopefully tighten up these rules to be more | |
155 | /// precise. | |
156 | fn reinit_expr(&mut self, expr: &hir::Expr<'_>) { | |
157 | // Walk the expression to find the base. For example, in an expression | |
158 | // like `*a[i].x`, we want to find the `a` and mark that as | |
159 | // reinitialized. | |
160 | match expr.kind { | |
161 | ExprKind::Path(hir::QPath::Resolved( | |
162 | _, | |
163 | hir::Path { res: hir::def::Res::Local(hir_id), .. }, | |
164 | )) => { | |
165 | // This is the base case, where we have found an actual named variable. | |
166 | ||
167 | let location = self.expr_index; | |
168 | debug!("reinitializing {:?} at {:?}", hir_id, location); | |
169 | self.drop_ranges.reinit_at(TrackedValue::Variable(*hir_id), location); | |
170 | } | |
171 | ||
172 | ExprKind::Field(base, _) => self.reinit_expr(base), | |
173 | ||
174 | // Most expressions do not refer to something where we need to track | |
175 | // reinitializations. | |
176 | // | |
177 | // Some of these may be interesting in the future | |
178 | ExprKind::Path(..) | |
179 | | ExprKind::Box(..) | |
180 | | ExprKind::ConstBlock(..) | |
181 | | ExprKind::Array(..) | |
182 | | ExprKind::Call(..) | |
183 | | ExprKind::MethodCall(..) | |
184 | | ExprKind::Tup(..) | |
185 | | ExprKind::Binary(..) | |
186 | | ExprKind::Unary(..) | |
187 | | ExprKind::Lit(..) | |
188 | | ExprKind::Cast(..) | |
189 | | ExprKind::Type(..) | |
190 | | ExprKind::DropTemps(..) | |
191 | | ExprKind::Let(..) | |
192 | | ExprKind::If(..) | |
193 | | ExprKind::Loop(..) | |
194 | | ExprKind::Match(..) | |
923072b8 | 195 | | ExprKind::Closure { .. } |
5099ac24 FG |
196 | | ExprKind::Block(..) |
197 | | ExprKind::Assign(..) | |
198 | | ExprKind::AssignOp(..) | |
199 | | ExprKind::Index(..) | |
200 | | ExprKind::AddrOf(..) | |
201 | | ExprKind::Break(..) | |
202 | | ExprKind::Continue(..) | |
203 | | ExprKind::Ret(..) | |
204 | | ExprKind::InlineAsm(..) | |
205 | | ExprKind::Struct(..) | |
206 | | ExprKind::Repeat(..) | |
207 | | ExprKind::Yield(..) | |
208 | | ExprKind::Err => (), | |
209 | } | |
210 | } | |
211 | ||
212 | /// For an expression with an uninhabited return type (e.g. a function that returns !), | |
2b03887a | 213 | /// this adds a self edge to the CFG to model the fact that the function does not |
5099ac24 FG |
214 | /// return. |
215 | fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { | |
216 | let ty = self.typeck_results.expr_ty(expr); | |
217 | let ty = self.tcx.erase_regions(ty); | |
218 | let m = self.tcx.parent_module(expr.hir_id).to_def_id(); | |
219 | let param_env = self.tcx.param_env(m.expect_local()); | |
220 | if self.tcx.is_ty_uninhabited_from(m, ty, param_env) { | |
221 | // This function will not return. We model this fact as an infinite loop. | |
222 | self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1); | |
223 | } | |
224 | } | |
225 | ||
226 | /// Map a Destination to an equivalent expression node | |
227 | /// | |
228 | /// The destination field of a Break or Continue expression can target either an | |
229 | /// expression or a block. The drop range analysis, however, only deals in | |
230 | /// expression nodes, so blocks that might be the destination of a Break or Continue | |
231 | /// will not have a PostOrderId. | |
232 | /// | |
233 | /// If the destination is an expression, this function will simply return that expression's | |
234 | /// hir_id. If the destination is a block, this function will return the hir_id of last | |
235 | /// expression in the block. | |
236 | fn find_target_expression_from_destination( | |
237 | &self, | |
238 | destination: hir::Destination, | |
239 | ) -> Result<HirId, LoopIdError> { | |
240 | destination.target_id.map(|target| { | |
241 | let node = self.hir.get(target); | |
242 | match node { | |
243 | hir::Node::Expr(_) => target, | |
244 | hir::Node::Block(b) => find_last_block_expression(b), | |
245 | hir::Node::Param(..) | |
246 | | hir::Node::Item(..) | |
247 | | hir::Node::ForeignItem(..) | |
248 | | hir::Node::TraitItem(..) | |
249 | | hir::Node::ImplItem(..) | |
250 | | hir::Node::Variant(..) | |
251 | | hir::Node::Field(..) | |
252 | | hir::Node::AnonConst(..) | |
253 | | hir::Node::Stmt(..) | |
254 | | hir::Node::PathSegment(..) | |
255 | | hir::Node::Ty(..) | |
923072b8 | 256 | | hir::Node::TypeBinding(..) |
5099ac24 | 257 | | hir::Node::TraitRef(..) |
5099ac24 | 258 | | hir::Node::Pat(..) |
f2b60f7d FG |
259 | | hir::Node::PatField(..) |
260 | | hir::Node::ExprField(..) | |
5099ac24 FG |
261 | | hir::Node::Arm(..) |
262 | | hir::Node::Local(..) | |
263 | | hir::Node::Ctor(..) | |
264 | | hir::Node::Lifetime(..) | |
265 | | hir::Node::GenericParam(..) | |
5099ac24 FG |
266 | | hir::Node::Crate(..) |
267 | | hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node), | |
268 | } | |
269 | }) | |
270 | } | |
271 | } | |
272 | ||
273 | fn find_last_block_expression(block: &hir::Block<'_>) -> HirId { | |
274 | block.expr.map_or_else( | |
275 | // If there is no tail expression, there will be at least one statement in the | |
276 | // block because the block contains a break or continue statement. | |
277 | || block.stmts.last().unwrap().hir_id, | |
278 | |expr| expr.hir_id, | |
279 | ) | |
280 | } | |
281 | ||
282 | impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { | |
283 | fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { | |
284 | let mut reinit = None; | |
285 | match expr.kind { | |
286 | ExprKind::Assign(lhs, rhs, _) => { | |
287 | self.visit_expr(lhs); | |
288 | self.visit_expr(rhs); | |
289 | ||
290 | reinit = Some(lhs); | |
291 | } | |
292 | ||
293 | ExprKind::If(test, if_true, if_false) => { | |
294 | self.visit_expr(test); | |
295 | ||
296 | let fork = self.expr_index; | |
297 | ||
298 | self.drop_ranges.add_control_edge(fork, self.expr_index + 1); | |
299 | self.visit_expr(if_true); | |
300 | let true_end = self.expr_index; | |
301 | ||
302 | self.drop_ranges.add_control_edge(fork, self.expr_index + 1); | |
303 | if let Some(if_false) = if_false { | |
304 | self.visit_expr(if_false); | |
305 | } | |
306 | ||
307 | self.drop_ranges.add_control_edge(true_end, self.expr_index + 1); | |
308 | } | |
309 | ExprKind::Match(scrutinee, arms, ..) => { | |
310 | // We walk through the match expression almost like a chain of if expressions. | |
311 | // Here's a diagram to follow along with: | |
312 | // | |
313 | // ┌─┐ | |
314 | // match │A│ { | |
315 | // ┌───┴─┘ | |
316 | // │ | |
317 | // ┌▼┌───►┌─┐ ┌─┐ | |
318 | // │B│ if │C│ =>│D│, | |
319 | // └─┘ ├─┴──►└─┴──────┐ | |
320 | // ┌──┘ │ | |
321 | // ┌──┘ │ | |
322 | // │ │ | |
323 | // ┌▼┌───►┌─┐ ┌─┐ │ | |
324 | // │E│ if │F│ =>│G│, │ | |
325 | // └─┘ ├─┴──►└─┴┐ │ | |
326 | // │ │ │ | |
327 | // } ▼ ▼ │ | |
328 | // ┌─┐◄───────────────────┘ | |
329 | // │H│ | |
330 | // └─┘ | |
331 | // | |
332 | // The order we want is that the scrutinee (A) flows into the first pattern (B), | |
333 | // which flows into the guard (C). Then the guard either flows into the arm body | |
334 | // (D) or into the start of the next arm (E). Finally, the body flows to the end | |
335 | // of the match block (H). | |
336 | // | |
337 | // The subsequent arms follow the same ordering. First we go to the pattern, then | |
338 | // the guard (if present, otherwise it flows straight into the body), then into | |
339 | // the body and then to the end of the match expression. | |
340 | // | |
341 | // The comments below show which edge is being added. | |
342 | self.visit_expr(scrutinee); | |
343 | ||
344 | let (guard_exit, arm_end_ids) = arms.iter().fold( | |
345 | (self.expr_index, vec![]), | |
346 | |(incoming_edge, mut arm_end_ids), hir::Arm { pat, body, guard, .. }| { | |
347 | // A -> B, or C -> E | |
348 | self.drop_ranges.add_control_edge(incoming_edge, self.expr_index + 1); | |
349 | self.visit_pat(pat); | |
350 | // B -> C and E -> F are added implicitly due to the traversal order. | |
351 | match guard { | |
352 | Some(Guard::If(expr)) => self.visit_expr(expr), | |
923072b8 FG |
353 | Some(Guard::IfLet(let_expr)) => { |
354 | self.visit_let_expr(let_expr); | |
5099ac24 FG |
355 | } |
356 | None => (), | |
357 | } | |
358 | // Likewise, C -> D and F -> G are added implicitly. | |
359 | ||
360 | // Save C, F, so we can add the other outgoing edge. | |
361 | let to_next_arm = self.expr_index; | |
362 | ||
363 | // The default edge does not get added since we also have an explicit edge, | |
364 | // so we also need to add an edge to the next node as well. | |
365 | // | |
366 | // This adds C -> D, F -> G | |
367 | self.drop_ranges.add_control_edge(self.expr_index, self.expr_index + 1); | |
368 | self.visit_expr(body); | |
369 | ||
370 | // Save the end of the body so we can add the exit edge once we know where | |
371 | // the exit is. | |
372 | arm_end_ids.push(self.expr_index); | |
373 | ||
374 | // Pass C to the next iteration, as well as vec![D] | |
375 | // | |
376 | // On the last round through, we pass F and vec![D, G] so that we can | |
377 | // add all the exit edges. | |
378 | (to_next_arm, arm_end_ids) | |
379 | }, | |
380 | ); | |
381 | // F -> H | |
382 | self.drop_ranges.add_control_edge(guard_exit, self.expr_index + 1); | |
383 | ||
384 | arm_end_ids.into_iter().for_each(|arm_end| { | |
385 | // D -> H, G -> H | |
386 | self.drop_ranges.add_control_edge(arm_end, self.expr_index + 1) | |
387 | }); | |
388 | } | |
389 | ||
390 | ExprKind::Loop(body, label, ..) => { | |
391 | let loop_begin = self.expr_index + 1; | |
392 | self.label_stack.push((label, loop_begin)); | |
393 | if body.stmts.is_empty() && body.expr.is_none() { | |
394 | // For empty loops we won't have updated self.expr_index after visiting the | |
395 | // body, meaning we'd get an edge from expr_index to expr_index + 1, but | |
396 | // instead we want an edge from expr_index + 1 to expr_index + 1. | |
397 | self.drop_ranges.add_control_edge(loop_begin, loop_begin); | |
398 | } else { | |
399 | self.visit_block(body); | |
400 | self.drop_ranges.add_control_edge(self.expr_index, loop_begin); | |
401 | } | |
402 | self.label_stack.pop(); | |
403 | } | |
404 | // Find the loop entry by searching through the label stack for either the last entry | |
405 | // (if label is none), or the first entry where the label matches this one. The Loop | |
406 | // case maintains this stack mapping labels to the PostOrderId for the loop entry. | |
407 | ExprKind::Continue(hir::Destination { label, .. }, ..) => self | |
408 | .label_stack | |
409 | .iter() | |
410 | .rev() | |
411 | .find(|(loop_label, _)| label.is_none() || *loop_label == label) | |
412 | .map_or((), |(_, target)| { | |
413 | self.drop_ranges.add_control_edge(self.expr_index, *target) | |
414 | }), | |
415 | ||
416 | ExprKind::Break(destination, ..) => { | |
417 | // destination either points to an expression or to a block. We use | |
418 | // find_target_expression_from_destination to use the last expression of the block | |
419 | // if destination points to a block. | |
420 | // | |
421 | // We add an edge to the hir_id of the expression/block we are breaking out of, and | |
422 | // then in process_deferred_edges we will map this hir_id to its PostOrderId, which | |
423 | // will refer to the end of the block due to the post order traversal. | |
424 | self.find_target_expression_from_destination(destination).map_or((), |target| { | |
425 | self.drop_ranges.add_control_edge_hir_id(self.expr_index, target) | |
426 | }) | |
427 | } | |
428 | ||
429 | ExprKind::Call(f, args) => { | |
430 | self.visit_expr(f); | |
431 | for arg in args { | |
432 | self.visit_expr(arg); | |
433 | } | |
434 | ||
435 | self.handle_uninhabited_return(expr); | |
436 | } | |
f2b60f7d FG |
437 | ExprKind::MethodCall(_, receiver, exprs, _) => { |
438 | self.visit_expr(receiver); | |
5099ac24 FG |
439 | for expr in exprs { |
440 | self.visit_expr(expr); | |
441 | } | |
442 | ||
443 | self.handle_uninhabited_return(expr); | |
444 | } | |
445 | ||
446 | ExprKind::AddrOf(..) | |
447 | | ExprKind::Array(..) | |
448 | | ExprKind::AssignOp(..) | |
449 | | ExprKind::Binary(..) | |
450 | | ExprKind::Block(..) | |
451 | | ExprKind::Box(..) | |
452 | | ExprKind::Cast(..) | |
923072b8 | 453 | | ExprKind::Closure { .. } |
5099ac24 FG |
454 | | ExprKind::ConstBlock(..) |
455 | | ExprKind::DropTemps(..) | |
456 | | ExprKind::Err | |
457 | | ExprKind::Field(..) | |
458 | | ExprKind::Index(..) | |
459 | | ExprKind::InlineAsm(..) | |
460 | | ExprKind::Let(..) | |
461 | | ExprKind::Lit(..) | |
462 | | ExprKind::Path(..) | |
463 | | ExprKind::Repeat(..) | |
464 | | ExprKind::Ret(..) | |
465 | | ExprKind::Struct(..) | |
466 | | ExprKind::Tup(..) | |
467 | | ExprKind::Type(..) | |
468 | | ExprKind::Unary(..) | |
469 | | ExprKind::Yield(..) => intravisit::walk_expr(self, expr), | |
470 | } | |
471 | ||
472 | self.expr_index = self.expr_index + 1; | |
473 | self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_index); | |
474 | self.consume_expr(expr); | |
475 | if let Some(expr) = reinit { | |
476 | self.reinit_expr(expr); | |
477 | } | |
478 | } | |
479 | ||
480 | fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { | |
481 | intravisit::walk_pat(self, pat); | |
482 | ||
483 | // Increment expr_count here to match what InteriorVisitor expects. | |
484 | self.expr_index = self.expr_index + 1; | |
485 | } | |
486 | } | |
487 | ||
488 | impl DropRangesBuilder { | |
489 | fn new( | |
490 | tracked_values: impl Iterator<Item = TrackedValue>, | |
491 | hir: Map<'_>, | |
492 | num_exprs: usize, | |
493 | ) -> Self { | |
494 | let mut tracked_value_map = FxHashMap::<_, TrackedValueIndex>::default(); | |
495 | let mut next = <_>::from(0u32); | |
496 | for value in tracked_values { | |
497 | for_each_consumable(hir, value, |value| { | |
498 | if !tracked_value_map.contains_key(&value) { | |
499 | tracked_value_map.insert(value, next); | |
500 | next = next + 1; | |
501 | } | |
502 | }); | |
503 | } | |
504 | debug!("hir_id_map: {:?}", tracked_value_map); | |
505 | let num_values = tracked_value_map.len(); | |
506 | Self { | |
507 | tracked_value_map, | |
508 | nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1), | |
509 | deferred_edges: <_>::default(), | |
510 | post_order_map: <_>::default(), | |
511 | } | |
512 | } | |
513 | ||
514 | fn tracked_value_index(&self, tracked_value: TrackedValue) -> TrackedValueIndex { | |
515 | *self.tracked_value_map.get(&tracked_value).unwrap() | |
516 | } | |
517 | ||
518 | /// Adds an entry in the mapping from HirIds to PostOrderIds | |
519 | /// | |
520 | /// Needed so that `add_control_edge_hir_id` can work. | |
521 | fn add_node_mapping(&mut self, node_hir_id: HirId, post_order_id: PostOrderId) { | |
522 | self.post_order_map.insert(node_hir_id, post_order_id); | |
523 | } | |
524 | ||
525 | /// Like add_control_edge, but uses a hir_id as the target. | |
526 | /// | |
527 | /// This can be used for branches where we do not know the PostOrderId of the target yet, | |
528 | /// such as when handling `break` or `continue`. | |
529 | fn add_control_edge_hir_id(&mut self, from: PostOrderId, to: HirId) { | |
530 | self.deferred_edges.push((from, to)); | |
531 | } | |
532 | ||
533 | fn drop_at(&mut self, value: TrackedValue, location: PostOrderId) { | |
534 | let value = self.tracked_value_index(value); | |
535 | self.node_mut(location).drops.push(value); | |
536 | } | |
537 | ||
538 | fn reinit_at(&mut self, value: TrackedValue, location: PostOrderId) { | |
539 | let value = match self.tracked_value_map.get(&value) { | |
540 | Some(value) => *value, | |
541 | // If there's no value, this is never consumed and therefore is never dropped. We can | |
542 | // ignore this. | |
543 | None => return, | |
544 | }; | |
545 | self.node_mut(location).reinits.push(value); | |
546 | } | |
547 | ||
548 | /// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them. | |
549 | /// | |
550 | /// Should be called after visiting the HIR but before solving the control flow, otherwise some | |
551 | /// edges will be missed. | |
552 | fn process_deferred_edges(&mut self) { | |
553 | trace!("processing deferred edges. post_order_map={:#?}", self.post_order_map); | |
554 | let mut edges = vec![]; | |
555 | swap(&mut edges, &mut self.deferred_edges); | |
556 | edges.into_iter().for_each(|(from, to)| { | |
557 | trace!("Adding deferred edge from {:?} to {:?}", from, to); | |
558 | let to = *self.post_order_map.get(&to).expect("Expression ID not found"); | |
559 | trace!("target edge PostOrderId={:?}", to); | |
560 | self.add_control_edge(from, to) | |
561 | }); | |
562 | } | |
563 | } |