diff --git a/src/librustc/middle/infer/coercion.rs b/src/librustc/middle/infer/coercion.rs index e268160a42b8..43d99ecb427a 100644 --- a/src/librustc/middle/infer/coercion.rs +++ b/src/librustc/middle/infer/coercion.rs @@ -286,7 +286,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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), diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs index 26bba55594b5..1b72a572e9ec 100644 --- a/src/librustc/middle/infer/combine.rs +++ b/src/librustc/middle/infer/combine.rs @@ -706,14 +706,38 @@ impl<'cx, 'tcx> ty_fold::TypeFolder<'tcx> for Generalizer<'cx, '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)) } } diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index ab685dd5dbc1..e1473847c235 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -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 @@ impl<'tcx> Resolvable<'tcx> for Ty<'tcx> { impl<'tcx> Resolvable<'tcx> for Rc> { fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>) -> Rc> { - 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) diff --git a/src/librustc/middle/infer/higher_ranked/doc.rs b/src/librustc/middle/infer/higher_ranked/doc.rs index 2bad3616a05d..f6f254c0e8df 100644 --- a/src/librustc/middle/infer/higher_ranked/doc.rs +++ b/src/librustc/middle/infer/higher_ranked/doc.rs @@ -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 diff --git a/src/librustc/middle/infer/higher_ranked/mod.rs b/src/librustc/middle/infer/higher_ranked/mod.rs index be053afcca43..0a2049bef48f 100644 --- a/src/librustc/middle/infer/higher_ranked/mod.rs +++ b/src/librustc/middle/infer/higher_ranked/mod.rs @@ -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 @@ pub trait HigherRankedRelations<'tcx> { where T : HigherRankedCombineable<'tcx>; } +trait InferCtxtExt<'tcx> { + fn tainted_regions(&self, snapshot: &CombinedSnapshot, r: ty::Region) -> Vec; + + fn region_vars_confined_to_snapshot(&self, + snapshot: &CombinedSnapshot) + -> Vec; +} + impl<'tcx,C> HigherRankedRelations<'tcx> for C where C : Combine<'tcx> { @@ -54,114 +61,115 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C // 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(); + 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); - // 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 + }) + }; - // 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())); - 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)); - // 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; } + } + }; - // 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; } + // 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)); } - _ => { - 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)); } } - } - 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(&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(); + 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); - // 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())); - // 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_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)); - // 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())); - debug!("lub({},{}) = {}", - a.repr(self.tcx()), - b.repr(self.tcx()), - result1.repr(self.tcx())); - - return Ok(result1); + Ok(result1) + }); fn generalize_region(infcx: &InferCtxt, span: Span, - mark: RegionMark, + snapshot: &CombinedSnapshot, debruijn: ty::DebruijnIndex, new_vars: &[ty::RegionVid], a_map: &FnvHashMap, @@ -174,7 +182,7 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C 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 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C 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(); + 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); - // 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())); - // 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_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)); - // 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())); - debug!("glb({},{}) = {}", - a.repr(self.tcx()), - b.repr(self.tcx()), - result1.repr(self.tcx())); - - return Ok(result1); + Ok(result1) + }); fn generalize_region(infcx: &InferCtxt, span: Span, - mark: RegionMark, + snapshot: &CombinedSnapshot, debruijn: ty::DebruijnIndex, new_vars: &[ty::RegionVid], a_map: &FnvHashMap, @@ -267,7 +277,7 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C 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 { + self.region_vars.tainted(&snapshot.region_vars_snapshot, r) + } + + fn region_vars_confined_to_snapshot(&self, + snapshot: &CombinedSnapshot) + -> Vec + { + /*! + * 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(|®ion_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 + } +} diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 14153907ee76..4ad7af713bd2 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -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, @@ -629,16 +630,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn commit_if_ok(&self, f: F) -> Result where F: FnOnce() -> Result { - 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(&self, f: F) -> Result where - F: FnOnce() -> Result + F: FnOnce(&CombinedSnapshot) -> Result { 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 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { 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 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn trait_ref_to_string(&self, t: &Rc>) -> 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 @@ impl<'a, 'tcx> InferCtxt<'a, '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>(&self, value: &T) -> T { + let mut r = resolve::DeepTypeResolver::new(self); + value.fold_with(&mut r) } // [Note-Type-error-reporting] @@ -929,9 +904,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { 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 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { 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) { diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index ca2860ae6b37..fef87c920679 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -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 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { 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 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { 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 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { ReInfer(ReVar(c)) } - pub fn vars_created_since_mark(&self, mark: RegionMark) - -> Vec + pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) + -> Vec { self.undo_log.borrow() .slice_from(mark.length) @@ -613,7 +597,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { /// 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 { + pub fn tainted(&self, mark: &RegionSnapshot, r0: Region) -> Vec { debug!("tainted(mark={}, r0={})", mark, r0.repr(self.tcx)); let _indenter = indenter(); @@ -668,7 +652,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } } &AddCombination(..) | - &Mark | &AddVar(..) | &OpenSnapshot | &CommitedSnapshot => { diff --git a/src/librustc/middle/infer/resolve.rs b/src/librustc/middle/infer/resolve.rs index eaf363ffc74c..5edf78a474b6 100644 --- a/src/librustc/middle/infer/resolve.rs +++ b/src/librustc/middle/infer/resolve.rs @@ -258,3 +258,38 @@ impl<'a, 'tcx> ResolveState<'a, '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) + } + } +} + + diff --git a/src/librustc/middle/infer/type_variable.rs b/src/librustc/middle/infer/type_variable.rs index 766e930486ca..0d7f542535c2 100644 --- a/src/librustc/middle/infer/type_variable.rs +++ b/src/librustc/middle/infer/type_variable.rs @@ -13,7 +13,9 @@ use self::TypeVariableValue::*; 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 @@ impl<'tcx> TypeVariableTable<'tcx> { /// /// 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 @@ impl<'tcx> TypeVariableTable<'tcx> { pub fn commit(&mut self, s: Snapshot) { self.values.commit(s.snapshot); } + + pub fn types_escaping_snapshot(&self, s: &Snapshot) -> Vec> { + /*! + * 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,UndoEntry> for Delegate { diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 4877512ce581..3da9fba0ee89 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -790,6 +790,17 @@ impl<'a, 'tcx, F> RegionFolder<'a, 'tcx, F> where F: FnMut(ty::Region, uint) -> } } +pub fn collect_regions<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> Vec + 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, { diff --git a/src/librustc/util/snapshot_vec.rs b/src/librustc/util/snapshot_vec.rs index e80e8dc53510..749c39d7a6b9 100644 --- a/src/librustc/util/snapshot_vec.rs +++ b/src/librustc/util/snapshot_vec.rs @@ -23,7 +23,7 @@ use self::UndoLog::*; use std::mem; #[deriving(PartialEq)] -enum UndoLog { +pub enum UndoLog { /// Indicates where a snapshot started. OpenSnapshot, @@ -113,6 +113,12 @@ impl> SnapshotVec { Snapshot { length: length } } + pub fn actions_since_snapshot(&self, + snapshot: &Snapshot) + -> &[UndoLog] { + 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); diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 943650ce8a63..14d36432afaa 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -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| { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index e3fec2c8b1df..a4fee8573c59 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -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 { diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index d97a9c9e39b1..ac7bc81b2f87 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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)) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index def82ecd6c85..ea33153f3b1a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1638,7 +1638,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, 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 @@ impl<'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)) } } } diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 415a3d53fb28..664705c89ab2 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -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( diff --git a/src/test/compile-fail/bad-match.rs b/src/test/compile-fail/bad-match.rs index 39c3ed3e2a3d..728b577df1dd 100644 --- a/src/test/compile-fail/bad-match.rs +++ b/src/test/compile-fail/bad-match.rs @@ -14,3 +14,6 @@ fn main() { let int x = 5; match x; } + +fn main() { +}