From c500b63e717261bc6cb80231936eca8f1233bc82 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 15 Dec 2014 12:32:54 +0100 Subject: [PATCH 1/4] libgraphviz: add `label` and `escaped` ctors taking any `str::IntoMaybeOwned`. --- src/libgraphviz/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index fa048346e99..9e4b7a6a4cc 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -421,6 +421,14 @@ pub trait Labeller<'a,N,E> { } impl<'a> LabelText<'a> { + pub fn label>(s: S) -> LabelText<'a> { + LabelStr(s.into_cow()) + } + + pub fn escaped>(s: S) -> LabelText<'a> { + EscStr(s.into_cow()) + } + fn escape_char(c: char, mut f: F) where F: FnMut(char) { match c { // not escaping \\, since Graphviz escString needs to From a5e0624a3216a9cf155370a71c9901e56638fa0d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 15 Dec 2014 12:37:42 +0100 Subject: [PATCH 2/4] libgraphviz: extend API with flags to indicate options like "do not include labels". --- src/libgraphviz/lib.rs | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 9e4b7a6a4cc..0576c46d3bd 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -513,11 +513,29 @@ pub trait GraphWalk<'a, N, E> { fn target(&'a self, edge: &E) -> N; } +#[deriving(Copy, PartialEq, Eq, Show)] +pub enum RenderOption { + NoEdgeLabels, + NoNodeLabels, +} + +/// Returns vec holding all the default render options. +pub fn default_options() -> Vec { vec![] } + /// Renders directed graph `g` into the writer `w` in DOT syntax. -/// (Main entry point for the library.) +/// (Simple wrapper around `render_opts` that passes a default set of options.) pub fn render<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, W:Writer>( g: &'a G, - w: &mut W) -> io::IoResult<()> + w: &mut W) -> io::IoResult<()> { + render_opts(g, w, &[]) +} + +/// Renders directed graph `g` into the writer `w` in DOT syntax. +/// (Main entry point for the library.) +pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, W:Writer>( + g: &'a G, + w: &mut W, + options: &[RenderOption]) -> io::IoResult<()> { fn writeln(w: &mut W, arg: &[&str]) -> io::IoResult<()> { for &s in arg.iter() { try!(w.write_str(s)); } @@ -532,9 +550,13 @@ pub fn render<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, for n in g.nodes().iter() { try!(indent(w)); let id = g.node_id(n); - let escaped = g.node_label(n).escape(); - try!(writeln(w, &[id.as_slice(), - "[label=\"", escaped.as_slice(), "\"];"])); + if options.contains(&RenderOption::NoNodeLabels) { + try!(writeln(w, &[id.as_slice(), ";"])); + } else { + let escaped = g.node_label(n).escape(); + try!(writeln(w, &[id.as_slice(), + "[label=\"", escaped.as_slice(), "\"];"])); + } } for e in g.edges().iter() { @@ -544,8 +566,14 @@ pub fn render<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, let target = g.target(e); let source_id = g.node_id(&source); let target_id = g.node_id(&target); - try!(writeln(w, &[source_id.as_slice(), " -> ", target_id.as_slice(), - "[label=\"", escaped_label.as_slice(), "\"];"])); + if options.contains(&RenderOption::NoEdgeLabels) { + try!(writeln(w, &[source_id.as_slice(), + " -> ", target_id.as_slice(), ";"])); + } else { + try!(writeln(w, &[source_id.as_slice(), + " -> ", target_id.as_slice(), + "[label=\"", escaped_label.as_slice(), "\"];"])); + } } writeln(w, &["}"]) From d6d0bb2030f870c0b4b942da793beb4edb82c191 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 15 Dec 2014 17:17:52 +0100 Subject: [PATCH 3/4] Added `-Z print-region-graph` debugging option; produces graphviz visualization of region inference constraint graph. Optionally uses environment variables `RUST_REGION_GRAPH=` and `RUST_REGION_GRAPH_NODE=` to select which file to output to and which AST node to print. Note that in some cases of method AST's, the identification of AST node is based on the id for the *body* of the method; this is largely due to having the body node-id already available at the relevant point in the control-flow of rustc in its current incarnation. Ideally we would handle identifying AST's by name in addition to node-id, e.g. the same way that the pretty-printer supports path suffixes as well as node-ids for identifying subtrees to print. --- src/librustc/middle/infer/mod.rs | 4 +- .../middle/infer/region_inference/graphviz.rs | 227 ++++++++++++++++++ .../middle/infer/region_inference/mod.rs | 12 +- src/librustc/session/config.rs | 8 +- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/check/regionck.rs | 41 +++- src/librustc_typeck/collect.rs | 2 +- 7 files changed, 277 insertions(+), 19 deletions(-) create mode 100644 src/librustc/middle/infer/region_inference/graphviz.rs diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 2b1d8776365..3a84712016e 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -814,8 +814,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.region_vars.new_bound(debruijn) } - pub fn resolve_regions_and_report_errors(&self) { - let errors = self.region_vars.resolve_regions(); + pub fn resolve_regions_and_report_errors(&self, subject_node_id: ast::NodeId) { + let errors = self.region_vars.resolve_regions(subject_node_id); self.report_region_errors(&errors); // see error_reporting.rs } diff --git a/src/librustc/middle/infer/region_inference/graphviz.rs b/src/librustc/middle/infer/region_inference/graphviz.rs new file mode 100644 index 00000000000..7be3ec15862 --- /dev/null +++ b/src/librustc/middle/infer/region_inference/graphviz.rs @@ -0,0 +1,227 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This module provides linkage between libgraphviz traits and +//! `rustc::middle::typeck::infer::region_inference`, generating a +//! rendering of the graph represented by the list of `Constraint` +//! instances (which make up the edges of the graph), as well as the +//! origin for each constraint (which are attached to the labels on +//! each edge). + +/// For clarity, rename the graphviz crate locally to dot. +use graphviz as dot; + +use middle::ty; +use super::Constraint; +use middle::typeck::infer::SubregionOrigin; +use middle::typeck::infer::region_inference::RegionVarBindings; +use session::config; +use util::nodemap::{FnvHashMap, FnvHashSet}; +use util::ppaux::Repr; + +use std::collections::hash_map::Vacant; +use std::io::{mod, File}; +use std::os; +use std::sync::atomic; +use syntax::ast; + +fn print_help_message() { + println!("\ +-Z print-region-graph by default prints a region constraint graph for every \n\ +function body, to the path `/tmp/constraints.nodeXXX.dot`, where the XXX is \n\ +replaced with the node id of the function under analysis. \n\ + \n\ +To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\ +where XXX is the node id desired. \n\ + \n\ +To generate output to some path other than the default \n\ +`/tmp/constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`; \n\ +occurrences of the character `%` in the requested path will be replaced with\n\ +the node id of the function under analysis. \n\ + \n\ +(Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\ +graphs will be printed. \n\ +"); +} + +pub fn maybe_print_constraints_for<'a, 'tcx>(region_vars: &RegionVarBindings<'a, 'tcx>, + subject_node: ast::NodeId) { + let tcx = region_vars.tcx; + + if !region_vars.tcx.sess.debugging_opt(config::PRINT_REGION_GRAPH) { + return; + } + + let requested_node : Option = + os::getenv("RUST_REGION_GRAPH_NODE").and_then(|s|from_str(s.as_slice())); + + if requested_node.is_some() && requested_node != Some(subject_node) { + return; + } + + let requested_output = os::getenv("RUST_REGION_GRAPH"); + debug!("requested_output: {} requested_node: {}", + requested_output, requested_node); + + let output_path = { + let output_template = match requested_output { + Some(ref s) if s.as_slice() == "help" => { + static PRINTED_YET : atomic::AtomicBool = atomic::INIT_ATOMIC_BOOL; + if !PRINTED_YET.load(atomic::SeqCst) { + print_help_message(); + PRINTED_YET.store(true, atomic::SeqCst); + } + return; + } + + Some(other_path) => other_path, + None => "/tmp/constraints.node%.dot".to_string(), + }; + + if output_template.len() == 0 { + tcx.sess.bug("empty string provided as RUST_REGION_GRAPH"); + } + + if output_template.contains_char('%') { + let mut new_str = String::new(); + for c in output_template.chars() { + if c == '%' { + new_str.push_str(subject_node.to_string().as_slice()); + } else { + new_str.push(c); + } + } + new_str + } else { + output_template + } + }; + + let constraints = &*region_vars.constraints.borrow(); + match dump_region_constraints_to(tcx, constraints, output_path.as_slice()) { + Ok(()) => {} + Err(e) => { + let msg = format!("io error dumping region constraints: {}", e); + region_vars.tcx.sess.err(msg.as_slice()) + } + } +} + +struct ConstraintGraph<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + graph_name: String, + map: &'a FnvHashMap>, + node_ids: FnvHashMap, +} + +#[deriving(Clone, Hash, PartialEq, Eq, Show)] +enum Node { + RegionVid(ty::RegionVid), + Region(ty::Region), +} + +type Edge = Constraint; + +impl<'a, 'tcx> ConstraintGraph<'a, 'tcx> { + fn new(tcx: &'a ty::ctxt<'tcx>, + name: String, + map: &'a ConstraintMap<'tcx>) -> ConstraintGraph<'a, 'tcx> { + let mut i = 0; + let mut node_ids = FnvHashMap::new(); + { + let add_node = |node| { + if let Vacant(e) = node_ids.entry(node) { + e.set(i); + i += 1; + } + }; + + for (n1, n2) in map.keys().map(|c|constraint_to_nodes(c)) { + add_node(n1); + add_node(n2); + } + } + + ConstraintGraph { tcx: tcx, + graph_name: name, + map: map, + node_ids: node_ids } + } +} + +impl<'a, 'tcx> dot::Labeller<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> { + fn graph_id(&self) -> dot::Id { + dot::Id::new(self.graph_name.as_slice()).unwrap() + } + fn node_id(&self, n: &Node) -> dot::Id { + dot::Id::new(format!("node_{}", self.node_ids.get(n).unwrap())).unwrap() + } + fn node_label(&self, n: &Node) -> dot::LabelText { + match *n { + Node::RegionVid(n_vid) => + dot::LabelText::label(format!("{}", n_vid)), + Node::Region(n_rgn) => + dot::LabelText::label(format!("{}", n_rgn.repr(self.tcx))), + } + } + fn edge_label(&self, e: &Edge) -> dot::LabelText { + dot::LabelText::label(format!("{}", self.map.get(e).unwrap().repr(self.tcx))) + } +} + +fn constraint_to_nodes(c: &Constraint) -> (Node, Node) { + match *c { + Constraint::ConstrainVarSubVar(rv_1, rv_2) => (Node::RegionVid(rv_1), + Node::RegionVid(rv_2)), + Constraint::ConstrainRegSubVar(r_1, rv_2) => (Node::Region(r_1), + Node::RegionVid(rv_2)), + Constraint::ConstrainVarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1), + Node::Region(r_2)), + } +} + +impl<'a, 'tcx> dot::GraphWalk<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> { + fn nodes(&self) -> dot::Nodes { + let mut set = FnvHashSet::new(); + for constraint in self.map.keys() { + let (n1, n2) = constraint_to_nodes(constraint); + set.insert(n1); + set.insert(n2); + } + debug!("constraint graph has {} nodes", set.len()); + set.into_iter().collect() + } + fn edges(&self) -> dot::Edges { + debug!("constraint graph has {} edges", self.map.len()); + self.map.keys().map(|e|*e).collect() + } + fn source(&self, edge: &Edge) -> Node { + let (n1, _) = constraint_to_nodes(edge); + debug!("edge {} has source {}", edge, n1); + n1 + } + fn target(&self, edge: &Edge) -> Node { + let (_, n2) = constraint_to_nodes(edge); + debug!("edge {} has target {}", edge, n2); + n2 + } +} + +pub type ConstraintMap<'tcx> = FnvHashMap>; + +fn dump_region_constraints_to<'a, 'tcx:'a >(tcx: &'a ty::ctxt<'tcx>, + map: &ConstraintMap<'tcx>, + path: &str) -> io::IoResult<()> { + debug!("dump_region_constraints map (len: {}) path: {}", map.len(), path); + let g = ConstraintGraph::new(tcx, format!("region_constraints"), map); + let mut f = File::create(&Path::new(path)); + debug!("dump_region_constraints calling render"); + dot::render(&g, &mut f) +} diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index 98f69f66b27..acd49f3f8df 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -37,9 +37,10 @@ use std::uint; use syntax::ast; mod doc; +mod graphviz; // A constraint that influences the inference process. -#[deriving(PartialEq, Eq, Hash)] +#[deriving(PartialEq, Eq, Hash, Show)] pub enum Constraint { // One region variable is subregion of another ConstrainVarSubVar(RegionVid, RegionVid), @@ -706,10 +707,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { /// fixed-point iteration to find region values which satisfy all /// constraints, assuming such values can be found; if they cannot, /// errors are reported. - pub fn resolve_regions(&self) -> Vec> { + pub fn resolve_regions(&self, subject_node: ast::NodeId) -> Vec> { debug!("RegionVarBindings: resolve_regions()"); let mut errors = vec!(); - let v = self.infer_variable_values(&mut errors); + let v = self.infer_variable_values(&mut errors, subject_node); *self.values.borrow_mut() = Some(v); errors } @@ -958,14 +959,15 @@ type RegionGraph = graph::Graph<(), Constraint>; impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { fn infer_variable_values(&self, - errors: &mut Vec>) - -> Vec + errors: &mut Vec>, + subject: ast::NodeId) -> Vec { let mut var_data = self.construct_var_data(); // Dorky hack to cause `dump_constraints` to only get called // if debug mode is enabled: debug!("----() End constraint listing {}---", self.dump_constraints()); + graphviz::maybe_print_constraints_for(self, subject); self.expansion(var_data.as_mut_slice()); self.contraction(var_data.as_mut_slice()); diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index c7b5e1e8de9..b3b44b60b6e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -276,7 +276,8 @@ debugging_opts!( FLOWGRAPH_PRINT_MOVES, FLOWGRAPH_PRINT_ASSIGNS, FLOWGRAPH_PRINT_ALL, - PRINT_SYSROOT + PRINT_SYSROOT, + PRINT_REGION_GRAPH ] 0 ) @@ -322,7 +323,10 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> { ("flowgraph-print-all", "Include all dataflow analysis data in \ --pretty flowgraph output", FLOWGRAPH_PRINT_ALL), ("print-sysroot", "Print the sysroot as used by this rustc invocation", - PRINT_SYSROOT)] + PRINT_SYSROOT), + ("print-region-graph", "Prints region inference graph. \ + Use with RUST_REGION_GRAPH=help for more info", + PRINT_REGION_GRAPH)] } #[deriving(Clone)] diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e0df94745d6..ad0046a1ad4 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1190,7 +1190,7 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, // Finally, resolve all regions. This catches wily misuses of lifetime // parameters. - infcx.resolve_regions_and_report_errors(); + infcx.resolve_regions_and_report_errors(impl_m_body_id); /// Check that region bounds on impl method are the same as those on the trait. In principle, /// it could be ok for there to be fewer region bounds on the impl method, but this leads to an diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 9f75b9764eb..501b9775f7f 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -139,27 +139,31 @@ use syntax::visit::Visitor; use std::cell::{RefCell}; use std::collections::hash_map::{Vacant, Occupied}; +use self::RepeatingScope::Repeating; +use self::SubjectNode::Subject; + + /////////////////////////////////////////////////////////////////////////// // PUBLIC ENTRY POINTS pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { - let mut rcx = Rcx::new(fcx, e.id); + let mut rcx = Rcx::new(fcx, Repeating(e.id), Subject(e.id)); if fcx.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_expr(e); rcx.visit_region_obligations(e.id); } - fcx.infcx().resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors(); } pub fn regionck_item(fcx: &FnCtxt, item: &ast::Item) { - let mut rcx = Rcx::new(fcx, item.id); + let mut rcx = Rcx::new(fcx, Repeating(item.id), Subject(item.id)); rcx.visit_region_obligations(item.id); - fcx.infcx().resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors(); } pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, decl: &ast::FnDecl, blk: &ast::Block) { - let mut rcx = Rcx::new(fcx, blk.id); + let mut rcx = Rcx::new(fcx, Repeating(blk.id), Subject(id)); if fcx.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_fn_body(id, decl, blk); @@ -169,7 +173,7 @@ pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, decl: &ast::FnDecl, blk: &ast: // particularly around closure bounds. vtable::select_all_fcx_obligations_or_error(fcx); - fcx.infcx().resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors(); } /// Checks that the types in `component_tys` are well-formed. This will add constraints into the @@ -177,7 +181,7 @@ pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, decl: &ast::FnDecl, blk: &ast: pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span: Span, component_tys: &[Ty<'tcx>]) { - let mut rcx = Rcx::new(fcx, 0); + let mut rcx = Rcx::new(fcx, Repeating(0), SubjectNode::None); for &component_ty in component_tys.iter() { // Check that each type outlives the empty region. Since the // empty region is a subregion of all others, this can't fail @@ -225,6 +229,9 @@ pub struct Rcx<'a, 'tcx: 'a> { // id of innermost fn or loop repeating_scope: ast::NodeId, + // id of AST node being analyzed (the subject of the analysis). + subject: SubjectNode, + // Possible region links we will establish if an upvar // turns out to be unique/mutable maybe_links: MaybeLinkMap<'tcx> @@ -251,11 +258,17 @@ fn region_of_def(fcx: &FnCtxt, def: def::Def) -> ty::Region { } } +pub enum RepeatingScope { Repeating(ast::NodeId) } +pub enum SubjectNode { Subject(ast::NodeId), None } + impl<'a, 'tcx> Rcx<'a, 'tcx> { pub fn new(fcx: &'a FnCtxt<'a, 'tcx>, - initial_repeating_scope: ast::NodeId) -> Rcx<'a, 'tcx> { + initial_repeating_scope: RepeatingScope, + subject: SubjectNode) -> Rcx<'a, 'tcx> { + let Repeating(initial_repeating_scope) = initial_repeating_scope; Rcx { fcx: fcx, repeating_scope: initial_repeating_scope, + subject: subject, region_param_pairs: Vec::new(), maybe_links: RefCell::new(FnvHashMap::new()) } } @@ -425,6 +438,18 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { debug!("<< relate_free_regions"); } + + fn resolve_regions_and_report_errors(&self) { + let subject_node_id = match self.subject { + Subject(s) => s, + SubjectNode::None => { + self.tcx().sess.bug("cannot resolve_regions_and_report_errors \ + without subject node"); + } + }; + + self.fcx.infcx().resolve_regions_and_report_errors(subject_node_id); + } } impl<'fcx, 'tcx> mc::Typer<'tcx> for Rcx<'fcx, 'tcx> { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 61b8e6c956c..962bb575dc8 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2237,6 +2237,6 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>( format!("mismatched self type: expected `{}`", ppaux::ty_to_string(crate_context.tcx, required_type)) })); - infcx.resolve_regions_and_report_errors(); + infcx.resolve_regions_and_report_errors(body_id); } } From 375b79a0fb0a4f9dfc1edfed1154cda97c1f2bc3 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 16 Dec 2014 17:07:26 +0100 Subject: [PATCH 4/4] Followup fixes that I missed during an earlier rebase. --- src/librustc/middle/infer/region_inference/graphviz.rs | 4 ++-- src/librustc/middle/infer/region_inference/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/infer/region_inference/graphviz.rs b/src/librustc/middle/infer/region_inference/graphviz.rs index 7be3ec15862..720de357a27 100644 --- a/src/librustc/middle/infer/region_inference/graphviz.rs +++ b/src/librustc/middle/infer/region_inference/graphviz.rs @@ -20,8 +20,8 @@ use graphviz as dot; use middle::ty; use super::Constraint; -use middle::typeck::infer::SubregionOrigin; -use middle::typeck::infer::region_inference::RegionVarBindings; +use middle::infer::SubregionOrigin; +use middle::infer::region_inference::RegionVarBindings; use session::config; use util::nodemap::{FnvHashMap, FnvHashSet}; use util::ppaux::Repr; diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index acd49f3f8df..a284dddc323 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -40,7 +40,7 @@ mod doc; mod graphviz; // A constraint that influences the inference process. -#[deriving(PartialEq, Eq, Hash, Show)] +#[deriving(Clone, PartialEq, Eq, Hash, Show)] pub enum Constraint { // One region variable is subregion of another ConstrainVarSubVar(RegionVid, RegionVid),