about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/infer/higher_ranked/mod.rs58
-rw-r--r--src/librustc/middle/infer/mod.rs9
-rw-r--r--src/librustc/middle/infer/region_inference/mod.rs2
-rw-r--r--src/librustc/middle/ty.rs64
-rw-r--r--src/librustc/util/ppaux.rs2
-rw-r--r--src/librustc_typeck/check/dropck.rs76
-rw-r--r--src/test/compile-fail/reject-specialized-drops-8142.rs8
7 files changed, 98 insertions, 121 deletions
diff --git a/src/librustc/middle/infer/higher_ranked/mod.rs b/src/librustc/middle/infer/higher_ranked/mod.rs
index fb8da9b65da..0c539a5d0e0 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<ty::Region>,
-                                   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<ty::Ty<'tcx>>,
-                                  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<T>,
                                                snapshot: &CombinedSnapshot)
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index 4e8ed01c6b9..158ef745de3 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<T>(&self,
                                            value: &ty::Binder<T>,
                                            snapshot: &CombinedSnapshot)
diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs
index 1785fe09f87..d81f8e0ae90 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 eff560653c1..cb00e3b9baf 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
@@ -2169,6 +2224,11 @@ pub struct RegionVid {
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct SkolemizedRegionVid {
+    pub index: u32
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub enum InferTy {
     TyVar(TyVid),
     IntVar(IntVid),
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 48c2e1e6dca..ac51f46a7e9 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 b6a91ce8a64..a8c77f863b7 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 1e189528f18..b12e26fddf6 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<Cok_nobound> Drop for O<Cok_nobound> { 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<One>         Drop for V<One,One>     { 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() { }