diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index 68269f24e9d..56b6dd9a284 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -3,9 +3,8 @@ //! is calculated in `rustc_const_eval::transform::generator` and may be a subset of the //! types computed here. -use self::drop_ranges::{DropRangeVisitor, DropRanges, ExprUseDelegate}; +use self::drop_ranges::DropRanges; use super::FnCtxt; -use crate::expr_use_visitor::ExprUseVisitor; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::pluralize; use rustc_hir as hir; @@ -187,42 +186,17 @@ pub fn resolve_interior<'a, 'tcx>( kind: hir::GeneratorKind, ) { let body = fcx.tcx.hir().body(body_id); - - let mut visitor = { - let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx.hir()); - - // Run ExprUseVisitor to find where values are consumed. - ExprUseVisitor::new( - &mut expr_use_visitor, - &fcx.infcx, - def_id.expect_local(), - fcx.param_env, - &fcx.typeck_results.borrow(), - ) - .consume_body(body); - - let region_scope_tree = fcx.tcx.region_scope_tree(def_id); - let mut drop_range_visitor = DropRangeVisitor::from_uses( - expr_use_visitor, - region_scope_tree.body_expr_count(body.id()).unwrap_or(0), - ); - intravisit::walk_body(&mut drop_range_visitor, body); - - let mut drop_ranges = drop_range_visitor.into_drop_ranges(); - drop_ranges.propagate_to_fixpoint(); - - InteriorVisitor { - fcx, - types: FxIndexSet::default(), - region_scope_tree, - expr_count: 0, - kind, - prev_unresolved_span: None, - guard_bindings: <_>::default(), - guard_bindings_set: <_>::default(), - linted_values: <_>::default(), - drop_ranges: drop_ranges, - } + let mut visitor = InteriorVisitor { + fcx, + types: FxIndexSet::default(), + region_scope_tree: fcx.tcx.region_scope_tree(def_id), + expr_count: 0, + kind, + prev_unresolved_span: None, + guard_bindings: <_>::default(), + guard_bindings_set: <_>::default(), + linted_values: <_>::default(), + drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body), }; intravisit::walk_body(&mut visitor, body); diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs index 7b76ff5e02b..d8bda36b14f 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs @@ -12,180 +12,42 @@ //! The end result is a data structure that maps the post-order index of each node in the HIR tree //! to a set of values that are known to be dropped at that location. -use std::collections::BTreeMap; -use std::fmt::Debug; -use std::mem::swap; - -use hir::intravisit::{self, NestedVisitorMap, Visitor}; -use hir::{Expr, ExprKind, Guard, HirId, HirIdMap, HirIdSet, Node}; -use rustc_graphviz as dot; +use self::cfg_build::DropRangeVisitor; +use self::record_consumed_borrow::ExprUseDelegate; +use crate::check::FnCtxt; +use hir::def_id::DefId; +use hir::{Body, HirId, HirIdMap, Node, intravisit}; use rustc_hir as hir; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_middle::hir::map::Map; -use rustc_middle::hir::place::{Place, PlaceBase}; -use rustc_middle::ty; +use std::collections::BTreeMap; +use std::fmt::Debug; +use std::mem::swap; -use crate::expr_use_visitor; +mod cfg_build; +mod record_consumed_borrow; +mod cfg_propagate; +mod cfg_visualize; -/// Works with ExprUseVisitor to find interesting values for the drop range analysis. -/// -/// Interesting values are those that are either dropped or borrowed. For dropped values, we also -/// record the parent expression, which is the point where the drop actually takes place. -pub struct ExprUseDelegate<'tcx> { - hir: Map<'tcx>, - /// Records the variables/expressions that are dropped by a given expression. - /// - /// The key is the hir-id of the expression, and the value is a set or hir-ids for variables - /// or values that are consumed by that expression. - /// - /// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is - /// not considered a drop of `x`, although it would be a drop of `x.y`. - consumed_places: HirIdMap, - /// A set of hir-ids of values or variables that are borrowed at some point within the body. - borrowed_places: HirIdSet, -} +pub fn compute_drop_ranges<'a, 'tcx>( + fcx: &'a FnCtxt<'a, 'tcx>, + def_id: DefId, + body: &'tcx Body<'tcx>, +) -> DropRanges { + let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx.hir()); + expr_use_visitor.consume_body(fcx, def_id, body); -impl<'tcx> ExprUseDelegate<'tcx> { - pub fn new(hir: Map<'tcx>) -> Self { - Self { hir, consumed_places: <_>::default(), borrowed_places: <_>::default() } - } + let mut drop_range_visitor = DropRangeVisitor::from_uses( + expr_use_visitor, + fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0), + ); + intravisit::walk_body(&mut drop_range_visitor, body); - fn mark_consumed(&mut self, consumer: HirId, target: HirId) { - if !self.consumed_places.contains_key(&consumer) { - self.consumed_places.insert(consumer, <_>::default()); - } - self.consumed_places.get_mut(&consumer).map(|places| places.insert(target)); - } -} + let mut drop_ranges = drop_range_visitor.into_drop_ranges(); + drop_ranges.propagate_to_fixpoint(); -impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { - fn consume( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - let parent = match self.hir.find_parent_node(place_with_id.hir_id) { - Some(parent) => parent, - None => place_with_id.hir_id, - }; - debug!( - "consume {:?}; diag_expr_id={:?}, using parent {:?}", - place_with_id, diag_expr_id, parent - ); - self.mark_consumed(parent, place_with_id.hir_id); - place_hir_id(&place_with_id.place).map(|place| self.mark_consumed(parent, place)); - } - - fn borrow( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - _diag_expr_id: hir::HirId, - _bk: rustc_middle::ty::BorrowKind, - ) { - place_hir_id(&place_with_id.place).map(|place| self.borrowed_places.insert(place)); - } - - fn mutate( - &mut self, - _assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>, - _diag_expr_id: hir::HirId, - ) { - } - - fn fake_read( - &mut self, - _place: expr_use_visitor::Place<'tcx>, - _cause: rustc_middle::mir::FakeReadCause, - _diag_expr_id: hir::HirId, - ) { - } -} - -/// Gives the hir_id associated with a place if one exists. This is the hir_id that we want to -/// track for a value in the drop range analysis. -fn place_hir_id(place: &Place<'_>) -> Option { - match place.base { - PlaceBase::Rvalue | PlaceBase::StaticItem => None, - PlaceBase::Local(hir_id) - | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => Some(hir_id), - } -} - -/// This struct is used to gather the information for `DropRanges` to determine the regions of the -/// HIR tree for which a value is dropped. -/// -/// We are interested in points where a variables is dropped or initialized, and the control flow -/// of the code. We identify locations in code by their post-order traversal index, so it is -/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`. -pub struct DropRangeVisitor<'tcx> { - hir: Map<'tcx>, - /// Maps a HirId to a set of HirIds that are dropped by that node. - /// - /// See also the more detailed comment on `ExprUseDelegate.consumed_places`. - consumed_places: HirIdMap, - borrowed_places: HirIdSet, - drop_ranges: DropRanges, - expr_count: usize, -} - -impl<'tcx> DropRangeVisitor<'tcx> { - pub fn from_uses(uses: ExprUseDelegate<'tcx>, num_exprs: usize) -> Self { - debug!("consumed_places: {:?}", uses.consumed_places); - let drop_ranges = DropRanges::new( - uses.consumed_places.iter().flat_map(|(_, places)| places.iter().copied()), - &uses.hir, - num_exprs, - ); - Self { - hir: uses.hir, - consumed_places: uses.consumed_places, - borrowed_places: uses.borrowed_places, - drop_ranges, - expr_count: 0, - } - } - - pub fn into_drop_ranges(self) -> DropRanges { - self.drop_ranges - } - - fn record_drop(&mut self, hir_id: HirId) { - if self.borrowed_places.contains(&hir_id) { - debug!("not marking {:?} as dropped because it is borrowed at some point", hir_id); - } else { - debug!("marking {:?} as dropped at {}", hir_id, self.expr_count); - let count = self.expr_count; - self.drop_ranges.drop_at(hir_id, count); - } - } - - /// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all - /// expressions. This method consumes a little deeper into the expression when needed. - fn consume_expr(&mut self, expr: &hir::Expr<'_>) { - debug!("consuming expr {:?}, count={}", expr.hir_id, self.expr_count); - let places = self - .consumed_places - .get(&expr.hir_id) - .map_or(vec![], |places| places.iter().cloned().collect()); - for place in places { - for_each_consumable(place, self.hir.find(place), |hir_id| self.record_drop(hir_id)); - } - } - - fn reinit_expr(&mut self, expr: &hir::Expr<'_>) { - if let ExprKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: hir::def::Res::Local(hir_id), .. }, - )) = expr.kind - { - let location = self.expr_count; - debug!("reinitializing {:?} at {}", hir_id, location); - self.drop_ranges.reinit_at(*hir_id, location); - } else { - debug!("reinitializing {:?} is not supported", expr); - } - } + drop_ranges } /// Applies `f` to consumable portion of a HIR node. @@ -206,92 +68,6 @@ fn for_each_consumable(place: HirId, node: Option>, mut f: impl FnMut(H } } -impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - let mut reinit = None; - match expr.kind { - ExprKind::If(test, if_true, if_false) => { - self.visit_expr(test); - - let fork = self.expr_count; - - self.drop_ranges.add_control_edge(fork, self.expr_count + 1); - self.visit_expr(if_true); - let true_end = self.expr_count; - - self.drop_ranges.add_control_edge(fork, self.expr_count + 1); - if let Some(if_false) = if_false { - self.visit_expr(if_false); - } - - self.drop_ranges.add_control_edge(true_end, self.expr_count + 1); - } - ExprKind::Assign(lhs, rhs, _) => { - self.visit_expr(lhs); - self.visit_expr(rhs); - - reinit = Some(lhs); - } - ExprKind::Loop(body, ..) => { - let loop_begin = self.expr_count + 1; - self.visit_block(body); - self.drop_ranges.add_control_edge(self.expr_count, loop_begin); - } - ExprKind::Match(scrutinee, arms, ..) => { - self.visit_expr(scrutinee); - - let fork = self.expr_count; - let arm_end_ids = arms - .iter() - .map(|hir::Arm { pat, body, guard, .. }| { - self.drop_ranges.add_control_edge(fork, self.expr_count + 1); - self.visit_pat(pat); - match guard { - Some(Guard::If(expr)) => self.visit_expr(expr), - Some(Guard::IfLet(pat, expr)) => { - self.visit_pat(pat); - self.visit_expr(expr); - } - None => (), - } - self.visit_expr(body); - self.expr_count - }) - .collect::>(); - arm_end_ids.into_iter().for_each(|arm_end| { - self.drop_ranges.add_control_edge(arm_end, self.expr_count + 1) - }); - } - ExprKind::Break(hir::Destination { target_id: Ok(target), .. }, ..) - | ExprKind::Continue(hir::Destination { target_id: Ok(target), .. }, ..) => { - self.drop_ranges.add_control_edge_hir_id(self.expr_count, target); - } - - _ => intravisit::walk_expr(self, expr), - } - - self.expr_count += 1; - self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_count); - self.consume_expr(expr); - if let Some(expr) = reinit { - self.reinit_expr(expr); - } - } - - fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { - intravisit::walk_pat(self, pat); - - // Increment expr_count here to match what InteriorVisitor expects. - self.expr_count += 1; - } -} - rustc_index::newtype_index! { pub struct PostOrderId { DEBUG_FORMAT = "id({})", @@ -426,127 +202,6 @@ impl DropRanges { self.node_mut(location.into()).reinits.push(value); } - pub fn propagate_to_fixpoint(&mut self) { - trace!("before fixpoint: {:#?}", self); - self.process_deferred_edges(); - let preds = self.compute_predecessors(); - - trace!("predecessors: {:#?}", preds.iter_enumerated().collect::>()); - - let mut propagate = || { - let mut changed = false; - for id in self.nodes.indices() { - let old_state = self.nodes[id].drop_state.clone(); - let mut new_state = if id.index() == 0 { - BitSet::new_empty(self.num_values()) - } else { - // If we are not the start node and we have no predecessors, treat - // everything as dropped because there's no way to get here anyway. - BitSet::new_filled(self.num_values()) - }; - - for pred in &preds[id] { - let state = &self.nodes[*pred].drop_state; - new_state.intersect(state); - } - - for drop in &self.nodes[id].drops { - new_state.insert(*drop); - } - - for reinit in &self.nodes[id].reinits { - new_state.remove(*reinit); - } - - changed |= old_state != new_state; - self.nodes[id].drop_state = new_state; - } - - changed - }; - - while propagate() { - trace!("drop_state changed, re-running propagation"); - } - - trace!("after fixpoint: {:#?}", self); - } - - fn compute_predecessors(&self) -> IndexVec> { - let mut preds = IndexVec::from_fn_n(|_| vec![], self.nodes.len()); - for (id, node) in self.nodes.iter_enumerated() { - if node.successors.len() == 0 && id.index() != self.nodes.len() - 1 { - preds[id + 1].push(id); - } else { - for succ in &node.successors { - preds[*succ].push(id); - } - } - } - preds - } -} - -impl<'a> dot::GraphWalk<'a> for DropRanges { - type Node = PostOrderId; - - type Edge = (PostOrderId, PostOrderId); - - fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> { - self.nodes.iter_enumerated().map(|(i, _)| i).collect() - } - - fn edges(&'a self) -> dot::Edges<'a, Self::Edge> { - self.nodes - .iter_enumerated() - .flat_map(|(i, node)| { - if node.successors.len() == 0 { - vec![(i, i + 1)] - } else { - node.successors.iter().map(move |&s| (i, s)).collect() - } - }) - .collect() - } - - fn source(&'a self, edge: &Self::Edge) -> Self::Node { - edge.0 - } - - fn target(&'a self, edge: &Self::Edge) -> Self::Node { - edge.1 - } -} - -impl<'a> dot::Labeller<'a> for DropRanges { - type Node = PostOrderId; - - type Edge = (PostOrderId, PostOrderId); - - fn graph_id(&'a self) -> dot::Id<'a> { - dot::Id::new("drop_ranges").unwrap() - } - - fn node_id(&'a self, n: &Self::Node) -> dot::Id<'a> { - dot::Id::new(format!("id{}", n.index())).unwrap() - } - - fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> { - dot::LabelText::LabelStr( - format!( - "{:?}, local_id: {}", - n, - self.post_order_map - .iter() - .find(|(_hir_id, &post_order_id)| post_order_id == n.index()) - .map_or("".into(), |(hir_id, _)| format!( - "{}", - hir_id.local_id.index() - )) - ) - .into(), - ) - } } #[derive(Debug)] diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs new file mode 100644 index 00000000000..594054fde9a --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -0,0 +1,169 @@ +use super::{for_each_consumable, record_consumed_borrow::ExprUseDelegate, DropRanges}; +use hir::{ + intravisit::{self, NestedVisitorMap, Visitor}, + Expr, ExprKind, Guard, HirId, HirIdMap, HirIdSet, +}; +use rustc_hir as hir; +use rustc_middle::hir::map::Map; + +/// This struct is used to gather the information for `DropRanges` to determine the regions of the +/// HIR tree for which a value is dropped. +/// +/// We are interested in points where a variables is dropped or initialized, and the control flow +/// of the code. We identify locations in code by their post-order traversal index, so it is +/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`. +pub struct DropRangeVisitor<'tcx> { + hir: Map<'tcx>, + /// Maps a HirId to a set of HirIds that are dropped by that node. + /// + /// See also the more detailed comment on `ExprUseDelegate.consumed_places`. + consumed_places: HirIdMap, + borrowed_places: HirIdSet, + drop_ranges: DropRanges, + expr_count: usize, +} + +impl<'tcx> DropRangeVisitor<'tcx> { + pub fn from_uses(uses: ExprUseDelegate<'tcx>, num_exprs: usize) -> Self { + debug!("consumed_places: {:?}", uses.consumed_places); + let drop_ranges = DropRanges::new( + uses.consumed_places.iter().flat_map(|(_, places)| places.iter().copied()), + &uses.hir, + num_exprs, + ); + Self { + hir: uses.hir, + consumed_places: uses.consumed_places, + borrowed_places: uses.borrowed_places, + drop_ranges, + expr_count: 0, + } + } + + pub fn into_drop_ranges(self) -> DropRanges { + self.drop_ranges + } + + fn record_drop(&mut self, hir_id: HirId) { + if self.borrowed_places.contains(&hir_id) { + debug!("not marking {:?} as dropped because it is borrowed at some point", hir_id); + } else { + debug!("marking {:?} as dropped at {}", hir_id, self.expr_count); + let count = self.expr_count; + self.drop_ranges.drop_at(hir_id, count); + } + } + + /// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all + /// expressions. This method consumes a little deeper into the expression when needed. + fn consume_expr(&mut self, expr: &hir::Expr<'_>) { + debug!("consuming expr {:?}, count={}", expr.hir_id, self.expr_count); + let places = self + .consumed_places + .get(&expr.hir_id) + .map_or(vec![], |places| places.iter().cloned().collect()); + for place in places { + for_each_consumable(place, self.hir.find(place), |hir_id| self.record_drop(hir_id)); + } + } + + fn reinit_expr(&mut self, expr: &hir::Expr<'_>) { + if let ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) = expr.kind + { + let location = self.expr_count; + debug!("reinitializing {:?} at {}", hir_id, location); + self.drop_ranges.reinit_at(*hir_id, location); + } else { + debug!("reinitializing {:?} is not supported", expr); + } + } +} + +impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> { + type Map = intravisit::ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + let mut reinit = None; + match expr.kind { + ExprKind::If(test, if_true, if_false) => { + self.visit_expr(test); + + let fork = self.expr_count; + + self.drop_ranges.add_control_edge(fork, self.expr_count + 1); + self.visit_expr(if_true); + let true_end = self.expr_count; + + self.drop_ranges.add_control_edge(fork, self.expr_count + 1); + if let Some(if_false) = if_false { + self.visit_expr(if_false); + } + + self.drop_ranges.add_control_edge(true_end, self.expr_count + 1); + } + ExprKind::Assign(lhs, rhs, _) => { + self.visit_expr(lhs); + self.visit_expr(rhs); + + reinit = Some(lhs); + } + ExprKind::Loop(body, ..) => { + let loop_begin = self.expr_count + 1; + self.visit_block(body); + self.drop_ranges.add_control_edge(self.expr_count, loop_begin); + } + ExprKind::Match(scrutinee, arms, ..) => { + self.visit_expr(scrutinee); + + let fork = self.expr_count; + let arm_end_ids = arms + .iter() + .map(|hir::Arm { pat, body, guard, .. }| { + self.drop_ranges.add_control_edge(fork, self.expr_count + 1); + self.visit_pat(pat); + match guard { + Some(Guard::If(expr)) => self.visit_expr(expr), + Some(Guard::IfLet(pat, expr)) => { + self.visit_pat(pat); + self.visit_expr(expr); + } + None => (), + } + self.visit_expr(body); + self.expr_count + }) + .collect::>(); + arm_end_ids.into_iter().for_each(|arm_end| { + self.drop_ranges.add_control_edge(arm_end, self.expr_count + 1) + }); + } + ExprKind::Break(hir::Destination { target_id: Ok(target), .. }, ..) + | ExprKind::Continue(hir::Destination { target_id: Ok(target), .. }, ..) => { + self.drop_ranges.add_control_edge_hir_id(self.expr_count, target); + } + + _ => intravisit::walk_expr(self, expr), + } + + self.expr_count += 1; + self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_count); + self.consume_expr(expr); + if let Some(expr) = reinit { + self.reinit_expr(expr); + } + } + + fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + intravisit::walk_pat(self, pat); + + // Increment expr_count here to match what InteriorVisitor expects. + self.expr_count += 1; + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs new file mode 100644 index 00000000000..ea7b9106b9a --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs @@ -0,0 +1,67 @@ +use std::collections::BTreeMap; + +use rustc_index::{bit_set::BitSet, vec::IndexVec}; + +use super::{DropRanges, PostOrderId}; + +impl DropRanges { + pub fn propagate_to_fixpoint(&mut self) { + trace!("before fixpoint: {:#?}", self); + self.process_deferred_edges(); + let preds = self.compute_predecessors(); + + trace!("predecessors: {:#?}", preds.iter_enumerated().collect::>()); + + let mut propagate = || { + let mut changed = false; + for id in self.nodes.indices() { + let old_state = self.nodes[id].drop_state.clone(); + let mut new_state = if id.index() == 0 { + BitSet::new_empty(self.num_values()) + } else { + // If we are not the start node and we have no predecessors, treat + // everything as dropped because there's no way to get here anyway. + BitSet::new_filled(self.num_values()) + }; + + for pred in &preds[id] { + let state = &self.nodes[*pred].drop_state; + new_state.intersect(state); + } + + for drop in &self.nodes[id].drops { + new_state.insert(*drop); + } + + for reinit in &self.nodes[id].reinits { + new_state.remove(*reinit); + } + + changed |= old_state != new_state; + self.nodes[id].drop_state = new_state; + } + + changed + }; + + while propagate() { + trace!("drop_state changed, re-running propagation"); + } + + trace!("after fixpoint: {:#?}", self); + } + + fn compute_predecessors(&self) -> IndexVec> { + let mut preds = IndexVec::from_fn_n(|_| vec![], self.nodes.len()); + for (id, node) in self.nodes.iter_enumerated() { + if node.successors.len() == 0 && id.index() != self.nodes.len() - 1 { + preds[id + 1].push(id); + } else { + for succ in &node.successors { + preds[*succ].push(id); + } + } + } + preds + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs new file mode 100644 index 00000000000..ebbbec1c472 --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs @@ -0,0 +1,68 @@ +//! Implementation of GraphWalk for DropRanges so we can visualize the control +//! flow graph when needed for debugging. + +use rustc_graphviz as dot; + +use super::{DropRanges, PostOrderId}; + +impl<'a> dot::GraphWalk<'a> for DropRanges { + type Node = PostOrderId; + + type Edge = (PostOrderId, PostOrderId); + + fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> { + self.nodes.iter_enumerated().map(|(i, _)| i).collect() + } + + fn edges(&'a self) -> dot::Edges<'a, Self::Edge> { + self.nodes + .iter_enumerated() + .flat_map(|(i, node)| { + if node.successors.len() == 0 { + vec![(i, i + 1)] + } else { + node.successors.iter().map(move |&s| (i, s)).collect() + } + }) + .collect() + } + + fn source(&'a self, edge: &Self::Edge) -> Self::Node { + edge.0 + } + + fn target(&'a self, edge: &Self::Edge) -> Self::Node { + edge.1 + } +} + +impl<'a> dot::Labeller<'a> for DropRanges { + type Node = PostOrderId; + + type Edge = (PostOrderId, PostOrderId); + + fn graph_id(&'a self) -> dot::Id<'a> { + dot::Id::new("drop_ranges").unwrap() + } + + fn node_id(&'a self, n: &Self::Node) -> dot::Id<'a> { + dot::Id::new(format!("id{}", n.index())).unwrap() + } + + fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> { + dot::LabelText::LabelStr( + format!( + "{:?}, local_id: {}", + n, + self.post_order_map + .iter() + .find(|(_hir_id, &post_order_id)| post_order_id == n.index()) + .map_or("".into(), |(hir_id, _)| format!( + "{}", + hir_id.local_id.index() + )) + ) + .into(), + ) + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs new file mode 100644 index 00000000000..93bb58cd8a0 --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -0,0 +1,112 @@ +use crate::{ + check::FnCtxt, + expr_use_visitor::{self, ExprUseVisitor}, +}; +use hir::{HirId, HirIdMap, HirIdSet, Body, def_id::DefId}; +use rustc_hir as hir; +use rustc_middle::hir::{ + map::Map, + place::{Place, PlaceBase}, +}; +use rustc_middle::ty; + +/// Works with ExprUseVisitor to find interesting values for the drop range analysis. +/// +/// Interesting values are those that are either dropped or borrowed. For dropped values, we also +/// record the parent expression, which is the point where the drop actually takes place. +pub struct ExprUseDelegate<'tcx> { + pub(super) hir: Map<'tcx>, + /// Records the variables/expressions that are dropped by a given expression. + /// + /// The key is the hir-id of the expression, and the value is a set or hir-ids for variables + /// or values that are consumed by that expression. + /// + /// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is + /// not considered a drop of `x`, although it would be a drop of `x.y`. + pub(super) consumed_places: HirIdMap, + /// A set of hir-ids of values or variables that are borrowed at some point within the body. + pub(super) borrowed_places: HirIdSet, +} + +impl<'tcx> ExprUseDelegate<'tcx> { + pub fn new(hir: Map<'tcx>) -> Self { + Self { hir, consumed_places: <_>::default(), borrowed_places: <_>::default() } + } + + pub fn consume_body( + &mut self, + fcx: &'_ FnCtxt<'_, 'tcx>, + def_id: DefId, + body: &'tcx Body<'tcx>, + ) { + // Run ExprUseVisitor to find where values are consumed. + ExprUseVisitor::new( + self, + &fcx.infcx, + def_id.expect_local(), + fcx.param_env, + &fcx.typeck_results.borrow(), + ) + .consume_body(body); + } + + fn mark_consumed(&mut self, consumer: HirId, target: HirId) { + if !self.consumed_places.contains_key(&consumer) { + self.consumed_places.insert(consumer, <_>::default()); + } + self.consumed_places.get_mut(&consumer).map(|places| places.insert(target)); + } +} + +impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { + fn consume( + &mut self, + place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, + diag_expr_id: HirId, + ) { + let parent = match self.hir.find_parent_node(place_with_id.hir_id) { + Some(parent) => parent, + None => place_with_id.hir_id, + }; + debug!( + "consume {:?}; diag_expr_id={:?}, using parent {:?}", + place_with_id, diag_expr_id, parent + ); + self.mark_consumed(parent, place_with_id.hir_id); + place_hir_id(&place_with_id.place).map(|place| self.mark_consumed(parent, place)); + } + + fn borrow( + &mut self, + place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, + _diag_expr_id: HirId, + _bk: rustc_middle::ty::BorrowKind, + ) { + place_hir_id(&place_with_id.place).map(|place| self.borrowed_places.insert(place)); + } + + fn mutate( + &mut self, + _assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>, + _diag_expr_id: HirId, + ) { + } + + fn fake_read( + &mut self, + _place: expr_use_visitor::Place<'tcx>, + _cause: rustc_middle::mir::FakeReadCause, + _diag_expr_id: HirId, + ) { + } +} + +/// Gives the hir_id associated with a place if one exists. This is the hir_id that we want to +/// track for a value in the drop range analysis. +fn place_hir_id(place: &Place<'_>) -> Option { + match place.base { + PlaceBase::Rvalue | PlaceBase::StaticItem => None, + PlaceBase::Local(hir_id) + | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => Some(hir_id), + } +}