rollup merge of #19892: pnkfelix/region-graphviz
Added -Z print-region-graph debugging option; produces graphviz visualization of region inference constraint graph. Optionally uses environment variables `RUST_REGION_GRAPH=<path_template>` and `RUST_REGION_GRAPH_NODE=<node-id>` to select which file to output to and which AST node to print.
This commit is contained in:
commit
4a7757038a
@ -421,6 +421,14 @@ pub trait Labeller<'a,N,E> {
|
||||
}
|
||||
|
||||
impl<'a> LabelText<'a> {
|
||||
pub fn label<S:IntoCow<'a, String, str>>(s: S) -> LabelText<'a> {
|
||||
LabelStr(s.into_cow())
|
||||
}
|
||||
|
||||
pub fn escaped<S:IntoCow<'a, String, str>>(s: S) -> LabelText<'a> {
|
||||
EscStr(s.into_cow())
|
||||
}
|
||||
|
||||
fn escape_char<F>(c: char, mut f: F) where F: FnMut(char) {
|
||||
match c {
|
||||
// not escaping \\, since Graphviz escString needs to
|
||||
@ -505,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<RenderOption> { 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:Writer>(w: &mut W, arg: &[&str]) -> io::IoResult<()> {
|
||||
for &s in arg.iter() { try!(w.write_str(s)); }
|
||||
@ -524,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() {
|
||||
@ -536,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, &["}"])
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
227
src/librustc/middle/infer/region_inference/graphviz.rs
Normal file
227
src/librustc/middle/infer/region_inference/graphviz.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::infer::SubregionOrigin;
|
||||
use middle::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<ast::NodeId> =
|
||||
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<Constraint, SubregionOrigin<'tcx>>,
|
||||
node_ids: FnvHashMap<Node, uint>,
|
||||
}
|
||||
|
||||
#[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<Node> {
|
||||
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<Edge> {
|
||||
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<Constraint, SubregionOrigin<'tcx>>;
|
||||
|
||||
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)
|
||||
}
|
@ -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(Clone, 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<RegionResolutionError<'tcx>> {
|
||||
pub fn resolve_regions(&self, subject_node: ast::NodeId) -> Vec<RegionResolutionError<'tcx>> {
|
||||
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<RegionResolutionError<'tcx>>)
|
||||
-> Vec<VarValue>
|
||||
errors: &mut Vec<RegionResolutionError<'tcx>>,
|
||||
subject: ast::NodeId) -> Vec<VarValue>
|
||||
{
|
||||
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());
|
||||
|
@ -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)]
|
||||
|
@ -1188,7 +1188,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
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user