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