]> git.proxmox.com Git - rustc.git/blame - src/librustc/middle/infer/region_inference/graphviz.rs
Imported Upstream version 1.0.0-alpha.2
[rustc.git] / src / librustc / middle / infer / region_inference / graphviz.rs
CommitLineData
1a4d82fc
JJ
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.
4//
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.
10
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
16//! each edge).
17
18/// For clarity, rename the graphviz crate locally to dot.
19use graphviz as dot;
20
21use middle::ty;
85aaf69f 22use middle::region::CodeExtent;
1a4d82fc
JJ
23use super::Constraint;
24use middle::infer::SubregionOrigin;
25use middle::infer::region_inference::RegionVarBindings;
26use util::nodemap::{FnvHashMap, FnvHashSet};
27use util::ppaux::Repr;
28
85aaf69f 29use std::borrow::Cow;
1a4d82fc 30use std::collections::hash_map::Entry::Vacant;
85aaf69f
SL
31use std::old_io::{self, File};
32use std::env;
1a4d82fc
JJ
33use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
34use syntax::ast;
35
36fn print_help_message() {
37 println!("\
38-Z print-region-graph by default prints a region constraint graph for every \n\
39function body, to the path `/tmp/constraints.nodeXXX.dot`, where the XXX is \n\
40replaced with the node id of the function under analysis. \n\
41 \n\
42To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\
43where XXX is the node id desired. \n\
44 \n\
45To generate output to some path other than the default \n\
46`/tmp/constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`; \n\
47occurrences of the character `%` in the requested path will be replaced with\n\
48the node id of the function under analysis. \n\
49 \n\
50(Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\
51graphs will be printed. \n\
52");
53}
54
55pub fn maybe_print_constraints_for<'a, 'tcx>(region_vars: &RegionVarBindings<'a, 'tcx>,
56 subject_node: ast::NodeId) {
57 let tcx = region_vars.tcx;
58
59 if !region_vars.tcx.sess.opts.debugging_opts.print_region_graph {
60 return;
61 }
62
63 let requested_node : Option<ast::NodeId> =
85aaf69f 64 env::var("RUST_REGION_GRAPH_NODE").ok().and_then(|s| s.parse().ok());
1a4d82fc
JJ
65
66 if requested_node.is_some() && requested_node != Some(subject_node) {
67 return;
68 }
69
85aaf69f 70 let requested_output = env::var("RUST_REGION_GRAPH").ok();
1a4d82fc
JJ
71 debug!("requested_output: {:?} requested_node: {:?}",
72 requested_output, requested_node);
73
74 let output_path = {
75 let output_template = match requested_output {
85aaf69f 76 Some(ref s) if &**s == "help" => {
1a4d82fc
JJ
77 static PRINTED_YET: AtomicBool = ATOMIC_BOOL_INIT;
78 if !PRINTED_YET.load(Ordering::SeqCst) {
79 print_help_message();
80 PRINTED_YET.store(true, Ordering::SeqCst);
81 }
82 return;
83 }
84
85 Some(other_path) => other_path,
86 None => "/tmp/constraints.node%.dot".to_string(),
87 };
88
89 if output_template.len() == 0 {
90 tcx.sess.bug("empty string provided as RUST_REGION_GRAPH");
91 }
92
93 if output_template.contains_char('%') {
94 let mut new_str = String::new();
95 for c in output_template.chars() {
96 if c == '%' {
85aaf69f 97 new_str.push_str(&subject_node.to_string());
1a4d82fc
JJ
98 } else {
99 new_str.push(c);
100 }
101 }
102 new_str
103 } else {
104 output_template
105 }
106 };
107
108 let constraints = &*region_vars.constraints.borrow();
85aaf69f 109 match dump_region_constraints_to(tcx, constraints, &output_path) {
1a4d82fc
JJ
110 Ok(()) => {}
111 Err(e) => {
112 let msg = format!("io error dumping region constraints: {}", e);
85aaf69f 113 region_vars.tcx.sess.err(&msg)
1a4d82fc
JJ
114 }
115 }
116}
117
118struct ConstraintGraph<'a, 'tcx: 'a> {
119 tcx: &'a ty::ctxt<'tcx>,
120 graph_name: String,
121 map: &'a FnvHashMap<Constraint, SubregionOrigin<'tcx>>,
122 node_ids: FnvHashMap<Node, uint>,
123}
124
85aaf69f 125#[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
1a4d82fc
JJ
126enum Node {
127 RegionVid(ty::RegionVid),
128 Region(ty::Region),
129}
130
85aaf69f
SL
131// type Edge = Constraint;
132#[derive(Clone, PartialEq, Eq, Debug, Copy)]
133enum Edge {
134 Constraint(Constraint),
135 EnclScope(CodeExtent, CodeExtent),
136}
1a4d82fc
JJ
137
138impl<'a, 'tcx> ConstraintGraph<'a, 'tcx> {
139 fn new(tcx: &'a ty::ctxt<'tcx>,
140 name: String,
141 map: &'a ConstraintMap<'tcx>) -> ConstraintGraph<'a, 'tcx> {
142 let mut i = 0;
85aaf69f 143 let mut node_ids = FnvHashMap();
1a4d82fc 144 {
85aaf69f 145 let mut add_node = |node| {
1a4d82fc
JJ
146 if let Vacant(e) = node_ids.entry(node) {
147 e.insert(i);
148 i += 1;
149 }
150 };
151
152 for (n1, n2) in map.keys().map(|c|constraint_to_nodes(c)) {
153 add_node(n1);
154 add_node(n2);
155 }
85aaf69f
SL
156
157 tcx.region_maps.each_encl_scope(|sub, sup| {
158 add_node(Node::Region(ty::ReScope(*sub)));
159 add_node(Node::Region(ty::ReScope(*sup)));
160 });
1a4d82fc
JJ
161 }
162
163 ConstraintGraph { tcx: tcx,
164 graph_name: name,
165 map: map,
166 node_ids: node_ids }
167 }
168}
169
170impl<'a, 'tcx> dot::Labeller<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
171 fn graph_id(&self) -> dot::Id {
85aaf69f 172 dot::Id::new(&*self.graph_name).ok().unwrap()
1a4d82fc
JJ
173 }
174 fn node_id(&self, n: &Node) -> dot::Id {
85aaf69f
SL
175 let node_id = match self.node_ids.get(n) {
176 Some(node_id) => node_id,
177 None => panic!("no node_id found for node: {:?}", n),
178 };
179 let name = || format!("node_{}", node_id);
180 match dot::Id::new(name()) {
181 Ok(id) => id,
182 Err(_) => {
183 panic!("failed to create graphviz node identified by {}", name());
184 }
185 }
1a4d82fc
JJ
186 }
187 fn node_label(&self, n: &Node) -> dot::LabelText {
188 match *n {
189 Node::RegionVid(n_vid) =>
190 dot::LabelText::label(format!("{:?}", n_vid)),
191 Node::Region(n_rgn) =>
192 dot::LabelText::label(format!("{}", n_rgn.repr(self.tcx))),
193 }
194 }
195 fn edge_label(&self, e: &Edge) -> dot::LabelText {
85aaf69f
SL
196 match *e {
197 Edge::Constraint(ref c) =>
198 dot::LabelText::label(format!("{}", self.map.get(c).unwrap().repr(self.tcx))),
199 Edge::EnclScope(..) =>
200 dot::LabelText::label(format!("(enclosed)")),
201 }
1a4d82fc
JJ
202 }
203}
204
205fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
206 match *c {
207 Constraint::ConstrainVarSubVar(rv_1, rv_2) => (Node::RegionVid(rv_1),
208 Node::RegionVid(rv_2)),
209 Constraint::ConstrainRegSubVar(r_1, rv_2) => (Node::Region(r_1),
210 Node::RegionVid(rv_2)),
211 Constraint::ConstrainVarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1),
212 Node::Region(r_2)),
213 }
214}
215
85aaf69f
SL
216fn edge_to_nodes(e: &Edge) -> (Node, Node) {
217 match *e {
218 Edge::Constraint(ref c) => constraint_to_nodes(c),
219 Edge::EnclScope(sub, sup) => {
220 (Node::Region(ty::ReScope(sub)), Node::Region(ty::ReScope(sup)))
221 }
222 }
223}
224
1a4d82fc
JJ
225impl<'a, 'tcx> dot::GraphWalk<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
226 fn nodes(&self) -> dot::Nodes<Node> {
85aaf69f
SL
227 let mut set = FnvHashSet();
228 for node in self.node_ids.keys() {
229 set.insert(*node);
1a4d82fc
JJ
230 }
231 debug!("constraint graph has {} nodes", set.len());
232 set.into_iter().collect()
233 }
234 fn edges(&self) -> dot::Edges<Edge> {
235 debug!("constraint graph has {} edges", self.map.len());
85aaf69f
SL
236 let mut v : Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect();
237 self.tcx.region_maps.each_encl_scope(|sub, sup| {
238 v.push(Edge::EnclScope(*sub, *sup))
239 });
240 debug!("region graph has {} edges", v.len());
241 Cow::Owned(v)
1a4d82fc
JJ
242 }
243 fn source(&self, edge: &Edge) -> Node {
85aaf69f 244 let (n1, _) = edge_to_nodes(edge);
1a4d82fc
JJ
245 debug!("edge {:?} has source {:?}", edge, n1);
246 n1
247 }
248 fn target(&self, edge: &Edge) -> Node {
85aaf69f 249 let (_, n2) = edge_to_nodes(edge);
1a4d82fc
JJ
250 debug!("edge {:?} has target {:?}", edge, n2);
251 n2
252 }
253}
254
255pub type ConstraintMap<'tcx> = FnvHashMap<Constraint, SubregionOrigin<'tcx>>;
256
257fn dump_region_constraints_to<'a, 'tcx:'a >(tcx: &'a ty::ctxt<'tcx>,
258 map: &ConstraintMap<'tcx>,
85aaf69f 259 path: &str) -> old_io::IoResult<()> {
1a4d82fc
JJ
260 debug!("dump_region_constraints map (len: {}) path: {}", map.len(), path);
261 let g = ConstraintGraph::new(tcx, format!("region_constraints"), map);
262 let mut f = File::create(&Path::new(path));
263 debug!("dump_region_constraints calling render");
264 dot::render(&g, &mut f)
265}