Auto merge of #41485 - arielb1:dtorck-constraint, r=eddyb
cache dtorck constraints on ADTs This avoids visiting the fields of all structs multiple times, improving item-bodies checking time by 10% (!). Not sure whether we want this in 1.18 or 1.19. It's a big-ish patch, but the 10% win is very tempting. r? @eddyb
This commit is contained in:
commit
2bd4b5c6db
9 changed files with 320 additions and 447 deletions
|
|
@ -91,6 +91,7 @@ pub enum DepNode<D: Clone + Debug> {
|
||||||
IsForeignItem(D),
|
IsForeignItem(D),
|
||||||
TypeParamPredicates((D, D)),
|
TypeParamPredicates((D, D)),
|
||||||
SizedConstraint(D),
|
SizedConstraint(D),
|
||||||
|
DtorckConstraint(D),
|
||||||
AdtDestructor(D),
|
AdtDestructor(D),
|
||||||
AssociatedItemDefIds(D),
|
AssociatedItemDefIds(D),
|
||||||
InherentImpls(D),
|
InherentImpls(D),
|
||||||
|
|
@ -228,6 +229,7 @@ impl<D: Clone + Debug> DepNode<D> {
|
||||||
Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param)))))
|
Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param)))))
|
||||||
}
|
}
|
||||||
SizedConstraint(ref d) => op(d).map(SizedConstraint),
|
SizedConstraint(ref d) => op(d).map(SizedConstraint),
|
||||||
|
DtorckConstraint(ref d) => op(d).map(DtorckConstraint),
|
||||||
AdtDestructor(ref d) => op(d).map(AdtDestructor),
|
AdtDestructor(ref d) => op(d).map(AdtDestructor),
|
||||||
AssociatedItemDefIds(ref d) => op(d).map(AssociatedItemDefIds),
|
AssociatedItemDefIds(ref d) => op(d).map(AssociatedItemDefIds),
|
||||||
InherentImpls(ref d) => op(d).map(InherentImpls),
|
InherentImpls(ref d) => op(d).map(InherentImpls),
|
||||||
|
|
|
||||||
|
|
@ -1829,6 +1829,7 @@ register_diagnostics! {
|
||||||
E0314, // closure outlives stack frame
|
E0314, // closure outlives stack frame
|
||||||
E0315, // cannot invoke closure outside of its lifetime
|
E0315, // cannot invoke closure outside of its lifetime
|
||||||
E0316, // nested quantification of lifetimes
|
E0316, // nested quantification of lifetimes
|
||||||
|
E0320, // recursive overflow during dropck
|
||||||
E0473, // dereference of reference outside its lifetime
|
E0473, // dereference of reference outside its lifetime
|
||||||
E0474, // captured variable `..` does not outlive the enclosing closure
|
E0474, // captured variable `..` does not outlive the enclosing closure
|
||||||
E0475, // index of slice outside its lifetime
|
E0475, // index of slice outside its lifetime
|
||||||
|
|
|
||||||
|
|
@ -1790,11 +1790,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||||
ty::TyAdt(def, substs) => {
|
ty::TyAdt(def, substs) => {
|
||||||
let sized_crit = def.sized_constraint(self.tcx());
|
let sized_crit = def.sized_constraint(self.tcx());
|
||||||
// (*) binder moved here
|
// (*) binder moved here
|
||||||
Where(ty::Binder(match sized_crit.sty {
|
Where(ty::Binder(
|
||||||
ty::TyTuple(tys, _) => tys.to_vec().subst(self.tcx(), substs),
|
sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect()
|
||||||
ty::TyBool => vec![],
|
))
|
||||||
_ => vec![sized_crit.subst(self.tcx(), substs)]
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None,
|
ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None,
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,13 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'tcx> Value<'tcx> for ty::DtorckConstraint<'tcx> {
|
||||||
|
fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
|
||||||
|
Self::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CycleError<'a, 'tcx: 'a> {
|
pub struct CycleError<'a, 'tcx: 'a> {
|
||||||
span: Span,
|
span: Span,
|
||||||
cycle: RefMut<'a, [(Span, Query<'tcx>)]>,
|
cycle: RefMut<'a, [(Span, Query<'tcx>)]>,
|
||||||
|
|
@ -396,7 +403,8 @@ define_maps! { <'tcx>
|
||||||
pub trait_def: ItemSignature(DefId) -> &'tcx ty::TraitDef,
|
pub trait_def: ItemSignature(DefId) -> &'tcx ty::TraitDef,
|
||||||
pub adt_def: ItemSignature(DefId) -> &'tcx ty::AdtDef,
|
pub adt_def: ItemSignature(DefId) -> &'tcx ty::AdtDef,
|
||||||
pub adt_destructor: AdtDestructor(DefId) -> Option<ty::Destructor>,
|
pub adt_destructor: AdtDestructor(DefId) -> Option<ty::Destructor>,
|
||||||
pub adt_sized_constraint: SizedConstraint(DefId) -> Ty<'tcx>,
|
pub adt_sized_constraint: SizedConstraint(DefId) -> &'tcx [Ty<'tcx>],
|
||||||
|
pub adt_dtorck_constraint: DtorckConstraint(DefId) -> ty::DtorckConstraint<'tcx>,
|
||||||
|
|
||||||
/// True if this is a foreign item (i.e., linked via `extern { ... }`).
|
/// True if this is a foreign item (i.e., linked via `extern { ... }`).
|
||||||
pub is_foreign_item: IsForeignItem(DefId) -> bool,
|
pub is_foreign_item: IsForeignItem(DefId) -> bool,
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,15 @@ use ty;
|
||||||
use ty::subst::{Subst, Substs};
|
use ty::subst::{Subst, Substs};
|
||||||
use ty::util::IntTypeExt;
|
use ty::util::IntTypeExt;
|
||||||
use ty::walk::TypeWalker;
|
use ty::walk::TypeWalker;
|
||||||
use util::nodemap::{NodeSet, DefIdMap, FxHashMap};
|
use util::common::ErrorReported;
|
||||||
|
use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use serialize::{self, Encodable, Encoder};
|
use serialize::{self, Encodable, Encoder};
|
||||||
use std::cell::{Cell, RefCell, Ref};
|
use std::cell::{Cell, RefCell, Ref};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::iter::FromIterator;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
@ -1332,17 +1334,6 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> {
|
||||||
pub struct Destructor {
|
pub struct Destructor {
|
||||||
/// The def-id of the destructor method
|
/// The def-id of the destructor method
|
||||||
pub did: DefId,
|
pub did: DefId,
|
||||||
/// Invoking the destructor of a dtorck type during usual cleanup
|
|
||||||
/// (e.g. the glue emitted for stack unwinding) requires all
|
|
||||||
/// lifetimes in the type-structure of `adt` to strictly outlive
|
|
||||||
/// the adt value itself.
|
|
||||||
///
|
|
||||||
/// If `adt` is not dtorck, then the adt's destructor can be
|
|
||||||
/// invoked even when there are lifetimes in the type-structure of
|
|
||||||
/// `adt` that do not strictly outlive the adt value itself.
|
|
||||||
/// (This allows programs to make cyclic structures without
|
|
||||||
/// resorting to unsafe means; see RFCs 769 and 1238).
|
|
||||||
pub is_dtorck: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
|
@ -1609,14 +1600,6 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this is a dtorck type. If this returns
|
|
||||||
/// true, this type being safe for destruction requires it to be
|
|
||||||
/// alive; Otherwise, only the contents are required to be.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_dtorck(&'gcx self, tcx: TyCtxt) -> bool {
|
|
||||||
self.destructor(tcx).map_or(false, |d| d.is_dtorck)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether this type is #[fundamental] for the purposes
|
/// Returns whether this type is #[fundamental] for the purposes
|
||||||
/// of coherence checking.
|
/// of coherence checking.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1780,16 +1763,9 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
queries::adt_destructor::get(tcx, DUMMY_SP, self.did)
|
queries::adt_destructor::get(tcx, DUMMY_SP, self.did)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a simpler type such that `Self: Sized` if and only
|
/// Returns a list of types such that `Self: Sized` if and only
|
||||||
/// if that type is Sized, or `TyErr` if this type is recursive.
|
/// if that type is Sized, or `TyErr` if this type is recursive.
|
||||||
///
|
///
|
||||||
/// HACK: instead of returning a list of types, this function can
|
|
||||||
/// return a tuple. In that case, the result is Sized only if
|
|
||||||
/// all elements of the tuple are Sized.
|
|
||||||
///
|
|
||||||
/// This is generally the `struct_tail` if this is a struct, or a
|
|
||||||
/// tuple of them if this is an enum.
|
|
||||||
///
|
|
||||||
/// Oddly enough, checking that the sized-constraint is Sized is
|
/// Oddly enough, checking that the sized-constraint is Sized is
|
||||||
/// actually more expressive than checking all members:
|
/// actually more expressive than checking all members:
|
||||||
/// the Sized trait is inductive, so an associated type that references
|
/// the Sized trait is inductive, so an associated type that references
|
||||||
|
|
@ -1797,16 +1773,16 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
///
|
///
|
||||||
/// Due to normalization being eager, this applies even if
|
/// Due to normalization being eager, this applies even if
|
||||||
/// the associated type is behind a pointer, e.g. issue #31299.
|
/// the associated type is behind a pointer, e.g. issue #31299.
|
||||||
pub fn sized_constraint(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
|
pub fn sized_constraint(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> &'tcx [Ty<'tcx>] {
|
||||||
match queries::adt_sized_constraint::try_get(tcx, DUMMY_SP, self.did) {
|
match queries::adt_sized_constraint::try_get(tcx, DUMMY_SP, self.did) {
|
||||||
Ok(ty) => ty,
|
Ok(tys) => tys,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!("adt_sized_constraint: {:?} is recursive", self);
|
debug!("adt_sized_constraint: {:?} is recursive", self);
|
||||||
// This should be reported as an error by `check_representable`.
|
// This should be reported as an error by `check_representable`.
|
||||||
//
|
//
|
||||||
// Consider the type as Sized in the meanwhile to avoid
|
// Consider the type as Sized in the meanwhile to avoid
|
||||||
// further errors.
|
// further errors.
|
||||||
tcx.types.err
|
tcx.intern_type_list(&[tcx.types.err])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1836,18 +1812,13 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
|
|
||||||
TyAdt(adt, substs) => {
|
TyAdt(adt, substs) => {
|
||||||
// recursive case
|
// recursive case
|
||||||
let adt_ty =
|
let adt_tys = adt.sized_constraint(tcx);
|
||||||
adt.sized_constraint(tcx)
|
|
||||||
.subst(tcx, substs);
|
|
||||||
debug!("sized_constraint_for_ty({:?}) intermediate = {:?}",
|
debug!("sized_constraint_for_ty({:?}) intermediate = {:?}",
|
||||||
ty, adt_ty);
|
ty, adt_tys);
|
||||||
if let ty::TyTuple(ref tys, _) = adt_ty.sty {
|
adt_tys.iter()
|
||||||
tys.iter().flat_map(|ty| {
|
.map(|ty| ty.subst(tcx, substs))
|
||||||
self.sized_constraint_for_ty(tcx, ty)
|
.flat_map(|ty| self.sized_constraint_for_ty(tcx, ty))
|
||||||
}).collect()
|
.collect()
|
||||||
} else {
|
|
||||||
self.sized_constraint_for_ty(tcx, adt_ty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TyProjection(..) | TyAnon(..) => {
|
TyProjection(..) | TyAnon(..) => {
|
||||||
|
|
@ -2697,13 +2668,7 @@ fn associated_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
|
||||||
|
|
||||||
/// Calculates the Sized-constraint.
|
/// Calculates the Sized-constraint.
|
||||||
///
|
///
|
||||||
/// As the Sized-constraint of enums can be a *set* of types,
|
/// In fact, there are only a few options for the types in the constraint:
|
||||||
/// the Sized-constraint may need to be a set also. Because introducing
|
|
||||||
/// a new type of IVar is currently a complex affair, the Sized-constraint
|
|
||||||
/// may be a tuple.
|
|
||||||
///
|
|
||||||
/// In fact, there are only a few options for the constraint:
|
|
||||||
/// - `bool`, if the type is always Sized
|
|
||||||
/// - an obviously-unsized type
|
/// - an obviously-unsized type
|
||||||
/// - a type parameter or projection whose Sizedness can't be known
|
/// - a type parameter or projection whose Sizedness can't be known
|
||||||
/// - a tuple of type parameters or projections, if there are multiple
|
/// - a tuple of type parameters or projections, if there are multiple
|
||||||
|
|
@ -2712,26 +2677,50 @@ fn associated_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
|
||||||
/// check should catch this case.
|
/// check should catch this case.
|
||||||
fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
def_id: DefId)
|
def_id: DefId)
|
||||||
-> Ty<'tcx> {
|
-> &'tcx [Ty<'tcx>] {
|
||||||
let def = tcx.lookup_adt_def(def_id);
|
let def = tcx.lookup_adt_def(def_id);
|
||||||
|
|
||||||
let tys: Vec<_> = def.variants.iter().flat_map(|v| {
|
let result = tcx.intern_type_list(&def.variants.iter().flat_map(|v| {
|
||||||
v.fields.last()
|
v.fields.last()
|
||||||
}).flat_map(|f| {
|
}).flat_map(|f| {
|
||||||
let ty = tcx.item_type(f.did);
|
def.sized_constraint_for_ty(tcx, tcx.item_type(f.did))
|
||||||
def.sized_constraint_for_ty(tcx, ty)
|
}).collect::<Vec<_>>());
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let ty = match tys.len() {
|
debug!("adt_sized_constraint: {:?} => {:?}", def, result);
|
||||||
_ if tys.references_error() => tcx.types.err,
|
|
||||||
0 => tcx.types.bool,
|
|
||||||
1 => tys[0],
|
|
||||||
_ => tcx.intern_tup(&tys[..], false)
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("adt_sized_constraint: {:?} => {:?}", def, ty);
|
result
|
||||||
|
}
|
||||||
|
|
||||||
ty
|
/// Calculates the dtorck constraint for a type.
|
||||||
|
fn adt_dtorck_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
|
def_id: DefId)
|
||||||
|
-> DtorckConstraint<'tcx> {
|
||||||
|
let def = tcx.lookup_adt_def(def_id);
|
||||||
|
let span = tcx.def_span(def_id);
|
||||||
|
debug!("dtorck_constraint: {:?}", def);
|
||||||
|
|
||||||
|
if def.is_phantom_data() {
|
||||||
|
let result = DtorckConstraint {
|
||||||
|
outlives: vec![],
|
||||||
|
dtorck_types: vec![
|
||||||
|
tcx.mk_param_from_def(&tcx.item_generics(def_id).types[0])
|
||||||
|
]
|
||||||
|
};
|
||||||
|
debug!("dtorck_constraint: {:?} => {:?}", def, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = def.all_fields()
|
||||||
|
.map(|field| tcx.item_type(field.did))
|
||||||
|
.map(|fty| tcx.dtorck_constraint_for_ty(span, fty, 0, fty))
|
||||||
|
.collect::<Result<DtorckConstraint, ErrorReported>>()
|
||||||
|
.unwrap_or(DtorckConstraint::empty());
|
||||||
|
result.outlives.extend(tcx.destructor_constraints(def));
|
||||||
|
result.dedup();
|
||||||
|
|
||||||
|
debug!("dtorck_constraint: {:?} => {:?}", def, result);
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn associated_item_def_ids<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
fn associated_item_def_ids<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
|
|
@ -2762,6 +2751,7 @@ pub fn provide(providers: &mut ty::maps::Providers) {
|
||||||
associated_item,
|
associated_item,
|
||||||
associated_item_def_ids,
|
associated_item_def_ids,
|
||||||
adt_sized_constraint,
|
adt_sized_constraint,
|
||||||
|
adt_dtorck_constraint,
|
||||||
..*providers
|
..*providers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -2769,6 +2759,7 @@ pub fn provide(providers: &mut ty::maps::Providers) {
|
||||||
pub fn provide_extern(providers: &mut ty::maps::Providers) {
|
pub fn provide_extern(providers: &mut ty::maps::Providers) {
|
||||||
*providers = ty::maps::Providers {
|
*providers = ty::maps::Providers {
|
||||||
adt_sized_constraint,
|
adt_sized_constraint,
|
||||||
|
adt_dtorck_constraint,
|
||||||
..*providers
|
..*providers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -2784,3 +2775,46 @@ pub fn provide_extern(providers: &mut ty::maps::Providers) {
|
||||||
pub struct CrateInherentImpls {
|
pub struct CrateInherentImpls {
|
||||||
pub inherent_impls: DefIdMap<Rc<Vec<DefId>>>,
|
pub inherent_impls: DefIdMap<Rc<Vec<DefId>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A set of constraints that need to be satisfied in order for
|
||||||
|
/// a type to be valid for destruction.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DtorckConstraint<'tcx> {
|
||||||
|
/// Types that are required to be alive in order for this
|
||||||
|
/// type to be valid for destruction.
|
||||||
|
pub outlives: Vec<ty::subst::Kind<'tcx>>,
|
||||||
|
/// Types that could not be resolved: projections and params.
|
||||||
|
pub dtorck_types: Vec<Ty<'tcx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx>
|
||||||
|
{
|
||||||
|
fn from_iter<I: IntoIterator<Item=DtorckConstraint<'tcx>>>(iter: I) -> Self {
|
||||||
|
let mut result = Self::empty();
|
||||||
|
|
||||||
|
for constraint in iter {
|
||||||
|
result.outlives.extend(constraint.outlives);
|
||||||
|
result.dtorck_types.extend(constraint.dtorck_types);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'tcx> DtorckConstraint<'tcx> {
|
||||||
|
fn empty() -> DtorckConstraint<'tcx> {
|
||||||
|
DtorckConstraint {
|
||||||
|
outlives: vec![],
|
||||||
|
dtorck_types: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dedup<'a>(&mut self) {
|
||||||
|
let mut outlives = FxHashSet();
|
||||||
|
let mut dtorck_types = FxHashSet();
|
||||||
|
|
||||||
|
self.outlives.retain(|&val| outlives.replace(val).is_none());
|
||||||
|
self.dtorck_types.retain(|&val| dtorck_types.replace(val).is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
|
||||||
use ty::ParameterEnvironment;
|
use ty::ParameterEnvironment;
|
||||||
use ty::fold::TypeVisitor;
|
use ty::fold::TypeVisitor;
|
||||||
use ty::layout::{Layout, LayoutError};
|
use ty::layout::{Layout, LayoutError};
|
||||||
|
use ty::subst::{Subst, Kind};
|
||||||
use ty::TypeVariants::*;
|
use ty::TypeVariants::*;
|
||||||
use util::common::ErrorReported;
|
use util::common::ErrorReported;
|
||||||
use util::nodemap::{FxHashMap, FxHashSet};
|
use util::nodemap::{FxHashMap, FxHashSet};
|
||||||
|
|
@ -385,6 +386,27 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Some(ty::Destructor { did: dtor_did })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the set of types that are required to be alive in
|
||||||
|
/// order to run the destructor of `def` (see RFCs 769 and
|
||||||
|
/// 1238).
|
||||||
|
///
|
||||||
|
/// Note that this returns only the constraints for the
|
||||||
|
/// destructor of `def` itself. For the destructors of the
|
||||||
|
/// contents, you need `adt_dtorck_constraint`.
|
||||||
|
pub fn destructor_constraints(self, def: &'tcx ty::AdtDef)
|
||||||
|
-> Vec<ty::subst::Kind<'tcx>>
|
||||||
|
{
|
||||||
|
let dtor = match def.destructor(self) {
|
||||||
|
None => {
|
||||||
|
debug!("destructor_constraints({:?}) - no dtor", def.did);
|
||||||
|
return vec![]
|
||||||
|
}
|
||||||
|
Some(dtor) => dtor.did
|
||||||
|
};
|
||||||
|
|
||||||
// RFC 1238: if the destructor method is tagged with the
|
// RFC 1238: if the destructor method is tagged with the
|
||||||
// attribute `unsafe_destructor_blind_to_params`, then the
|
// attribute `unsafe_destructor_blind_to_params`, then the
|
||||||
// compiler is being instructed to *assume* that the
|
// compiler is being instructed to *assume* that the
|
||||||
|
|
@ -394,11 +416,147 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||||
// Such access can be in plain sight (e.g. dereferencing
|
// Such access can be in plain sight (e.g. dereferencing
|
||||||
// `*foo.0` of `Foo<'a>(&'a u32)`) or indirectly hidden
|
// `*foo.0` of `Foo<'a>(&'a u32)`) or indirectly hidden
|
||||||
// (e.g. calling `foo.0.clone()` of `Foo<T:Clone>`).
|
// (e.g. calling `foo.0.clone()` of `Foo<T:Clone>`).
|
||||||
let is_dtorck = !self.has_attr(dtor_did, "unsafe_destructor_blind_to_params");
|
if self.has_attr(dtor, "unsafe_destructor_blind_to_params") {
|
||||||
Some(ty::Destructor { did: dtor_did, is_dtorck: is_dtorck })
|
debug!("destructor_constraint({:?}) - blind", def.did);
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let impl_def_id = self.associated_item(dtor).container.id();
|
||||||
|
let impl_generics = self.item_generics(impl_def_id);
|
||||||
|
|
||||||
|
// We have a destructor - all the parameters that are not
|
||||||
|
// pure_wrt_drop (i.e, don't have a #[may_dangle] attribute)
|
||||||
|
// must be live.
|
||||||
|
|
||||||
|
// We need to return the list of parameters from the ADTs
|
||||||
|
// generics/substs that correspond to impure parameters on the
|
||||||
|
// impl's generics. This is a bit ugly, but conceptually simple:
|
||||||
|
//
|
||||||
|
// Suppose our ADT looks like the following
|
||||||
|
//
|
||||||
|
// struct S<X, Y, Z>(X, Y, Z);
|
||||||
|
//
|
||||||
|
// and the impl is
|
||||||
|
//
|
||||||
|
// impl<#[may_dangle] P0, P1, P2> Drop for S<P1, P2, P0>
|
||||||
|
//
|
||||||
|
// We want to return the parameters (X, Y). For that, we match
|
||||||
|
// up the item-substs <X, Y, Z> with the substs on the impl ADT,
|
||||||
|
// <P1, P2, P0>, and then look up which of the impl substs refer to
|
||||||
|
// parameters marked as pure.
|
||||||
|
|
||||||
|
let impl_substs = match self.item_type(impl_def_id).sty {
|
||||||
|
ty::TyAdt(def_, substs) if def_ == def => substs,
|
||||||
|
_ => bug!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_substs = match self.item_type(def.did).sty {
|
||||||
|
ty::TyAdt(def_, substs) if def_ == def => substs,
|
||||||
|
_ => bug!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = item_substs.iter().zip(impl_substs.iter())
|
||||||
|
.filter(|&(_, &k)| {
|
||||||
|
if let Some(&ty::Region::ReEarlyBound(ref ebr)) = k.as_region() {
|
||||||
|
!impl_generics.region_param(ebr).pure_wrt_drop
|
||||||
|
} else if let Some(&ty::TyS {
|
||||||
|
sty: ty::TypeVariants::TyParam(ref pt), ..
|
||||||
|
}) = k.as_type() {
|
||||||
|
!impl_generics.type_param(pt).pure_wrt_drop
|
||||||
|
} else {
|
||||||
|
// not a type or region param - this should be reported
|
||||||
|
// as an error.
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}).map(|(&item_param, _)| item_param).collect();
|
||||||
|
debug!("destructor_constraint({:?}) = {:?}", def.did, result);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn closure_base_def_id(&self, def_id: DefId) -> DefId {
|
/// Return a set of constraints that needs to be satisfied in
|
||||||
|
/// order for `ty` to be valid for destruction.
|
||||||
|
pub fn dtorck_constraint_for_ty(self,
|
||||||
|
span: Span,
|
||||||
|
for_ty: Ty<'tcx>,
|
||||||
|
depth: usize,
|
||||||
|
ty: Ty<'tcx>)
|
||||||
|
-> Result<ty::DtorckConstraint<'tcx>, ErrorReported>
|
||||||
|
{
|
||||||
|
debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})",
|
||||||
|
span, for_ty, depth, ty);
|
||||||
|
|
||||||
|
if depth >= self.sess.recursion_limit.get() {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.sess, span, E0320,
|
||||||
|
"overflow while adding drop-check rules for {}", for_ty);
|
||||||
|
err.note(&format!("overflowed on {}", ty));
|
||||||
|
err.emit();
|
||||||
|
return Err(ErrorReported);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = match ty.sty {
|
||||||
|
ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
|
||||||
|
ty::TyFloat(_) | ty::TyStr | ty::TyNever |
|
||||||
|
ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => {
|
||||||
|
// these types never have a destructor
|
||||||
|
Ok(ty::DtorckConstraint::empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::TyArray(ety, _) | ty::TySlice(ety) => {
|
||||||
|
// single-element containers, behave like their element
|
||||||
|
self.dtorck_constraint_for_ty(span, for_ty, depth+1, ety)
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::TyTuple(tys, _) => {
|
||||||
|
tys.iter().map(|ty| {
|
||||||
|
self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::TyClosure(def_id, substs) => {
|
||||||
|
substs.upvar_tys(def_id, self).map(|ty| {
|
||||||
|
self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::TyAdt(def, substs) => {
|
||||||
|
let ty::DtorckConstraint {
|
||||||
|
dtorck_types, outlives
|
||||||
|
} = ty::queries::adt_dtorck_constraint::get(self, span, def.did);
|
||||||
|
Ok(ty::DtorckConstraint {
|
||||||
|
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
|
||||||
|
// there, but that needs some way to handle cycles.
|
||||||
|
dtorck_types: dtorck_types.subst(self, substs),
|
||||||
|
outlives: outlives.subst(self, substs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Objects must be alive in order for their destructor
|
||||||
|
// to be called.
|
||||||
|
ty::TyDynamic(..) => Ok(ty::DtorckConstraint {
|
||||||
|
outlives: vec![Kind::from(ty)],
|
||||||
|
dtorck_types: vec![],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Types that can't be resolved. Pass them forward.
|
||||||
|
ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => {
|
||||||
|
Ok(ty::DtorckConstraint {
|
||||||
|
outlives: vec![],
|
||||||
|
dtorck_types: vec![ty],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::TyInfer(..) | ty::TyError => {
|
||||||
|
self.sess.delay_span_bug(span, "unresolved type in dtorck");
|
||||||
|
Err(ErrorReported)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn closure_base_def_id(self, def_id: DefId) -> DefId {
|
||||||
let mut def_id = def_id;
|
let mut def_id = def_id;
|
||||||
while self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr {
|
while self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr {
|
||||||
def_id = self.parent_def_id(def_id).unwrap_or_else(|| {
|
def_id = self.parent_def_id(def_id).unwrap_or_else(|| {
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,11 @@ use middle::free_region::FreeRegionMap;
|
||||||
use rustc::infer::{self, InferOk};
|
use rustc::infer::{self, InferOk};
|
||||||
use middle::region;
|
use middle::region;
|
||||||
use rustc::ty::subst::{Subst, Substs};
|
use rustc::ty::subst::{Subst, Substs};
|
||||||
use rustc::ty::{self, AdtKind, Ty, TyCtxt};
|
use rustc::ty::{self, Ty, TyCtxt};
|
||||||
use rustc::traits::{self, ObligationCause, Reveal};
|
use rustc::traits::{self, ObligationCause, Reveal};
|
||||||
use util::common::ErrorReported;
|
use util::common::ErrorReported;
|
||||||
use util::nodemap::FxHashSet;
|
use util::nodemap::FxHashSet;
|
||||||
|
|
||||||
use syntax::ast;
|
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
|
||||||
/// check_drop_impl confirms that the Drop implementation identfied by
|
/// check_drop_impl confirms that the Drop implementation identfied by
|
||||||
|
|
@ -270,389 +269,64 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
|
||||||
///
|
///
|
||||||
pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
|
pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
|
||||||
rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>,
|
rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>,
|
||||||
typ: ty::Ty<'tcx>,
|
ty: ty::Ty<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
scope: region::CodeExtent)
|
scope: region::CodeExtent)
|
||||||
|
-> Result<(), ErrorReported>
|
||||||
{
|
{
|
||||||
debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}",
|
debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}",
|
||||||
typ, scope);
|
ty, scope);
|
||||||
|
|
||||||
|
|
||||||
let parent_scope = match rcx.tcx.region_maps.opt_encl_scope(scope) {
|
let parent_scope = match rcx.tcx.region_maps.opt_encl_scope(scope) {
|
||||||
Some(parent_scope) => parent_scope,
|
Some(parent_scope) => parent_scope,
|
||||||
// If no enclosing scope, then it must be the root scope which cannot be outlived.
|
// If no enclosing scope, then it must be the root scope
|
||||||
None => return
|
// which cannot be outlived.
|
||||||
|
None => return Ok(())
|
||||||
};
|
};
|
||||||
|
let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope));
|
||||||
|
let origin = || infer::SubregionOrigin::SafeDestructor(span);
|
||||||
|
|
||||||
let result = iterate_over_potentially_unsafe_regions_in_type(
|
let ty = rcx.fcx.resolve_type_vars_if_possible(&ty);
|
||||||
&mut DropckContext {
|
let for_ty = ty;
|
||||||
rcx: rcx,
|
let mut types = vec![(ty, 0)];
|
||||||
span: span,
|
let mut known = FxHashSet();
|
||||||
parent_scope: parent_scope,
|
while let Some((ty, depth)) = types.pop() {
|
||||||
breadcrumbs: FxHashSet()
|
let ty::DtorckConstraint {
|
||||||
},
|
dtorck_types, outlives
|
||||||
TypeContext::Root,
|
} = rcx.tcx.dtorck_constraint_for_ty(span, for_ty, depth, ty)?;
|
||||||
typ,
|
|
||||||
0);
|
for ty in dtorck_types {
|
||||||
match result {
|
let ty = rcx.fcx.normalize_associated_types_in(span, &ty);
|
||||||
Ok(()) => {}
|
let ty = rcx.fcx.resolve_type_vars_with_obligations(ty);
|
||||||
Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => {
|
let ty = rcx.fcx.resolve_type_and_region_vars_if_possible(&ty);
|
||||||
let tcx = rcx.tcx;
|
match ty.sty {
|
||||||
let mut err = struct_span_err!(tcx.sess, span, E0320,
|
// All parameters live for the duration of the
|
||||||
"overflow while adding drop-check rules for {}", typ);
|
// function.
|
||||||
match *ctxt {
|
ty::TyParam(..) => {}
|
||||||
TypeContext::Root => {
|
|
||||||
// no need for an additional note if the overflow
|
// A projection that we couldn't resolve - it
|
||||||
// was somehow on the root.
|
// might have a destructor.
|
||||||
|
ty::TyProjection(..) | ty::TyAnon(..) => {
|
||||||
|
rcx.type_must_outlive(origin(), ty, parent_scope);
|
||||||
}
|
}
|
||||||
TypeContext::ADT { def_id, variant, field } => {
|
|
||||||
let adt = tcx.lookup_adt_def(def_id);
|
_ => {
|
||||||
let variant_name = match adt.adt_kind() {
|
if let None = known.replace(ty) {
|
||||||
AdtKind::Enum => format!("enum {} variant {}",
|
types.push((ty, depth+1));
|
||||||
tcx.item_path_str(def_id),
|
}
|
||||||
variant),
|
|
||||||
AdtKind::Struct => format!("struct {}",
|
|
||||||
tcx.item_path_str(def_id)),
|
|
||||||
AdtKind::Union => format!("union {}",
|
|
||||||
tcx.item_path_str(def_id)),
|
|
||||||
};
|
|
||||||
span_note!(
|
|
||||||
&mut err,
|
|
||||||
span,
|
|
||||||
"overflowed on {} field {} type: {}",
|
|
||||||
variant_name,
|
|
||||||
field,
|
|
||||||
detected_on_typ);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Error<'tcx> {
|
|
||||||
Overflow(TypeContext, ty::Ty<'tcx>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
enum TypeContext {
|
|
||||||
Root,
|
|
||||||
ADT {
|
|
||||||
def_id: DefId,
|
|
||||||
variant: ast::Name,
|
|
||||||
field: ast::Name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DropckContext<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
|
|
||||||
rcx: &'a mut RegionCtxt<'b, 'gcx, 'tcx>,
|
|
||||||
/// types that have already been traversed
|
|
||||||
breadcrumbs: FxHashSet<Ty<'tcx>>,
|
|
||||||
/// span for error reporting
|
|
||||||
span: Span,
|
|
||||||
/// the scope reachable dtorck types must outlive
|
|
||||||
parent_scope: region::CodeExtent
|
|
||||||
}
|
|
||||||
|
|
||||||
// `context` is used for reporting overflow errors
|
|
||||||
fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
|
|
||||||
cx: &mut DropckContext<'a, 'b, 'gcx, 'tcx>,
|
|
||||||
context: TypeContext,
|
|
||||||
ty: Ty<'tcx>,
|
|
||||||
depth: usize)
|
|
||||||
-> Result<(), Error<'tcx>>
|
|
||||||
{
|
|
||||||
let tcx = cx.rcx.tcx;
|
|
||||||
// Issue #22443: Watch out for overflow. While we are careful to
|
|
||||||
// handle regular types properly, non-regular ones cause problems.
|
|
||||||
let recursion_limit = tcx.sess.recursion_limit.get();
|
|
||||||
if depth / 4 >= recursion_limit {
|
|
||||||
// This can get into rather deep recursion, especially in the
|
|
||||||
// presence of things like Vec<T> -> Unique<T> -> PhantomData<T> -> T.
|
|
||||||
// use a higher recursion limit to avoid errors.
|
|
||||||
return Err(Error::Overflow(context, ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
// canoncialize the regions in `ty` before inserting - infinitely many
|
|
||||||
// region variables can refer to the same region.
|
|
||||||
let ty = cx.rcx.resolve_type_and_region_vars_if_possible(&ty);
|
|
||||||
|
|
||||||
if !cx.breadcrumbs.insert(ty) {
|
|
||||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
|
||||||
{}ty: {} scope: {:?} - cached",
|
|
||||||
(0..depth).map(|_| ' ').collect::<String>(),
|
|
||||||
ty, cx.parent_scope);
|
|
||||||
return Ok(()); // we already visited this type
|
|
||||||
}
|
|
||||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
|
||||||
{}ty: {} scope: {:?}",
|
|
||||||
(0..depth).map(|_| ' ').collect::<String>(),
|
|
||||||
ty, cx.parent_scope);
|
|
||||||
|
|
||||||
// If `typ` has a destructor, then we must ensure that all
|
|
||||||
// borrowed data reachable via `typ` must outlive the parent
|
|
||||||
// of `scope`. This is handled below.
|
|
||||||
//
|
|
||||||
// However, there is an important special case: for any Drop
|
|
||||||
// impl that is tagged as "blind" to their parameters,
|
|
||||||
// we assume that data borrowed via such type parameters
|
|
||||||
// remains unreachable via that Drop impl.
|
|
||||||
//
|
|
||||||
// For example, consider:
|
|
||||||
//
|
|
||||||
// ```rust
|
|
||||||
// #[unsafe_destructor_blind_to_params]
|
|
||||||
// impl<T> Drop for Vec<T> { ... }
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// which does have to be able to drop instances of `T`, but
|
|
||||||
// otherwise cannot read data from `T`.
|
|
||||||
//
|
|
||||||
// Of course, for the type expression passed in for any such
|
|
||||||
// unbounded type parameter `T`, we must resume the recursive
|
|
||||||
// analysis on `T` (since it would be ignored by
|
|
||||||
// type_must_outlive).
|
|
||||||
let dropck_kind = has_dtor_of_interest(tcx, ty);
|
|
||||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
|
||||||
ty: {:?} dropck_kind: {:?}", ty, dropck_kind);
|
|
||||||
match dropck_kind {
|
|
||||||
DropckKind::NoBorrowedDataAccessedInMyDtor => {
|
|
||||||
// The maximally blind attribute.
|
|
||||||
}
|
|
||||||
DropckKind::BorrowedDataMustStrictlyOutliveSelf => {
|
|
||||||
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
|
|
||||||
ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
DropckKind::RevisedSelf(revised_ty) => {
|
|
||||||
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
|
|
||||||
revised_ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
|
|
||||||
// Do not return early from this case; we want
|
|
||||||
// to recursively process the internal structure of Self
|
|
||||||
// (because even though the Drop for Self has been asserted
|
|
||||||
// safe, the types instantiated for the generics of Self
|
|
||||||
// may themselves carry dropck constraints.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
|
||||||
{}ty: {} scope: {:?} - checking interior",
|
|
||||||
(0..depth).map(|_| ' ').collect::<String>(),
|
|
||||||
ty, cx.parent_scope);
|
|
||||||
|
|
||||||
// We still need to ensure all referenced data is safe.
|
|
||||||
match ty.sty {
|
|
||||||
ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
|
|
||||||
ty::TyFloat(_) | ty::TyStr | ty::TyNever => {
|
|
||||||
// primitive - definitely safe
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::TyArray(ity, _) | ty::TySlice(ity) => {
|
for outlive in outlives {
|
||||||
// single-element containers, behave like their element
|
if let Some(r) = outlive.as_region() {
|
||||||
iterate_over_potentially_unsafe_regions_in_type(
|
rcx.sub_regions(origin(), parent_scope, r);
|
||||||
cx, context, ity, depth+1)
|
} else if let Some(ty) = outlive.as_type() {
|
||||||
}
|
rcx.type_must_outlive(origin(), ty, parent_scope);
|
||||||
|
|
||||||
ty::TyAdt(def, substs) if def.is_phantom_data() => {
|
|
||||||
// PhantomData<T> - behaves identically to T
|
|
||||||
let ity = substs.type_at(0);
|
|
||||||
iterate_over_potentially_unsafe_regions_in_type(
|
|
||||||
cx, context, ity, depth+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::TyAdt(def, substs) => {
|
|
||||||
let did = def.did;
|
|
||||||
for variant in &def.variants {
|
|
||||||
for field in variant.fields.iter() {
|
|
||||||
let fty = field.ty(tcx, substs);
|
|
||||||
let fty = cx.rcx.fcx.resolve_type_vars_with_obligations(
|
|
||||||
cx.rcx.fcx.normalize_associated_types_in(cx.span, &fty));
|
|
||||||
iterate_over_potentially_unsafe_regions_in_type(
|
|
||||||
cx,
|
|
||||||
TypeContext::ADT {
|
|
||||||
def_id: did,
|
|
||||||
field: field.name,
|
|
||||||
variant: variant.name,
|
|
||||||
},
|
|
||||||
fty,
|
|
||||||
depth+1)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::TyClosure(def_id, substs) => {
|
|
||||||
for ty in substs.upvar_tys(def_id, tcx) {
|
|
||||||
iterate_over_potentially_unsafe_regions_in_type(cx, context, ty, depth+1)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::TyTuple(tys, _) => {
|
|
||||||
for ty in tys {
|
|
||||||
iterate_over_potentially_unsafe_regions_in_type(cx, context, ty, depth+1)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyParam(..) => {
|
|
||||||
// these always come with a witness of liveness (references
|
|
||||||
// explicitly, pointers implicitly, parameters by the
|
|
||||||
// caller).
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
|
|
||||||
// FIXME(#26656): this type is always destruction-safe, but
|
|
||||||
// it implicitly witnesses Self: Fn, which can be false.
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::TyInfer(..) | ty::TyError => {
|
|
||||||
tcx.sess.delay_span_bug(cx.span, "unresolved type in regionck");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// these are always dtorck
|
|
||||||
ty::TyDynamic(..) | ty::TyProjection(_) | ty::TyAnon(..) => bug!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
enum DropckKind<'tcx> {
|
|
||||||
/// The "safe" kind; i.e. conservatively assume any borrow
|
|
||||||
/// accessed by dtor, and therefore such data must strictly
|
|
||||||
/// outlive self.
|
|
||||||
///
|
|
||||||
/// Equivalent to RevisedTy with no change to the self type.
|
|
||||||
BorrowedDataMustStrictlyOutliveSelf,
|
|
||||||
|
|
||||||
/// The nearly completely-unsafe kind.
|
|
||||||
///
|
|
||||||
/// Equivalent to RevisedSelf with *all* parameters remapped to ()
|
|
||||||
/// (maybe...?)
|
|
||||||
NoBorrowedDataAccessedInMyDtor,
|
|
||||||
|
|
||||||
/// Assume all borrowed data access by dtor occurs as if Self has the
|
|
||||||
/// type carried by this variant. In practice this means that some
|
|
||||||
/// of the type parameters are remapped to `()` (and some lifetime
|
|
||||||
/// parameters remapped to `'static`), because the developer has asserted
|
|
||||||
/// that the destructor will not access their contents.
|
|
||||||
RevisedSelf(Ty<'tcx>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the classification of what kind of check should be applied
|
|
||||||
/// to `ty`, which may include a revised type where some of the type
|
|
||||||
/// parameters are re-mapped to `()` to reflect the destructor's
|
|
||||||
/// "purity" with respect to their actual contents.
|
|
||||||
fn has_dtor_of_interest<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
|
||||||
ty: Ty<'tcx>)
|
|
||||||
-> DropckKind<'tcx> {
|
|
||||||
match ty.sty {
|
|
||||||
ty::TyAdt(adt_def, substs) => {
|
|
||||||
if !adt_def.is_dtorck(tcx) {
|
|
||||||
return DropckKind::NoBorrowedDataAccessedInMyDtor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the `impl<..> Drop for _` to inspect any
|
|
||||||
// attributes attached to the impl's generics.
|
|
||||||
let dtor_method = adt_def.destructor(tcx)
|
|
||||||
.expect("dtorck type without destructor impossible");
|
|
||||||
let method = tcx.associated_item(dtor_method.did);
|
|
||||||
let impl_def_id = method.container.id();
|
|
||||||
let revised_ty = revise_self_ty(tcx, adt_def, impl_def_id, substs);
|
|
||||||
return DropckKind::RevisedSelf(revised_ty);
|
|
||||||
}
|
|
||||||
ty::TyDynamic(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
|
|
||||||
debug!("ty: {:?} isn't known, and therefore is a dropck type", ty);
|
|
||||||
return DropckKind::BorrowedDataMustStrictlyOutliveSelf;
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return DropckKind::NoBorrowedDataAccessedInMyDtor;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
// Constructs new Ty just like the type defined by `adt_def` coupled
|
|
||||||
// with `substs`, except each type and lifetime parameter marked as
|
|
||||||
// `#[may_dangle]` in the Drop impl (identified by `impl_def_id`) is
|
|
||||||
// respectively mapped to `()` or `'static`.
|
|
||||||
//
|
|
||||||
// For example: If the `adt_def` maps to:
|
|
||||||
//
|
|
||||||
// enum Foo<'a, X, Y> { ... }
|
|
||||||
//
|
|
||||||
// and the `impl_def_id` maps to:
|
|
||||||
//
|
|
||||||
// impl<#[may_dangle] 'a, X, #[may_dangle] Y> Drop for Foo<'a, X, Y> { ... }
|
|
||||||
//
|
|
||||||
// then revises input: `Foo<'r,i64,&'r i64>` to: `Foo<'static,i64,()>`
|
|
||||||
fn revise_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
|
||||||
adt_def: &'tcx ty::AdtDef,
|
|
||||||
impl_def_id: DefId,
|
|
||||||
substs: &Substs<'tcx>)
|
|
||||||
-> Ty<'tcx> {
|
|
||||||
// Get generics for `impl Drop` to query for `#[may_dangle]` attr.
|
|
||||||
let impl_bindings = tcx.item_generics(impl_def_id);
|
|
||||||
|
|
||||||
// Get Substs attached to Self on `impl Drop`; process in parallel
|
|
||||||
// with `substs`, replacing dangling entries as appropriate.
|
|
||||||
let self_substs = {
|
|
||||||
let impl_self_ty: Ty<'tcx> = tcx.item_type(impl_def_id);
|
|
||||||
if let ty::TyAdt(self_adt_def, self_substs) = impl_self_ty.sty {
|
|
||||||
assert_eq!(adt_def, self_adt_def);
|
|
||||||
self_substs
|
|
||||||
} else {
|
|
||||||
bug!("Self in `impl Drop for _` must be an Adt.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Walk `substs` + `self_substs`, build new substs appropriate for
|
|
||||||
// `adt_def`; each non-dangling param reuses entry from `substs`.
|
|
||||||
//
|
|
||||||
// Note: The manner we map from a right-hand side (i.e. Region or
|
|
||||||
// Ty) for a given `def` to generic parameter associated with that
|
|
||||||
// right-hand side is tightly coupled to `Drop` impl constraints.
|
|
||||||
//
|
|
||||||
// E.g. we know such a Ty must be `TyParam`, because a destructor
|
|
||||||
// for `struct Foo<X>` is defined via `impl<Y> Drop for Foo<Y>`,
|
|
||||||
// and never by (for example) `impl<Z> Drop for Foo<Vec<Z>>`.
|
|
||||||
let substs = Substs::for_item(
|
|
||||||
tcx,
|
|
||||||
adt_def.did,
|
|
||||||
|def, _| {
|
|
||||||
let r_orig = substs.region_for_def(def);
|
|
||||||
let impl_self_orig = self_substs.region_for_def(def);
|
|
||||||
let r = if let ty::Region::ReEarlyBound(ref ebr) = *impl_self_orig {
|
|
||||||
if impl_bindings.region_param(ebr).pure_wrt_drop {
|
|
||||||
tcx.types.re_static
|
|
||||||
} else {
|
|
||||||
r_orig
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bug!("substs for an impl must map regions to ReEarlyBound");
|
|
||||||
};
|
|
||||||
debug!("has_dtor_of_interest mapping def {:?} orig {:?} to {:?}",
|
|
||||||
def, r_orig, r);
|
|
||||||
r
|
|
||||||
},
|
|
||||||
|def, _| {
|
|
||||||
let t_orig = substs.type_for_def(def);
|
|
||||||
let impl_self_orig = self_substs.type_for_def(def);
|
|
||||||
let t = if let ty::TypeVariants::TyParam(ref pt) = impl_self_orig.sty {
|
|
||||||
if impl_bindings.type_param(pt).pure_wrt_drop {
|
|
||||||
tcx.mk_nil()
|
|
||||||
} else {
|
|
||||||
t_orig
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bug!("substs for an impl must map types to TyParam");
|
|
||||||
};
|
|
||||||
debug!("has_dtor_of_interest mapping def {:?} orig {:?} {:?} to {:?} {:?}",
|
|
||||||
def, t_orig, t_orig.sty, t, t.sty);
|
|
||||||
t
|
|
||||||
});
|
|
||||||
|
|
||||||
tcx.mk_adt(adt_def, &substs)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -457,7 +457,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||||
self.type_of_node_must_outlive(origin, id, var_region);
|
self.type_of_node_must_outlive(origin, id, var_region);
|
||||||
|
|
||||||
let typ = self.resolve_node_type(id);
|
let typ = self.resolve_node_type(id);
|
||||||
dropck::check_safety_of_destructor_if_necessary(self, typ, span, var_scope);
|
let _ = dropck::check_safety_of_destructor_if_necessary(
|
||||||
|
self, typ, span, var_scope);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -995,10 +996,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||||
match *region {
|
match *region {
|
||||||
ty::ReScope(rvalue_scope) => {
|
ty::ReScope(rvalue_scope) => {
|
||||||
let typ = self.resolve_type(cmt.ty);
|
let typ = self.resolve_type(cmt.ty);
|
||||||
dropck::check_safety_of_destructor_if_necessary(self,
|
let _ = dropck::check_safety_of_destructor_if_necessary(
|
||||||
typ,
|
self, typ, span, rvalue_scope);
|
||||||
span,
|
|
||||||
rvalue_scope);
|
|
||||||
}
|
}
|
||||||
ty::ReStatic => {}
|
ty::ReStatic => {}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
||||||
|
|
@ -4154,7 +4154,6 @@ register_diagnostics! {
|
||||||
// E0248, // value used as a type, now reported earlier during resolution as E0412
|
// E0248, // value used as a type, now reported earlier during resolution as E0412
|
||||||
// E0249,
|
// E0249,
|
||||||
// E0319, // trait impls for defaulted traits allowed just for structs/enums
|
// E0319, // trait impls for defaulted traits allowed just for structs/enums
|
||||||
E0320, // recursive overflow during dropck
|
|
||||||
// E0372, // coherence not object safe
|
// E0372, // coherence not object safe
|
||||||
E0377, // the trait `CoerceUnsized` may only be implemented for a coercion
|
E0377, // the trait `CoerceUnsized` may only be implemented for a coercion
|
||||||
// between structures with the same definition
|
// between structures with the same definition
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue