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