Skip to content

Commit

Permalink
use the parameter environment when checking dtors
Browse files Browse the repository at this point in the history
This makes it more uniform. No functional changes.
  • Loading branch information
Ariel Ben-Yehuda committed Aug 25, 2015
1 parent 5c630a6 commit 2f052eb
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 121 deletions.
58 changes: 0 additions & 58 deletions src/librustc/middle/infer/higher_ranked/mod.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 0 additions & 9 deletions src/librustc/middle/infer/mod.rs
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/infer/region_inference/mod.rs
Expand Up @@ -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 {
Expand Down
64 changes: 62 additions & 2 deletions src/librustc/middle/ty.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/util/ppaux.rs
Expand Up @@ -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")
Expand Down
76 changes: 29 additions & 47 deletions src/librustc_typeck/check/dropck.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -75,61 +77,41 @@ 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);
tcx.sess.span_note(item_span,
"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
Expand Down
8 changes: 5 additions & 3 deletions src/test/compile-fail/reject-specialized-drops-8142.rs
Expand Up @@ -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

Expand All @@ -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() { }

0 comments on commit 2f052eb

Please sign in to comment.