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