rustc_borrowck/region_infer/
graphviz.rs

1//! This module provides linkage between RegionInferenceContext and
2//! `rustc_graphviz` traits, specialized to attaching borrowck analysis
3//! data to rendered labels.
4
5use std::borrow::Cow;
6use std::io::{self, Write};
7
8use itertools::Itertools;
9use rustc_graphviz as dot;
10use rustc_middle::ty::UniverseIndex;
11
12use super::*;
13
14fn render_outlives_constraint(constraint: &OutlivesConstraint<'_>) -> String {
15    match constraint.locations {
16        Locations::All(_) => "All(...)".to_string(),
17        Locations::Single(loc) => format!("{loc:?}"),
18    }
19}
20
21fn render_universe(u: UniverseIndex) -> String {
22    if u.is_root() {
23        return "".to_string();
24    }
25
26    format!("/{:?}", u)
27}
28
29fn render_region_vid(rvid: RegionVid, regioncx: &RegionInferenceContext<'_>) -> String {
30    let universe_str = render_universe(regioncx.region_definition(rvid).universe);
31
32    let external_name_str = if let Some(external_name) =
33        regioncx.region_definition(rvid).external_name.and_then(|e| e.get_name())
34    {
35        format!(" ({external_name})")
36    } else {
37        "".to_string()
38    };
39
40    format!("{:?}{universe_str}{external_name_str}", rvid)
41}
42
43impl<'tcx> RegionInferenceContext<'tcx> {
44    /// Write out the region constraint graph.
45    pub(crate) fn dump_graphviz_raw_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
46        dot::render(&RawConstraints { regioncx: self }, &mut w)
47    }
48
49    /// Write out the region constraint SCC graph.
50    pub(crate) fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
51        let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> =
52            self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect();
53
54        for region in self.definitions.indices() {
55            let scc = self.constraint_sccs.scc(region);
56            nodes_per_scc[scc].push(region);
57        }
58
59        dot::render(&SccConstraints { regioncx: self, nodes_per_scc }, &mut w)
60    }
61}
62
63struct RawConstraints<'a, 'tcx> {
64    regioncx: &'a RegionInferenceContext<'tcx>,
65}
66
67impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
68    type Node = RegionVid;
69    type Edge = OutlivesConstraint<'tcx>;
70
71    fn graph_id(&'this self) -> dot::Id<'this> {
72        dot::Id::new("RegionInferenceContext").unwrap()
73    }
74    fn node_id(&'this self, n: &RegionVid) -> dot::Id<'this> {
75        dot::Id::new(format!("r{}", n.index())).unwrap()
76    }
77    fn node_shape(&'this self, _node: &RegionVid) -> Option<dot::LabelText<'this>> {
78        Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
79    }
80    fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
81        dot::LabelText::LabelStr(render_region_vid(*n, self.regioncx).into())
82    }
83    fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> {
84        dot::LabelText::LabelStr(render_outlives_constraint(e).into())
85    }
86}
87
88impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> {
89    type Node = RegionVid;
90    type Edge = OutlivesConstraint<'tcx>;
91
92    fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
93        let vids: Vec<RegionVid> = self.regioncx.definitions.indices().collect();
94        vids.into()
95    }
96    fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint<'tcx>> {
97        (&self.regioncx.constraints.outlives().raw[..]).into()
98    }
99
100    // Render `a: b` as `a -> b`, indicating the flow
101    // of data during inference.
102
103    fn source(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
104        edge.sup
105    }
106
107    fn target(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
108        edge.sub
109    }
110}
111
112struct SccConstraints<'a, 'tcx> {
113    regioncx: &'a RegionInferenceContext<'tcx>,
114    nodes_per_scc: IndexVec<ConstraintSccIndex, Vec<RegionVid>>,
115}
116
117impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> {
118    type Node = ConstraintSccIndex;
119    type Edge = (ConstraintSccIndex, ConstraintSccIndex);
120
121    fn graph_id(&'this self) -> dot::Id<'this> {
122        dot::Id::new("RegionInferenceContext".to_string()).unwrap()
123    }
124    fn node_id(&'this self, n: &ConstraintSccIndex) -> dot::Id<'this> {
125        dot::Id::new(format!("r{}", n.index())).unwrap()
126    }
127    fn node_shape(&'this self, _node: &ConstraintSccIndex) -> Option<dot::LabelText<'this>> {
128        Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
129    }
130    fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> {
131        let nodes_str =
132            self.nodes_per_scc[*n].iter().map(|n| render_region_vid(*n, self.regioncx)).join(", ");
133        dot::LabelText::LabelStr(format!("SCC({n}) = {{{nodes_str}}}", n = n.as_usize()).into())
134    }
135}
136
137impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for SccConstraints<'a, 'tcx> {
138    type Node = ConstraintSccIndex;
139    type Edge = (ConstraintSccIndex, ConstraintSccIndex);
140
141    fn nodes(&'this self) -> dot::Nodes<'this, ConstraintSccIndex> {
142        let vids: Vec<ConstraintSccIndex> = self.regioncx.constraint_sccs.all_sccs().collect();
143        vids.into()
144    }
145    fn edges(&'this self) -> dot::Edges<'this, (ConstraintSccIndex, ConstraintSccIndex)> {
146        let edges: Vec<_> = self
147            .regioncx
148            .constraint_sccs
149            .all_sccs()
150            .flat_map(|scc_a| {
151                self.regioncx
152                    .constraint_sccs
153                    .successors(scc_a)
154                    .iter()
155                    .map(move |&scc_b| (scc_a, scc_b))
156            })
157            .collect();
158
159        edges.into()
160    }
161
162    // Render `a: b` as `a -> b`, indicating the flow
163    // of data during inference.
164
165    fn source(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
166        edge.0
167    }
168
169    fn target(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
170        edge.1
171    }
172}