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