diff --git a/src/librustc/middle/infer/higher_ranked/mod.rs b/src/librustc/middle/infer/higher_ranked/mod.rs index fb8da9b65daee..0c539a5d0e0c2 100644 --- a/src/librustc/middle/infer/higher_ranked/mod.rs +++ b/src/librustc/middle/infer/higher_ranked/mod.rs @@ -14,7 +14,6 @@ use super::{CombinedSnapshot, InferCtxt, HigherRankedType, SkolemizationMap}; use super::combine::CombineFields; -use middle::subst; use middle::ty::{self, TypeError, Binder}; use middle::ty_fold::{self, TypeFoldable}; use middle::ty_relate::{Relate, RelateResult, TypeRelation}; @@ -455,63 +454,6 @@ impl<'a,'tcx> InferCtxtExt for InferCtxt<'a,'tcx> { } } -/// Constructs and returns a substitution that, for a given type -/// scheme parameterized by `generics`, will replace every generic -/// parameter in the type with a skolemized type/region (which one can -/// think of as a "fresh constant", except at the type/region level of -/// reasoning). -/// -/// Since we currently represent bound/free type parameters in the -/// same way, this only has an effect on regions. -/// -/// (Note that unlike a substitution from `ty::construct_free_substs`, -/// this inserts skolemized regions rather than free regions; this -/// allows one to use `fn leak_check` to catch attmepts to unify the -/// skolemized regions with e.g. the `'static` lifetime) -pub fn construct_skolemized_substs<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, - generics: &ty::Generics<'tcx>, - snapshot: &CombinedSnapshot) - -> (subst::Substs<'tcx>, SkolemizationMap) -{ - let mut map = FnvHashMap(); - - // map T => T - let mut types = subst::VecPerParamSpace::empty(); - push_types_from_defs(infcx.tcx, &mut types, generics.types.as_slice()); - - // map early- or late-bound 'a => fresh 'a - let mut regions = subst::VecPerParamSpace::empty(); - push_region_params(infcx, &mut map, &mut regions, generics.regions.as_slice(), snapshot); - - let substs = subst::Substs { types: types, - regions: subst::NonerasedRegions(regions) }; - return (substs, map); - - fn push_region_params<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, - map: &mut SkolemizationMap, - regions: &mut subst::VecPerParamSpace, - region_params: &[ty::RegionParameterDef], - snapshot: &CombinedSnapshot) - { - for r in region_params { - let br = r.to_bound_region(); - let skol_var = infcx.region_vars.new_skolemized(br, &snapshot.region_vars_snapshot); - let sanity_check = map.insert(br, skol_var); - assert!(sanity_check.is_none()); - regions.push(r.space, skol_var); - } - } - - fn push_types_from_defs<'tcx>(tcx: &ty::ctxt<'tcx>, - types: &mut subst::VecPerParamSpace>, - defs: &[ty::TypeParameterDef<'tcx>]) { - for def in defs { - let ty = tcx.mk_param_from_def(def); - types.push(def.space, ty); - } - } -} - pub fn skolemize_late_bound_regions<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, binder: &ty::Binder, snapshot: &CombinedSnapshot) diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 4e8ed01c6b9e0..158ef745de33f 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -948,15 +948,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }) } - pub fn construct_skolemized_subst(&self, - generics: &ty::Generics<'tcx>, - snapshot: &CombinedSnapshot) - -> (subst::Substs<'tcx>, SkolemizationMap) { - /*! See `higher_ranked::construct_skolemized_subst` */ - - higher_ranked::construct_skolemized_substs(self, generics, snapshot) - } - pub fn skolemize_late_bound_regions(&self, value: &ty::Binder, snapshot: &CombinedSnapshot) diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index 1785fe09f87a4..d81f8e0ae9093 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -373,7 +373,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { let sc = self.skolemization_count.get(); self.skolemization_count.set(sc + 1); - ReSkolemized(sc, br) + ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br) } pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index eff560653c140..cb00e3b9baf25 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1502,7 +1502,62 @@ pub struct DebruijnIndex { pub depth: u32, } -/// Representation of regions: +/// Representation of regions. +/// +/// Unlike types, most region variants are "fictitious", not concrete, +/// regions. Among these, `ReStatic`, `ReEmpty` and `ReScope` are the only +/// ones representing concrete regions. +/// +/// ## Bound Regions +/// +/// These are regions that are stored behind a binder and must be substituted +/// with some concrete region before being used. There are 2 kind of +/// bound regions: early-bound, which are bound in a TypeScheme/TraitDef, +/// and are substituted by a Substs, and late-bound, which are part of +/// higher-ranked types (e.g. `for<'a> fn(&'a ())`) and are substituted by +/// the likes of `liberate_late_bound_regions`. The distinction exists +/// because higher-ranked lifetimes aren't supported in all places. See [1][2]. +/// +/// Unlike TyParam-s, bound regions are not supposed to exist "in the wild" +/// outside their binder, e.g. in types passed to type inference, and +/// should first be substituted (by skolemized regions, free regions, +/// or region variables). +/// +/// ## Skolemized and Free Regions +/// +/// One often wants to work with bound regions without knowing their precise +/// identity. For example, when checking a function, the lifetime of a borrow +/// can end up being assigned to some region parameter. In these cases, +/// it must be ensured that bounds on the region can't be accidentally +/// assumed without being checked. +/// +/// The process of doing that is called "skolemization". The bound regions +/// are replaced by skolemized markers, which don't satisfy any relation +/// not explicity provided. +/// +/// There are 2 kinds of skolemized regions in rustc: `ReFree` and +/// `ReSkolemized`. When checking an item's body, `ReFree` is supposed +/// to be used. These also support explicit bounds: both the internally-stored +/// *scope*, which the region is assumed to outlive, as well as other +/// relations stored in the `FreeRegionMap`. Note that these relations +/// aren't checked when you `make_subregion` (or `mk_eqty`), only by +/// `resolve_regions_and_report_errors`. +/// +/// When working with higher-ranked types, some region relations aren't +/// yet known, so you can't just call `resolve_regions_and_report_errors`. +/// `ReSkolemized` is designed for this purpose. In these contexts, +/// there's also the risk that some inference variable laying around will +/// get unified with your skolemized region: if you want to check whether +/// `for<'a> Foo<'_>: 'a`, and you substitute your bound region `'a` +/// with a skolemized region `'%a`, the variable `'_` would just be +/// instantiated to the skolemized region `'%a`, which is wrong because +/// the inference variable is supposed to satisfy the relation +/// *for every value of the skolemized region*. To ensure that doesn't +/// happen, you can use `leak_check`. This is more clearly explained +/// by infer/higher_ranked/README.md. +/// +/// [1] http://smallcultfollowing.com/babysteps/blog/2013/10/29/intermingled-parameter-lists/ +/// [2] http://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ #[derive(Clone, PartialEq, Eq, Hash, Copy)] pub enum Region { // Region bound in a type or fn declaration which will be @@ -1532,7 +1587,7 @@ pub enum Region { /// A skolemized region - basically the higher-ranked version of ReFree. /// Should not exist after typeck. - ReSkolemized(u32, BoundRegion), + ReSkolemized(SkolemizedRegionVid, BoundRegion), /// Empty lifetime is for data that is never accessed. /// Bottom in the region lattice. We treat ReEmpty somewhat @@ -2168,6 +2223,11 @@ pub struct RegionVid { pub index: u32 } +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct SkolemizedRegionVid { + pub index: u32 +} + #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum InferTy { TyVar(TyVid), diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 48c2e1e6dca7a..ac51f46a7e943 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -418,7 +418,7 @@ impl fmt::Debug for ty::Region { } ty::ReSkolemized(id, ref bound_region) => { - write!(f, "ReSkolemized({}, {:?})", id, bound_region) + write!(f, "ReSkolemized({}, {:?})", id.index, bound_region) } ty::ReEmpty => write!(f, "ReEmpty") diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index b6a91ce8a64e3..a8c77f863b700 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -11,9 +11,11 @@ use check::regionck::{self, Rcx}; use middle::def_id::{DefId, LOCAL_CRATE}; +use middle::free_region::FreeRegionMap; use middle::infer; use middle::region; use middle::subst::{self, Subst}; +use middle::traits; use middle::ty::{self, Ty}; use util::nodemap::FnvHashSet; @@ -75,53 +77,23 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( drop_impl_ty: &ty::Ty<'tcx>, self_type_did: DefId) -> Result<(), ()> { - // New strategy based on review suggestion from nikomatsakis. - // - // (In the text and code below, "named" denotes "struct/enum", and - // "generic params" denotes "type and region params") - // - // 1. Create fresh skolemized type/region "constants" for each of - // the named type's generic params. Instantiate the named type - // with the fresh constants, yielding `named_skolem`. - // - // 2. Create unification variables for each of the Drop impl's - // generic params. Instantiate the impl's Self's type with the - // unification-vars, yielding `drop_unifier`. - // - // 3. Attempt to unify Self_unif with Type_skolem. If unification - // succeeds, continue (i.e. with the predicate checks). - - let ty::TypeScheme { generics: ref named_type_generics, - ty: named_type } = - tcx.lookup_item_type(self_type_did); - - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); - - infcx.commit_if_ok(|snapshot| { - let (named_type_to_skolem, skol_map) = - infcx.construct_skolemized_subst(named_type_generics, snapshot); - let named_type_skolem = named_type.subst(tcx, &named_type_to_skolem); - - let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP); - let drop_to_unifier = - infcx.fresh_substs_for_generics(drop_impl_span, drop_impl_generics); - let drop_unifier = drop_impl_ty.subst(tcx, &drop_to_unifier); - - if let Ok(()) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span), - named_type_skolem, drop_unifier) { - // Even if we did manage to equate the types, the process - // may have just gathered unsolvable region constraints - // like `R == 'static` (represented as a pair of subregion - // constraints) for some skolemization constant R. - // - // However, the leak_check method allows us to confirm - // that no skolemized regions escaped (i.e. were related - // to other regions in the constraint graph). - if let Ok(()) = infcx.leak_check(&skol_map, snapshot) { - return Ok(()) - } - } + assert!(drop_impl_did.is_local() && self_type_did.is_local()); + + // check that the impl type can be made to match the trait type. + + let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_did.node); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(impl_param_env), true); + + let named_type = tcx.lookup_item_type(self_type_did).ty; + let named_type = named_type.subst(tcx, &infcx.parameter_environment.free_substs); + let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP); + let fresh_impl_substs = + infcx.fresh_substs_for_generics(drop_impl_span, drop_impl_generics); + let fresh_impl_self_ty = drop_impl_ty.subst(tcx, &fresh_impl_substs); + + if let Err(_) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span), + named_type, fresh_impl_self_ty) { span_err!(tcx.sess, drop_impl_span, E0366, "Implementations of Drop cannot be specialized"); let item_span = tcx.map.span(self_type_did.node); @@ -129,7 +101,17 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( "Use same sequence of generic type and region \ parameters that is on the struct/enum definition"); return Err(()); - }) + } + + if let Err(ref errors) = infcx.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { + // this could be reached when we get lazy normalization + traits::report_fulfillment_errors(&infcx, errors); + return Err(()); + } + + let free_regions = FreeRegionMap::new(); + infcx.resolve_regions_and_report_errors(&free_regions, drop_impl_did.node); + Ok(()) } /// Confirms that every predicate imposed by dtor_predicates is diff --git a/src/test/compile-fail/reject-specialized-drops-8142.rs b/src/test/compile-fail/reject-specialized-drops-8142.rs index 1e189528f18c7..b12e26fddf6d2 100644 --- a/src/test/compile-fail/reject-specialized-drops-8142.rs +++ b/src/test/compile-fail/reject-specialized-drops-8142.rs @@ -37,7 +37,9 @@ impl<'al,'adds_bnd> Drop for L<'al,'adds_bnd> where 'adds_bnd:'al { // RE impl<'ml> Drop for M<'ml> { fn drop(&mut self) { } } // ACCEPT impl Drop for N<'static> { fn drop(&mut self) { } } // REJECT -//~^ ERROR Implementations of Drop cannot be specialized +//~^ ERROR mismatched types +//~| expected `N<'n>` +//~| found `N<'static>` impl Drop for O { fn drop(&mut self) { } } // ACCEPT @@ -57,9 +59,9 @@ impl<'t,Bt:'t> Drop for T<'t,Bt> { fn drop(&mut self) { } } // ACCEPT impl Drop for U { fn drop(&mut self) { } } // ACCEPT impl Drop for V { fn drop(&mut self) { } } // REJECT -//~^ERROR Implementations of Drop cannot be specialized +//~^ ERROR Implementations of Drop cannot be specialized impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT -//~^ERROR Implementations of Drop cannot be specialized +//~^ ERROR cannot infer an appropriate lifetime pub fn main() { }