Additional cleanup

This cleans up the refactoring from the previous patch and cleans things
up a bit. Each module has a clear entry point and everything else is
private.
This commit is contained in:
Eric Holk 2021-12-13 15:01:26 -08:00
parent f5f98d7ee4
commit 9347bf498a
4 changed files with 151 additions and 130 deletions
compiler/rustc_typeck/src/check/generator_interior

View file

@ -12,39 +12,33 @@
//! 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 self::cfg_build::DropRangeVisitor;
use self::record_consumed_borrow::ExprUseDelegate;
use self::cfg_build::build_control_flow_graph;
use self::record_consumed_borrow::find_consumed_and_borrowed;
use crate::check::FnCtxt;
use hir::def_id::DefId;
use hir::{Body, HirId, HirIdMap, Node, intravisit};
use hir::{Body, HirId, HirIdMap, Node};
use rustc_hir as hir;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_middle::hir::map::Map;
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::mem::swap;
mod cfg_build;
mod record_consumed_borrow;
mod cfg_propagate;
mod cfg_visualize;
mod record_consumed_borrow;
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);
let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
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);
let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0);
let mut drop_ranges =
build_control_flow_graph(fcx.tcx.hir(), consumed_borrowed_places, body, num_exprs);
let mut drop_ranges = drop_range_visitor.into_drop_ranges();
drop_ranges.propagate_to_fixpoint();
drop_ranges
@ -105,31 +99,6 @@ impl Debug for DropRanges {
/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely
/// dropped at the point of the node identified by post_order_id.
impl DropRanges {
pub fn new(hir_ids: impl Iterator<Item = HirId>, hir: &Map<'_>, num_exprs: usize) -> Self {
let mut hir_id_map = HirIdMap::<HirIdIndex>::default();
let mut next = <_>::from(0u32);
for hir_id in hir_ids {
for_each_consumable(hir_id, hir.find(hir_id), |hir_id| {
if !hir_id_map.contains_key(&hir_id) {
hir_id_map.insert(hir_id, next);
next = <_>::from(next.index() + 1);
}
});
}
debug!("hir_id_map: {:?}", hir_id_map);
let num_values = hir_id_map.len();
Self {
hir_id_map,
nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1),
deferred_edges: <_>::default(),
post_order_map: <_>::default(),
}
}
fn hidx(&self, hir_id: HirId) -> HirIdIndex {
*self.hir_id_map.get(&hir_id).unwrap()
}
pub fn is_dropped_at(&mut self, hir_id: HirId, location: usize) -> bool {
self.hir_id_map
.get(&hir_id)
@ -142,13 +111,6 @@ impl DropRanges {
self.hir_id_map.len()
}
/// Adds an entry in the mapping from HirIds to PostOrderIds
///
/// Needed so that `add_control_edge_hir_id` can work.
pub fn add_node_mapping(&mut self, hir_id: HirId, post_order_id: usize) {
self.post_order_map.insert(hir_id, post_order_id);
}
/// Returns a reference to the NodeInfo for a node, panicking if it does not exist
fn expect_node(&self, id: PostOrderId) -> &NodeInfo {
&self.nodes[id]
@ -160,48 +122,10 @@ impl DropRanges {
&mut self.nodes[id]
}
pub fn add_control_edge(&mut self, from: usize, to: usize) {
fn add_control_edge(&mut self, from: usize, to: usize) {
trace!("adding control edge from {} to {}", from, to);
self.node_mut(from.into()).successors.push(to.into());
}
/// Like add_control_edge, but uses a hir_id as the target.
///
/// This can be used for branches where we do not know the PostOrderId of the target yet,
/// such as when handling `break` or `continue`.
pub fn add_control_edge_hir_id(&mut self, from: usize, to: HirId) {
self.deferred_edges.push((from, to));
}
/// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them.
///
/// Should be called after visiting the HIR but before solving the control flow, otherwise some
/// edges will be missed.
fn process_deferred_edges(&mut self) {
let mut edges = vec![];
swap(&mut edges, &mut self.deferred_edges);
edges.into_iter().for_each(|(from, to)| {
let to = *self.post_order_map.get(&to).expect("Expression ID not found");
trace!("Adding deferred edge from {} to {}", from, to);
self.add_control_edge(from, to)
});
}
pub fn drop_at(&mut self, value: HirId, location: usize) {
let value = self.hidx(value);
self.node_mut(location.into()).drops.push(value);
}
pub fn reinit_at(&mut self, value: HirId, location: usize) {
let value = match self.hir_id_map.get(&value) {
Some(value) => *value,
// If there's no value, this is never consumed and therefore is never dropped. We can
// ignore this.
None => return,
};
self.node_mut(location.into()).reinits.push(value);
}
}
#[derive(Debug)]

View file

@ -1,51 +1,57 @@
use super::{for_each_consumable, record_consumed_borrow::ExprUseDelegate, DropRanges};
use super::{
for_each_consumable, record_consumed_borrow::ConsumedAndBorrowedPlaces, DropRanges, HirIdIndex,
NodeInfo,
};
use hir::{
intravisit::{self, NestedVisitorMap, Visitor},
Expr, ExprKind, Guard, HirId, HirIdMap, HirIdSet,
Body, Expr, ExprKind, Guard, HirId, HirIdMap,
};
use rustc_hir as hir;
use rustc_index::vec::IndexVec;
use rustc_middle::hir::map::Map;
/// Traverses the body to find the control flow graph and locations for the
/// relevant places are dropped or reinitialized.
///
/// The resulting structure still needs to be iterated to a fixed point, which
/// can be done with propagate_to_fixpoint in cfg_propagate.
pub fn build_control_flow_graph<'tcx>(
hir: Map<'tcx>,
consumed_borrowed_places: ConsumedAndBorrowedPlaces,
body: &'tcx Body<'tcx>,
num_exprs: usize,
) -> DropRanges {
let mut drop_range_visitor = DropRangeVisitor::new(hir, consumed_borrowed_places, num_exprs);
intravisit::walk_body(&mut drop_range_visitor, body);
drop_range_visitor.drop_ranges
}
/// 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> {
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<HirIdSet>,
borrowed_places: HirIdSet,
places: ConsumedAndBorrowedPlaces,
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);
fn new(hir: Map<'tcx>, places: ConsumedAndBorrowedPlaces, num_exprs: usize) -> Self {
debug!("consumed_places: {:?}", places.consumed);
let drop_ranges = DropRanges::new(
uses.consumed_places.iter().flat_map(|(_, places)| places.iter().copied()),
&uses.hir,
places.consumed.iter().flat_map(|(_, places)| places.iter().copied()),
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
Self { hir, places, drop_ranges, expr_count: 0 }
}
fn record_drop(&mut self, hir_id: HirId) {
if self.borrowed_places.contains(&hir_id) {
if self.places.borrowed.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);
@ -59,7 +65,8 @@ impl<'tcx> DropRangeVisitor<'tcx> {
fn consume_expr(&mut self, expr: &hir::Expr<'_>) {
debug!("consuming expr {:?}, count={}", expr.hir_id, self.expr_count);
let places = self
.consumed_places
.places
.consumed
.get(&expr.hir_id)
.map_or(vec![], |places| places.iter().cloned().collect());
for place in places {
@ -167,3 +174,60 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
self.expr_count += 1;
}
}
impl DropRanges {
fn new(hir_ids: impl Iterator<Item = HirId>, hir: Map<'_>, num_exprs: usize) -> Self {
let mut hir_id_map = HirIdMap::<HirIdIndex>::default();
let mut next = <_>::from(0u32);
for hir_id in hir_ids {
for_each_consumable(hir_id, hir.find(hir_id), |hir_id| {
if !hir_id_map.contains_key(&hir_id) {
hir_id_map.insert(hir_id, next);
next = <_>::from(next.index() + 1);
}
});
}
debug!("hir_id_map: {:?}", hir_id_map);
let num_values = hir_id_map.len();
Self {
hir_id_map,
nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1),
deferred_edges: <_>::default(),
post_order_map: <_>::default(),
}
}
fn hidx(&self, hir_id: HirId) -> HirIdIndex {
*self.hir_id_map.get(&hir_id).unwrap()
}
/// Adds an entry in the mapping from HirIds to PostOrderIds
///
/// Needed so that `add_control_edge_hir_id` can work.
fn add_node_mapping(&mut self, hir_id: HirId, post_order_id: usize) {
self.post_order_map.insert(hir_id, post_order_id);
}
/// Like add_control_edge, but uses a hir_id as the target.
///
/// This can be used for branches where we do not know the PostOrderId of the target yet,
/// such as when handling `break` or `continue`.
fn add_control_edge_hir_id(&mut self, from: usize, to: HirId) {
self.deferred_edges.push((from, to));
}
fn drop_at(&mut self, value: HirId, location: usize) {
let value = self.hidx(value);
self.node_mut(location.into()).drops.push(value);
}
fn reinit_at(&mut self, value: HirId, location: usize) {
let value = match self.hir_id_map.get(&value) {
Some(value) => *value,
// If there's no value, this is never consumed and therefore is never dropped. We can
// ignore this.
None => return,
};
self.node_mut(location.into()).reinits.push(value);
}
}

View file

@ -1,8 +1,7 @@
use std::collections::BTreeMap;
use rustc_index::{bit_set::BitSet, vec::IndexVec};
use super::{DropRanges, PostOrderId};
use rustc_index::{bit_set::BitSet, vec::IndexVec};
use std::collections::BTreeMap;
use std::mem::swap;
impl DropRanges {
pub fn propagate_to_fixpoint(&mut self) {
@ -64,4 +63,18 @@ impl DropRanges {
}
preds
}
/// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them.
///
/// Should be called after visiting the HIR but before solving the control flow, otherwise some
/// edges will be missed.
fn process_deferred_edges(&mut self) {
let mut edges = vec![];
swap(&mut edges, &mut self.deferred_edges);
edges.into_iter().for_each(|(from, to)| {
let to = *self.post_order_map.get(&to).expect("Expression ID not found");
trace!("Adding deferred edge from {} to {}", from, to);
self.add_control_edge(from, to)
});
}
}

View file

@ -2,7 +2,7 @@ use crate::{
check::FnCtxt,
expr_use_visitor::{self, ExprUseVisitor},
};
use hir::{HirId, HirIdMap, HirIdSet, Body, def_id::DefId};
use hir::{def_id::DefId, Body, HirId, HirIdMap, HirIdSet};
use rustc_hir as hir;
use rustc_middle::hir::{
map::Map,
@ -10,12 +10,17 @@ use rustc_middle::hir::{
};
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>,
pub fn find_consumed_and_borrowed<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
def_id: DefId,
body: &'tcx Body<'tcx>,
) -> ConsumedAndBorrowedPlaces {
let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx.hir());
expr_use_visitor.consume_body(fcx, def_id, body);
expr_use_visitor.places
}
pub struct ConsumedAndBorrowedPlaces {
/// 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
@ -23,17 +28,32 @@ pub struct ExprUseDelegate<'tcx> {
///
/// 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<HirIdSet>,
pub consumed: HirIdMap<HirIdSet>,
/// A set of hir-ids of values or variables that are borrowed at some point within the body.
pub(super) borrowed_places: HirIdSet,
pub borrowed: HirIdSet,
}
/// 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.
struct ExprUseDelegate<'tcx> {
hir: Map<'tcx>,
places: ConsumedAndBorrowedPlaces,
}
impl<'tcx> ExprUseDelegate<'tcx> {
pub fn new(hir: Map<'tcx>) -> Self {
Self { hir, consumed_places: <_>::default(), borrowed_places: <_>::default() }
fn new(hir: Map<'tcx>) -> Self {
Self {
hir,
places: ConsumedAndBorrowedPlaces {
consumed: <_>::default(),
borrowed: <_>::default(),
},
}
}
pub fn consume_body(
fn consume_body(
&mut self,
fcx: &'_ FnCtxt<'_, 'tcx>,
def_id: DefId,
@ -51,10 +71,10 @@ impl<'tcx> ExprUseDelegate<'tcx> {
}
fn mark_consumed(&mut self, consumer: HirId, target: HirId) {
if !self.consumed_places.contains_key(&consumer) {
self.consumed_places.insert(consumer, <_>::default());
if !self.places.consumed.contains_key(&consumer) {
self.places.consumed.insert(consumer, <_>::default());
}
self.consumed_places.get_mut(&consumer).map(|places| places.insert(target));
self.places.consumed.get_mut(&consumer).map(|places| places.insert(target));
}
}
@ -82,7 +102,7 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
_diag_expr_id: HirId,
_bk: rustc_middle::ty::BorrowKind,
) {
place_hir_id(&place_with_id.place).map(|place| self.borrowed_places.insert(place));
place_hir_id(&place_with_id.place).map(|place| self.places.borrowed.insert(place));
}
fn mutate(