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_inference`, 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 ty
::{self, TyCtxt}
;
22 use middle
::region
::CodeExtent
;
23 use super::Constraint
;
24 use infer
::SubregionOrigin
;
25 use infer
::region_inference
::RegionVarBindings
;
26 use util
::nodemap
::{FxHashMap, FxHashSet}
;
29 use std
::collections
::hash_map
::Entry
::Vacant
;
33 use std
::io
::prelude
::*;
34 use std
::sync
::atomic
::{AtomicBool, Ordering}
;
37 fn print_help_message() {
39 -Z print-region-graph by default prints a region constraint graph for every \n\
40 function body, to the path `/tmp/constraints.nodeXXX.dot`, where the XXX is \n\
41 replaced with the node id of the function under analysis. \n\
43 To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\
44 where XXX is the node id desired. \n\
46 To generate output to some path other than the default \n\
47 `/tmp/constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`; \n\
48 occurrences of the character `%` in the requested path will be replaced with\n\
49 the node id of the function under analysis. \n\
51 (Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\
52 graphs will be printed. \n\
56 pub fn maybe_print_constraints_for
<'a
, 'gcx
, 'tcx
>(
57 region_vars
: &RegionVarBindings
<'a
, 'gcx
, 'tcx
>,
58 subject_node
: ast
::NodeId
)
60 let tcx
= region_vars
.tcx
;
62 if !region_vars
.tcx
.sess
.opts
.debugging_opts
.print_region_graph
{
66 let requested_node
= env
::var("RUST_REGION_GRAPH_NODE")
67 .ok().and_then(|s
| s
.parse().map(ast
::NodeId
::new
).ok());
69 if requested_node
.is_some() && requested_node
!= Some(subject_node
) {
73 let requested_output
= env
::var("RUST_REGION_GRAPH");
74 debug
!("requested_output: {:?} requested_node: {:?}",
79 let output_template
= match requested_output
{
80 Ok(ref s
) if s
== "help" => {
81 static PRINTED_YET
: AtomicBool
= AtomicBool
::new(false);
82 if !PRINTED_YET
.load(Ordering
::SeqCst
) {
84 PRINTED_YET
.store(true, Ordering
::SeqCst
);
89 Ok(other_path
) => other_path
,
90 Err(_
) => "/tmp/constraints.node%.dot".to_string(),
93 if output_template
.is_empty() {
94 panic
!("empty string provided as RUST_REGION_GRAPH");
97 if output_template
.contains('
%'
) {
98 let mut new_str
= String
::new();
99 for c
in output_template
.chars() {
101 new_str
.push_str(&subject_node
.to_string());
112 let constraints
= &*region_vars
.constraints
.borrow();
113 match dump_region_constraints_to(tcx
, constraints
, &output_path
) {
116 let msg
= format
!("io error dumping region constraints: {}", e
);
117 region_vars
.tcx
.sess
.err(&msg
)
122 struct ConstraintGraph
<'a
, 'gcx
: 'a
+'tcx
, 'tcx
: 'a
> {
123 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
125 map
: &'a FxHashMap
<Constraint
<'tcx
>, SubregionOrigin
<'tcx
>>,
126 node_ids
: FxHashMap
<Node
, usize>,
129 #[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
131 RegionVid(ty
::RegionVid
),
135 // type Edge = Constraint;
136 #[derive(Clone, PartialEq, Eq, Debug, Copy)]
138 Constraint(Constraint
<'tcx
>),
139 EnclScope(CodeExtent
, CodeExtent
),
142 impl<'a
, 'gcx
, 'tcx
> ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
143 fn new(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
145 map
: &'a ConstraintMap
<'tcx
>)
146 -> ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
148 let mut node_ids
= FxHashMap();
150 let mut add_node
= |node
| {
151 if let Vacant(e
) = node_ids
.entry(node
) {
157 for (n1
, n2
) in map
.keys().map(|c
| constraint_to_nodes(c
)) {
162 tcx
.region_maps
.each_encl_scope(|sub
, sup
| {
163 add_node(Node
::Region(ty
::ReScope(*sub
)));
164 add_node(Node
::Region(ty
::ReScope(*sup
)));
177 impl<'a
, 'gcx
, 'tcx
> dot
::Labeller
<'a
> for ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
179 type Edge
= Edge
<'tcx
>;
180 fn graph_id(&self) -> dot
::Id
{
181 dot
::Id
::new(&*self.graph_name
).unwrap()
183 fn node_id(&self, n
: &Node
) -> dot
::Id
{
184 let node_id
= match self.node_ids
.get(n
) {
185 Some(node_id
) => node_id
,
186 None
=> bug
!("no node_id found for node: {:?}", n
),
188 let name
= || format
!("node_{}", node_id
);
189 match dot
::Id
::new(name()) {
192 bug
!("failed to create graphviz node identified by {}", name());
196 fn node_label(&self, n
: &Node
) -> dot
::LabelText
{
198 Node
::RegionVid(n_vid
) => dot
::LabelText
::label(format
!("{:?}", n_vid
)),
199 Node
::Region(n_rgn
) => dot
::LabelText
::label(format
!("{:?}", n_rgn
)),
202 fn edge_label(&self, e
: &Edge
) -> dot
::LabelText
{
204 Edge
::Constraint(ref c
) =>
205 dot
::LabelText
::label(format
!("{:?}", self.map
.get(c
).unwrap())),
206 Edge
::EnclScope(..) => dot
::LabelText
::label(format
!("(enclosed)")),
211 fn constraint_to_nodes(c
: &Constraint
) -> (Node
, Node
) {
213 Constraint
::ConstrainVarSubVar(rv_1
, rv_2
) =>
214 (Node
::RegionVid(rv_1
), Node
::RegionVid(rv_2
)),
215 Constraint
::ConstrainRegSubVar(r_1
, rv_2
) =>
216 (Node
::Region(*r_1
), Node
::RegionVid(rv_2
)),
217 Constraint
::ConstrainVarSubReg(rv_1
, r_2
) =>
218 (Node
::RegionVid(rv_1
), Node
::Region(*r_2
)),
219 Constraint
::ConstrainRegSubReg(r_1
, r_2
) =>
220 (Node
::Region(*r_1
), Node
::Region(*r_2
)),
224 fn edge_to_nodes(e
: &Edge
) -> (Node
, Node
) {
226 Edge
::Constraint(ref c
) => constraint_to_nodes(c
),
227 Edge
::EnclScope(sub
, sup
) => {
228 (Node
::Region(ty
::ReScope(sub
)),
229 Node
::Region(ty
::ReScope(sup
)))
234 impl<'a
, 'gcx
, 'tcx
> dot
::GraphWalk
<'a
> for ConstraintGraph
<'a
, 'gcx
, 'tcx
> {
236 type Edge
= Edge
<'tcx
>;
237 fn nodes(&self) -> dot
::Nodes
<Node
> {
238 let mut set
= FxHashSet();
239 for node
in self.node_ids
.keys() {
242 debug
!("constraint graph has {} nodes", set
.len());
243 set
.into_iter().collect()
245 fn edges(&self) -> dot
::Edges
<Edge
<'tcx
>> {
246 debug
!("constraint graph has {} edges", self.map
.len());
247 let mut v
: Vec
<_
> = self.map
.keys().map(|e
| Edge
::Constraint(*e
)).collect();
248 self.tcx
.region_maps
.each_encl_scope(|sub
, sup
| v
.push(Edge
::EnclScope(*sub
, *sup
)));
249 debug
!("region graph has {} edges", v
.len());
252 fn source(&self, edge
: &Edge
<'tcx
>) -> Node
{
253 let (n1
, _
) = edge_to_nodes(edge
);
254 debug
!("edge {:?} has source {:?}", edge
, n1
);
257 fn target(&self, edge
: &Edge
<'tcx
>) -> Node
{
258 let (_
, n2
) = edge_to_nodes(edge
);
259 debug
!("edge {:?} has target {:?}", edge
, n2
);
264 pub type ConstraintMap
<'tcx
> = FxHashMap
<Constraint
<'tcx
>, SubregionOrigin
<'tcx
>>;
266 fn dump_region_constraints_to
<'a
, 'gcx
, 'tcx
>(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
267 map
: &ConstraintMap
<'tcx
>,
270 debug
!("dump_region_constraints map (len: {}) path: {}",
273 let g
= ConstraintGraph
::new(tcx
, format
!("region_constraints"), map
);
274 debug
!("dump_region_constraints calling render");
275 let mut v
= Vec
::new();
276 dot
::render(&g
, &mut v
).unwrap();
277 File
::create(path
).and_then(|mut f
| f
.write_all(&v
))