rustc_borrowck/region_infer/
graphviz.rs1use 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 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 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 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 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}