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 if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = constraint.category {
16 format!("{unnameable:?} unnameable")
17 } else {
18 match constraint.locations {
19 Locations::All(_) => "All(...)".to_string(),
20 Locations::Single(loc) => format!("{loc:?}"),
21 }
22 }
23}
24
25fn render_universe(u: UniverseIndex) -> String {
26 if u.is_root() {
27 return "".to_string();
28 }
29
30 format!("/{:?}", u)
31}
32
33fn render_region_vid<'tcx>(
34 tcx: TyCtxt<'tcx>,
35 rvid: RegionVid,
36 regioncx: &RegionInferenceContext<'tcx>,
37) -> String {
38 let universe_str = render_universe(regioncx.region_definition(rvid).universe);
39
40 let external_name_str = if let Some(external_name) =
41 regioncx.region_definition(rvid).external_name.and_then(|e| e.get_name(tcx))
42 {
43 format!(" ({external_name})")
44 } else {
45 "".to_string()
46 };
47
48 let extra_info = match regioncx.region_definition(rvid).origin {
49 NllRegionVariableOrigin::FreeRegion => "".to_string(),
50 NllRegionVariableOrigin::Placeholder(p) => match p.bound.kind {
51 ty::BoundRegionKind::Named(def_id) => {
52 format!(" (for<{}>)", tcx.item_name(def_id))
53 }
54 ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(),
55 ty::BoundRegionKind::NamedAnon(_) => {
56 bug!("only used for pretty printing")
57 }
58 },
59 NllRegionVariableOrigin::Existential { name: Some(name), .. } => format!(" (ex<{name}>)"),
60 NllRegionVariableOrigin::Existential { .. } => format!(" (ex<'_>)"),
61 };
62
63 format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid)
64}
65
66impl<'tcx> RegionInferenceContext<'tcx> {
67 pub(crate) fn dump_graphviz_raw_constraints(
69 &self,
70 tcx: TyCtxt<'tcx>,
71 mut w: &mut dyn Write,
72 ) -> io::Result<()> {
73 dot::render(&RawConstraints { tcx, regioncx: self }, &mut w)
74 }
75
76 pub(crate) fn dump_graphviz_scc_constraints(
78 &self,
79 tcx: TyCtxt<'tcx>,
80 mut w: &mut dyn Write,
81 ) -> io::Result<()> {
82 let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> =
83 self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect();
84
85 for region in self.definitions.indices() {
86 let scc = self.constraint_sccs.scc(region);
87 nodes_per_scc[scc].push(region);
88 }
89
90 dot::render(&SccConstraints { tcx, regioncx: self, nodes_per_scc }, &mut w)
91 }
92}
93
94struct RawConstraints<'a, 'tcx> {
95 tcx: TyCtxt<'tcx>,
96 regioncx: &'a RegionInferenceContext<'tcx>,
97}
98
99impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
100 type Node = RegionVid;
101 type Edge = OutlivesConstraint<'tcx>;
102
103 fn graph_id(&'this self) -> dot::Id<'this> {
104 dot::Id::new("RegionInferenceContext").unwrap()
105 }
106 fn node_id(&'this self, n: &RegionVid) -> dot::Id<'this> {
107 dot::Id::new(format!("r{}", n.index())).unwrap()
108 }
109 fn node_shape(&'this self, _node: &RegionVid) -> Option<dot::LabelText<'this>> {
110 Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
111 }
112 fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
113 dot::LabelText::LabelStr(render_region_vid(self.tcx, *n, self.regioncx).into())
114 }
115 fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> {
116 dot::LabelText::LabelStr(render_outlives_constraint(e).into())
117 }
118}
119
120impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> {
121 type Node = RegionVid;
122 type Edge = OutlivesConstraint<'tcx>;
123
124 fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
125 let vids: Vec<RegionVid> = self.regioncx.definitions.indices().collect();
126 vids.into()
127 }
128 fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint<'tcx>> {
129 (&self.regioncx.constraints.outlives().raw[..]).into()
130 }
131
132 fn source(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
136 edge.sup
137 }
138
139 fn target(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
140 edge.sub
141 }
142}
143
144struct SccConstraints<'a, 'tcx> {
145 tcx: TyCtxt<'tcx>,
146 regioncx: &'a RegionInferenceContext<'tcx>,
147 nodes_per_scc: IndexVec<ConstraintSccIndex, Vec<RegionVid>>,
148}
149
150impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> {
151 type Node = ConstraintSccIndex;
152 type Edge = (ConstraintSccIndex, ConstraintSccIndex);
153
154 fn graph_id(&'this self) -> dot::Id<'this> {
155 dot::Id::new("RegionInferenceContext".to_string()).unwrap()
156 }
157 fn node_id(&'this self, n: &ConstraintSccIndex) -> dot::Id<'this> {
158 dot::Id::new(format!("r{}", n.index())).unwrap()
159 }
160 fn node_shape(&'this self, _node: &ConstraintSccIndex) -> Option<dot::LabelText<'this>> {
161 Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
162 }
163 fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> {
164 let nodes_str = self.nodes_per_scc[*n]
165 .iter()
166 .map(|n| render_region_vid(self.tcx, *n, self.regioncx))
167 .join(", ");
168 dot::LabelText::LabelStr(format!("SCC({n}) = {{{nodes_str}}}", n = n.as_usize()).into())
169 }
170}
171
172impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for SccConstraints<'a, 'tcx> {
173 type Node = ConstraintSccIndex;
174 type Edge = (ConstraintSccIndex, ConstraintSccIndex);
175
176 fn nodes(&'this self) -> dot::Nodes<'this, ConstraintSccIndex> {
177 let vids: Vec<ConstraintSccIndex> = self.regioncx.constraint_sccs.all_sccs().collect();
178 vids.into()
179 }
180 fn edges(&'this self) -> dot::Edges<'this, (ConstraintSccIndex, ConstraintSccIndex)> {
181 let edges: Vec<_> = self
182 .regioncx
183 .constraint_sccs
184 .all_sccs()
185 .flat_map(|scc_a| {
186 self.regioncx
187 .constraint_sccs
188 .successors(scc_a)
189 .iter()
190 .map(move |&scc_b| (scc_a, scc_b))
191 })
192 .collect();
193
194 edges.into()
195 }
196
197 fn source(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
201 edge.0
202 }
203
204 fn target(&'this self, edge: &(ConstraintSccIndex, ConstraintSccIndex)) -> ConstraintSccIndex {
205 edge.1
206 }
207}