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.
22 use middle
::region
::CodeExtent
;
23 use super::Constraint
;
24 use middle
::infer
::SubregionOrigin
;
25 use middle
::infer
::region_inference
::RegionVarBindings
;
26 use util
::nodemap
::{FnvHashMap, FnvHashSet}
;
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
, 'tcx
>(region_vars
: &RegionVarBindings
<'a
, 'tcx
>,
57 subject_node
: ast
::NodeId
) {
58 let tcx
= region_vars
.tcx
;
60 if !region_vars
.tcx
.sess
.opts
.debugging_opts
.print_region_graph
{
64 let requested_node
: Option
<ast
::NodeId
> = env
::var("RUST_REGION_GRAPH_NODE")
66 .and_then(|s
| s
.parse().ok());
68 if requested_node
.is_some() && requested_node
!= Some(subject_node
) {
72 let requested_output
= env
::var("RUST_REGION_GRAPH");
73 debug
!("requested_output: {:?} requested_node: {:?}",
78 let output_template
= match requested_output
{
79 Ok(ref s
) if s
== "help" => {
80 static PRINTED_YET
: AtomicBool
= AtomicBool
::new(false);
81 if !PRINTED_YET
.load(Ordering
::SeqCst
) {
83 PRINTED_YET
.store(true, Ordering
::SeqCst
);
88 Ok(other_path
) => other_path
,
89 Err(_
) => "/tmp/constraints.node%.dot".to_string(),
92 if output_template
.is_empty() {
93 tcx
.sess
.bug("empty string provided as RUST_REGION_GRAPH");
96 if output_template
.contains('
%'
) {
97 let mut new_str
= String
::new();
98 for c
in output_template
.chars() {
100 new_str
.push_str(&subject_node
.to_string());
111 let constraints
= &*region_vars
.constraints
.borrow();
112 match dump_region_constraints_to(tcx
, constraints
, &output_path
) {
115 let msg
= format
!("io error dumping region constraints: {}", e
);
116 region_vars
.tcx
.sess
.err(&msg
)
121 struct ConstraintGraph
<'a
, 'tcx
: 'a
> {
122 tcx
: &'a ty
::ctxt
<'tcx
>,
124 map
: &'a FnvHashMap
<Constraint
, SubregionOrigin
<'tcx
>>,
125 node_ids
: FnvHashMap
<Node
, usize>,
128 #[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
130 RegionVid(ty
::RegionVid
),
134 // type Edge = Constraint;
135 #[derive(Clone, PartialEq, Eq, Debug, Copy)]
137 Constraint(Constraint
),
138 EnclScope(CodeExtent
, CodeExtent
),
141 impl<'a
, 'tcx
> ConstraintGraph
<'a
, 'tcx
> {
142 fn new(tcx
: &'a ty
::ctxt
<'tcx
>,
144 map
: &'a ConstraintMap
<'tcx
>)
145 -> ConstraintGraph
<'a
, 'tcx
> {
147 let mut node_ids
= FnvHashMap();
149 let mut add_node
= |node
| {
150 if let Vacant(e
) = node_ids
.entry(node
) {
156 for (n1
, n2
) in map
.keys().map(|c
| constraint_to_nodes(c
)) {
161 tcx
.region_maps
.each_encl_scope(|sub
, sup
| {
162 add_node(Node
::Region(ty
::ReScope(*sub
)));
163 add_node(Node
::Region(ty
::ReScope(*sup
)));
176 impl<'a
, 'tcx
> dot
::Labeller
<'a
, Node
, Edge
> for ConstraintGraph
<'a
, 'tcx
> {
177 fn graph_id(&self) -> dot
::Id
{
178 dot
::Id
::new(&*self.graph_name
).unwrap()
180 fn node_id(&self, n
: &Node
) -> dot
::Id
{
181 let node_id
= match self.node_ids
.get(n
) {
182 Some(node_id
) => node_id
,
183 None
=> panic
!("no node_id found for node: {:?}", n
),
185 let name
= || format
!("node_{}", node_id
);
186 match dot
::Id
::new(name()) {
189 panic
!("failed to create graphviz node identified by {}", name());
193 fn node_label(&self, n
: &Node
) -> dot
::LabelText
{
195 Node
::RegionVid(n_vid
) => dot
::LabelText
::label(format
!("{:?}", n_vid
)),
196 Node
::Region(n_rgn
) => dot
::LabelText
::label(format
!("{:?}", n_rgn
)),
199 fn edge_label(&self, e
: &Edge
) -> dot
::LabelText
{
201 Edge
::Constraint(ref c
) =>
202 dot
::LabelText
::label(format
!("{:?}", self.map
.get(c
).unwrap())),
203 Edge
::EnclScope(..) => dot
::LabelText
::label(format
!("(enclosed)")),
208 fn constraint_to_nodes(c
: &Constraint
) -> (Node
, Node
) {
210 Constraint
::ConstrainVarSubVar(rv_1
, rv_2
) =>
211 (Node
::RegionVid(rv_1
), Node
::RegionVid(rv_2
)),
212 Constraint
::ConstrainRegSubVar(r_1
, rv_2
) => (Node
::Region(r_1
), Node
::RegionVid(rv_2
)),
213 Constraint
::ConstrainVarSubReg(rv_1
, r_2
) => (Node
::RegionVid(rv_1
), Node
::Region(r_2
)),
217 fn edge_to_nodes(e
: &Edge
) -> (Node
, Node
) {
219 Edge
::Constraint(ref c
) => constraint_to_nodes(c
),
220 Edge
::EnclScope(sub
, sup
) => {
221 (Node
::Region(ty
::ReScope(sub
)),
222 Node
::Region(ty
::ReScope(sup
)))
227 impl<'a
, 'tcx
> dot
::GraphWalk
<'a
, Node
, Edge
> for ConstraintGraph
<'a
, 'tcx
> {
228 fn nodes(&self) -> dot
::Nodes
<Node
> {
229 let mut set
= FnvHashSet();
230 for node
in self.node_ids
.keys() {
233 debug
!("constraint graph has {} nodes", set
.len());
234 set
.into_iter().collect()
236 fn edges(&self) -> dot
::Edges
<Edge
> {
237 debug
!("constraint graph has {} edges", self.map
.len());
238 let mut v
: Vec
<_
> = self.map
.keys().map(|e
| Edge
::Constraint(*e
)).collect();
239 self.tcx
.region_maps
.each_encl_scope(|sub
, sup
| v
.push(Edge
::EnclScope(*sub
, *sup
)));
240 debug
!("region graph has {} edges", v
.len());
243 fn source(&self, edge
: &Edge
) -> Node
{
244 let (n1
, _
) = edge_to_nodes(edge
);
245 debug
!("edge {:?} has source {:?}", edge
, n1
);
248 fn target(&self, edge
: &Edge
) -> Node
{
249 let (_
, n2
) = edge_to_nodes(edge
);
250 debug
!("edge {:?} has target {:?}", edge
, n2
);
255 pub type ConstraintMap
<'tcx
> = FnvHashMap
<Constraint
, SubregionOrigin
<'tcx
>>;
257 fn dump_region_constraints_to
<'a
, 'tcx
: 'a
>(tcx
: &'a ty
::ctxt
<'tcx
>,
258 map
: &ConstraintMap
<'tcx
>,
261 debug
!("dump_region_constraints map (len: {}) path: {}",
264 let g
= ConstraintGraph
::new(tcx
, format
!("region_constraints"), map
);
265 debug
!("dump_region_constraints calling render");
266 let mut v
= Vec
::new();
267 dot
::render(&g
, &mut v
).unwrap();
268 File
::create(path
).and_then(|mut f
| f
.write_all(&v
))