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