rustc_borrowck/region_infer/
graphviz.rs
1use 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 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 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 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 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}