commit
af83d98d24
20 changed files with 252 additions and 246 deletions
|
|
@ -548,7 +548,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||
e: &ast::Expr, node_ty: Ty<'tcx>) {
|
||||
match node_ty.sty {
|
||||
ty::TyStruct(def, _) |
|
||||
ty::TyEnum(def, _) if def.has_dtor(v.tcx) => {
|
||||
ty::TyEnum(def, _) if def.has_dtor() => {
|
||||
v.add_qualif(ConstQualif::NEEDS_DROP);
|
||||
if v.mode != Mode::Var {
|
||||
v.tcx.sess.span_err(e.span,
|
||||
|
|
|
|||
|
|
@ -239,8 +239,9 @@ impl OverloadedCallType {
|
|||
// mem_categorization, it requires a TYPER, which is a type that
|
||||
// supplies types from the tree. After type checking is complete, you
|
||||
// can just use the tcx as the typer.
|
||||
|
||||
pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d> {
|
||||
//
|
||||
// FIXME(stage0): the :'t here is probably only important for stage0
|
||||
pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d+'t> {
|
||||
typer: &'t infer::InferCtxt<'a, 'tcx>,
|
||||
mc: mc::MemCategorizationContext<'t, 'a, 'tcx>,
|
||||
delegate: &'d mut (Delegate<'tcx>+'d),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -355,9 +355,11 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
|
|||
// this properly would result in the necessity of computing *type*
|
||||
// reachability, which might result in a compile time loss.
|
||||
fn mark_destructors_reachable(&mut self) {
|
||||
for (_, destructor_def_id) in self.tcx.destructor_for_type.borrow().iter() {
|
||||
if destructor_def_id.is_local() {
|
||||
self.reachable_symbols.insert(destructor_def_id.node);
|
||||
for adt in self.tcx.adt_defs() {
|
||||
if let Some(destructor_def_id) = adt.destructor() {
|
||||
if destructor_def_id.is_local() {
|
||||
self.reachable_symbols.insert(destructor_def_id.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ pub struct CrateAnalysis {
|
|||
#[derive(Copy, Clone)]
|
||||
pub enum DtorKind {
|
||||
NoDtor,
|
||||
TraitDtor(DefId, bool)
|
||||
TraitDtor(bool)
|
||||
}
|
||||
|
||||
impl DtorKind {
|
||||
|
|
@ -126,7 +126,7 @@ impl DtorKind {
|
|||
pub fn has_drop_flag(&self) -> bool {
|
||||
match self {
|
||||
&NoDtor => false,
|
||||
&TraitDtor(_, flag) => flag
|
||||
&TraitDtor(flag) => flag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -797,12 +797,6 @@ pub struct ctxt<'tcx> {
|
|||
/// True if the variance has been computed yet; false otherwise.
|
||||
pub variance_computed: Cell<bool>,
|
||||
|
||||
/// A mapping from the def ID of an enum or struct type to the def ID
|
||||
/// of the method that implements its destructor. If the type is not
|
||||
/// present in this map, it does not have a destructor. This map is
|
||||
/// populated during the coherence phase of typechecking.
|
||||
pub destructor_for_type: RefCell<DefIdMap<DefId>>,
|
||||
|
||||
/// A method will be in this list if and only if it is a destructor.
|
||||
pub destructors: RefCell<DefIdSet>,
|
||||
|
||||
|
|
@ -1502,7 +1496,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 +1581,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 +2217,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),
|
||||
|
|
@ -2997,7 +3051,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
|||
_ => return Err(TypeIsStructural),
|
||||
};
|
||||
|
||||
if adt.has_dtor(tcx) {
|
||||
if adt.has_dtor() {
|
||||
return Err(TypeHasDestructor)
|
||||
}
|
||||
|
||||
|
|
@ -3202,6 +3256,7 @@ bitflags! {
|
|||
const IS_PHANTOM_DATA = 1 << 3,
|
||||
const IS_SIMD = 1 << 4,
|
||||
const IS_FUNDAMENTAL = 1 << 5,
|
||||
const IS_NO_DROP_FLAG = 1 << 6,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3252,6 +3307,7 @@ pub struct FieldDefData<'tcx, 'container: 'tcx> {
|
|||
pub struct AdtDefData<'tcx, 'container: 'tcx> {
|
||||
pub did: DefId,
|
||||
pub variants: Vec<VariantDefData<'tcx, 'container>>,
|
||||
destructor: Cell<Option<DefId>>,
|
||||
flags: Cell<AdtFlags>,
|
||||
}
|
||||
|
||||
|
|
@ -3287,6 +3343,9 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
|
|||
if attr::contains_name(&attrs, "fundamental") {
|
||||
flags = flags | AdtFlags::IS_FUNDAMENTAL;
|
||||
}
|
||||
if attr::contains_name(&attrs, "unsafe_no_drop_flag") {
|
||||
flags = flags | AdtFlags::IS_NO_DROP_FLAG;
|
||||
}
|
||||
if tcx.lookup_simd(did) {
|
||||
flags = flags | AdtFlags::IS_SIMD;
|
||||
}
|
||||
|
|
@ -3300,6 +3359,7 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
|
|||
did: did,
|
||||
variants: variants,
|
||||
flags: Cell::new(flags),
|
||||
destructor: Cell::new(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3350,8 +3410,11 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
|
|||
}
|
||||
|
||||
/// Returns whether this type has a destructor.
|
||||
pub fn has_dtor(&self, tcx: &ctxt<'tcx>) -> bool {
|
||||
tcx.destructor_for_type.borrow().contains_key(&self.did)
|
||||
pub fn has_dtor(&self) -> bool {
|
||||
match self.dtor_kind() {
|
||||
NoDtor => false,
|
||||
TraitDtor(..) => true
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts this is a struct and returns the struct's unique
|
||||
|
|
@ -3413,6 +3476,24 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
|
|||
_ => panic!("unexpected def {:?} in variant_of_def", def)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destructor(&self) -> Option<DefId> {
|
||||
self.destructor.get()
|
||||
}
|
||||
|
||||
pub fn set_destructor(&self, dtor: DefId) {
|
||||
assert!(self.destructor.get().is_none());
|
||||
self.destructor.set(Some(dtor));
|
||||
}
|
||||
|
||||
pub fn dtor_kind(&self) -> DtorKind {
|
||||
match self.destructor.get() {
|
||||
Some(_) => {
|
||||
TraitDtor(!self.flags.get().intersects(AdtFlags::IS_NO_DROP_FLAG))
|
||||
}
|
||||
None => NoDtor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, 'container> VariantDefData<'tcx, 'container> {
|
||||
|
|
@ -3796,7 +3877,6 @@ impl<'tcx> ctxt<'tcx> {
|
|||
normalized_cache: RefCell::new(FnvHashMap()),
|
||||
lang_items: lang_items,
|
||||
provided_method_sources: RefCell::new(DefIdMap()),
|
||||
destructor_for_type: RefCell::new(DefIdMap()),
|
||||
destructors: RefCell::new(DefIdSet()),
|
||||
inherent_impls: RefCell::new(DefIdMap()),
|
||||
impl_items: RefCell::new(DefIdMap()),
|
||||
|
|
@ -4619,7 +4699,7 @@ impl<'tcx> TyS<'tcx> {
|
|||
})
|
||||
});
|
||||
|
||||
if def.has_dtor(cx) {
|
||||
if def.has_dtor() {
|
||||
res = res | TC::OwnsDtor;
|
||||
}
|
||||
|
||||
|
|
@ -5957,18 +6037,6 @@ impl<'tcx> ctxt<'tcx> {
|
|||
self.with_path(id, |path| ast_map::path_to_string(path))
|
||||
}
|
||||
|
||||
/* If struct_id names a struct with a dtor. */
|
||||
pub fn ty_dtor(&self, struct_id: DefId) -> DtorKind {
|
||||
match self.destructor_for_type.borrow().get(&struct_id) {
|
||||
Some(&method_def_id) => {
|
||||
let flag = !self.has_attr(struct_id, "unsafe_no_drop_flag");
|
||||
|
||||
TraitDtor(method_def_id, flag)
|
||||
}
|
||||
None => NoDtor,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_path<T, F>(&self, id: DefId, f: F) -> T where
|
||||
F: FnOnce(ast_map::PathElems) -> T,
|
||||
{
|
||||
|
|
@ -6053,6 +6121,11 @@ impl<'tcx> ctxt<'tcx> {
|
|||
self.lookup_adt_def_master(did)
|
||||
}
|
||||
|
||||
/// Return the list of all interned ADT definitions
|
||||
pub fn adt_defs(&self) -> Vec<AdtDef<'tcx>> {
|
||||
self.adt_defs.borrow().values().cloned().collect()
|
||||
}
|
||||
|
||||
/// Given the did of an item, returns its full set of predicates.
|
||||
pub fn lookup_predicates(&self, did: DefId) -> GenericPredicates<'tcx> {
|
||||
lookup_locally_or_in_crate_store(
|
||||
|
|
@ -6700,8 +6773,8 @@ impl<'tcx> ctxt<'tcx> {
|
|||
/// Returns true if this ADT is a dtorck type, i.e. whether it being
|
||||
/// safe for destruction requires it to be alive
|
||||
fn is_adt_dtorck(&self, adt: AdtDef<'tcx>) -> bool {
|
||||
let dtor_method = match self.destructor_for_type.borrow().get(&adt.did) {
|
||||
Some(dtor) => *dtor,
|
||||
let dtor_method = match adt.destructor() {
|
||||
Some(dtor) => dtor,
|
||||
None => return false
|
||||
};
|
||||
let impl_did = self.impl_of_method(dtor_method).unwrap_or_else(|| {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -747,7 +747,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
|||
}
|
||||
LpExtend(ref lp_base, _, LpInterior(InteriorField(_))) => {
|
||||
match lp_base.to_type().sty {
|
||||
ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor(self.tcx()) => {
|
||||
ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor() => {
|
||||
// In the case where the owner implements drop, then
|
||||
// the path must be initialized to prevent a case of
|
||||
// partial reinitialization
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
|
|||
mc::cat_interior(ref b, mc::InteriorElement(Kind::Pattern, _)) => {
|
||||
match b.ty.sty {
|
||||
ty::TyStruct(def, _) | ty::TyEnum(def, _) => {
|
||||
if def.has_dtor(bccx.tcx) {
|
||||
if def.has_dtor() {
|
||||
Some(cmt.clone())
|
||||
} else {
|
||||
check_and_get_illegal_move_origin(bccx, b)
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
|
|||
mc::cat_interior(ref b, mc::InteriorField(_)) => {
|
||||
match b.ty.sty {
|
||||
ty::TyStruct(def, _) |
|
||||
ty::TyEnum(def, _) if def.has_dtor(bccx.tcx) => {
|
||||
ty::TyEnum(def, _) if def.has_dtor() => {
|
||||
bccx.span_err(
|
||||
move_from.span,
|
||||
&format!("cannot move out of type `{}`, \
|
||||
|
|
|
|||
|
|
@ -1952,26 +1952,26 @@ impl LintPass for MissingCopyImplementations {
|
|||
if !cx.exported_items.contains(&item.id) {
|
||||
return;
|
||||
}
|
||||
if cx.tcx.destructor_for_type.borrow().contains_key(&DefId::local(item.id)) {
|
||||
return;
|
||||
}
|
||||
let ty = match item.node {
|
||||
let (def, ty) = match item.node {
|
||||
ast::ItemStruct(_, ref ast_generics) => {
|
||||
if ast_generics.is_parameterized() {
|
||||
return;
|
||||
}
|
||||
cx.tcx.mk_struct(cx.tcx.lookup_adt_def(DefId::local(item.id)),
|
||||
cx.tcx.mk_substs(Substs::empty()))
|
||||
let def = cx.tcx.lookup_adt_def(DefId::local(item.id));
|
||||
(def, cx.tcx.mk_struct(def,
|
||||
cx.tcx.mk_substs(Substs::empty())))
|
||||
}
|
||||
ast::ItemEnum(_, ref ast_generics) => {
|
||||
if ast_generics.is_parameterized() {
|
||||
return;
|
||||
}
|
||||
cx.tcx.mk_enum(cx.tcx.lookup_adt_def(DefId::local(item.id)),
|
||||
cx.tcx.mk_substs(Substs::empty()))
|
||||
let def = cx.tcx.lookup_adt_def(DefId::local(item.id));
|
||||
(def, cx.tcx.mk_enum(def,
|
||||
cx.tcx.mk_substs(Substs::empty())))
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
if def.has_dtor() { return; }
|
||||
let parameter_environment = cx.tcx.empty_parameter_environment();
|
||||
// FIXME (@jroesch) should probably inver this so that the parameter env still impls this
|
||||
// method
|
||||
|
|
@ -2583,7 +2583,7 @@ impl LintPass for DropWithReprExtern {
|
|||
let self_type_did = self_type_def.did;
|
||||
let hints = ctx.tcx.lookup_repr_hints(self_type_did);
|
||||
if hints.iter().any(|attr| *attr == attr::ReprExtern) &&
|
||||
ctx.tcx.ty_dtor(self_type_did).has_drop_flag() {
|
||||
self_type_def.dtor_kind().has_drop_flag() {
|
||||
let drop_impl_span = ctx.tcx.map.def_id_span(drop_impl_did,
|
||||
codemap::DUMMY_SP);
|
||||
let self_defn_span = ctx.tcx.map.def_id_span(self_type_did,
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
monomorphize::field_ty(cx.tcx(), substs, field)
|
||||
}).collect::<Vec<_>>();
|
||||
let packed = cx.tcx().lookup_packed(def.did);
|
||||
let dtor = cx.tcx().ty_dtor(def.did).has_drop_flag();
|
||||
let dtor = def.dtor_kind().has_drop_flag();
|
||||
if dtor {
|
||||
ftys.push(cx.tcx().dtor_type());
|
||||
}
|
||||
|
|
@ -265,7 +265,7 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
let hint = *cx.tcx().lookup_repr_hints(def.did).get(0)
|
||||
.unwrap_or(&attr::ReprAny);
|
||||
|
||||
let dtor = cx.tcx().ty_dtor(def.did).has_drop_flag();
|
||||
let dtor = def.dtor_kind().has_drop_flag();
|
||||
|
||||
if cases.is_empty() {
|
||||
// Uninhabitable; represent as unit
|
||||
|
|
|
|||
|
|
@ -1267,7 +1267,7 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
def::DefStruct(_) => {
|
||||
let ty = expr_ty(bcx, ref_expr);
|
||||
match ty.sty {
|
||||
ty::TyStruct(def, _) if def.has_dtor(bcx.tcx()) => {
|
||||
ty::TyStruct(def, _) if def.has_dtor() => {
|
||||
let repr = adt::represent_type(bcx.ccx(), ty);
|
||||
adt::trans_set_discr(bcx, &*repr, lldest, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,9 @@
|
|||
use back::link::*;
|
||||
use llvm;
|
||||
use llvm::{ValueRef, get_param};
|
||||
use metadata::csearch;
|
||||
use middle::def_id::{DefId, LOCAL_CRATE};
|
||||
use middle::lang_items::ExchangeFreeFnLangItem;
|
||||
use middle::subst;
|
||||
use middle::subst::{Subst, Substs};
|
||||
use middle::subst::{Substs};
|
||||
use middle::traits;
|
||||
use middle::ty::{self, Ty};
|
||||
use trans::adt;
|
||||
use trans::adt::GetDtorType; // for tcx.dtor_type()
|
||||
|
|
@ -33,16 +31,15 @@ use trans::common::*;
|
|||
use trans::debuginfo::DebugLoc;
|
||||
use trans::declare;
|
||||
use trans::expr;
|
||||
use trans::foreign;
|
||||
use trans::inline;
|
||||
use trans::machine::*;
|
||||
use trans::monomorphize;
|
||||
use trans::type_of::{type_of, type_of_dtor, sizing_type_of, align_of};
|
||||
use trans::type_of::{type_of, sizing_type_of, align_of};
|
||||
use trans::type_::Type;
|
||||
|
||||
use arena::TypedArena;
|
||||
use libc::c_uint;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::DUMMY_SP;
|
||||
|
||||
pub fn trans_exchange_free_dyn<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
|
||||
v: ValueRef,
|
||||
|
|
@ -287,10 +284,7 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||
|
||||
fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
|
||||
t: Ty<'tcx>,
|
||||
struct_data: ValueRef,
|
||||
dtor_did: DefId,
|
||||
class_did: DefId,
|
||||
substs: &subst::Substs<'tcx>)
|
||||
struct_data: ValueRef)
|
||||
-> Block<'blk, 'tcx> {
|
||||
assert!(type_is_sized(bcx.tcx(), t), "Precondition: caller must ensure t is sized");
|
||||
|
||||
|
|
@ -318,59 +312,19 @@ fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
|
|||
|
||||
let drop_flag_dtor_needed = ICmp(bcx, llvm::IntEQ, loaded, init_val, DebugLoc::None);
|
||||
with_cond(bcx, drop_flag_dtor_needed, |cx| {
|
||||
trans_struct_drop(cx, t, struct_data, dtor_did, class_did, substs)
|
||||
trans_struct_drop(cx, t, struct_data)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_res_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
did: DefId,
|
||||
parent_id: DefId,
|
||||
substs: &Substs<'tcx>)
|
||||
-> ValueRef {
|
||||
let _icx = push_ctxt("trans_res_dtor");
|
||||
let did = inline::maybe_instantiate_inline(ccx, did);
|
||||
|
||||
if !substs.types.is_empty() {
|
||||
assert_eq!(did.krate, LOCAL_CRATE);
|
||||
|
||||
// Since we're in trans we don't care for any region parameters
|
||||
let substs = ccx.tcx().mk_substs(Substs::erased(substs.types.clone()));
|
||||
|
||||
let (val, _, _) = monomorphize::monomorphic_fn(ccx, did, substs, None);
|
||||
|
||||
val
|
||||
} else if did.is_local() {
|
||||
get_item_val(ccx, did.node)
|
||||
} else {
|
||||
let tcx = ccx.tcx();
|
||||
let name = csearch::get_symbol(&ccx.sess().cstore, did);
|
||||
let class_ty = tcx.lookup_item_type(parent_id).ty.subst(tcx, substs);
|
||||
let llty = type_of_dtor(ccx, class_ty);
|
||||
foreign::get_extern_fn(ccx, &mut *ccx.externs().borrow_mut(), &name[..], llvm::CCallConv,
|
||||
llty, ccx.tcx().mk_nil())
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
t: Ty<'tcx>,
|
||||
v0: ValueRef,
|
||||
dtor_did: DefId,
|
||||
class_did: DefId,
|
||||
substs: &subst::Substs<'tcx>)
|
||||
v0: ValueRef)
|
||||
-> Block<'blk, 'tcx>
|
||||
{
|
||||
debug!("trans_struct_drop t: {}", t);
|
||||
let tcx = bcx.tcx();
|
||||
let mut bcx = bcx;
|
||||
|
||||
// Find and call the actual destructor
|
||||
let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, class_did, substs);
|
||||
|
||||
// Class dtors have no explicit args, so the params should
|
||||
// just consist of the environment (self).
|
||||
let params = unsafe {
|
||||
let ty = Type::from_ref(llvm::LLVMTypeOf(dtor_addr));
|
||||
ty.element_type().func_params()
|
||||
};
|
||||
assert_eq!(params.len(), if type_is_sized(bcx.tcx(), t) { 1 } else { 2 });
|
||||
let def = t.ty_adt_def().unwrap();
|
||||
|
||||
// Be sure to put the contents into a scope so we can use an invoke
|
||||
// instruction to call the user destructor but still call the field
|
||||
|
|
@ -384,15 +338,37 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
// discriminant (if any) in case of variant swap in drop code.
|
||||
bcx.fcx.schedule_drop_adt_contents(cleanup::CustomScope(contents_scope), v0, t);
|
||||
|
||||
let glue_type = get_drop_glue_type(bcx.ccx(), t);
|
||||
let dtor_ty = bcx.tcx().mk_ctor_fn(class_did, &[glue_type], bcx.tcx().mk_nil());
|
||||
let (_, bcx) = if type_is_sized(bcx.tcx(), t) {
|
||||
invoke(bcx, dtor_addr, &[v0], dtor_ty, DebugLoc::None)
|
||||
let (sized_args, unsized_args);
|
||||
let args: &[ValueRef] = if type_is_sized(tcx, t) {
|
||||
sized_args = [v0];
|
||||
&sized_args
|
||||
} else {
|
||||
let args = [Load(bcx, expr::get_dataptr(bcx, v0)), Load(bcx, expr::get_meta(bcx, v0))];
|
||||
invoke(bcx, dtor_addr, &args, dtor_ty, DebugLoc::None)
|
||||
unsized_args = [Load(bcx, expr::get_dataptr(bcx, v0)), Load(bcx, expr::get_meta(bcx, v0))];
|
||||
&unsized_args
|
||||
};
|
||||
|
||||
bcx = callee::trans_call_inner(bcx, DebugLoc::None, |bcx, _| {
|
||||
let trait_ref = ty::Binder(ty::TraitRef {
|
||||
def_id: tcx.lang_items.drop_trait().unwrap(),
|
||||
substs: tcx.mk_substs(Substs::trans_empty().with_self_ty(t))
|
||||
});
|
||||
let vtbl = match fulfill_obligation(bcx.ccx(), DUMMY_SP, trait_ref) {
|
||||
traits::VtableImpl(data) => data,
|
||||
_ => tcx.sess.bug(&format!("dtor for {:?} is not an impl???", t))
|
||||
};
|
||||
let dtor_did = def.destructor().unwrap();
|
||||
let datum = callee::trans_fn_ref_with_substs(bcx.ccx(),
|
||||
dtor_did,
|
||||
ExprId(0),
|
||||
bcx.fcx.param_substs,
|
||||
vtbl.substs);
|
||||
callee::Callee {
|
||||
bcx: bcx,
|
||||
data: callee::Fn(datum.val),
|
||||
ty: datum.ty
|
||||
}
|
||||
}, callee::ArgVals(args), Some(expr::Ignore)).bcx;
|
||||
|
||||
bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, contents_scope)
|
||||
}
|
||||
|
||||
|
|
@ -557,27 +533,26 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
|
|||
})
|
||||
}
|
||||
}
|
||||
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
|
||||
let tcx = bcx.tcx();
|
||||
match (tcx.ty_dtor(def.did), skip_dtor) {
|
||||
(ty::TraitDtor(dtor, true), false) => {
|
||||
ty::TyStruct(def, _) | ty::TyEnum(def, _) => {
|
||||
match (def.dtor_kind(), skip_dtor) {
|
||||
(ty::TraitDtor(true), false) => {
|
||||
// FIXME(16758) Since the struct is unsized, it is hard to
|
||||
// find the drop flag (which is at the end of the struct).
|
||||
// Lets just ignore the flag and pretend everything will be
|
||||
// OK.
|
||||
if type_is_sized(bcx.tcx(), t) {
|
||||
trans_struct_drop_flag(bcx, t, v0, dtor, def.did, substs)
|
||||
trans_struct_drop_flag(bcx, t, v0)
|
||||
} else {
|
||||
// Give the user a heads up that we are doing something
|
||||
// stupid and dangerous.
|
||||
bcx.sess().warn(&format!("Ignoring drop flag in destructor for {}\
|
||||
because the struct is unsized. See issue\
|
||||
#16758", t));
|
||||
trans_struct_drop(bcx, t, v0, dtor, def.did, substs)
|
||||
trans_struct_drop(bcx, t, v0)
|
||||
}
|
||||
}
|
||||
(ty::TraitDtor(dtor, false), false) => {
|
||||
trans_struct_drop(bcx, t, v0, dtor, def.did, substs)
|
||||
(ty::TraitDtor(false), false) => {
|
||||
trans_struct_drop(bcx, t, v0)
|
||||
}
|
||||
(ty::NoDtor, _) | (_, true) => {
|
||||
// No dtor? Just the default case
|
||||
|
|
|
|||
|
|
@ -487,11 +487,3 @@ fn llvm_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
format!("{}.{}", did.krate, tstr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_of_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, self_ty: Ty<'tcx>) -> Type {
|
||||
if type_is_sized(ccx.tcx(), self_ty) {
|
||||
Type::func(&[type_of(ccx, self_ty).ptr_to()], &Type::void(ccx))
|
||||
} else {
|
||||
Type::func(&type_of(ccx, self_ty).field_types(), &Type::void(ccx))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
assert!(drop_impl_did.is_local() && self_type_did.is_local());
|
||||
|
||||
let ty::TypeScheme { generics: ref named_type_generics,
|
||||
ty: named_type } =
|
||||
tcx.lookup_item_type(self_type_did);
|
||||
// check that the impl type can be made to match the trait type.
|
||||
|
||||
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
|
||||
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);
|
||||
|
||||
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 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 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(())
|
||||
}
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -311,9 +311,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
|
|||
match self_type.ty.sty {
|
||||
ty::TyEnum(type_def, _) |
|
||||
ty::TyStruct(type_def, _) => {
|
||||
tcx.destructor_for_type
|
||||
.borrow_mut()
|
||||
.insert(type_def.did, method_def_id.def_id());
|
||||
type_def.set_destructor(method_def_id.def_id());
|
||||
tcx.destructors
|
||||
.borrow_mut()
|
||||
.insert(method_def_id.def_id());
|
||||
|
|
|
|||
|
|
@ -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() { }
|
||||
|
|
|
|||
48
src/test/run-pass/issue-27997.rs
Normal file
48
src/test/run-pass/issue-27997.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(const_fn)]
|
||||
|
||||
use std::sync::atomic::{Ordering, AtomicUsize};
|
||||
|
||||
use std::mem;
|
||||
struct S<U,V> {
|
||||
_u: U,
|
||||
size_of_u: usize,
|
||||
_v: V,
|
||||
size_of_v: usize
|
||||
}
|
||||
|
||||
impl<U, V> S<U, V> {
|
||||
fn new(u: U, v: V) -> Self {
|
||||
S {
|
||||
_u: u,
|
||||
size_of_u: mem::size_of::<U>(),
|
||||
_v: v,
|
||||
size_of_v: mem::size_of::<V>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
impl<V, U> Drop for S<U, V> {
|
||||
fn drop(&mut self) {
|
||||
assert_eq!(mem::size_of::<U>(), self.size_of_u);
|
||||
assert_eq!(mem::size_of::<V>(), self.size_of_v);
|
||||
COUNT.store(COUNT.load(Ordering::SeqCst)+1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(COUNT.load(Ordering::SeqCst), 0);
|
||||
{ S::new(0u8, 1u16); }
|
||||
assert_eq!(COUNT.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue