about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-04-23 20:11:50 +0000
committerbors <bors@rust-lang.org>2017-04-23 20:11:50 +0000
commit2bd4b5c6db1468235f730bce403bf657123ecc57 (patch)
treed906f9a263abe6302ee7f0a90e1f9b9b280fa0ca
parent23de823e93dc19ef1b9cb65b48860a892cd6ff18 (diff)
parentd3476f4b7694b8453cf52268880057a9df938089 (diff)
downloadrust-2bd4b5c6db1468235f730bce403bf657123ecc57.tar.gz
rust-2bd4b5c6db1468235f730bce403bf657123ecc57.zip
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
-rw-r--r--src/librustc/dep_graph/dep_node.rs2
-rw-r--r--src/librustc/diagnostics.rs1
-rw-r--r--src/librustc/traits/select.rs8
-rw-r--r--src/librustc/ty/maps.rs10
-rw-r--r--src/librustc/ty/mod.rs160
-rw-r--r--src/librustc/ty/util.rs164
-rw-r--r--src/librustc_typeck/check/dropck.rs412
-rw-r--r--src/librustc_typeck/check/regionck.rs9
-rw-r--r--src/librustc_typeck/diagnostics.rs1
9 files changed, 320 insertions, 447 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 9a657438522..33133f6834b 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -91,6 +91,7 @@ pub enum DepNode<D: Clone + Debug> {
     IsForeignItem(D),
     TypeParamPredicates((D, D)),
     SizedConstraint(D),
+    DtorckConstraint(D),
     AdtDestructor(D),
     AssociatedItemDefIds(D),
     InherentImpls(D),
@@ -228,6 +229,7 @@ impl<D: Clone + Debug> DepNode<D> {
                 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 2851191dc14..30e9f502abc 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 6442487ead9..e61e5743790 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 4247bff0f50..4595a8c4f77 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<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 { ... }`).
     pub is_foreign_item: IsForeignItem(DefId) -> bool,
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index cc5eb768d9b..352b6ab1d35 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::<Vec<_>>());
 
-    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::<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>,
@@ -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<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());
+    }
+}
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index cdf3cf00b24..f68cf6f97f8 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<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
         // 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<T:Clone>`).
-        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>(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
+    }
+
+    /// 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 {
+    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 09bfe45f540..c125d7c0255 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<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) => {
-            // 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<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)?
+                _ => {
+                    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<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)
+    Ok(())
 }
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 8bfb390bd2a..dcef22da879 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 c50156fa5f2..2bae6a0d9e1 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