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.
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.
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
18 /// For clarity, rename the graphviz crate locally to dot.
21 use hir::def_id::DefIndex;
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};
31 use std::collections::hash_map::Entry::Vacant;
35 use std::io::prelude::*;
36 use std::sync::atomic::{AtomicBool, Ordering};
38 fn print_help_message() {
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\
44 To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\
45 where XXX is the node id desired. \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\
52 (Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\
53 graphs will be printed. \n\
57 pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>(
58 region_vars: &RegionVarBindings<'a, 'gcx, 'tcx>,
59 region_rels: &RegionRelations<'a, 'gcx, 'tcx>)
61 let context = region_rels.context;
63 if !region_vars.tcx.sess.opts.debugging_opts.print_region_graph {
67 let requested_node = env::var("RUST_REGION_GRAPH_NODE")
68 .ok().and_then(|s| s.parse().map(DefIndex::new).ok());
70 if requested_node.is_some() && requested_node != Some(context.index) {
74 let requested_output = env::var("RUST_REGION_GRAPH");
75 debug!("requested_output: {:?} requested_node: {:?}",
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) {
85 PRINTED_YET.store(true, Ordering::SeqCst);
90 Ok(other_path) => other_path,
91 Err(_) => "/tmp/constraints.node%.dot".to_string(),
94 if output_template.is_empty() {
95 panic!("empty string provided as RUST_REGION_GRAPH");
98 if output_template.contains('%') {
99 let mut new_str = String::new();
100 for c in output_template.chars() {
102 new_str.push_str(&context.index.as_usize().to_string());
113 let constraints = &*region_vars.constraints.borrow();
114 match dump_region_constraints_to(region_rels, constraints, &output_path) {
117 let msg = format!("io error dumping region constraints: {}", e);
118 region_vars.tcx.sess.err(&msg)
123 struct ConstraintGraph<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
125 region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
126 map: &'a FxHashMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
127 node_ids: FxHashMap<Node<'tcx>, usize>,
130 #[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
132 RegionVid(ty::RegionVid),
133 Region(ty::RegionKind<'tcx>),
136 // type Edge = Constraint;
137 #[derive(Clone, PartialEq, Eq, Debug, Copy)]
139 Constraint(Constraint<'tcx>),
140 EnclScope(CodeExtent<'tcx>, CodeExtent<'tcx>),
143 impl<'a, 'gcx, 'tcx> ConstraintGraph<'a, 'gcx, 'tcx> {
145 region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
146 map: &'a ConstraintMap<'tcx>)
147 -> ConstraintGraph<'a, 'gcx, 'tcx> {
149 let mut node_ids = FxHashMap();
151 let mut add_node = |node| {
152 if let Vacant(e) = node_ids.entry(node) {
158 for (n1, n2) in map.keys().map(|c| constraint_to_nodes(c)) {
163 region_rels.region_maps.each_encl_scope(|sub, sup| {
164 add_node(Node::Region(ty::ReScope(sub)));
165 add_node(Node::Region(ty::ReScope(sup)));
178 impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
179 type Node = Node<'tcx>;
180 type Edge = Edge<'tcx>;
181 fn graph_id(&self) -> dot::Id {
182 dot::Id::new(&*self.graph_name).unwrap()
184 fn node_id(&self, n: &Node) -> dot::Id {
185 let node_id = match self.node_ids.get(n) {
186 Some(node_id) => node_id,
187 None => bug!("no node_id found for node: {:?}", n),
189 let name = || format!("node_{}", node_id);
190 match dot::Id::new(name()) {
193 bug!("failed to create graphviz node identified by {}", name());
197 fn node_label(&self, n: &Node) -> dot::LabelText {
199 Node::RegionVid(n_vid) => dot::LabelText::label(format!("{:?}", n_vid)),
200 Node::Region(n_rgn) => dot::LabelText::label(format!("{:?}", n_rgn)),
203 fn edge_label(&self, e: &Edge) -> dot::LabelText {
205 Edge::Constraint(ref c) =>
206 dot::LabelText::label(format!("{:?}", self.map.get(c).unwrap())),
207 Edge::EnclScope(..) => dot::LabelText::label(format!("(enclosed)")),
212 fn constraint_to_nodes<'tcx>(c: &Constraint<'tcx>) -> (Node<'tcx>, Node<'tcx>) {
214 Constraint::ConstrainVarSubVar(rv_1, rv_2) =>
215 (Node::RegionVid(rv_1), Node::RegionVid(rv_2)),
216 Constraint::ConstrainRegSubVar(r_1, rv_2) =>
217 (Node::Region(*r_1), Node::RegionVid(rv_2)),
218 Constraint::ConstrainVarSubReg(rv_1, r_2) =>
219 (Node::RegionVid(rv_1), Node::Region(*r_2)),
220 Constraint::ConstrainRegSubReg(r_1, r_2) =>
221 (Node::Region(*r_1), Node::Region(*r_2)),
225 fn edge_to_nodes<'tcx>(e: &Edge<'tcx>) -> (Node<'tcx>, Node<'tcx>) {
227 Edge::Constraint(ref c) => constraint_to_nodes(c),
228 Edge::EnclScope(sub, sup) => {
229 (Node::Region(ty::ReScope(sub)),
230 Node::Region(ty::ReScope(sup)))
235 impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
236 type Node = Node<'tcx>;
237 type Edge = Edge<'tcx>;
238 fn nodes(&self) -> dot::Nodes<Node<'tcx>> {
239 let mut set = FxHashSet();
240 for node in self.node_ids.keys() {
243 debug!("constraint graph has {} nodes", set.len());
244 set.into_iter().collect()
246 fn edges(&self) -> dot::Edges<Edge<'tcx>> {
247 debug!("constraint graph has {} edges", self.map.len());
248 let mut v: Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect();
249 self.region_rels.region_maps.each_encl_scope(|sub, sup| v.push(Edge::EnclScope(sub, sup)));
250 debug!("region graph has {} edges", v.len());
253 fn source(&self, edge: &Edge<'tcx>) -> Node<'tcx> {
254 let (n1, _) = edge_to_nodes(edge);
255 debug!("edge {:?} has source {:?}", edge, n1);
258 fn target(&self, edge: &Edge<'tcx>) -> Node<'tcx> {
259 let (_, n2) = edge_to_nodes(edge);
260 debug!("edge {:?} has target {:?}", edge, n2);
265 pub type ConstraintMap<'tcx> = FxHashMap<Constraint<'tcx>, SubregionOrigin<'tcx>>;
267 fn dump_region_constraints_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>,
268 map: &ConstraintMap<'tcx>,
271 debug!("dump_region_constraints map (len: {}) path: {}",
274 let g = ConstraintGraph::new(format!("region_constraints"), region_rels, map);
275 debug!("dump_region_constraints calling render");
276 let mut v = Vec::new();
277 dot::render(&g, &mut v).unwrap();
278 File::create(path).and_then(|mut f| f.write_all(&v))