2 for_each_consumable, record_consumed_borrow::ConsumedAndBorrowedPlaces, DropRangesBuilder,
3 NodeInfo, PostOrderId, TrackedValue, TrackedValueIndex,
6 intravisit::{self, Visitor},
7 Body, Expr, ExprKind, Guard, HirId, LoopIdError,
9 use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet};
11 use rustc_index::vec::IndexVec;
14 ty::{TyCtxt, TypeckResults},
18 /// Traverses the body to find the control flow graph and locations for the
19 /// relevant places are dropped or reinitialized.
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>(
26 typeck_results: &TypeckResults<'tcx>,
27 consumed_borrowed_places: ConsumedAndBorrowedPlaces,
28 body: &'tcx Body<'tcx>,
30 ) -> (DropRangesBuilder, FxHashSet<HirId>) {
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);
35 drop_range_visitor.drop_ranges.process_deferred_edges();
37 (drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries)
40 /// This struct is used to gather the information for `DropRanges` to determine the regions of the
41 /// HIR tree for which a value is dropped.
43 /// We are interested in points where a variables is dropped or initialized, and the control flow
44 /// of the code. We identify locations in code by their post-order traversal index, so it is
45 /// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`.
47 /// We make several simplifying assumptions, with the goal of being more conservative than
48 /// necessary rather than less conservative (since being less conservative is unsound, but more
49 /// conservative is still safe). These assumptions are:
51 /// 1. Moving a variable `a` counts as a move of the whole variable.
52 /// 2. Moving a partial path like `a.b.c` is ignored.
53 /// 3. Reinitializing through a field (e.g. `a.b.c = 5`) counts as a reinitialization of all of
60 /// let mut a = (vec![0], vec![0]);
62 /// // `a` is not considered initialized.
67 /// let mut a = (vec![0], vec![0]);
70 /// // `a` is still considered initialized.
74 /// ```compile_fail,E0382
75 /// let mut a = (vec![0], vec![0]);
78 /// // all of `a` is considered initialized
81 struct DropRangeVisitor<'a, 'tcx> {
83 places: ConsumedAndBorrowedPlaces,
84 drop_ranges: DropRangesBuilder,
85 expr_index: PostOrderId,
87 typeck_results: &'a TypeckResults<'tcx>,
88 label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>,
91 impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
95 typeck_results: &'a TypeckResults<'tcx>,
96 places: ConsumedAndBorrowedPlaces,
99 debug!("consumed_places: {:?}", places.consumed);
100 let drop_ranges = DropRangesBuilder::new(
101 places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()),
109 expr_index: PostOrderId::from_u32(0),
116 fn record_drop(&mut self, value: TrackedValue) {
117 if self.places.borrowed.contains(&value) {
118 debug!("not marking {:?} as dropped because it is borrowed at some point", value);
120 debug!("marking {:?} as dropped at {:?}", value, self.expr_index);
121 let count = self.expr_index;
122 self.drop_ranges.drop_at(value, count);
126 /// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all
127 /// expressions. This method consumes a little deeper into the expression when needed.
128 fn consume_expr(&mut self, expr: &hir::Expr<'_>) {
129 debug!("consuming expr {:?}, count={:?}", expr.hir_id, self.expr_index);
134 .map_or(vec![], |places| places.iter().cloned().collect());
135 for place in places {
136 for_each_consumable(self.hir, place, |value| self.record_drop(value));
140 /// Marks an expression as being reinitialized.
142 /// Note that we always approximated on the side of things being more
143 /// initialized than they actually are, as opposed to less. In cases such
144 /// as `x.y = ...`, we would consider all of `x` as being initialized
145 /// instead of just the `y` field.
147 /// This is because it is always safe to consider something initialized
148 /// even when it is not, but the other way around will cause problems.
150 /// In the future, we will hopefully tighten up these rules to be more
152 fn reinit_expr(&mut self, expr: &hir::Expr<'_>) {
153 // Walk the expression to find the base. For example, in an expression
154 // like `*a[i].x`, we want to find the `a` and mark that as
157 ExprKind::Path(hir::QPath::Resolved(
159 hir::Path { res: hir::def::Res::Local(hir_id), .. },
161 // This is the base case, where we have found an actual named variable.
163 let location = self.expr_index;
164 debug!("reinitializing {:?} at {:?}", hir_id, location);
165 self.drop_ranges.reinit_at(TrackedValue::Variable(*hir_id), location);
168 ExprKind::Field(base, _) => self.reinit_expr(base),
170 // Most expressions do not refer to something where we need to track
171 // reinitializations.
173 // Some of these may be interesting in the future
176 | ExprKind::ConstBlock(..)
177 | ExprKind::Array(..)
179 | ExprKind::MethodCall(..)
181 | ExprKind::Binary(..)
182 | ExprKind::Unary(..)
186 | ExprKind::DropTemps(..)
190 | ExprKind::Match(..)
191 | ExprKind::Closure(..)
192 | ExprKind::Block(..)
193 | ExprKind::Assign(..)
194 | ExprKind::AssignOp(..)
195 | ExprKind::Index(..)
196 | ExprKind::AddrOf(..)
197 | ExprKind::Break(..)
198 | ExprKind::Continue(..)
200 | ExprKind::InlineAsm(..)
201 | ExprKind::Struct(..)
202 | ExprKind::Repeat(..)
203 | ExprKind::Yield(..)
204 | ExprKind::Err => (),
208 /// For an expression with an uninhabited return type (e.g. a function that returns !),
209 /// this adds a self edge to to the CFG to model the fact that the function does not
211 fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) {
212 let ty = self.typeck_results.expr_ty(expr);
213 let ty = self.tcx.erase_regions(ty);
214 let m = self.tcx.parent_module(expr.hir_id).to_def_id();
215 let param_env = self.tcx.param_env(m.expect_local());
216 if self.tcx.is_ty_uninhabited_from(m, ty, param_env) {
217 // This function will not return. We model this fact as an infinite loop.
218 self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1);
222 /// Map a Destination to an equivalent expression node
224 /// The destination field of a Break or Continue expression can target either an
225 /// expression or a block. The drop range analysis, however, only deals in
226 /// expression nodes, so blocks that might be the destination of a Break or Continue
227 /// will not have a PostOrderId.
229 /// If the destination is an expression, this function will simply return that expression's
230 /// hir_id. If the destination is a block, this function will return the hir_id of last
231 /// expression in the block.
232 fn find_target_expression_from_destination(
234 destination: hir::Destination,
235 ) -> Result<HirId, LoopIdError> {
236 destination.target_id.map(|target| {
237 let node = self.hir.get(target);
239 hir::Node::Expr(_) => target,
240 hir::Node::Block(b) => find_last_block_expression(b),
242 | hir::Node::Item(..)
243 | hir::Node::ForeignItem(..)
244 | hir::Node::TraitItem(..)
245 | hir::Node::ImplItem(..)
246 | hir::Node::Variant(..)
247 | hir::Node::Field(..)
248 | hir::Node::AnonConst(..)
249 | hir::Node::Stmt(..)
250 | hir::Node::PathSegment(..)
252 | hir::Node::TraitRef(..)
253 | hir::Node::Binding(..)
256 | hir::Node::Local(..)
257 | hir::Node::Ctor(..)
258 | hir::Node::Lifetime(..)
259 | hir::Node::GenericParam(..)
260 | hir::Node::Crate(..)
261 | hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
267 fn find_last_block_expression(block: &hir::Block<'_>) -> HirId {
268 block.expr.map_or_else(
269 // If there is no tail expression, there will be at least one statement in the
270 // block because the block contains a break or continue statement.
271 || block.stmts.last().unwrap().hir_id,
276 impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
277 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
278 let mut reinit = None;
280 ExprKind::Assign(lhs, rhs, _) => {
281 self.visit_expr(lhs);
282 self.visit_expr(rhs);
287 ExprKind::If(test, if_true, if_false) => {
288 self.visit_expr(test);
290 let fork = self.expr_index;
292 self.drop_ranges.add_control_edge(fork, self.expr_index + 1);
293 self.visit_expr(if_true);
294 let true_end = self.expr_index;
296 self.drop_ranges.add_control_edge(fork, self.expr_index + 1);
297 if let Some(if_false) = if_false {
298 self.visit_expr(if_false);
301 self.drop_ranges.add_control_edge(true_end, self.expr_index + 1);
303 ExprKind::Match(scrutinee, arms, ..) => {
304 // We walk through the match expression almost like a chain of if expressions.
305 // Here's a diagram to follow along with:
313 // └─┘ ├─┴──►└─┴──────┐
318 // │E│ if │F│ =>│G│, │
322 // ┌─┐◄───────────────────┘
326 // The order we want is that the scrutinee (A) flows into the first pattern (B),
327 // which flows into the guard (C). Then the guard either flows into the arm body
328 // (D) or into the start of the next arm (E). Finally, the body flows to the end
329 // of the match block (H).
331 // The subsequent arms follow the same ordering. First we go to the pattern, then
332 // the guard (if present, otherwise it flows straight into the body), then into
333 // the body and then to the end of the match expression.
335 // The comments below show which edge is being added.
336 self.visit_expr(scrutinee);
338 let (guard_exit, arm_end_ids) = arms.iter().fold(
339 (self.expr_index, vec![]),
340 |(incoming_edge, mut arm_end_ids), hir::Arm { pat, body, guard, .. }| {
342 self.drop_ranges.add_control_edge(incoming_edge, self.expr_index + 1);
344 // B -> C and E -> F are added implicitly due to the traversal order.
346 Some(Guard::If(expr)) => self.visit_expr(expr),
347 Some(Guard::IfLet(pat, expr)) => {
349 self.visit_expr(expr);
353 // Likewise, C -> D and F -> G are added implicitly.
355 // Save C, F, so we can add the other outgoing edge.
356 let to_next_arm = self.expr_index;
358 // The default edge does not get added since we also have an explicit edge,
359 // so we also need to add an edge to the next node as well.
361 // This adds C -> D, F -> G
362 self.drop_ranges.add_control_edge(self.expr_index, self.expr_index + 1);
363 self.visit_expr(body);
365 // Save the end of the body so we can add the exit edge once we know where
367 arm_end_ids.push(self.expr_index);
369 // Pass C to the next iteration, as well as vec![D]
371 // On the last round through, we pass F and vec![D, G] so that we can
372 // add all the exit edges.
373 (to_next_arm, arm_end_ids)
377 self.drop_ranges.add_control_edge(guard_exit, self.expr_index + 1);
379 arm_end_ids.into_iter().for_each(|arm_end| {
381 self.drop_ranges.add_control_edge(arm_end, self.expr_index + 1)
385 ExprKind::Loop(body, label, ..) => {
386 let loop_begin = self.expr_index + 1;
387 self.label_stack.push((label, loop_begin));
388 if body.stmts.is_empty() && body.expr.is_none() {
389 // For empty loops we won't have updated self.expr_index after visiting the
390 // body, meaning we'd get an edge from expr_index to expr_index + 1, but
391 // instead we want an edge from expr_index + 1 to expr_index + 1.
392 self.drop_ranges.add_control_edge(loop_begin, loop_begin);
394 self.visit_block(body);
395 self.drop_ranges.add_control_edge(self.expr_index, loop_begin);
397 self.label_stack.pop();
399 // Find the loop entry by searching through the label stack for either the last entry
400 // (if label is none), or the first entry where the label matches this one. The Loop
401 // case maintains this stack mapping labels to the PostOrderId for the loop entry.
402 ExprKind::Continue(hir::Destination { label, .. }, ..) => self
406 .find(|(loop_label, _)| label.is_none() || *loop_label == label)
407 .map_or((), |(_, target)| {
408 self.drop_ranges.add_control_edge(self.expr_index, *target)
411 ExprKind::Break(destination, ..) => {
412 // destination either points to an expression or to a block. We use
413 // find_target_expression_from_destination to use the last expression of the block
414 // if destination points to a block.
416 // We add an edge to the hir_id of the expression/block we are breaking out of, and
417 // then in process_deferred_edges we will map this hir_id to its PostOrderId, which
418 // will refer to the end of the block due to the post order traversal.
419 self.find_target_expression_from_destination(destination).map_or((), |target| {
420 self.drop_ranges.add_control_edge_hir_id(self.expr_index, target)
424 ExprKind::Call(f, args) => {
427 self.visit_expr(arg);
430 self.handle_uninhabited_return(expr);
432 ExprKind::MethodCall(_, exprs, _) => {
434 self.visit_expr(expr);
437 self.handle_uninhabited_return(expr);
441 | ExprKind::Array(..)
442 | ExprKind::AssignOp(..)
443 | ExprKind::Binary(..)
444 | ExprKind::Block(..)
447 | ExprKind::Closure(..)
448 | ExprKind::ConstBlock(..)
449 | ExprKind::DropTemps(..)
451 | ExprKind::Field(..)
452 | ExprKind::Index(..)
453 | ExprKind::InlineAsm(..)
457 | ExprKind::Repeat(..)
459 | ExprKind::Struct(..)
462 | ExprKind::Unary(..)
463 | ExprKind::Yield(..) => intravisit::walk_expr(self, expr),
466 self.expr_index = self.expr_index + 1;
467 self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_index);
468 self.consume_expr(expr);
469 if let Some(expr) = reinit {
470 self.reinit_expr(expr);
474 fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
475 intravisit::walk_pat(self, pat);
477 // Increment expr_count here to match what InteriorVisitor expects.
478 self.expr_index = self.expr_index + 1;
482 impl DropRangesBuilder {
484 tracked_values: impl Iterator<Item = TrackedValue>,
488 let mut tracked_value_map = FxHashMap::<_, TrackedValueIndex>::default();
489 let mut next = <_>::from(0u32);
490 for value in tracked_values {
491 for_each_consumable(hir, value, |value| {
492 if !tracked_value_map.contains_key(&value) {
493 tracked_value_map.insert(value, next);
498 debug!("hir_id_map: {:?}", tracked_value_map);
499 let num_values = tracked_value_map.len();
502 nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1),
503 deferred_edges: <_>::default(),
504 post_order_map: <_>::default(),
508 fn tracked_value_index(&self, tracked_value: TrackedValue) -> TrackedValueIndex {
509 *self.tracked_value_map.get(&tracked_value).unwrap()
512 /// Adds an entry in the mapping from HirIds to PostOrderIds
514 /// Needed so that `add_control_edge_hir_id` can work.
515 fn add_node_mapping(&mut self, node_hir_id: HirId, post_order_id: PostOrderId) {
516 self.post_order_map.insert(node_hir_id, post_order_id);
519 /// Like add_control_edge, but uses a hir_id as the target.
521 /// This can be used for branches where we do not know the PostOrderId of the target yet,
522 /// such as when handling `break` or `continue`.
523 fn add_control_edge_hir_id(&mut self, from: PostOrderId, to: HirId) {
524 self.deferred_edges.push((from, to));
527 fn drop_at(&mut self, value: TrackedValue, location: PostOrderId) {
528 let value = self.tracked_value_index(value);
529 self.node_mut(location).drops.push(value);
532 fn reinit_at(&mut self, value: TrackedValue, location: PostOrderId) {
533 let value = match self.tracked_value_map.get(&value) {
534 Some(value) => *value,
535 // If there's no value, this is never consumed and therefore is never dropped. We can
539 self.node_mut(location).reinits.push(value);
542 /// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them.
544 /// Should be called after visiting the HIR but before solving the control flow, otherwise some
545 /// edges will be missed.
546 fn process_deferred_edges(&mut self) {
547 trace!("processing deferred edges. post_order_map={:#?}", self.post_order_map);
548 let mut edges = vec![];
549 swap(&mut edges, &mut self.deferred_edges);
550 edges.into_iter().for_each(|(from, to)| {
551 trace!("Adding deferred edge from {:?} to {:?}", from, to);
552 let to = *self.post_order_map.get(&to).expect("Expression ID not found");
553 trace!("target edge PostOrderId={:?}", to);
554 self.add_control_edge(from, to)