about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs9
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs89
2 files changed, 90 insertions, 8 deletions
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index 641d7ed342c..acd9223e425 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -295,9 +295,16 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
     // Also dump the inference graph constraints as a graphviz file.
     let _: io::Result<()> = do catch {
         let mut file =
-            pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?;
+            pretty::create_dump_file(infcx.tcx, "regioncx.all.dot", None, "nll", &0, source)?;
         regioncx.dump_graphviz_raw_constraints(&mut file)?;
     };
+
+    // Also dump the inference graph constraints as a graphviz file.
+    let _: io::Result<()> = do catch {
+        let mut file =
+            pretty::create_dump_file(infcx.tcx, "regioncx.scc.dot", None, "nll", &0, source)?;
+        regioncx.dump_graphviz_scc_constraints(&mut file)?;
+    };
 }
 
 fn dump_annotation<'a, 'gcx, 'tcx>(
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
index 2e41c8f8dd6..13d754f5cab 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
@@ -12,22 +12,37 @@
 //! libgraphviz traits, specialized to attaching borrowck analysis
 //! data to rendered labels.
 
+use super::*;
+use borrow_check::nll::constraints::OutlivesConstraint;
 use dot::{self, IntoCow};
 use rustc_data_structures::indexed_vec::Idx;
 use std::borrow::Cow;
 use std::io::{self, Write};
-use super::*;
-use borrow_check::nll::constraints::OutlivesConstraint;
 
 impl<'tcx> RegionInferenceContext<'tcx> {
     /// Write out the region constraint graph.
-    pub(crate) fn dump_graphviz_raw_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
+    crate fn dump_graphviz_raw_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
         dot::render(&RawConstraints { regioncx: self }, &mut w)
     }
+
+    /// Write out the region constraint graph.
+    crate fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
+        let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> = self.constraint_sccs
+            .all_sccs()
+            .map(|_| Vec::new())
+            .collect();
+
+        for region in self.definitions.indices() {
+            let scc = self.constraint_sccs.scc(region);
+            nodes_per_scc[scc].push(region);
+        }
+
+        dot::render(&SccConstraints { regioncx: self, nodes_per_scc }, &mut w)
+    }
 }
 
 struct RawConstraints<'a, 'tcx: 'a> {
-    regioncx: &'a RegionInferenceContext<'tcx>
+    regioncx: &'a RegionInferenceContext<'tcx>,
 }
 
 impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
@@ -63,14 +78,74 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> {
         (&self.regioncx.constraints.raw[..]).into_cow()
     }
 
-    // Render `a: b` as `a <- b`, indicating the flow
+    // Render `a: b` as `a -> b`, indicating the flow
     // of data during inference.
 
     fn source(&'this self, edge: &OutlivesConstraint) -> RegionVid {
-        edge.sub
+        edge.sup
     }
 
     fn target(&'this self, edge: &OutlivesConstraint) -> RegionVid {
-        edge.sup
+        edge.sub
+    }
+}
+
+struct SccConstraints<'a, 'tcx: 'a> {
+    regioncx: &'a RegionInferenceContext<'tcx>,
+    nodes_per_scc: IndexVec<ConstraintSccIndex, Vec<RegionVid>>,
+}
+
+impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> {
+    type Node = ConstraintSccIndex;
+    type Edge = (ConstraintSccIndex, ConstraintSccIndex);
+
+    fn graph_id(&'this self) -> dot::Id<'this> {
+        dot::Id::new(format!("RegionInferenceContext")).unwrap()
+    }
+    fn node_id(&'this self, n: &ConstraintSccIndex) -> dot::Id<'this> {
+        dot::Id::new(format!("r{}", n.index())).unwrap()
+    }
+    fn node_shape(&'this self, _node: &ConstraintSccIndex) -> Option<dot::LabelText<'this>> {
+        Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
+    }
+    fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> {
+        let nodes = &self.nodes_per_scc[*n];
+        dot::LabelText::LabelStr(format!("{:?} = {:?}", n, nodes).into_cow())
+    }
+}
+
+impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for SccConstraints<'a, 'tcx> {
+    type Node = ConstraintSccIndex;
+    type Edge = (ConstraintSccIndex, ConstraintSccIndex);
+
+    fn nodes(&'this self) -> dot::Nodes<'this, ConstraintSccIndex> {
+        let vids: Vec<ConstraintSccIndex> = self.regioncx.constraint_sccs.all_sccs().collect();
+        vids.into_cow()
+    }
+    fn edges(&'this self) -> dot::Edges<'this, (ConstraintSccIndex, ConstraintSccIndex)> {
+        let edges: Vec<_> = self.regioncx
+            .constraint_sccs
+            .all_sccs()
+            .flat_map(|scc_a| {
+                self.regioncx
+                    .constraint_sccs
+                    .successors(scc_a)
+                    .iter()
+                    .map(move |&scc_b| (scc_a, scc_b))
+            })
+            .collect();
+
+        edges.into_cow()
+    }
+
+    // Render `a: b` as `a -> b`, indicating the flow
+    // of data during inference.
+
+    fn source(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
+        edge.0
+    }
+
+    fn target(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
+        edge.1
     }
 }