Skip to content

Commit d9c5724

Browse files
committed
---
yaml --- r: 163548 b: refs/heads/snap-stage3 c: d6d0bb2 h: refs/heads/master v: v3
1 parent 6980e4b commit d9c5724

File tree

8 files changed

+278
-20
lines changed

8 files changed

+278
-20
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
refs/heads/master: 9146a919b616e39e528e4d7100d16eef52f1f852
33
refs/heads/snap-stage1: e33de59e47c5076a89eadeb38f4934f58a3618a6
4-
refs/heads/snap-stage3: a5e0624a3216a9cf155370a71c9901e56638fa0d
4+
refs/heads/snap-stage3: d6d0bb2030f870c0b4b942da793beb4edb82c191
55
refs/heads/try: 20cbbffeefc1f35e2ea63afce7b42fbd79611d42
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
77
refs/heads/dist-snap: ba4081a5a8573875fed17545846f6f6902c8ba8d

branches/snap-stage3/src/librustc/middle/infer/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
814814
self.region_vars.new_bound(debruijn)
815815
}
816816

817-
pub fn resolve_regions_and_report_errors(&self) {
818-
let errors = self.region_vars.resolve_regions();
817+
pub fn resolve_regions_and_report_errors(&self, subject_node_id: ast::NodeId) {
818+
let errors = self.region_vars.resolve_regions(subject_node_id);
819819
self.report_region_errors(&errors); // see error_reporting.rs
820820
}
821821

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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 middle::ty;
22+
use super::Constraint;
23+
use middle::typeck::infer::SubregionOrigin;
24+
use middle::typeck::infer::region_inference::RegionVarBindings;
25+
use session::config;
26+
use util::nodemap::{FnvHashMap, FnvHashSet};
27+
use util::ppaux::Repr;
28+
29+
use std::collections::hash_map::Vacant;
30+
use std::io::{mod, File};
31+
use std::os;
32+
use std::sync::atomic;
33+
use syntax::ast;
34+
35+
fn print_help_message() {
36+
println!("\
37+
-Z print-region-graph by default prints a region constraint graph for every \n\
38+
function body, to the path `/tmp/constraints.nodeXXX.dot`, where the XXX is \n\
39+
replaced with the node id of the function under analysis. \n\
40+
\n\
41+
To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\
42+
where XXX is the node id desired. \n\
43+
\n\
44+
To generate output to some path other than the default \n\
45+
`/tmp/constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`; \n\
46+
occurrences of the character `%` in the requested path will be replaced with\n\
47+
the node id of the function under analysis. \n\
48+
\n\
49+
(Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\
50+
graphs will be printed. \n\
51+
");
52+
}
53+
54+
pub fn maybe_print_constraints_for<'a, 'tcx>(region_vars: &RegionVarBindings<'a, 'tcx>,
55+
subject_node: ast::NodeId) {
56+
let tcx = region_vars.tcx;
57+
58+
if !region_vars.tcx.sess.debugging_opt(config::PRINT_REGION_GRAPH) {
59+
return;
60+
}
61+
62+
let requested_node : Option<ast::NodeId> =
63+
os::getenv("RUST_REGION_GRAPH_NODE").and_then(|s|from_str(s.as_slice()));
64+
65+
if requested_node.is_some() && requested_node != Some(subject_node) {
66+
return;
67+
}
68+
69+
let requested_output = os::getenv("RUST_REGION_GRAPH");
70+
debug!("requested_output: {} requested_node: {}",
71+
requested_output, requested_node);
72+
73+
let output_path = {
74+
let output_template = match requested_output {
75+
Some(ref s) if s.as_slice() == "help" => {
76+
static PRINTED_YET : atomic::AtomicBool = atomic::INIT_ATOMIC_BOOL;
77+
if !PRINTED_YET.load(atomic::SeqCst) {
78+
print_help_message();
79+
PRINTED_YET.store(true, atomic::SeqCst);
80+
}
81+
return;
82+
}
83+
84+
Some(other_path) => other_path,
85+
None => "/tmp/constraints.node%.dot".to_string(),
86+
};
87+
88+
if output_template.len() == 0 {
89+
tcx.sess.bug("empty string provided as RUST_REGION_GRAPH");
90+
}
91+
92+
if output_template.contains_char('%') {
93+
let mut new_str = String::new();
94+
for c in output_template.chars() {
95+
if c == '%' {
96+
new_str.push_str(subject_node.to_string().as_slice());
97+
} else {
98+
new_str.push(c);
99+
}
100+
}
101+
new_str
102+
} else {
103+
output_template
104+
}
105+
};
106+
107+
let constraints = &*region_vars.constraints.borrow();
108+
match dump_region_constraints_to(tcx, constraints, output_path.as_slice()) {
109+
Ok(()) => {}
110+
Err(e) => {
111+
let msg = format!("io error dumping region constraints: {}", e);
112+
region_vars.tcx.sess.err(msg.as_slice())
113+
}
114+
}
115+
}
116+
117+
struct ConstraintGraph<'a, 'tcx: 'a> {
118+
tcx: &'a ty::ctxt<'tcx>,
119+
graph_name: String,
120+
map: &'a FnvHashMap<Constraint, SubregionOrigin<'tcx>>,
121+
node_ids: FnvHashMap<Node, uint>,
122+
}
123+
124+
#[deriving(Clone, Hash, PartialEq, Eq, Show)]
125+
enum Node {
126+
RegionVid(ty::RegionVid),
127+
Region(ty::Region),
128+
}
129+
130+
type Edge = Constraint;
131+
132+
impl<'a, 'tcx> ConstraintGraph<'a, 'tcx> {
133+
fn new(tcx: &'a ty::ctxt<'tcx>,
134+
name: String,
135+
map: &'a ConstraintMap<'tcx>) -> ConstraintGraph<'a, 'tcx> {
136+
let mut i = 0;
137+
let mut node_ids = FnvHashMap::new();
138+
{
139+
let add_node = |node| {
140+
if let Vacant(e) = node_ids.entry(node) {
141+
e.set(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+
152+
ConstraintGraph { tcx: tcx,
153+
graph_name: name,
154+
map: map,
155+
node_ids: node_ids }
156+
}
157+
}
158+
159+
impl<'a, 'tcx> dot::Labeller<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
160+
fn graph_id(&self) -> dot::Id {
161+
dot::Id::new(self.graph_name.as_slice()).unwrap()
162+
}
163+
fn node_id(&self, n: &Node) -> dot::Id {
164+
dot::Id::new(format!("node_{}", self.node_ids.get(n).unwrap())).unwrap()
165+
}
166+
fn node_label(&self, n: &Node) -> dot::LabelText {
167+
match *n {
168+
Node::RegionVid(n_vid) =>
169+
dot::LabelText::label(format!("{}", n_vid)),
170+
Node::Region(n_rgn) =>
171+
dot::LabelText::label(format!("{}", n_rgn.repr(self.tcx))),
172+
}
173+
}
174+
fn edge_label(&self, e: &Edge) -> dot::LabelText {
175+
dot::LabelText::label(format!("{}", self.map.get(e).unwrap().repr(self.tcx)))
176+
}
177+
}
178+
179+
fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
180+
match *c {
181+
Constraint::ConstrainVarSubVar(rv_1, rv_2) => (Node::RegionVid(rv_1),
182+
Node::RegionVid(rv_2)),
183+
Constraint::ConstrainRegSubVar(r_1, rv_2) => (Node::Region(r_1),
184+
Node::RegionVid(rv_2)),
185+
Constraint::ConstrainVarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1),
186+
Node::Region(r_2)),
187+
}
188+
}
189+
190+
impl<'a, 'tcx> dot::GraphWalk<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
191+
fn nodes(&self) -> dot::Nodes<Node> {
192+
let mut set = FnvHashSet::new();
193+
for constraint in self.map.keys() {
194+
let (n1, n2) = constraint_to_nodes(constraint);
195+
set.insert(n1);
196+
set.insert(n2);
197+
}
198+
debug!("constraint graph has {} nodes", set.len());
199+
set.into_iter().collect()
200+
}
201+
fn edges(&self) -> dot::Edges<Edge> {
202+
debug!("constraint graph has {} edges", self.map.len());
203+
self.map.keys().map(|e|*e).collect()
204+
}
205+
fn source(&self, edge: &Edge) -> Node {
206+
let (n1, _) = constraint_to_nodes(edge);
207+
debug!("edge {} has source {}", edge, n1);
208+
n1
209+
}
210+
fn target(&self, edge: &Edge) -> Node {
211+
let (_, n2) = constraint_to_nodes(edge);
212+
debug!("edge {} has target {}", edge, n2);
213+
n2
214+
}
215+
}
216+
217+
pub type ConstraintMap<'tcx> = FnvHashMap<Constraint, SubregionOrigin<'tcx>>;
218+
219+
fn dump_region_constraints_to<'a, 'tcx:'a >(tcx: &'a ty::ctxt<'tcx>,
220+
map: &ConstraintMap<'tcx>,
221+
path: &str) -> io::IoResult<()> {
222+
debug!("dump_region_constraints map (len: {}) path: {}", map.len(), path);
223+
let g = ConstraintGraph::new(tcx, format!("region_constraints"), map);
224+
let mut f = File::create(&Path::new(path));
225+
debug!("dump_region_constraints calling render");
226+
dot::render(&g, &mut f)
227+
}

branches/snap-stage3/src/librustc/middle/infer/region_inference/mod.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ use std::uint;
3737
use syntax::ast;
3838

3939
mod doc;
40+
mod graphviz;
4041

4142
// A constraint that influences the inference process.
42-
#[deriving(PartialEq, Eq, Hash)]
43+
#[deriving(PartialEq, Eq, Hash, Show)]
4344
pub enum Constraint {
4445
// One region variable is subregion of another
4546
ConstrainVarSubVar(RegionVid, RegionVid),
@@ -706,10 +707,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
706707
/// fixed-point iteration to find region values which satisfy all
707708
/// constraints, assuming such values can be found; if they cannot,
708709
/// errors are reported.
709-
pub fn resolve_regions(&self) -> Vec<RegionResolutionError<'tcx>> {
710+
pub fn resolve_regions(&self, subject_node: ast::NodeId) -> Vec<RegionResolutionError<'tcx>> {
710711
debug!("RegionVarBindings: resolve_regions()");
711712
let mut errors = vec!();
712-
let v = self.infer_variable_values(&mut errors);
713+
let v = self.infer_variable_values(&mut errors, subject_node);
713714
*self.values.borrow_mut() = Some(v);
714715
errors
715716
}
@@ -958,14 +959,15 @@ type RegionGraph = graph::Graph<(), Constraint>;
958959

959960
impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
960961
fn infer_variable_values(&self,
961-
errors: &mut Vec<RegionResolutionError<'tcx>>)
962-
-> Vec<VarValue>
962+
errors: &mut Vec<RegionResolutionError<'tcx>>,
963+
subject: ast::NodeId) -> Vec<VarValue>
963964
{
964965
let mut var_data = self.construct_var_data();
965966

966967
// Dorky hack to cause `dump_constraints` to only get called
967968
// if debug mode is enabled:
968969
debug!("----() End constraint listing {}---", self.dump_constraints());
970+
graphviz::maybe_print_constraints_for(self, subject);
969971

970972
self.expansion(var_data.as_mut_slice());
971973
self.contraction(var_data.as_mut_slice());

branches/snap-stage3/src/librustc/session/config.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ debugging_opts!(
276276
FLOWGRAPH_PRINT_MOVES,
277277
FLOWGRAPH_PRINT_ASSIGNS,
278278
FLOWGRAPH_PRINT_ALL,
279-
PRINT_SYSROOT
279+
PRINT_SYSROOT,
280+
PRINT_REGION_GRAPH
280281
]
281282
0
282283
)
@@ -322,7 +323,10 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
322323
("flowgraph-print-all", "Include all dataflow analysis data in \
323324
--pretty flowgraph output", FLOWGRAPH_PRINT_ALL),
324325
("print-sysroot", "Print the sysroot as used by this rustc invocation",
325-
PRINT_SYSROOT)]
326+
PRINT_SYSROOT),
327+
("print-region-graph", "Prints region inference graph. \
328+
Use with RUST_REGION_GRAPH=help for more info",
329+
PRINT_REGION_GRAPH)]
326330
}
327331

328332
#[deriving(Clone)]

branches/snap-stage3/src/librustc_typeck/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,7 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
11901190

11911191
// Finally, resolve all regions. This catches wily misuses of lifetime
11921192
// parameters.
1193-
infcx.resolve_regions_and_report_errors();
1193+
infcx.resolve_regions_and_report_errors(impl_m_body_id);
11941194

11951195
/// Check that region bounds on impl method are the same as those on the trait. In principle,
11961196
/// it could be ok for there to be fewer region bounds on the impl method, but this leads to an

0 commit comments

Comments
 (0)