1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! This module provides linkage between libgraphviz traits and
12 //! `rustc::middle::typeck::infer::region_constraints`, generating a
13 //! rendering of the graph represented by the list of `Constraint`
14 //! instances (which make up the edges of the graph), as well as the
15 //! origin for each constraint (which are attached to the labels on
18 /// For clarity, rename the graphviz crate locally to dot.
21 use hir
::def_id
::DefIndex
;
23 use middle
::free_region
::RegionRelations
;
25 use super::Constraint
;
26 use infer
::SubregionOrigin
;
27 use infer
::region_constraints
::RegionConstraintData
;
28 use util
::nodemap
::{FxHashMap, FxHashSet}
;
31 use std
::collections
::hash_map
::Entry
::Vacant
;
32 use std
::collections
::btree_map
::BTreeMap
;
36 use std
::io
::prelude
::*;
37 use std
::sync
::atomic
::{AtomicBool, Ordering}
;
39 fn print_help_message() {
41 -Z print-region-graph by default prints a region constraint graph for every \n\
42 function body, to the path `constraints.nodeXXX.dot`, where the XXX is \n\
43 replaced with the node id of the function under analysis. \n\
45 To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\
46 where XXX is the node id desired. \n\
48 To generate output to some path other than the default \n\
49 `constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`; \n\
50 occurrences of the character `%` in the requested path will be replaced with\n\
51 the node id of the function under analysis. \n\
53 (Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\
54 graphs will be printed. \n\
58 pub fn maybe_print_constraints_for
<'a
, 'gcx
, 'tcx
>(
59 region_data
: &RegionConstraintData
<'tcx
>,
60 region_rels
: &RegionRelations
<'a
, 'gcx
, 'tcx
>)
62 let tcx
= region_rels
.tcx
;
63 let context
= region_rels
.context
;
65 if !tcx
.sess
.opts
.debugging_opts
.print_region_graph
{
69 let requested_node
= env
::var("RUST_REGION_GRAPH_NODE")
70 .ok().and_then(|s
| s
.parse().map(DefIndex
::from_raw_u32
).ok());
72 if requested_node
.is_some() && requested_node
!= Some(context
.index
) {
76 let requested_output
= env
::var("RUST_REGION_GRAPH");
77 debug
!("requested_output: {:?} requested_node: {:?}",
82 let output_template
= match requested_output
{
83 Ok(ref s
) if s
== "help" => {
84 static PRINTED_YET
: AtomicBool
= AtomicBool
::new(false);
85 if !PRINTED_YET
.load(Ordering
::SeqCst
) {
87 PRINTED_YET
.store(true, Ordering
::SeqCst
);
92 Ok(other_path
) => other_path
,
93 Err(_
) => "constraints.node%.dot".to_string(),
96 if output_template
.is_empty() {
97 panic
!("empty string provided as RUST_REGION_GRAPH");
100 if output_template
.contains('
%'
) {
101 let mut new_str
= String
::new();
102 for c
in output_template
.chars() {
104 new_str
.push_str(&context
.index
.as_raw_u32().to_string());
115 if let Err(e
) = dump_region_data_to(region_rels
, ®ion_data
.constraints
, &output_path
) {
116 let msg
= format
!("io error dumping region constraints: {}", e
);
121 struct ConstraintGraph
<'a
, 'gcx
: 'a
+'tcx
, 'tcx
: 'a
> {
123 region_rels
: &'a RegionRelations
<'a
, 'gcx
, 'tcx
>,
124 map
: &'a BTreeMap
<Constraint
<'tcx
>, SubregionOrigin
<'tcx
>>,
125 node_ids
: FxHashMap
<Node
, usize>,
128 #[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
130 RegionVid(ty
::RegionVid
),
131 Region(ty
::RegionKind
),
134 #[derive(Clone, PartialEq, Eq, Debug, Copy)]
136 Constraint(Constraint
<'tcx
>),
137 EnclScope(region
::Scope
, region
::Scope
),
140 impl<'a
, 'gcx
, 'tcx
> ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
142 region_rels
: &'a RegionRelations
<'a
, 'gcx
, 'tcx
>,
143 map
: &'a ConstraintMap
<'tcx
>)
144 -> ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
146 let mut node_ids
= FxHashMap
::default();
148 let mut add_node
= |node
| {
149 if let Vacant(e
) = node_ids
.entry(node
) {
155 for (n1
, n2
) in map
.keys().map(|c
| constraint_to_nodes(c
)) {
160 region_rels
.region_scope_tree
.each_encl_scope(|sub
, sup
| {
161 add_node(Node
::Region(ty
::ReScope(sub
)));
162 add_node(Node
::Region(ty
::ReScope(sup
)));
175 impl<'a
, 'gcx
, 'tcx
> dot
::Labeller
<'a
> for ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
177 type Edge
= Edge
<'tcx
>;
178 fn graph_id(&self) -> dot
::Id
<'_
> {
179 dot
::Id
::new(&*self.graph_name
).unwrap()
181 fn node_id(&self, n
: &Node
) -> dot
::Id
<'_
> {
182 let node_id
= match self.node_ids
.get(n
) {
183 Some(node_id
) => node_id
,
184 None
=> bug
!("no node_id found for node: {:?}", n
),
186 let name
= || format
!("node_{}", node_id
);
188 dot
::Id
::new(name()).unwrap_or_else(|_
|
189 bug
!("failed to create graphviz node identified by {}", name()))
191 fn node_label(&self, n
: &Node
) -> dot
::LabelText
<'_
> {
193 Node
::RegionVid(n_vid
) => dot
::LabelText
::label(format
!("{:?}", n_vid
)),
194 Node
::Region(n_rgn
) => dot
::LabelText
::label(format
!("{:?}", n_rgn
)),
197 fn edge_label(&self, e
: &Edge
<'_
>) -> dot
::LabelText
<'_
> {
199 Edge
::Constraint(ref c
) =>
200 dot
::LabelText
::label(format
!("{:?}", self.map
.get(c
).unwrap())),
201 Edge
::EnclScope(..) => dot
::LabelText
::label("(enclosed)".to_owned()),
206 fn constraint_to_nodes(c
: &Constraint
<'_
>) -> (Node
, Node
) {
208 Constraint
::VarSubVar(rv_1
, rv_2
) =>
209 (Node
::RegionVid(rv_1
), Node
::RegionVid(rv_2
)),
210 Constraint
::RegSubVar(r_1
, rv_2
) =>
211 (Node
::Region(*r_1
), Node
::RegionVid(rv_2
)),
212 Constraint
::VarSubReg(rv_1
, r_2
) =>
213 (Node
::RegionVid(rv_1
), Node
::Region(*r_2
)),
214 Constraint
::RegSubReg(r_1
, r_2
) =>
215 (Node
::Region(*r_1
), Node
::Region(*r_2
)),
219 fn edge_to_nodes(e
: &Edge
<'_
>) -> (Node
, Node
) {
221 Edge
::Constraint(ref c
) => constraint_to_nodes(c
),
222 Edge
::EnclScope(sub
, sup
) => {
223 (Node
::Region(ty
::ReScope(sub
)),
224 Node
::Region(ty
::ReScope(sup
)))
229 impl<'a
, 'gcx
, 'tcx
> dot
::GraphWalk
<'a
> for ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
231 type Edge
= Edge
<'tcx
>;
232 fn nodes(&self) -> dot
::Nodes
<'_
, Node
> {
233 let mut set
= FxHashSet
::default();
234 for node
in self.node_ids
.keys() {
237 debug
!("constraint graph has {} nodes", set
.len());
238 set
.into_iter().collect()
240 fn edges(&self) -> dot
::Edges
<'_
, Edge
<'tcx
>> {
241 debug
!("constraint graph has {} edges", self.map
.len());
242 let mut v
: Vec
<_
> = self.map
.keys().map(|e
| Edge
::Constraint(*e
)).collect();
243 self.region_rels
.region_scope_tree
.each_encl_scope(|sub
, sup
| {
244 v
.push(Edge
::EnclScope(sub
, sup
))
246 debug
!("region graph has {} edges", v
.len());
249 fn source(&self, edge
: &Edge
<'tcx
>) -> Node
{
250 let (n1
, _
) = edge_to_nodes(edge
);
251 debug
!("edge {:?} has source {:?}", edge
, n1
);
254 fn target(&self, edge
: &Edge
<'tcx
>) -> Node
{
255 let (_
, n2
) = edge_to_nodes(edge
);
256 debug
!("edge {:?} has target {:?}", edge
, n2
);
261 pub type ConstraintMap
<'tcx
> = BTreeMap
<Constraint
<'tcx
>, SubregionOrigin
<'tcx
>>;
263 fn dump_region_data_to
<'a
, 'gcx
, 'tcx
>(region_rels
: &RegionRelations
<'a
, 'gcx
, 'tcx
>,
264 map
: &ConstraintMap
<'tcx
>,
267 debug
!("dump_region_data map (len: {}) path: {}",
270 let g
= ConstraintGraph
::new("region_data".to_string(), region_rels
, map
);
271 debug
!("dump_region_data calling render");
272 let mut v
= Vec
::new();
273 dot
::render(&g
, &mut v
).unwrap();
274 File
::create(path
).and_then(|mut f
| f
.write_all(&v
))