提交 3efc9d2c 编写于 作者: N Niko Matsakis

Fix bug in higher-ranked code that would sometimes leak skolemized regions...

Fix bug in higher-ranked code that would sometimes leak skolemized regions and/or cause incorrect results.
上级 514dfdbf
......@@ -286,7 +286,7 @@ fn coerce_unsized(&self,
let ty = ty::mk_rptr(self.get_ref().infcx.tcx,
r_borrow,
ty::mt{ty: ty, mutbl: mt_b.mutbl});
try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoPtr(AutoUnsize({})))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
......@@ -309,7 +309,7 @@ fn coerce_unsized(&self,
let ty = ty::mk_ptr(self.get_ref().infcx.tcx,
ty::mt{ty: ty, mutbl: mt_b.mutbl});
try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoPtr(AutoUnsize({})))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
......@@ -327,7 +327,7 @@ fn coerce_unsized(&self,
match self.unsize_ty(t_a, sty_a, t_b) {
Some((ty, kind)) => {
let ty = ty::mk_uniq(self.get_ref().infcx.tcx, ty);
try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoUnsizeUniq({}))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
......@@ -384,7 +384,7 @@ fn unsize_ty(&self,
let mut result = None;
let mut tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
for (i, (tp_a, tp_b)) in tps {
if self.get_ref().infcx.try(|| sub.tys(*tp_a, *tp_b)).is_ok() {
if self.get_ref().infcx.try(|_| sub.tys(*tp_a, *tp_b)).is_ok() {
continue;
}
match
......@@ -397,7 +397,7 @@ fn unsize_ty(&self,
let mut new_substs = substs_a.clone();
new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
let ty = ty::mk_struct(tcx, did_a, new_substs);
if self.get_ref().infcx.try(|| sub.tys(ty, ty_b)).is_err() {
if self.get_ref().infcx.try(|_| sub.tys(ty, ty_b)).is_err() {
debug!("Unsized type parameter '{}', but still \
could not match types {} and {}",
ppaux::ty_to_string(tcx, *tp_a),
......
......@@ -706,14 +706,38 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
fn fold_region(&mut self, r: ty::Region) -> ty::Region {
match r {
ty::ReLateBound(..) | ty::ReEarlyBound(..) => r,
_ if self.make_region_vars => {
// FIXME: This is non-ideal because we don't give a
// very descriptive origin for this region variable.
self.infcx.next_region_var(MiscVariable(self.span))
// Never make variables for regions bound within the type itself.
ty::ReLateBound(..) => { return r; }
// Early-bound regions should really have been substituted away before
// we get to this point.
ty::ReEarlyBound(..) => {
self.tcx().sess.span_bug(
self.span,
format!("Encountered early bound region when generalizing: {}",
r.repr(self.tcx()))[]);
}
// Always make a fresh region variable for skolemized regions;
// the higher-ranked decision procedures rely on this.
ty::ReInfer(ty::ReSkolemized(..)) => { }
// For anything else, we make a region variable, unless we
// are *equating*, in which case it's just wasteful.
ty::ReEmpty |
ty::ReStatic |
ty::ReScope(..) |
ty::ReInfer(ty::ReVar(..)) |
ty::ReFree(..) => {
if !self.make_region_vars {
return r;
}
}
_ => r,
}
// FIXME: This is non-ideal because we don't give a
// very descriptive origin for this region variable.
self.infcx.next_region_var(MiscVariable(self.span))
}
}
......
......@@ -1640,7 +1640,7 @@ pub trait Resolvable<'tcx> {
impl<'tcx> Resolvable<'tcx> for Ty<'tcx> {
fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>) -> Ty<'tcx> {
infcx.resolve_type_vars_if_possible(*self)
infcx.resolve_type_vars_if_possible(self)
}
fn contains_error(&self) -> bool {
ty::type_is_error(*self)
......@@ -1650,7 +1650,7 @@ fn contains_error(&self) -> bool {
impl<'tcx> Resolvable<'tcx> for Rc<ty::TraitRef<'tcx>> {
fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>)
-> Rc<ty::TraitRef<'tcx>> {
Rc::new(infcx.resolve_type_vars_in_trait_ref_if_possible(&**self))
Rc::new(infcx.resolve_type_vars_if_possible(&**self))
}
fn contains_error(&self) -> bool {
ty::trait_ref_contains_error(&**self)
......
......@@ -249,19 +249,21 @@
//! in T and try to, in some cases, replace them with bound regions to
//! yield the final result.
//!
//! To decide whether to replace a region `R` that appears in `T` with a
//! bound region, the algorithms make use of two bits of information.
//! First is a set `V` that contains all region variables created as part
//! of the LUB/GLB computation. `V` will contain the region variables
//! created to replace the bound regions in the input types, but it also
//! contains 'intermediate' variables created to represent the LUB/GLB of
//! individual regions. Basically, when asked to compute the LUB/GLB of a
//! region variable with another region, the inferencer cannot oblige
//! immediately since the values of that variables are not known.
//! Therefore, it creates a new variable that is related to the two
//! regions. For example, the LUB of two variables `$x` and `$y` is a
//! fresh variable `$z` that is constrained such that `$x <= $z` and `$y
//! <= $z`. So `V` will contain these intermediate variables as well.
//! To decide whether to replace a region `R` that appears in `T` with
//! a bound region, the algorithms make use of two bits of
//! information. First is a set `V` that contains all region
//! variables created as part of the LUB/GLB computation (roughly; see
//! `region_vars_confined_to_snapshot()` for full details). `V` will
//! contain the region variables created to replace the bound regions
//! in the input types, but it also contains 'intermediate' variables
//! created to represent the LUB/GLB of individual regions.
//! Basically, when asked to compute the LUB/GLB of a region variable
//! with another region, the inferencer cannot oblige immediately
//! since the values of that variables are not known. Therefore, it
//! creates a new variable that is related to the two regions. For
//! example, the LUB of two variables `$x` and `$y` is a fresh
//! variable `$z` that is constrained such that `$x <= $z` and `$y <=
//! $z`. So `V` will contain these intermediate variables as well.
//!
//! The other important factor in deciding how to replace a region in T is
//! the function `Tainted($r)` which, for a region variable, identifies
......
......@@ -11,14 +11,13 @@
//! Helper routines for higher-ranked things. See the `doc` module at
//! the end of the file for details.
use super::{combine, cres, InferCtxt, HigherRankedType};
use super::{combine, CombinedSnapshot, cres, InferCtxt, HigherRankedType};
use super::combine::Combine;
use super::region_inference::{RegionMark};
use middle::ty::{mod, Ty, replace_late_bound_regions};
use middle::ty_fold::{mod, HigherRankedFoldable, TypeFoldable};
use syntax::codemap::Span;
use util::nodemap::FnvHashMap;
use util::nodemap::{FnvHashMap, FnvHashSet};
use util::ppaux::{bound_region_to_string, Repr};
pub trait HigherRankedCombineable<'tcx>: HigherRankedFoldable<'tcx> +
......@@ -37,6 +36,14 @@ fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
where T : HigherRankedCombineable<'tcx>;
}
trait InferCtxtExt<'tcx> {
fn tainted_regions(&self, snapshot: &CombinedSnapshot, r: ty::Region) -> Vec<ty::Region>;
fn region_vars_confined_to_snapshot(&self,
snapshot: &CombinedSnapshot)
-> Vec<ty::RegionVid>;
}
impl<'tcx,C> HigherRankedRelations<'tcx> for C
where C : Combine<'tcx>
{
......@@ -54,114 +61,115 @@ fn higher_ranked_sub<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
// please see the large comment at the end of the file in the (inlined) module
// `doc`.
// Make a mark so we can examine "all bindings that were
// Start a snapshot so we can examine "all bindings that were
// created as part of this type comparison".
let mark = self.infcx().region_vars.mark();
// First, we instantiate each bound region in the subtype with a fresh
// region variable.
let (a_prime, _) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(),
HigherRankedType,
a);
// Second, we instantiate each bound region in the supertype with a
// fresh concrete region.
let (b_prime, skol_map) = {
replace_late_bound_regions(self.tcx(), b, |br, _| {
let skol = self.infcx().region_vars.new_skolemized(br);
debug!("Bound region {} skolemized to {}",
bound_region_to_string(self.tcx(), "", false, br),
skol);
skol
})
};
debug!("a_prime={}", a_prime.repr(self.tcx()));
debug!("b_prime={}", b_prime.repr(self.tcx()));
// Compare types now that bound regions have been replaced.
let result = try!(HigherRankedCombineable::super_combine(self, &a_prime, &b_prime));
// Presuming type comparison succeeds, we need to check
// that the skolemized regions do not "leak".
let new_vars =
self.infcx().region_vars.vars_created_since_mark(mark);
for (&skol_br, &skol) in skol_map.iter() {
let tainted = self.infcx().region_vars.tainted(mark, skol);
for tainted_region in tainted.iter() {
// Each skolemized should only be relatable to itself
// or new variables:
match *tainted_region {
ty::ReInfer(ty::ReVar(ref vid)) => {
if new_vars.iter().any(|x| x == vid) { continue; }
}
_ => {
if *tainted_region == skol { continue; }
return self.infcx().try(|snapshot| {
// First, we instantiate each bound region in the subtype with a fresh
// region variable.
let (a_prime, _) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(),
HigherRankedType,
a);
// Second, we instantiate each bound region in the supertype with a
// fresh concrete region.
let (b_prime, skol_map) = {
replace_late_bound_regions(self.tcx(), b, |br, _| {
let skol = self.infcx().region_vars.new_skolemized(br);
debug!("Bound region {} skolemized to {}",
bound_region_to_string(self.tcx(), "", false, br),
skol);
skol
})
};
debug!("a_prime={}", a_prime.repr(self.tcx()));
debug!("b_prime={}", b_prime.repr(self.tcx()));
// Compare types now that bound regions have been replaced.
let result = try!(HigherRankedCombineable::super_combine(self, &a_prime, &b_prime));
// Presuming type comparison succeeds, we need to check
// that the skolemized regions do not "leak".
let new_vars = self.infcx().region_vars_confined_to_snapshot(snapshot);
for (&skol_br, &skol) in skol_map.iter() {
let tainted = self.infcx().tainted_regions(snapshot, skol);
for tainted_region in tainted.iter() {
// Each skolemized should only be relatable to itself
// or new variables:
match *tainted_region {
ty::ReInfer(ty::ReVar(ref vid)) => {
if new_vars.iter().any(|x| x == vid) { continue; }
}
_ => {
if *tainted_region == skol { continue; }
}
};
// A is not as polymorphic as B:
if self.a_is_expected() {
debug!("Not as polymorphic!");
return Err(ty::terr_regions_insufficiently_polymorphic(skol_br,
*tainted_region));
} else {
debug!("Overly polymorphic!");
return Err(ty::terr_regions_overly_polymorphic(skol_br,
*tainted_region));
}
};
// A is not as polymorphic as B:
if self.a_is_expected() {
debug!("Not as polymorphic!");
return Err(ty::terr_regions_insufficiently_polymorphic(
skol_br, *tainted_region));
} else {
debug!("Overly polymorphic!");
return Err(ty::terr_regions_overly_polymorphic(
skol_br, *tainted_region));
}
}
}
debug!("higher_ranked_sub: OK result={}",
result.repr(self.tcx()));
debug!("higher_ranked_sub: OK result={}",
result.repr(self.tcx()));
return Ok(result);
Ok(result)
});
}
fn higher_ranked_lub<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
where T : HigherRankedCombineable<'tcx>
{
// Make a mark so we can examine "all bindings that were
// Start a snapshot so we can examine "all bindings that were
// created as part of this type comparison".
let mark = self.infcx().region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let span = self.trace().origin.span();
let (a_with_fresh, a_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
span, HigherRankedType, a);
let (b_with_fresh, _) =
self.infcx().replace_late_bound_regions_with_fresh_var(
span, HigherRankedType, b);
// Collect constraints.
let result0 =
try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
debug!("lub result0 = {}", result0.repr(self.tcx()));
// Generalize the regions appearing in result0 if possible
let new_vars = self.infcx().region_vars.vars_created_since_mark(mark);
let span = self.trace().origin.span();
let result1 =
fold_regions_in(
self.tcx(),
&result0,
|r, debruijn| generalize_region(self.infcx(), span, mark, debruijn,
new_vars.as_slice(), &a_map, r));
debug!("lub({},{}) = {}",
a.repr(self.tcx()),
b.repr(self.tcx()),
result1.repr(self.tcx()));
return Ok(result1);
return self.infcx().try(|snapshot| {
// Instantiate each bound region with a fresh region variable.
let span = self.trace().origin.span();
let (a_with_fresh, a_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
span, HigherRankedType, a);
let (b_with_fresh, _) =
self.infcx().replace_late_bound_regions_with_fresh_var(
span, HigherRankedType, b);
// Collect constraints.
let result0 =
try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
let result0 =
self.infcx().resolve_type_vars_if_possible(&result0);
debug!("lub result0 = {}", result0.repr(self.tcx()));
// Generalize the regions appearing in result0 if possible
let new_vars = self.infcx().region_vars_confined_to_snapshot(snapshot);
let span = self.trace().origin.span();
let result1 =
fold_regions_in(
self.tcx(),
&result0,
|r, debruijn| generalize_region(self.infcx(), span, snapshot, debruijn,
new_vars.as_slice(), &a_map, r));
debug!("lub({},{}) = {}",
a.repr(self.tcx()),
b.repr(self.tcx()),
result1.repr(self.tcx()));
Ok(result1)
});
fn generalize_region(infcx: &InferCtxt,
span: Span,
mark: RegionMark,
snapshot: &CombinedSnapshot,
debruijn: ty::DebruijnIndex,
new_vars: &[ty::RegionVid],
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
......@@ -174,7 +182,7 @@ fn generalize_region(infcx: &InferCtxt,
return r0;
}
let tainted = infcx.region_vars.tainted(mark, r0);
let tainted = infcx.tainted_regions(snapshot, r0);
// Variables created during LUB computation which are
// *related* to regions that pre-date the LUB computation
......@@ -215,47 +223,49 @@ fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
debug!("{}.higher_ranked_glb({}, {})",
self.tag(), a.repr(self.tcx()), b.repr(self.tcx()));
// Make a mark so we can examine "all bindings that were
// Make a snapshot so we can examine "all bindings that were
// created as part of this type comparison".
let mark = self.infcx().region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(), HigherRankedType, a);
let (b_with_fresh, b_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(), HigherRankedType, b);
let a_vars = var_ids(self, &a_map);
let b_vars = var_ids(self, &b_map);
// Collect constraints.
let result0 =
try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
debug!("glb result0 = {}", result0.repr(self.tcx()));
// Generalize the regions appearing in fn_ty0 if possible
let new_vars = self.infcx().region_vars.vars_created_since_mark(mark);
let span = self.trace().origin.span();
let result1 =
fold_regions_in(
self.tcx(),
&result0,
|r, debruijn| generalize_region(self.infcx(), span, mark, debruijn,
new_vars.as_slice(),
&a_map, a_vars.as_slice(), b_vars.as_slice(),
r));
debug!("glb({},{}) = {}",
a.repr(self.tcx()),
b.repr(self.tcx()),
result1.repr(self.tcx()));
return Ok(result1);
return self.infcx().try(|snapshot| {
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(), HigherRankedType, a);
let (b_with_fresh, b_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(), HigherRankedType, b);
let a_vars = var_ids(self, &a_map);
let b_vars = var_ids(self, &b_map);
// Collect constraints.
let result0 =
try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
let result0 =
self.infcx().resolve_type_vars_if_possible(&result0);
debug!("glb result0 = {}", result0.repr(self.tcx()));
// Generalize the regions appearing in fn_ty0 if possible
let new_vars = self.infcx().region_vars_confined_to_snapshot(snapshot);
let span = self.trace().origin.span();
let result1 =
fold_regions_in(
self.tcx(),
&result0,
|r, debruijn| generalize_region(self.infcx(), span, snapshot, debruijn,
new_vars.as_slice(),
&a_map, a_vars.as_slice(), b_vars.as_slice(),
r));
debug!("glb({},{}) = {}",
a.repr(self.tcx()),
b.repr(self.tcx()),
result1.repr(self.tcx()));
Ok(result1)
});
fn generalize_region(infcx: &InferCtxt,
span: Span,
mark: RegionMark,
snapshot: &CombinedSnapshot,
debruijn: ty::DebruijnIndex,
new_vars: &[ty::RegionVid],
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
......@@ -267,7 +277,7 @@ fn generalize_region(infcx: &InferCtxt,
return r0;
}
let tainted = infcx.region_vars.tainted(mark, r0);
let tainted = infcx.tainted_regions(snapshot, r0);
let mut a_r = None;
let mut b_r = None;
......@@ -443,3 +453,86 @@ fn fold_regions_in<'tcx, T, F>(tcx: &ty::ctxt<'tcx>, value: &T, mut fldr: F) ->
}))
}
impl<'a,'tcx> InferCtxtExt<'tcx> for InferCtxt<'a,'tcx> {
fn tainted_regions(&self, snapshot: &CombinedSnapshot, r: ty::Region) -> Vec<ty::Region> {
self.region_vars.tainted(&snapshot.region_vars_snapshot, r)
}
fn region_vars_confined_to_snapshot(&self,
snapshot: &CombinedSnapshot)
-> Vec<ty::RegionVid>
{
/*!
* Returns the set of region variables that do not affect any
* types/regions which existed before `snapshot` was
* started. This is used in the sub/lub/glb computations. The
* idea here is that when we are computing lub/glb of two
* regions, we sometimes create intermediate region variables.
* Those region variables may touch some of the skolemized or
* other "forbidden" regions we created to replace bound
* regions, but they don't really represent an "external"
* constraint.
*
* However, sometimes fresh variables are created for other
* purposes too, and those *may* represent an external
* constraint. In particular, when a type variable is
* instantiated, we create region variables for all the
* regions that appear within, and if that type variable
* pre-existed the snapshot, then those region variables
* represent external constraints.
*
* An example appears in the unit test
* `sub_free_bound_false_infer`. In this test, we want to
* know whether
*
* ```rust
* fn(_#0t) <: for<'a> fn(&'a int)
* ```
*
* Note that the subtype has a type variable. Because the type
* variable can't be instantiated with a region that is bound
* in the fn signature, this comparison ought to fail. But if
* we're not careful, it will succeed.
*
* The reason is that when we walk through the subtyping
* algorith, we begin by replacing `'a` with a skolemized
* variable `'0`. We then have `fn(_#0t) <: fn(&'0 int)`. This
* can be made true by unifying `_#0t` with `&'0 int`. In the
* process, we create a fresh variable for the skolemized
* region, `'$0`, and hence we have that `_#0t == &'$0
* int`. However, because `'$0` was created during the sub
* computation, if we're not careful we will erroneously
* assume it is one of the transient region variables
* representing a lub/glb internally. Not good.
*
* To prevent this, we check for type variables which were
* unified during the snapshot, and say that any region
* variable created during the snapshot but which finds its
* way into a type variable is considered to "escape" the
* snapshot.
*/
let mut region_vars =
self.region_vars.vars_created_since_snapshot(&snapshot.region_vars_snapshot);
let escaping_types =
self.type_variables.borrow().types_escaping_snapshot(&snapshot.type_snapshot);
let escaping_region_vars: FnvHashSet<_> =
escaping_types
.iter()
.flat_map(|&t| ty_fold::collect_regions(self.tcx, &t).into_iter())
.collect();
region_vars.retain(|&region_vid| {
let r = ty::ReInfer(ty::ReVar(region_vid));
!escaping_region_vars.contains(&r)
});
debug!("region_vars_confined_to_snapshot: region_vars={} escaping_types={}",
region_vars.repr(self.tcx),
escaping_types.repr(self.tcx));
region_vars
}
}
......@@ -520,6 +520,7 @@ pub fn uok<'tcx>() -> ures<'tcx> {
Ok(())
}
#[must_use = "once you start a snapshot, you should always consume it"]
pub struct CombinedSnapshot {
type_snapshot: type_variable::Snapshot,
int_snapshot: unify::Snapshot<ty::IntVid>,
......@@ -629,16 +630,16 @@ pub fn commit_unconditionally<R, F>(&self, f: F) -> R where
pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where
F: FnOnce() -> Result<T, E>
{
self.commit_unconditionally(move || self.try(move || f()))
self.commit_unconditionally(move || self.try(move |_| f()))
}
/// Execute `f`, unroll bindings on panic
pub fn try<T, E, F>(&self, f: F) -> Result<T, E> where
F: FnOnce() -> Result<T, E>
F: FnOnce(&CombinedSnapshot) -> Result<T, E>
{
debug!("try()");
let snapshot = self.start_snapshot();
let r = f();
let r = f(&snapshot);
debug!("try() -- r.is_ok() = {}", r.is_ok());
match r {
Ok(_) => {
......@@ -821,7 +822,7 @@ pub fn resolve_regions_and_report_errors(&self, subject_node_id: ast::NodeId) {
pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
ty_to_string(self.tcx,
self.resolve_type_vars_if_possible(t))
self.resolve_type_vars_if_possible(&t))
}
pub fn tys_to_string(&self, ts: &[Ty<'tcx>]) -> String {
......@@ -830,7 +831,7 @@ pub fn tys_to_string(&self, ts: &[Ty<'tcx>]) -> String {
}
pub fn trait_ref_to_string(&self, t: &Rc<ty::TraitRef<'tcx>>) -> String {
let t = self.resolve_type_vars_in_trait_ref_if_possible(&**t);
let t = self.resolve_type_vars_if_possible(&**t);
trait_ref_to_string(self.tcx, &t)
}
......@@ -867,35 +868,9 @@ pub fn shallow_resolve(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
}
}
pub fn resolve_type_vars_if_possible(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
match resolve_type(self,
None,
typ, resolve_nested_tvar | resolve_ivar) {
Ok(new_type) => new_type,
Err(_) => typ
}
}
pub fn resolve_type_vars_in_trait_ref_if_possible(&self,
trait_ref: &ty::TraitRef<'tcx>)
-> ty::TraitRef<'tcx> {
// make up a dummy type just to reuse/abuse the resolve machinery
let dummy0 = ty::mk_trait(self.tcx,
(*trait_ref).clone(),
ty::region_existential_bound(ty::ReStatic));
let dummy1 = self.resolve_type_vars_if_possible(dummy0);
match dummy1.sty {
ty::ty_trait(box ty::TyTrait { ref principal, .. }) => {
(*principal).clone()
}
_ => {
self.tcx.sess.bug(
format!("resolve_type_vars_if_possible() yielded {} \
when supplied with {}",
self.ty_to_string(dummy0),
self.ty_to_string(dummy1)).as_slice());
}
}
pub fn resolve_type_vars_if_possible<T:TypeFoldable<'tcx>>(&self, value: &T) -> T {
let mut r = resolve::DeepTypeResolver::new(self);
value.fold_with(&mut r)
}
// [Note-Type-error-reporting]
......@@ -929,9 +904,7 @@ pub fn type_error_message_str_with_expected<M>(&self,
{
debug!("hi! expected_ty = {}, actual_ty = {}", expected_ty, actual_ty);
let resolved_expected = expected_ty.map(|e_ty| {
self.resolve_type_vars_if_possible(e_ty)
});
let resolved_expected = expected_ty.map(|e_ty| self.resolve_type_vars_if_possible(&e_ty));
match resolved_expected {
Some(t) if ty::type_is_error(t) => (),
......@@ -958,7 +931,7 @@ pub fn type_error_message<M>(&self,
err: Option<&ty::type_err<'tcx>>) where
M: FnOnce(String) -> String,
{
let actual_ty = self.resolve_type_vars_if_possible(actual_ty);
let actual_ty = self.resolve_type_vars_if_possible(&actual_ty);
// Don't report an error if actual type is ty_err.
if ty::type_is_error(actual_ty) {
......
......@@ -81,7 +81,6 @@ impl Copy for TwoRegions {}
pub enum UndoLogEntry {
OpenSnapshot,
CommitedSnapshot,
Mark,
AddVar(RegionVid),
AddConstraint(Constraint),
AddVerify(uint),
......@@ -225,19 +224,11 @@ pub struct RegionVarBindings<'a, 'tcx: 'a> {
}
#[deriving(Show)]
#[allow(missing_copy_implementations)]
pub struct RegionSnapshot {
length: uint
}
impl Copy for RegionSnapshot {}
#[deriving(Show)]
pub struct RegionMark {
length: uint
}
impl Copy for RegionMark {}
impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
pub fn new(tcx: &'a ty::ctxt<'tcx>) -> RegionVarBindings<'a, 'tcx> {
RegionVarBindings {
......@@ -266,13 +257,6 @@ pub fn start_snapshot(&self) -> RegionSnapshot {
RegionSnapshot { length: length }
}
pub fn mark(&self) -> RegionMark {
let length = self.undo_log.borrow().len();
debug!("RegionVarBindings: mark({})", length);
self.undo_log.borrow_mut().push(Mark);
RegionMark { length: length }
}
pub fn commit(&self, snapshot: RegionSnapshot) {
debug!("RegionVarBindings: commit({})", snapshot.length);
assert!(self.undo_log.borrow().len() > snapshot.length);
......@@ -296,7 +280,7 @@ pub fn rollback_to(&self, snapshot: RegionSnapshot) {
OpenSnapshot => {
panic!("Failure to observe stack discipline");
}
Mark | CommitedSnapshot => { }
CommitedSnapshot => { }
AddVar(vid) => {
let mut var_origins = self.var_origins.borrow_mut();
var_origins.pop().unwrap();
......@@ -597,8 +581,8 @@ pub fn combine_vars<F>(&self,
ReInfer(ReVar(c))
}
pub fn vars_created_since_mark(&self, mark: RegionMark)
-> Vec<RegionVid>
pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot)
-> Vec<RegionVid>
{
self.undo_log.borrow()
.slice_from(mark.length)
......@@ -613,7 +597,7 @@ pub fn vars_created_since_mark(&self, mark: RegionMark)
/// Computes all regions that have been related to `r0` in any way since the mark `mark` was
/// made---`r0` itself will be the first entry. This is used when checking whether skolemized
/// regions are being improperly related to other regions.
pub fn tainted(&self, mark: RegionMark, r0: Region) -> Vec<Region> {
pub fn tainted(&self, mark: &RegionSnapshot, r0: Region) -> Vec<Region> {
debug!("tainted(mark={}, r0={})", mark, r0.repr(self.tcx));
let _indenter = indenter();
......@@ -668,7 +652,6 @@ pub fn tainted(&self, mark: RegionMark, r0: Region) -> Vec<Region> {
}
}
&AddCombination(..) |
&Mark |
&AddVar(..) |
&OpenSnapshot |
&CommitedSnapshot => {
......
......@@ -258,3 +258,38 @@ pub fn resolve_float_var(&mut self, vid: FloatVid) -> Ty<'tcx> {
}
}
}
///////////////////////////////////////////////////////////////////////////
/// DEEP TYPE RESOLVER
///
/// This kind of resolver can be used at any time. It simply replaces
/// type variables that have been unified with the things they have
/// been unified with (similar to `shallow_resolve`, but deep). This is
/// useful for printing messages etc but also required at various
/// points for correctness.
pub struct DeepTypeResolver<'a, 'tcx:'a> {
infcx: &'a InferCtxt<'a, 'tcx>,
}
impl<'a, 'tcx> DeepTypeResolver<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> DeepTypeResolver<'a, 'tcx> {
DeepTypeResolver { infcx: infcx }
}
}
impl<'a, 'tcx> ty_fold::TypeFolder<'tcx> for DeepTypeResolver<'a, 'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> {
self.infcx.tcx
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if !ty::type_has_ty_infer(t) {
t // micro-optimize -- if there is nothing in this type that this fold affects...
} else {
let t0 = self.infcx.shallow_resolve(t);
ty_fold::super_fold_ty(self, t0)
}
}
}
......@@ -13,7 +13,9 @@
use self::UndoEntry::*;
use middle::ty::{mod, Ty};
use std::cmp::min;
use std::mem;
use std::uint;
use util::snapshot_vec as sv;
pub struct TypeVariableTable<'tcx> {
......@@ -78,7 +80,6 @@ pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
///
/// Precondition: neither `a` nor `b` are known.
pub fn relate_vars(&mut self, a: ty::TyVid, dir: RelationDir, b: ty::TyVid) {
if a != b {
self.relations(a).push((dir, b));
self.relations(b).push((dir.opposite(), a));
......@@ -151,6 +152,49 @@ pub fn rollback_to(&mut self, s: Snapshot) {
pub fn commit(&mut self, s: Snapshot) {
self.values.commit(s.snapshot);
}
pub fn types_escaping_snapshot(&self, s: &Snapshot) -> Vec<Ty<'tcx>> {
/*!
* Find the set of type variables that existed *before* `s`
* but which have only been unified since `s` started, and
* return the types with which they were unified. So if we had
* a type variable `V0`, then we started the snapshot, then we
* created a type variable `V1`, unifed `V0` with `T0`, and
* unified `V1` with `T1`, this function would return `{T0}`.
*/
let mut new_elem_threshold = uint::MAX;
let mut escaping_types = Vec::new();
let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len());
for action in actions_since_snapshot.iter() {
match *action {
sv::UndoLog::NewElem(index) => {
// if any new variables were created during the
// snapshot, remember the lower index (which will
// always be the first one we see). Note that this
// action must precede those variables being
// specified.
new_elem_threshold = min(new_elem_threshold, index);
debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold);
}
sv::UndoLog::Other(SpecifyVar(vid, _)) => {
if vid.index < new_elem_threshold {
// quick check to see if this variable was
// created since the snapshot started or not.
let escaping_type = self.probe(vid).unwrap();
escaping_types.push(escaping_type);
}
debug!("SpecifyVar({}) new_elem_threshold={}", vid, new_elem_threshold);
}
_ => { }
}
}
escaping_types
}
}
impl<'tcx> sv::SnapshotVecDelegate<TypeVariableData<'tcx>,UndoEntry> for Delegate {
......
......@@ -790,6 +790,17 @@ pub fn new(tcx: &'a ty::ctxt<'tcx>, fld_r: F) -> RegionFolder<'a, 'tcx, F> {
}
}
pub fn collect_regions<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> Vec<ty::Region>
where T : TypeFoldable<'tcx>
{
let mut vec = Vec::new();
{
let mut folder = RegionFolder::new(tcx, |r, _| { vec.push(r); r });
value.fold_with(&mut folder);
}
vec
}
impl<'a, 'tcx, F> TypeFolder<'tcx> for RegionFolder<'a, 'tcx, F> where
F: FnMut(ty::Region, uint) -> ty::Region,
{
......
......@@ -23,7 +23,7 @@
use std::mem;
#[deriving(PartialEq)]
enum UndoLog<T,U> {
pub enum UndoLog<T,U> {
/// Indicates where a snapshot started.
OpenSnapshot,
......@@ -113,6 +113,12 @@ pub fn start_snapshot(&mut self) -> Snapshot {
Snapshot { length: length }
}
pub fn actions_since_snapshot(&self,
snapshot: &Snapshot)
-> &[UndoLog<T,U>] {
self.undo_log[snapshot.length..]
}
fn assert_open_snapshot(&self, snapshot: &Snapshot) {
// Or else there was a failure to follow a stack discipline:
assert!(self.undo_log.len() > snapshot.length);
......
......@@ -502,6 +502,26 @@ fn sub_free_bound_false_infer() {
})
}
#[test]
fn lub_free_bound_infer() {
//! Test result of:
//!
//! LUB(fn(_#1), for<'b> fn(&'b int))
//!
//! This should yield `fn(&'_ int)`. We check
//! that it yields `fn(&'x int)` for some free `'x`,
//! anyhow.
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
let t_infer1 = env.infcx.next_ty_var();
let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_rptr_free1 = env.t_rptr_free(0, 1);
env.check_lub(env.t_fn(&[t_infer1], ty::mk_int()),
env.t_fn(&[t_rptr_bound1], ty::mk_int()),
env.t_fn(&[t_rptr_free1], ty::mk_int()));
});
}
#[test]
fn lub_bound_bound() {
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
......@@ -605,6 +625,28 @@ fn glb_bound_free() {
})
}
#[test]
fn glb_bound_free_infer() {
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_infer1 = env.infcx.next_ty_var();
// compute GLB(fn(_) -> int, for<'b> fn(&'b int) -> int),
// which should yield for<'b> fn(&'b int) -> int
env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
env.t_fn(&[t_infer1], ty::mk_int()),
env.t_fn(&[t_rptr_bound1], ty::mk_int()));
// as a side-effect, computing GLB should unify `_` with
// `&'_ int`
let t_resolve1 = env.infcx.shallow_resolve(t_infer1);
match t_resolve1.sty {
ty::ty_rptr(..) => { }
_ => { panic!("t_resolve1={}", t_resolve1.repr(env.infcx.tcx)); }
}
})
}
#[test]
fn glb_bound_static() {
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
......
......@@ -196,7 +196,7 @@ fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
debug!("found object type {}", kind);
let arg_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 0);
let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(arg_param_ty);
let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(&arg_param_ty);
debug!("arg_param_ty {}", arg_param_ty.repr(tcx));
let input_tys = match arg_param_ty.sty {
......@@ -206,7 +206,7 @@ fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
debug!("input_tys {}", input_tys.repr(tcx));
let ret_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 1);
let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(ret_param_ty);
let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(&ret_param_ty);
debug!("ret_param_ty {}", ret_param_ty.repr(tcx));
let fn_sig = ty::FnSig {
......
......@@ -100,7 +100,7 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr.repr(fcx.tcx()),
self_expr.repr(fcx.tcx()));
let self_ty = fcx.infcx().resolve_type_vars_if_possible(self_ty);
let self_ty = fcx.infcx().resolve_type_vars_if_possible(&self_ty);
let pick = try!(probe::probe(fcx, span, method_name, self_ty, call_expr.id));
Ok(confirm::confirm(fcx, span, self_expr, call_expr, self_ty, pick, supplied_method_types))
}
......
......@@ -1638,7 +1638,7 @@ pub fn local_ty(&self, span: Span, nid: ast::NodeId) -> Ty<'tcx> {
pub fn default_diverging_type_variables_to_nil(&self) {
for (_, &ref ty) in self.inh.node_types.borrow_mut().iter_mut() {
if self.infcx().type_var_diverges(self.infcx().resolve_type_vars_if_possible(*ty)) {
if self.infcx().type_var_diverges(self.infcx().resolve_type_vars_if_possible(ty)) {
demand::eqtype(self, codemap::DUMMY_SP, *ty, ty::mk_nil(self.tcx()));
}
}
......@@ -2486,7 +2486,7 @@ fn lookup_method_for_for_loop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let method_type = match method {
Some(ref method) => method.ty,
None => {
let true_expr_type = fcx.infcx().resolve_type_vars_if_possible(expr_type);
let true_expr_type = fcx.infcx().resolve_type_vars_if_possible(&expr_type);
if !ty::type_is_error(true_expr_type) {
let ty_string = fcx.infcx().ty_to_string(true_expr_type);
......@@ -3976,7 +3976,7 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
vtable::select_new_fcx_obligations(fcx);
debug!("ExprForLoop each item has type {}",
fcx.infcx().resolve_type_vars_if_possible(typ).repr(fcx.tcx()));
fcx.infcx().resolve_type_vars_if_possible(&typ).repr(fcx.tcx()));
let pcx = pat_ctxt {
fcx: fcx,
......@@ -4371,11 +4371,11 @@ fn resolve<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
}
ExpectCastableToType(t) => {
ExpectCastableToType(
fcx.infcx().resolve_type_vars_if_possible(t))
fcx.infcx().resolve_type_vars_if_possible(&t))
}
ExpectHasType(t) => {
ExpectHasType(
fcx.infcx().resolve_type_vars_if_possible(t))
fcx.infcx().resolve_type_vars_if_possible(&t))
}
}
}
......
......@@ -331,7 +331,7 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
match obligation.trait_ref {
ty::Predicate::Trait(ref trait_ref) => {
let trait_ref =
fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(&**trait_ref);
fcx.infcx().resolve_type_vars_if_possible(&**trait_ref);
fcx.tcx().sess.span_err(
obligation.cause.span,
format!(
......@@ -341,8 +341,8 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
ty::Predicate::Equate(a, b) => {
let a = fcx.infcx().resolve_type_vars_if_possible(a);
let b = fcx.infcx().resolve_type_vars_if_possible(b);
let a = fcx.infcx().resolve_type_vars_if_possible(&a);
let b = fcx.infcx().resolve_type_vars_if_possible(&b);
fcx.tcx().sess.span_err(
obligation.cause.span,
format!(
......@@ -373,8 +373,7 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
match obligation.trait_ref {
ty::Predicate::Trait(ref trait_ref) => {
let trait_ref =
fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
&**trait_ref);
fcx.infcx().resolve_type_vars_if_possible(&**trait_ref);
if !ty::type_is_error(trait_ref.self_ty()) {
fcx.tcx().sess.span_err(
obligation.cause.span,
......@@ -387,8 +386,8 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
ty::Predicate::Equate(a, b) => {
let a = fcx.infcx().resolve_type_vars_if_possible(a);
let b = fcx.infcx().resolve_type_vars_if_possible(b);
let a = fcx.infcx().resolve_type_vars_if_possible(&a);
let b = fcx.infcx().resolve_type_vars_if_possible(&b);
let err = infer::can_mk_eqty(fcx.infcx(), a, b).unwrap_err();
fcx.tcx().sess.span_err(
obligation.cause.span,
......@@ -413,10 +412,10 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, ref e) => {
let expected_trait_ref =
fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
fcx.infcx().resolve_type_vars_if_possible(
&**expected_trait_ref);
let actual_trait_ref =
fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
fcx.infcx().resolve_type_vars_if_possible(
&**actual_trait_ref);
if !ty::type_is_error(actual_trait_ref.self_ty()) {
fcx.tcx().sess.span_err(
......@@ -443,7 +442,7 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let trait_ref = match obligation.trait_ref {
ty::Predicate::Trait(ref trait_ref) => {
fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(&**trait_ref)
fcx.infcx().resolve_type_vars_if_possible(&**trait_ref)
}
_ => {
fcx.tcx().sess.span_bug(
......
......@@ -14,3 +14,6 @@ fn main() {
let int x = 5;
match x;
}
fn main() {
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册