diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 9a6574385222a..33133f6834b9a 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -91,6 +91,7 @@ pub enum DepNode { IsForeignItem(D), TypeParamPredicates((D, D)), SizedConstraint(D), + DtorckConstraint(D), AdtDestructor(D), AssociatedItemDefIds(D), InherentImpls(D), @@ -228,6 +229,7 @@ impl DepNode { Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param))))) } SizedConstraint(ref d) => op(d).map(SizedConstraint), + DtorckConstraint(ref d) => op(d).map(DtorckConstraint), AdtDestructor(ref d) => op(d).map(AdtDestructor), AssociatedItemDefIds(ref d) => op(d).map(AssociatedItemDefIds), InherentImpls(ref d) => op(d).map(InherentImpls), diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 2851191dc141a..30e9f502abc8c 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1829,6 +1829,7 @@ register_diagnostics! { E0314, // closure outlives stack frame E0315, // cannot invoke closure outside of its lifetime E0316, // nested quantification of lifetimes + E0320, // recursive overflow during dropck E0473, // dereference of reference outside its lifetime E0474, // captured variable `..` does not outlive the enclosing closure E0475, // index of slice outside its lifetime diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 6442487ead957..e61e574379069 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1790,11 +1790,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::TyAdt(def, substs) => { let sized_crit = def.sized_constraint(self.tcx()); // (*) binder moved here - Where(ty::Binder(match sized_crit.sty { - ty::TyTuple(tys, _) => tys.to_vec().subst(self.tcx(), substs), - ty::TyBool => vec![], - _ => vec![sized_crit.subst(self.tcx(), substs)] - })) + Where(ty::Binder( + sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect() + )) } ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None, diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index 4247bff0f5026..4595a8c4f7784 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -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> { span: Span, cycle: RefMut<'a, [(Span, Query<'tcx>)]>, @@ -396,7 +403,8 @@ define_maps! { <'tcx> pub trait_def: ItemSignature(DefId) -> &'tcx ty::TraitDef, pub adt_def: ItemSignature(DefId) -> &'tcx ty::AdtDef, pub adt_destructor: AdtDestructor(DefId) -> Option, - 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 { ... }`). pub is_foreign_item: IsForeignItem(DefId) -> bool, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index cc5eb768d9b32..352b6ab1d356f 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -31,13 +31,15 @@ use ty; use ty::subst::{Subst, Substs}; use ty::util::IntTypeExt; 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 std::cell::{Cell, RefCell, Ref}; use std::collections::BTreeMap; use std::cmp; use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; use std::ops::Deref; use std::rc::Rc; use std::slice; @@ -1332,17 +1334,6 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> { pub struct Destructor { /// The def-id of the destructor method 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! { @@ -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 /// of coherence checking. #[inline] @@ -1780,16 +1763,9 @@ impl<'a, 'gcx, 'tcx> AdtDef { 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. /// - /// 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 /// actually more expressive than checking all members: /// 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 /// 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) { - Ok(ty) => ty, + Ok(tys) => tys, Err(_) => { debug!("adt_sized_constraint: {:?} is recursive", self); // This should be reported as an error by `check_representable`. // // Consider the type as Sized in the meanwhile to avoid // further errors. - tcx.types.err + tcx.intern_type_list(&[tcx.types.err]) } } } @@ -1836,18 +1812,13 @@ impl<'a, 'gcx, 'tcx> AdtDef { TyAdt(adt, substs) => { // recursive case - let adt_ty = - adt.sized_constraint(tcx) - .subst(tcx, substs); + let adt_tys = adt.sized_constraint(tcx); debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", - ty, adt_ty); - if let ty::TyTuple(ref tys, _) = adt_ty.sty { - tys.iter().flat_map(|ty| { - self.sized_constraint_for_ty(tcx, ty) - }).collect() - } else { - self.sized_constraint_for_ty(tcx, adt_ty) - } + ty, adt_tys); + adt_tys.iter() + .map(|ty| ty.subst(tcx, substs)) + .flat_map(|ty| self.sized_constraint_for_ty(tcx, ty)) + .collect() } TyProjection(..) | TyAnon(..) => { @@ -2697,13 +2668,7 @@ fn associated_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) /// Calculates the Sized-constraint. /// -/// As the Sized-constraint of enums can be a *set* of types, -/// 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 +/// In fact, there are only a few options for the types in the constraint: /// - an obviously-unsized type /// - a type parameter or projection whose Sizedness can't be known /// - 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. fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> Ty<'tcx> { + -> &'tcx [Ty<'tcx>] { 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() }).flat_map(|f| { - let ty = tcx.item_type(f.did); - def.sized_constraint_for_ty(tcx, ty) - }).collect(); - - let ty = match tys.len() { - _ if tys.references_error() => tcx.types.err, - 0 => tcx.types.bool, - 1 => tys[0], - _ => tcx.intern_tup(&tys[..], false) - }; + def.sized_constraint_for_ty(tcx, tcx.item_type(f.did)) + }).collect::>()); - debug!("adt_sized_constraint: {:?} => {:?}", def, ty); + debug!("adt_sized_constraint: {:?} => {:?}", def, result); - ty + result +} + +/// 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::>() + .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>, @@ -2762,6 +2751,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { associated_item, associated_item_def_ids, adt_sized_constraint, + adt_dtorck_constraint, ..*providers }; } @@ -2769,6 +2759,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { pub fn provide_extern(providers: &mut ty::maps::Providers) { *providers = ty::maps::Providers { adt_sized_constraint, + adt_dtorck_constraint, ..*providers }; } @@ -2784,3 +2775,46 @@ pub fn provide_extern(providers: &mut ty::maps::Providers) { pub struct CrateInherentImpls { pub inherent_impls: DefIdMap>>, } + +/// 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>, + /// Types that could not be resolved: projections and params. + pub dtorck_types: Vec>, +} + +impl<'tcx> FromIterator> for DtorckConstraint<'tcx> +{ + fn from_iter>>(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()); + } +} diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index cdf3cf00b24ee..f68cf6f97f857 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -19,6 +19,7 @@ use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable}; use ty::ParameterEnvironment; use ty::fold::TypeVisitor; use ty::layout::{Layout, LayoutError}; +use ty::subst::{Subst, Kind}; use ty::TypeVariants::*; use util::common::ErrorReported; use util::nodemap::{FxHashMap, FxHashSet}; @@ -385,6 +386,27 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { 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> + { + 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 // attribute `unsafe_destructor_blind_to_params`, then 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 // `*foo.0` of `Foo<'a>(&'a u32)`) or indirectly hidden // (e.g. calling `foo.0.clone()` of `Foo`). - let is_dtorck = !self.has_attr(dtor_did, "unsafe_destructor_blind_to_params"); - Some(ty::Destructor { did: dtor_did, is_dtorck: is_dtorck }) + if self.has_attr(dtor, "unsafe_destructor_blind_to_params") { + 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); + // + // and the impl is + // + // impl<#[may_dangle] P0, P1, P2> Drop for S + // + // We want to return the parameters (X, Y). For that, we match + // up the item-substs with the substs on the impl ADT, + // , 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 + } + + /// 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, 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 { + pub fn closure_base_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; while self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr { def_id = self.parent_def_id(def_id).unwrap_or_else(|| { diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 09bfe45f5404b..c125d7c02556f 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -15,12 +15,11 @@ use middle::free_region::FreeRegionMap; use rustc::infer::{self, InferOk}; use middle::region; 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 util::common::ErrorReported; use util::nodemap::FxHashSet; -use syntax::ast; use syntax_pos::Span; /// 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>( rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>, - typ: ty::Ty<'tcx>, + ty: ty::Ty<'tcx>, span: Span, scope: region::CodeExtent) + -> Result<(), ErrorReported> { 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) { - Some(parent_scope) => parent_scope, - // If no enclosing scope, then it must be the root scope which cannot be outlived. - None => return + Some(parent_scope) => parent_scope, + // If no enclosing scope, then it must be the root scope + // which cannot be outlived. + None => return Ok(()) }; - - let result = iterate_over_potentially_unsafe_regions_in_type( - &mut DropckContext { - rcx: rcx, - span: span, - parent_scope: parent_scope, - breadcrumbs: FxHashSet() - }, - TypeContext::Root, - typ, - 0); - match result { - Ok(()) => {} - Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => { - let tcx = rcx.tcx; - let mut err = struct_span_err!(tcx.sess, span, E0320, - "overflow while adding drop-check rules for {}", typ); - match *ctxt { - TypeContext::Root => { - // no need for an additional note if the overflow - // was somehow on the root. - } - TypeContext::ADT { def_id, variant, field } => { - let adt = tcx.lookup_adt_def(def_id); - let variant_name = match adt.adt_kind() { - AdtKind::Enum => format!("enum {} variant {}", - 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); + let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope)); + let origin = || infer::SubregionOrigin::SafeDestructor(span); + + let ty = rcx.fcx.resolve_type_vars_if_possible(&ty); + let for_ty = ty; + let mut types = vec![(ty, 0)]; + let mut known = FxHashSet(); + while let Some((ty, depth)) = types.pop() { + let ty::DtorckConstraint { + dtorck_types, outlives + } = rcx.tcx.dtorck_constraint_for_ty(span, for_ty, depth, ty)?; + + for ty in dtorck_types { + let ty = rcx.fcx.normalize_associated_types_in(span, &ty); + let ty = rcx.fcx.resolve_type_vars_with_obligations(ty); + let ty = rcx.fcx.resolve_type_and_region_vars_if_possible(&ty); + match ty.sty { + // All parameters live for the duration of the + // function. + ty::TyParam(..) => {} + + // A projection that we couldn't resolve - it + // might have a destructor. + ty::TyProjection(..) | ty::TyAnon(..) => { + rcx.type_must_outlive(origin(), ty, parent_scope); } - } - 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>, - /// 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 -> Unique -> PhantomData -> 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::(), - 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::(), - 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 Drop for Vec { ... } - // ``` - // - // 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::(), - 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) => { - // single-element containers, behave like their element - iterate_over_potentially_unsafe_regions_in_type( - cx, context, ity, depth+1) - } - - ty::TyAdt(def, substs) if def.is_phantom_data() => { - // PhantomData - 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)? + _ => { + if let None = known.replace(ty) { + types.push((ty, 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; + for outlive in outlives { + if let Some(r) = outlive.as_region() { + rcx.sub_regions(origin(), parent_scope, r); + } else if let Some(ty) = outlive.as_type() { + rcx.type_must_outlive(origin(), ty, parent_scope); } - - // 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; } } -} -// 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` is defined via `impl Drop for Foo`, - // and never by (for example) `impl Drop for Foo>`. - 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) + Ok(()) } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 8bfb390bd2a55..dcef22da87964 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -457,7 +457,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_of_node_must_outlive(origin, id, var_region); 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 { ty::ReScope(rvalue_scope) => { let typ = self.resolve_type(cmt.ty); - dropck::check_safety_of_destructor_if_necessary(self, - typ, - span, - rvalue_scope); + let _ = dropck::check_safety_of_destructor_if_necessary( + self, typ, span, rvalue_scope); } ty::ReStatic => {} _ => { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index c50156fa5f271..2bae6a0d9e11a 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4154,7 +4154,6 @@ register_diagnostics! { // E0248, // value used as a type, now reported earlier during resolution as E0412 // E0249, // E0319, // trait impls for defaulted traits allowed just for structs/enums - E0320, // recursive overflow during dropck // E0372, // coherence not object safe E0377, // the trait `CoerceUnsized` may only be implemented for a coercion // between structures with the same definition