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<'tcx>(
30    tcx: TyCtxt<'tcx>,
31    rvid: RegionVid,
32    regioncx: &RegionInferenceContext<'tcx>,
33) -> String {
34    let universe_str = render_universe(regioncx.region_definition(rvid).universe);
35
36    let external_name_str = if let Some(external_name) =
37        regioncx.region_definition(rvid).external_name.and_then(|e| e.get_name(tcx))
38    {
39        format!(" ({external_name})")
40    } else {
41        "".to_string()
42    };
43
44    format!("{:?}{universe_str}{external_name_str}", rvid)
45}
46
47impl<'tcx> RegionInferenceContext<'tcx> {
48    /// Write out the region constraint graph.
49    pub(crate) fn dump_graphviz_raw_constraints(
50        &self,
51        tcx: TyCtxt<'tcx>,
52        mut w: &mut dyn Write,
53    ) -> io::Result<()> {
54        dot::render(&RawConstraints { tcx, regioncx: self }, &mut w)
55    }
56
57    /// Write out the region constraint SCC graph.
58    pub(crate) fn dump_graphviz_scc_constraints(
59        &self,
60        tcx: TyCtxt<'tcx>,
61        mut w: &mut dyn Write,
62    ) -> io::Result<()> {
63        let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> =
64            self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect();
65
66        for region in self.definitions.indices() {
67            let scc = self.constraint_sccs.scc(region);
68            nodes_per_scc[scc].push(region);
69        }
70
71        dot::render(&SccConstraints { tcx, regioncx: self, nodes_per_scc }, &mut w)
72    }
73}
74
75struct RawConstraints<'a, 'tcx> {
76    tcx: TyCtxt<'tcx>,
77    regioncx: &'a RegionInferenceContext<'tcx>,
78}
79
80impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
81    type Node = RegionVid;
82    type Edge = OutlivesConstraint<'tcx>;
83
84    fn graph_id(&'this self) -> dot::Id<'this> {
85        dot::Id::new("RegionInferenceContext").unwrap()
86    }
87    fn node_id(&'this self, n: &RegionVid) -> dot::Id<'this> {
88        dot::Id::new(format!("r{}", n.index())).unwrap()
89    }
90    fn node_shape(&'this self, _node: &RegionVid) -> Option<dot::LabelText<'this>> {
91        Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
92    }
93    fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
94        dot::LabelText::LabelStr(render_region_vid(self.tcx, *n, self.regioncx).into())
95    }
96    fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> {
97        dot::LabelText::LabelStr(render_outlives_constraint(e).into())
98    }
99}
100
101impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> {
102    type Node = RegionVid;
103    type Edge = OutlivesConstraint<'tcx>;
104
105    fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
106        let vids: Vec<RegionVid> = self.regioncx.definitions.indices().collect();
107        vids.into()
108    }
109    fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint<'tcx>> {
110        (&self.regioncx.constraints.outlives().raw[..]).into()
111    }
112
113    // Render `a: b` as `a -> b`, indicating the flow
114    // of data during inference.
115
116    fn source(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
117        edge.sup
118    }
119
120    fn target(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
121        edge.sub
122    }
123}
124
125struct SccConstraints<'a, 'tcx> {
126    tcx: TyCtxt<'tcx>,
127    regioncx: &'a RegionInferenceContext<'tcx>,
128    nodes_per_scc: IndexVec<ConstraintSccIndex, Vec<RegionVid>>,
129}
130
131impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> {
132    type Node = ConstraintSccIndex;
133    type Edge = (ConstraintSccIndex, ConstraintSccIndex);
134
135    fn graph_id(&'this self) -> dot::Id<'this> {
136        dot::Id::new("RegionInferenceContext".to_string()).unwrap()
137    }
138    fn node_id(&'this self, n: &ConstraintSccIndex) -> dot::Id<'this> {
139        dot::Id::new(format!("r{}", n.index())).unwrap()
140    }
141    fn node_shape(&'this self, _node: &ConstraintSccIndex) -> Option<dot::LabelText<'this>> {
142        Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
143    }
144    fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> {
145        let nodes_str = self.nodes_per_scc[*n]
146            .iter()
147            .map(|n| render_region_vid(self.tcx, *n, self.regioncx))
148            .join(", ");
149        dot::LabelText::LabelStr(format!("SCC({n}) = {{{nodes_str}}}", n = n.as_usize()).into())
150    }
151}
152
153impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for SccConstraints<'a, 'tcx> {
154    type Node = ConstraintSccIndex;
155    type Edge = (ConstraintSccIndex, ConstraintSccIndex);
156
157    fn nodes(&'this self) -> dot::Nodes<'this, ConstraintSccIndex> {
158        let vids: Vec<ConstraintSccIndex> = self.regioncx.constraint_sccs.all_sccs().collect();
159        vids.into()
160    }
161    fn edges(&'this self) -> dot::Edges<'this, (ConstraintSccIndex, ConstraintSccIndex)> {
162        let edges: Vec<_> = self
163            .regioncx
164            .constraint_sccs
165            .all_sccs()
166            .flat_map(|scc_a| {
167                self.regioncx
168                    .constraint_sccs
169                    .successors(scc_a)
170                    .iter()
171                    .map(move |&scc_b| (scc_a, scc_b))
172            })
173            .collect();
174
175        edges.into()
176    }
177
178    // Render `a: b` as `a -> b`, indicating the flow
179    // of data during inference.
180
181    fn source(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
182        edge.0
183    }
184
185    fn target(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
186        edge.1
187    }
188}