Skip to main content

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