Skip to content

Commit

Permalink
make it possible to customize the RegionGraph direction
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed Aug 7, 2018
1 parent a92bf8d commit 887296e
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 31 deletions.
110 changes: 86 additions & 24 deletions src/librustc_mir/borrow_check/nll/constraints/graph.rs
Expand Up @@ -8,41 +8,100 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use borrow_check::nll::constraints::{ConstraintIndex, ConstraintSet};
use borrow_check::nll::constraints::{ConstraintIndex, ConstraintSet, OutlivesConstraint};
use rustc::ty::RegionVid;
use rustc_data_structures::graph;
use rustc_data_structures::indexed_vec::IndexVec;

crate struct ConstraintGraph {
/// The construct graph organizes the constraints by their end-points.
/// It can be used to view a `R1: R2` constraint as either an edge `R1
/// -> R2` or `R2 -> R1` depending on the direction type `D`.
crate struct ConstraintGraph<D: ConstraintGraphDirecton> {
_direction: D,
first_constraints: IndexVec<RegionVid, Option<ConstraintIndex>>,
next_constraints: IndexVec<ConstraintIndex, Option<ConstraintIndex>>,
}

impl ConstraintGraph {
crate type NormalConstraintGraph = ConstraintGraph<Normal>;

/// Marker trait that controls whether a `R1: R2` constraint
/// represents an edge `R1 -> R2` or `R2 -> R1`.
crate trait ConstraintGraphDirecton: Copy + 'static {
fn start_region(c: &OutlivesConstraint) -> RegionVid;
fn end_region(c: &OutlivesConstraint) -> RegionVid;
}

/// In normal mode, a `R1: R2` constraint results in an edge `R1 ->
/// R2`. This is what we use when constructing the SCCs for
/// inference. This is because we compute the value of R1 by union'ing
/// all the things that it relies on.
#[derive(Copy, Clone, Debug)]
crate struct Normal;

impl ConstraintGraphDirecton for Normal {
fn start_region(c: &OutlivesConstraint) -> RegionVid {
c.sup
}

fn end_region(c: &OutlivesConstraint) -> RegionVid {
c.sub
}
}

/// In reverse mode, a `R1: R2` constraint results in an edge `R2 ->
/// R1`. We use this for optimizing liveness computation, because then
/// we wish to iterate from a region (e.g., R2) to all the regions
/// that will outlive it (e.g., R1).
#[derive(Copy, Clone, Debug)]
crate struct Reverse;

impl ConstraintGraphDirecton for Reverse {
fn start_region(c: &OutlivesConstraint) -> RegionVid {
c.sub
}

fn end_region(c: &OutlivesConstraint) -> RegionVid {
c.sup
}
}

impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
/// Create a "dependency graph" where each region constraint `R1:
/// R2` is treated as an edge `R1 -> R2`. We use this graph to
/// construct SCCs for region inference but also for error
/// reporting.
crate fn new(set: &ConstraintSet, num_region_vars: usize) -> Self {
crate fn new(
direction: D,
set: &ConstraintSet,
num_region_vars: usize,
) -> Self {
let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars);
let mut next_constraints = IndexVec::from_elem(None, &set.constraints);

for (idx, constraint) in set.constraints.iter_enumerated().rev() {
let head = &mut first_constraints[constraint.sup];
let head = &mut first_constraints[D::start_region(constraint)];
let next = &mut next_constraints[idx];
debug_assert!(next.is_none());
*next = *head;
*head = Some(idx);
}

Self {
_direction: direction,
first_constraints,
next_constraints,
}
}

/// Given the constraint set from which this graph was built
/// creates a region graph so that you can iterate over *regions*
/// and not constraints.
crate fn region_graph<'rg>(&'rg self, set: &'rg ConstraintSet) -> RegionGraph<'rg, D> {
RegionGraph::new(set, self)
}

/// Given a region `R`, iterate over all constraints `R: R1`.
crate fn outgoing_edges(&self, region_sup: RegionVid) -> Edges<'_> {
crate fn outgoing_edges(&self, region_sup: RegionVid) -> Edges<'_, D> {
let first = self.first_constraints[region_sup];
Edges {
graph: self,
Expand All @@ -51,12 +110,12 @@ impl ConstraintGraph {
}
}

crate struct Edges<'s> {
graph: &'s ConstraintGraph,
crate struct Edges<'s, D: ConstraintGraphDirecton> {
graph: &'s ConstraintGraph<D>,
pointer: Option<ConstraintIndex>,
}

impl<'s> Iterator for Edges<'s> {
impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
type Item = ConstraintIndex;

fn next(&mut self) -> Option<Self::Item> {
Expand All @@ -69,17 +128,20 @@ impl<'s> Iterator for Edges<'s> {
}
}

crate struct RegionGraph<'s> {
/// This struct brings together a constraint set and a (normal, not
/// reverse) constraint graph. It implements the graph traits and is
/// usd for doing the SCC computation.
crate struct RegionGraph<'s, D: ConstraintGraphDirecton> {
set: &'s ConstraintSet,
constraint_graph: &'s ConstraintGraph,
constraint_graph: &'s ConstraintGraph<D>,
}

impl<'s> RegionGraph<'s> {
impl<'s, D: ConstraintGraphDirecton> RegionGraph<'s, D> {
/// Create a "dependency graph" where each region constraint `R1:
/// R2` is treated as an edge `R1 -> R2`. We use this graph to
/// construct SCCs for region inference but also for error
/// reporting.
crate fn new(set: &'s ConstraintSet, constraint_graph: &'s ConstraintGraph) -> Self {
crate fn new(set: &'s ConstraintSet, constraint_graph: &'s ConstraintGraph<D>) -> Self {
Self {
set,
constraint_graph,
Expand All @@ -88,47 +150,47 @@ impl<'s> RegionGraph<'s> {

/// Given a region `R`, iterate over all regions `R1` such that
/// there exists a constraint `R: R1`.
crate fn sub_regions(&self, region_sup: RegionVid) -> Successors<'_> {
crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'_, D> {
Successors {
set: self.set,
edges: self.constraint_graph.outgoing_edges(region_sup),
}
}
}

crate struct Successors<'s> {
crate struct Successors<'s, D: ConstraintGraphDirecton> {
set: &'s ConstraintSet,
edges: Edges<'s>,
edges: Edges<'s, D>,
}

impl<'s> Iterator for Successors<'s> {
impl<'s, D: ConstraintGraphDirecton> Iterator for Successors<'s, D> {
type Item = RegionVid;

fn next(&mut self) -> Option<Self::Item> {
self.edges.next().map(|c| self.set[c].sub)
self.edges.next().map(|c| D::end_region(&self.set[c]))
}
}

impl<'s> graph::DirectedGraph for RegionGraph<'s> {
impl<'s, D: ConstraintGraphDirecton> graph::DirectedGraph for RegionGraph<'s, D> {
type Node = RegionVid;
}

impl<'s> graph::WithNumNodes for RegionGraph<'s> {
impl<'s, D: ConstraintGraphDirecton> graph::WithNumNodes for RegionGraph<'s, D> {
fn num_nodes(&self) -> usize {
self.constraint_graph.first_constraints.len()
}
}

impl<'s> graph::WithSuccessors for RegionGraph<'s> {
impl<'s, D: ConstraintGraphDirecton> graph::WithSuccessors for RegionGraph<'s, D> {
fn successors<'graph>(
&'graph self,
node: Self::Node,
) -> <Self as graph::GraphSuccessors<'graph>>::Iter {
self.sub_regions(node)
self.outgoing_regions(node)
}
}

impl<'s, 'graph> graph::GraphSuccessors<'graph> for RegionGraph<'s> {
impl<'s, 'graph, D: ConstraintGraphDirecton> graph::GraphSuccessors<'graph> for RegionGraph<'s, D> {
type Item = RegionVid;
type Iter = Successors<'graph>;
type Iter = Successors<'graph, D>;
}
10 changes: 5 additions & 5 deletions src/librustc_mir/borrow_check/nll/constraints/mod.rs
Expand Up @@ -36,24 +36,24 @@ impl ConstraintSet {
self.constraints.push(constraint);
}

/// Constructs a graph from the constraint set; the graph makes it
/// Constructs a "normal" graph from the constraint set; the graph makes it
/// easy to find the constraints affecting a particular region.
///
/// NB: This graph contains a "frozen" view of the current
/// constraints. any new constraints added to the `ConstraintSet`
/// after the graph is built will not be present in the graph.
crate fn graph(&self, num_region_vars: usize) -> graph::ConstraintGraph {
graph::ConstraintGraph::new(self, num_region_vars)
crate fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph {
graph::ConstraintGraph::new(graph::Normal, self, num_region_vars)
}

/// Compute cycles (SCCs) in the graph of regions. In particular,
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
/// them into an SCC, and find the relationships between SCCs.
crate fn compute_sccs(
&self,
constraint_graph: &graph::ConstraintGraph,
constraint_graph: &graph::NormalConstraintGraph,
) -> Sccs<RegionVid, ConstraintSccIndex> {
let region_graph = &graph::RegionGraph::new(self, constraint_graph);
let region_graph = &constraint_graph.region_graph(self);
Sccs::new(region_graph)
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/borrow_check/nll/region_infer/mod.rs
Expand Up @@ -9,7 +9,7 @@
// except according to those terms.

use super::universal_regions::UniversalRegions;
use borrow_check::nll::constraints::graph::ConstraintGraph;
use borrow_check::nll::constraints::graph::NormalConstraintGraph;
use borrow_check::nll::constraints::{
ConstraintIndex, ConstraintSccIndex, ConstraintSet, OutlivesConstraint,
};
Expand Down Expand Up @@ -61,7 +61,7 @@ pub struct RegionInferenceContext<'tcx> {
/// The constraint-set, but in graph form, making it easy to traverse
/// the constraints adjacent to a particular region. Used to construct
/// the SCC (see `constraint_sccs`) and for error reporting.
constraint_graph: Rc<ConstraintGraph>,
constraint_graph: Rc<NormalConstraintGraph>,

/// The SCC computed from `constraints` and the constraint graph. Used to compute the values
/// of each region.
Expand Down

0 comments on commit 887296e

Please sign in to comment.