1 //! This module provides linkage between libgraphviz traits and
2 //! `rustc::middle::typeck::infer::region_constraints`, generating a
3 //! rendering of the graph represented by the list of `Constraint`
4 //! instances (which make up the edges of the graph), as well as the
5 //! origin for each constraint (which are attached to the labels on
8 /// For clarity, rename the graphviz crate locally to dot.
11 use super::Constraint
;
12 use crate::infer
::region_constraints
::RegionConstraintData
;
13 use crate::infer
::SubregionOrigin
;
14 use rustc
::middle
::free_region
::RegionRelations
;
15 use rustc
::middle
::region
;
17 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
18 use rustc_hir
::def_id
::DefIndex
;
21 use std
::collections
::btree_map
::BTreeMap
;
22 use std
::collections
::hash_map
::Entry
::Vacant
;
26 use std
::sync
::atomic
::{AtomicBool, Ordering}
;
28 fn print_help_message() {
31 -Z print-region-graph by default prints a region constraint graph for every \n\
32 function body, to the path `constraints.nodeXXX.dot`, where the XXX is \n\
33 replaced with the node id of the function under analysis. \n\
35 To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\
36 where XXX is the node id desired. \n\
38 To generate output to some path other than the default \n\
39 `constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`; \n\
40 occurrences of the character `%` in the requested path will be replaced with\n\
41 the node id of the function under analysis. \n\
43 (Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\
44 graphs will be printed. \n\
49 pub fn maybe_print_constraints_for
<'a
, 'tcx
>(
50 region_data
: &RegionConstraintData
<'tcx
>,
51 region_rels
: &RegionRelations
<'a
, 'tcx
>,
53 let tcx
= region_rels
.tcx
;
54 let context
= region_rels
.context
;
56 if !tcx
.sess
.opts
.debugging_opts
.print_region_graph
{
60 let requested_node
= env
::var("RUST_REGION_GRAPH_NODE")
62 .and_then(|s
| s
.parse().map(DefIndex
::from_u32
).ok());
64 if requested_node
.is_some() && requested_node
!= Some(context
.index
) {
68 let requested_output
= env
::var("RUST_REGION_GRAPH");
69 debug
!("requested_output: {:?} requested_node: {:?}", requested_output
, requested_node
);
72 let output_template
= match requested_output
{
73 Ok(ref s
) if s
== "help" => {
74 static PRINTED_YET
: AtomicBool
= AtomicBool
::new(false);
75 if !PRINTED_YET
.load(Ordering
::SeqCst
) {
77 PRINTED_YET
.store(true, Ordering
::SeqCst
);
82 Ok(other_path
) => other_path
,
83 Err(_
) => "constraints.node%.dot".to_string(),
86 if output_template
.is_empty() {
87 panic
!("empty string provided as RUST_REGION_GRAPH");
90 if output_template
.contains('
%'
) {
91 let mut new_str
= String
::new();
92 for c
in output_template
.chars() {
94 new_str
.push_str(&context
.index
.as_u32().to_string());
105 if let Err(e
) = dump_region_data_to(region_rels
, ®ion_data
.constraints
, &output_path
) {
106 let msg
= format
!("io error dumping region constraints: {}", e
);
111 struct ConstraintGraph
<'a
, 'tcx
> {
113 region_rels
: &'a RegionRelations
<'a
, 'tcx
>,
114 map
: &'a BTreeMap
<Constraint
<'tcx
>, SubregionOrigin
<'tcx
>>,
115 node_ids
: FxHashMap
<Node
, usize>,
118 #[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
120 RegionVid(ty
::RegionVid
),
121 Region(ty
::RegionKind
),
124 #[derive(Clone, PartialEq, Eq, Debug, Copy)]
126 Constraint(Constraint
<'tcx
>),
127 EnclScope(region
::Scope
, region
::Scope
),
130 impl<'a
, 'tcx
> ConstraintGraph
<'a
, 'tcx
> {
133 region_rels
: &'a RegionRelations
<'a
, 'tcx
>,
134 map
: &'a ConstraintMap
<'tcx
>,
135 ) -> ConstraintGraph
<'a
, 'tcx
> {
137 let mut node_ids
= FxHashMap
::default();
139 let mut add_node
= |node
| {
140 if let Vacant(e
) = node_ids
.entry(node
) {
146 for (n1
, n2
) in map
.keys().map(|c
| constraint_to_nodes(c
)) {
151 region_rels
.region_scope_tree
.each_encl_scope(|sub
, sup
| {
152 add_node(Node
::Region(ty
::ReScope(sub
)));
153 add_node(Node
::Region(ty
::ReScope(sup
)));
157 ConstraintGraph { map, node_ids, region_rels, graph_name: name }
161 impl<'a
, 'tcx
> dot
::Labeller
<'a
> for ConstraintGraph
<'a
, 'tcx
> {
163 type Edge
= Edge
<'tcx
>;
164 fn graph_id(&self) -> dot
::Id
<'_
> {
165 dot
::Id
::new(&*self.graph_name
).unwrap()
167 fn node_id(&self, n
: &Node
) -> dot
::Id
<'_
> {
168 let node_id
= match self.node_ids
.get(n
) {
169 Some(node_id
) => node_id
,
170 None
=> bug
!("no node_id found for node: {:?}", n
),
172 let name
= || format
!("node_{}", node_id
);
175 .unwrap_or_else(|_
| bug
!("failed to create graphviz node identified by {}", name()))
177 fn node_label(&self, n
: &Node
) -> dot
::LabelText
<'_
> {
179 Node
::RegionVid(n_vid
) => dot
::LabelText
::label(format
!("{:?}", n_vid
)),
180 Node
::Region(n_rgn
) => dot
::LabelText
::label(format
!("{:?}", n_rgn
)),
183 fn edge_label(&self, e
: &Edge
<'_
>) -> dot
::LabelText
<'_
> {
185 Edge
::Constraint(ref c
) => {
186 dot
::LabelText
::label(format
!("{:?}", self.map
.get(c
).unwrap()))
188 Edge
::EnclScope(..) => dot
::LabelText
::label("(enclosed)".to_owned()),
193 fn constraint_to_nodes(c
: &Constraint
<'_
>) -> (Node
, Node
) {
195 Constraint
::VarSubVar(rv_1
, rv_2
) => (Node
::RegionVid(rv_1
), Node
::RegionVid(rv_2
)),
196 Constraint
::RegSubVar(r_1
, rv_2
) => (Node
::Region(*r_1
), Node
::RegionVid(rv_2
)),
197 Constraint
::VarSubReg(rv_1
, r_2
) => (Node
::RegionVid(rv_1
), Node
::Region(*r_2
)),
198 Constraint
::RegSubReg(r_1
, r_2
) => (Node
::Region(*r_1
), Node
::Region(*r_2
)),
202 fn edge_to_nodes(e
: &Edge
<'_
>) -> (Node
, Node
) {
204 Edge
::Constraint(ref c
) => constraint_to_nodes(c
),
205 Edge
::EnclScope(sub
, sup
) => {
206 (Node
::Region(ty
::ReScope(sub
)), Node
::Region(ty
::ReScope(sup
)))
211 impl<'a
, 'tcx
> dot
::GraphWalk
<'a
> for ConstraintGraph
<'a
, 'tcx
> {
213 type Edge
= Edge
<'tcx
>;
214 fn nodes(&self) -> dot
::Nodes
<'_
, Node
> {
215 let set
= self.node_ids
.keys().cloned().collect
::<FxHashSet
<_
>>();
216 debug
!("constraint graph has {} nodes", set
.len());
217 set
.into_iter().collect()
219 fn edges(&self) -> dot
::Edges
<'_
, Edge
<'tcx
>> {
220 debug
!("constraint graph has {} edges", self.map
.len());
221 let mut v
: Vec
<_
> = self.map
.keys().map(|e
| Edge
::Constraint(*e
)).collect();
224 .each_encl_scope(|sub
, sup
| v
.push(Edge
::EnclScope(sub
, sup
)));
225 debug
!("region graph has {} edges", v
.len());
228 fn source(&self, edge
: &Edge
<'tcx
>) -> Node
{
229 let (n1
, _
) = edge_to_nodes(edge
);
230 debug
!("edge {:?} has source {:?}", edge
, n1
);
233 fn target(&self, edge
: &Edge
<'tcx
>) -> Node
{
234 let (_
, n2
) = edge_to_nodes(edge
);
235 debug
!("edge {:?} has target {:?}", edge
, n2
);
240 pub type ConstraintMap
<'tcx
> = BTreeMap
<Constraint
<'tcx
>, SubregionOrigin
<'tcx
>>;
242 fn dump_region_data_to
<'a
, 'tcx
>(
243 region_rels
: &RegionRelations
<'a
, 'tcx
>,
244 map
: &ConstraintMap
<'tcx
>,
246 ) -> io
::Result
<()> {
247 debug
!("dump_region_data map (len: {}) path: {}", map
.len(), path
);
248 let g
= ConstraintGraph
::new("region_data".to_string(), region_rels
, map
);
249 debug
!("dump_region_data calling render");
250 let mut v
= Vec
::new();
251 dot
::render(&g
, &mut v
).unwrap();