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