From 887296e8c32165de83deb5156f0255c1f286ee6a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 14:14:38 -0400 Subject: [PATCH] make it possible to customize the `RegionGraph` direction --- .../borrow_check/nll/constraints/graph.rs | 110 ++++++++++++++---- .../borrow_check/nll/constraints/mod.rs | 10 +- .../borrow_check/nll/region_infer/mod.rs | 4 +- 3 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs index 5f05ae8ade5..6fd6c41bebe 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/graph.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/graph.rs @@ -8,27 +8,78 @@ // 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 { + _direction: D, first_constraints: IndexVec>, next_constraints: IndexVec>, } -impl ConstraintGraph { +crate type NormalConstraintGraph = ConstraintGraph; + +/// 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 ConstraintGraph { /// 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; @@ -36,13 +87,21 @@ impl ConstraintGraph { } 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, @@ -51,12 +110,12 @@ impl ConstraintGraph { } } -crate struct Edges<'s> { - graph: &'s ConstraintGraph, +crate struct Edges<'s, D: ConstraintGraphDirecton> { + graph: &'s ConstraintGraph, pointer: Option, } -impl<'s> Iterator for Edges<'s> { +impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> { type Item = ConstraintIndex; fn next(&mut self) -> Option { @@ -69,17 +128,20 @@ fn next(&mut self) -> Option { } } -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, } -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) -> Self { Self { set, constraint_graph, @@ -88,7 +150,7 @@ 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), @@ -96,39 +158,39 @@ impl<'s> RegionGraph<'s> { } } -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.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, ) -> >::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>; } diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index 289557a9a07..f5cab56bd29 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -36,14 +36,14 @@ 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, @@ -51,9 +51,9 @@ impl ConstraintSet { /// them into an SCC, and find the relationships between SCCs. crate fn compute_sccs( &self, - constraint_graph: &graph::ConstraintGraph, + constraint_graph: &graph::NormalConstraintGraph, ) -> Sccs { - let region_graph = &graph::RegionGraph::new(self, constraint_graph); + let region_graph = &constraint_graph.region_graph(self); Sccs::new(region_graph) } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index afd4e2859ac..867b05639f5 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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, }; @@ -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, + constraint_graph: Rc, /// The SCC computed from `constraints` and the constraint graph. Used to compute the values /// of each region.