about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-02-12 19:31:49 +0000
committerbors <bors@rust-lang.org>2020-02-12 19:31:49 +0000
commita1912f2e89b77cfe2a0e64b96f444848fe4e2d49 (patch)
tree3d83c761220edeb1195c6a876f8baef0b1d90c56 /src
parent2d2be570970d784db5539a1d309cd22b85be910a (diff)
parent30a8353f372f7cc719d1de6811996ce5215183a6 (diff)
downloadrust-a1912f2e89b77cfe2a0e64b96f444848fe4e2d49.tar.gz
rust-a1912f2e89b77cfe2a0e64b96f444848fe4e2d49.zip
Auto merge of #68679 - matthewjasper:needs-type-op, r=varkor
Improve `ty.needs_drop`

* Handle cycles in `needs_drop` correctly
* Normalize types when computing `needs_drop`
* Move queries from rustc to rustc_ty
* Avoid query in simple cases

reopens #65918
Diffstat (limited to 'src')
-rw-r--r--src/librustc/query/mod.rs19
-rw-r--r--src/librustc/traits/misc.rs132
-rw-r--r--src/librustc/traits/mod.rs1
-rw-r--r--src/librustc/ty/instance.rs2
-rw-r--r--src/librustc/ty/mod.rs22
-rw-r--r--src/librustc/ty/query/mod.rs2
-rw-r--r--src/librustc/ty/query/values.rs7
-rw-r--r--src/librustc/ty/util.rs92
-rw-r--r--src/librustc_ty/common_traits.rs40
-rw-r--r--src/librustc_ty/lib.rs5
-rw-r--r--src/librustc_ty/needs_drop.rs165
-rw-r--r--src/librustc_ty/ty.rs6
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/test/ui/recursion/recursion.rs1
-rw-r--r--src/test/ui/recursion/recursion.stderr2
-rw-r--r--src/test/ui/type-alias-impl-trait/issue-65918.rs2
16 files changed, 337 insertions, 163 deletions
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index 425f14a5740..11b079806af 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -669,26 +669,29 @@ rustc_queries! {
             no_force
             desc { "computing whether `{}` is `Copy`", env.value }
         }
+        /// Query backing `TyS::is_sized`.
         query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
             no_force
             desc { "computing whether `{}` is `Sized`", env.value }
         }
+        /// Query backing `TyS::is_freeze`.
         query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
             no_force
             desc { "computing whether `{}` is freeze", env.value }
         }
-
-        // The cycle error here should be reported as an error by `check_representable`.
-        // We consider the type as not needing drop in the meanwhile to avoid
-        // further errors (done in impl Value for NeedsDrop).
-        // Use `cycle_delay_bug` to delay the cycle error here to be emitted later
-        // in case we accidentally otherwise don't emit an error.
-        query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop {
-            cycle_delay_bug
+        /// Query backing `TyS::needs_drop`.
+        query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
             no_force
             desc { "computing whether `{}` needs drop", env.value }
         }
 
+        /// A list of types where the ADT requires drop if and only if any of
+        /// those types require drop. If the ADT is known to always need drop
+        /// then `Err(AlwaysRequiresDrop)` is returned.
+        query adt_drop_tys(_: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
+            cache_on_disk_if { true }
+        }
+
         query layout_raw(
             env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
         ) -> Result<&'tcx ty::layout::LayoutDetails, ty::layout::LayoutError<'tcx>> {
diff --git a/src/librustc/traits/misc.rs b/src/librustc/traits/misc.rs
index 08c3a77bf3a..3fd0d12c626 100644
--- a/src/librustc/traits/misc.rs
+++ b/src/librustc/traits/misc.rs
@@ -1,12 +1,9 @@
 //! Miscellaneous type-system utilities that are too small to deserve their own modules.
 
-use crate::middle::lang_items;
 use crate::traits::{self, ObligationCause};
-use crate::ty::util::NeedsDrop;
 use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
 
 use rustc_hir as hir;
-use rustc_span::DUMMY_SP;
 
 #[derive(Clone)]
 pub enum CopyImplementationError<'tcx> {
@@ -71,132 +68,3 @@ pub fn can_type_implement_copy(
         Ok(())
     })
 }
-
-fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    is_item_raw(tcx, query, lang_items::CopyTraitLangItem)
-}
-
-fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    is_item_raw(tcx, query, lang_items::SizedTraitLangItem)
-}
-
-fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    is_item_raw(tcx, query, lang_items::FreezeTraitLangItem)
-}
-
-fn is_item_raw<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
-    item: lang_items::LangItem,
-) -> bool {
-    let (param_env, ty) = query.into_parts();
-    let trait_def_id = tcx.require_lang_item(item, None);
-    tcx.infer_ctxt().enter(|infcx| {
-        traits::type_known_to_meet_bound_modulo_regions(
-            &infcx,
-            param_env,
-            ty,
-            trait_def_id,
-            DUMMY_SP,
-        )
-    })
-}
-
-fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> NeedsDrop {
-    let (param_env, ty) = query.into_parts();
-
-    let needs_drop = |ty: Ty<'tcx>| -> bool { tcx.needs_drop_raw(param_env.and(ty)).0 };
-
-    assert!(!ty.needs_infer());
-
-    NeedsDrop(match ty.kind {
-        // Fast-path for primitive types
-        ty::Infer(ty::FreshIntTy(_))
-        | ty::Infer(ty::FreshFloatTy(_))
-        | ty::Bool
-        | ty::Int(_)
-        | ty::Uint(_)
-        | ty::Float(_)
-        | ty::Never
-        | ty::FnDef(..)
-        | ty::FnPtr(_)
-        | ty::Char
-        | ty::GeneratorWitness(..)
-        | ty::RawPtr(_)
-        | ty::Ref(..)
-        | ty::Str => false,
-
-        // Foreign types can never have destructors
-        ty::Foreign(..) => false,
-
-        // `ManuallyDrop` doesn't have a destructor regardless of field types.
-        ty::Adt(def, _) if Some(def.did) == tcx.lang_items().manually_drop() => false,
-
-        // Issue #22536: We first query `is_copy_modulo_regions`.  It sees a
-        // normalized version of the type, and therefore will definitely
-        // know whether the type implements Copy (and thus needs no
-        // cleanup/drop/zeroing) ...
-        _ if ty.is_copy_modulo_regions(tcx, param_env, DUMMY_SP) => false,
-
-        // ... (issue #22536 continued) but as an optimization, still use
-        // prior logic of asking for the structural "may drop".
-
-        // FIXME(#22815): Note that this is a conservative heuristic;
-        // it may report that the type "may drop" when actual type does
-        // not actually have a destructor associated with it. But since
-        // the type absolutely did not have the `Copy` bound attached
-        // (see above), it is sound to treat it as having a destructor.
-
-        // User destructors are the only way to have concrete drop types.
-        ty::Adt(def, _) if def.has_dtor(tcx) => true,
-
-        // Can refer to a type which may drop.
-        // FIXME(eddyb) check this against a ParamEnv.
-        ty::Dynamic(..)
-        | ty::Projection(..)
-        | ty::Param(_)
-        | ty::Bound(..)
-        | ty::Placeholder(..)
-        | ty::Opaque(..)
-        | ty::Infer(_)
-        | ty::Error => true,
-
-        ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
-
-        // Zero-length arrays never contain anything to drop.
-        ty::Array(_, len) if len.try_eval_usize(tcx, param_env) == Some(0) => false,
-
-        // Structural recursion.
-        ty::Array(ty, _) | ty::Slice(ty) => needs_drop(ty),
-
-        ty::Closure(def_id, ref substs) => {
-            substs.as_closure().upvar_tys(def_id, tcx).any(needs_drop)
-        }
-
-        // Pessimistically assume that all generators will require destructors
-        // as we don't know if a destructor is a noop or not until after the MIR
-        // state transformation pass
-        ty::Generator(..) => true,
-
-        ty::Tuple(..) => ty.tuple_fields().any(needs_drop),
-
-        // unions don't have destructors because of the child types,
-        // only if they manually implement `Drop` (handled above).
-        ty::Adt(def, _) if def.is_union() => false,
-
-        ty::Adt(def, substs) => def
-            .variants
-            .iter()
-            .any(|variant| variant.fields.iter().any(|field| needs_drop(field.ty(tcx, substs)))),
-    })
-}
-
-pub fn provide(providers: &mut ty::query::Providers<'_>) {
-    *providers = ty::query::Providers {
-        is_copy_raw,
-        is_sized_raw,
-        is_freeze_raw,
-        needs_drop_raw,
-        ..*providers
-    };
-}
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 417b52c38a7..556e69b04f8 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -633,7 +633,6 @@ impl<'tcx> TraitObligation<'tcx> {
 }
 
 pub fn provide(providers: &mut ty::query::Providers<'_>) {
-    misc::provide(providers);
     *providers = ty::query::Providers {
         is_object_safe: object_safety::is_object_safe_provider,
         specialization_graph_of: specialize::specialization_graph_provider,
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index b5e17661c5d..335953fc41e 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -285,7 +285,7 @@ impl<'tcx> Instance<'tcx> {
                 _ => {
                     if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
                         let ty = substs.type_at(0);
-                        if ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) {
+                        if ty.needs_drop(tcx, param_env.with_reveal_all()) {
                             debug!(" => nontrivial drop glue");
                             ty::InstanceDef::DropGlue(def_id, Some(ty))
                         } else {
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 60296b8116d..393d49a4e4b 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1796,19 +1796,22 @@ bitflags! {
         const IS_STRUCT           = 1 << 2;
         /// Indicates whether the ADT is a struct and has a constructor.
         const HAS_CTOR            = 1 << 3;
-        /// Indicates whether the type is a `PhantomData`.
+        /// Indicates whether the type is `PhantomData`.
         const IS_PHANTOM_DATA     = 1 << 4;
         /// Indicates whether the type has a `#[fundamental]` attribute.
         const IS_FUNDAMENTAL      = 1 << 5;
-        /// Indicates whether the type is a `Box`.
+        /// Indicates whether the type is `Box`.
         const IS_BOX              = 1 << 6;
+        /// Indicates whether the type is `ManuallyDrop`.
+        const IS_MANUALLY_DROP    = 1 << 7;
+        // FIXME(matthewjasper) replace these with diagnostic items
         /// Indicates whether the type is an `Arc`.
-        const IS_ARC              = 1 << 7;
+        const IS_ARC              = 1 << 8;
         /// Indicates whether the type is an `Rc`.
-        const IS_RC               = 1 << 8;
+        const IS_RC               = 1 << 9;
         /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`.
         /// (i.e., this flag is never set unless this ADT is an enum).
-        const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 9;
+        const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 10;
     }
 }
 
@@ -2190,6 +2193,9 @@ impl<'tcx> AdtDef {
         if Some(did) == tcx.lang_items().owned_box() {
             flags |= AdtFlags::IS_BOX;
         }
+        if Some(did) == tcx.lang_items().manually_drop() {
+            flags |= AdtFlags::IS_MANUALLY_DROP;
+        }
         if Some(did) == tcx.lang_items().arc() {
             flags |= AdtFlags::IS_ARC;
         }
@@ -2290,6 +2296,12 @@ impl<'tcx> AdtDef {
         self.flags.contains(AdtFlags::IS_BOX)
     }
 
+    /// Returns `true` if this is `ManuallyDrop<T>`.
+    #[inline]
+    pub fn is_manually_drop(&self) -> bool {
+        self.flags.contains(AdtFlags::IS_MANUALLY_DROP)
+    }
+
     /// Returns `true` if this type has a destructor.
     pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool {
         self.destructor(tcx).is_some()
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index e7b95af103c..2c51a093427 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -33,7 +33,7 @@ use crate::traits::Clauses;
 use crate::traits::{self, Vtable};
 use crate::ty::steal::Steal;
 use crate::ty::subst::SubstsRef;
-use crate::ty::util::NeedsDrop;
+use crate::ty::util::AlwaysRequiresDrop;
 use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
 use crate::util::common::ErrorReported;
 use rustc_data_structures::fingerprint::Fingerprint;
diff --git a/src/librustc/ty/query/values.rs b/src/librustc/ty/query/values.rs
index 900a91fe5cf..b01d15c29b2 100644
--- a/src/librustc/ty/query/values.rs
+++ b/src/librustc/ty/query/values.rs
@@ -1,4 +1,3 @@
-use crate::ty::util::NeedsDrop;
 use crate::ty::{self, AdtSizedConstraint, Ty, TyCtxt};
 
 use rustc_span::symbol::Symbol;
@@ -26,12 +25,6 @@ impl<'tcx> Value<'tcx> for ty::SymbolName {
     }
 }
 
-impl<'tcx> Value<'tcx> for NeedsDrop {
-    fn from_cycle_error(_: TyCtxt<'tcx>) -> Self {
-        NeedsDrop(false)
-    }
-}
-
 impl<'tcx> Value<'tcx> for AdtSizedConstraint<'tcx> {
     fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self {
         AdtSizedConstraint(tcx.intern_type_list(&[tcx.types.err]))
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 5d5fa4090c8..db7b8d8cfd9 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -18,6 +18,8 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
 use rustc_span::Span;
+use rustc_target::abi::TargetDataLayout;
+use smallvec::SmallVec;
 use std::{cmp, fmt};
 use syntax::ast;
 
@@ -724,7 +726,23 @@ impl<'tcx> ty::TyS<'tcx> {
     /// Note that this method is used to check eligible types in unions.
     #[inline]
     pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
-        tcx.needs_drop_raw(param_env.and(self)).0
+        // Avoid querying in simple cases.
+        match needs_drop_components(self, &tcx.data_layout) {
+            Err(AlwaysRequiresDrop) => true,
+            Ok(components) => {
+                let query_ty = match *components {
+                    [] => return false,
+                    // If we've got a single component, call the query with that
+                    // to increase the chance that we hit the query cache.
+                    [component_ty] => component_ty,
+                    _ => self,
+                };
+                // This doesn't depend on regions, so try to minimize distinct
+                // query keys used.
+                let erased = tcx.normalize_erasing_regions(param_env, query_ty);
+                tcx.needs_drop_raw(param_env.and(erased))
+            }
+        }
     }
 
     pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
@@ -923,9 +941,6 @@ impl<'tcx> ty::TyS<'tcx> {
     }
 }
 
-#[derive(Clone, HashStable)]
-pub struct NeedsDrop(pub bool);
-
 pub enum ExplicitSelf<'tcx> {
     ByValue,
     ByReference(ty::Region<'tcx>, hir::Mutability),
@@ -974,3 +989,72 @@ impl<'tcx> ExplicitSelf<'tcx> {
         }
     }
 }
+
+/// Returns a list of types such that the given type needs drop if and only if
+/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if
+/// this type always needs drop.
+pub fn needs_drop_components(
+    ty: Ty<'tcx>,
+    target_layout: &TargetDataLayout,
+) -> Result<SmallVec<[Ty<'tcx>; 2]>, AlwaysRequiresDrop> {
+    match ty.kind {
+        ty::Infer(ty::FreshIntTy(_))
+        | ty::Infer(ty::FreshFloatTy(_))
+        | ty::Bool
+        | ty::Int(_)
+        | ty::Uint(_)
+        | ty::Float(_)
+        | ty::Never
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::Char
+        | ty::GeneratorWitness(..)
+        | ty::RawPtr(_)
+        | ty::Ref(..)
+        | ty::Str => Ok(SmallVec::new()),
+
+        // Foreign types can never have destructors.
+        ty::Foreign(..) => Ok(SmallVec::new()),
+
+        // Pessimistically assume that all generators will require destructors
+        // as we don't know if a destructor is a noop or not until after the MIR
+        // state transformation pass.
+        ty::Generator(..) | ty::Dynamic(..) | ty::Error => Err(AlwaysRequiresDrop),
+
+        ty::Slice(ty) => needs_drop_components(ty, target_layout),
+        ty::Array(elem_ty, size) => {
+            match needs_drop_components(elem_ty, target_layout) {
+                Ok(v) if v.is_empty() => Ok(v),
+                res => match size.val.try_to_bits(target_layout.pointer_size) {
+                    // Arrays of size zero don't need drop, even if their element
+                    // type does.
+                    Some(0) => Ok(SmallVec::new()),
+                    Some(_) => res,
+                    // We don't know which of the cases above we are in, so
+                    // return the whole type and let the caller decide what to
+                    // do.
+                    None => Ok(smallvec![ty]),
+                },
+            }
+        }
+        // If any field needs drop, then the whole tuple does.
+        ty::Tuple(..) => ty.tuple_fields().try_fold(SmallVec::new(), move |mut acc, elem| {
+            acc.extend(needs_drop_components(elem, target_layout)?);
+            Ok(acc)
+        }),
+
+        // These require checking for `Copy` bounds or `Adt` destructors.
+        ty::Adt(..)
+        | ty::Projection(..)
+        | ty::UnnormalizedProjection(..)
+        | ty::Param(_)
+        | ty::Bound(..)
+        | ty::Placeholder(..)
+        | ty::Opaque(..)
+        | ty::Infer(_)
+        | ty::Closure(..) => Ok(smallvec![ty]),
+    }
+}
+
+#[derive(Copy, Clone, Debug, HashStable, RustcEncodable, RustcDecodable)]
+pub struct AlwaysRequiresDrop;
diff --git a/src/librustc_ty/common_traits.rs b/src/librustc_ty/common_traits.rs
new file mode 100644
index 00000000000..9fe8a19311f
--- /dev/null
+++ b/src/librustc_ty/common_traits.rs
@@ -0,0 +1,40 @@
+//! Queries for checking whether a type implements one of a few common traits.
+
+use rustc::middle::lang_items;
+use rustc::traits;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+
+fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+    is_item_raw(tcx, query, lang_items::CopyTraitLangItem)
+}
+
+fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+    is_item_raw(tcx, query, lang_items::SizedTraitLangItem)
+}
+
+fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+    is_item_raw(tcx, query, lang_items::FreezeTraitLangItem)
+}
+
+fn is_item_raw<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+    item: lang_items::LangItem,
+) -> bool {
+    let (param_env, ty) = query.into_parts();
+    let trait_def_id = tcx.require_lang_item(item, None);
+    tcx.infer_ctxt().enter(|infcx| {
+        traits::type_known_to_meet_bound_modulo_regions(
+            &infcx,
+            param_env,
+            ty,
+            trait_def_id,
+            DUMMY_SP,
+        )
+    })
+}
+
+pub(crate) fn provide(providers: &mut ty::query::Providers<'_>) {
+    *providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers };
+}
diff --git a/src/librustc_ty/lib.rs b/src/librustc_ty/lib.rs
index 37faa2281b3..7eef19b94e4 100644
--- a/src/librustc_ty/lib.rs
+++ b/src/librustc_ty/lib.rs
@@ -6,7 +6,6 @@
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
 #![feature(bool_to_option)]
-#![feature(in_band_lifetimes)]
 #![feature(nll)]
 #![recursion_limit = "256"]
 
@@ -17,8 +16,12 @@ extern crate log;
 
 use rustc::ty::query::Providers;
 
+mod common_traits;
+mod needs_drop;
 mod ty;
 
 pub fn provide(providers: &mut Providers<'_>) {
+    common_traits::provide(providers);
+    needs_drop::provide(providers);
     ty::provide(providers);
 }
diff --git a/src/librustc_ty/needs_drop.rs b/src/librustc_ty/needs_drop.rs
new file mode 100644
index 00000000000..0f71246c737
--- /dev/null
+++ b/src/librustc_ty/needs_drop.rs
@@ -0,0 +1,165 @@
+//! Check whether a type has (potentially) non-trivial drop glue.
+
+use rustc::ty::subst::Subst;
+use rustc::ty::util::{needs_drop_components, AlwaysRequiresDrop};
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::DefId;
+use rustc_span::DUMMY_SP;
+
+type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
+
+fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+    let adt_fields =
+        move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter().copied());
+    // If we don't know a type doesn't need drop, for example if it's a type
+    // parameter without a `Copy` bound, then we conservatively return that it
+    // needs drop.
+    let res = NeedsDropTypes::new(tcx, query.param_env, query.value, adt_fields).next().is_some();
+    debug!("needs_drop_raw({:?}) = {:?}", query, res);
+    res
+}
+
+struct NeedsDropTypes<'tcx, F> {
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    query_ty: Ty<'tcx>,
+    seen_tys: FxHashSet<Ty<'tcx>>,
+    /// A stack of types left to process, and the recursion depth when we
+    /// pushed that type. Each round, we pop something from the stack and check
+    /// if it needs drop. If the result depends on whether some other types
+    /// need drop we push them onto the stack.
+    unchecked_tys: Vec<(Ty<'tcx>, usize)>,
+    recursion_limit: usize,
+    adt_components: F,
+}
+
+impl<'tcx, F> NeedsDropTypes<'tcx, F> {
+    fn new(
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+        adt_components: F,
+    ) -> Self {
+        let mut seen_tys = FxHashSet::default();
+        seen_tys.insert(ty);
+        let recursion_limit = *tcx.sess.recursion_limit.get();
+        Self {
+            tcx,
+            param_env,
+            seen_tys,
+            query_ty: ty,
+            unchecked_tys: vec![(ty, 0)],
+            recursion_limit,
+            adt_components,
+        }
+    }
+}
+
+impl<'tcx, F, I> Iterator for NeedsDropTypes<'tcx, F>
+where
+    F: Fn(&ty::AdtDef) -> NeedsDropResult<I>,
+    I: Iterator<Item = Ty<'tcx>>,
+{
+    type Item = NeedsDropResult<Ty<'tcx>>;
+
+    fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> {
+        let tcx = self.tcx;
+
+        while let Some((ty, level)) = self.unchecked_tys.pop() {
+            if level > self.recursion_limit {
+                // Not having a `Span` isn't great. But there's hopefully some other
+                // recursion limit error as well.
+                tcx.sess.span_err(
+                    DUMMY_SP,
+                    &format!("overflow while checking whether `{}` requires drop", self.query_ty),
+                );
+                return Some(Err(AlwaysRequiresDrop));
+            }
+
+            let components = match needs_drop_components(ty, &tcx.data_layout) {
+                Err(e) => return Some(Err(e)),
+                Ok(components) => components,
+            };
+            debug!("needs_drop_components({:?}) = {:?}", ty, components);
+
+            let queue_type = move |this: &mut Self, component: Ty<'tcx>| {
+                if this.seen_tys.insert(component) {
+                    this.unchecked_tys.push((component, level + 1));
+                }
+            };
+
+            for component in components {
+                match component.kind {
+                    _ if component.is_copy_modulo_regions(tcx, self.param_env, DUMMY_SP) => (),
+
+                    ty::Closure(def_id, substs) => {
+                        for upvar_ty in substs.as_closure().upvar_tys(def_id, tcx) {
+                            queue_type(self, upvar_ty);
+                        }
+                    }
+
+                    // Check for a `Drop` impl and whether this is a union or
+                    // `ManuallyDrop`. If it's a struct or enum without a `Drop`
+                    // impl then check whether the field types need `Drop`.
+                    ty::Adt(adt_def, substs) => {
+                        let tys = match (self.adt_components)(adt_def) {
+                            Err(e) => return Some(Err(e)),
+                            Ok(tys) => tys,
+                        };
+                        for required_ty in tys {
+                            let subst_ty = tcx.normalize_erasing_regions(
+                                self.param_env,
+                                required_ty.subst(tcx, substs),
+                            );
+                            queue_type(self, subst_ty);
+                        }
+                    }
+                    ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => {
+                        if ty == component {
+                            // Return the type to the caller: they may be able
+                            // to normalize further than we can.
+                            return Some(Ok(component));
+                        } else {
+                            // Store the type for later. We can't return here
+                            // because we would then lose any other components
+                            // of the type.
+                            queue_type(self, component);
+                        }
+                    }
+                    _ => return Some(Err(AlwaysRequiresDrop)),
+                }
+            }
+        }
+
+        return None;
+    }
+}
+
+fn adt_drop_tys(tcx: TyCtxt<'_>, def_id: DefId) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
+    let adt_components = move |adt_def: &ty::AdtDef| {
+        if adt_def.is_manually_drop() {
+            debug!("adt_drop_tys: `{:?}` is manually drop", adt_def);
+            return Ok(Vec::new().into_iter());
+        } else if adt_def.destructor(tcx).is_some() {
+            debug!("adt_drop_tys: `{:?}` implements `Drop`", adt_def);
+            return Err(AlwaysRequiresDrop);
+        } else if adt_def.is_union() {
+            debug!("adt_drop_tys: `{:?}` is a union", adt_def);
+            return Ok(Vec::new().into_iter());
+        }
+        Ok(adt_def.all_fields().map(|field| tcx.type_of(field.did)).collect::<Vec<_>>().into_iter())
+    };
+
+    let adt_ty = tcx.type_of(def_id);
+    let param_env = tcx.param_env(def_id);
+    let res: Result<Vec<_>, _> =
+        NeedsDropTypes::new(tcx, param_env, adt_ty, adt_components).collect();
+
+    debug!("adt_drop_tys(`{}`) = `{:?}`", tcx.def_path_str(def_id), res);
+    res.map(|components| tcx.intern_type_list(&components))
+}
+
+pub(crate) fn provide(providers: &mut ty::query::Providers<'_>) {
+    *providers = ty::query::Providers { needs_drop_raw, adt_drop_tys, ..*providers };
+}
diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs
index b032acf82f5..ddb7c8bc791 100644
--- a/src/librustc_ty/ty.rs
+++ b/src/librustc_ty/ty.rs
@@ -9,7 +9,11 @@ use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
 
-fn sized_constraint_for_ty(tcx: TyCtxt<'tcx>, adtdef: &ty::AdtDef, ty: Ty<'tcx>) -> Vec<Ty<'tcx>> {
+fn sized_constraint_for_ty<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    adtdef: &ty::AdtDef,
+    ty: Ty<'tcx>,
+) -> Vec<Ty<'tcx>> {
     use ty::TyKind::*;
 
     let result = match ty.kind {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index be2052dce3c..0bdf7ba1a56 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1558,11 +1558,11 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool {
     if let ty::Adt(def, substs) = item_type.kind {
         assert!(def.is_union());
         let fields = &def.non_enum_variant().fields;
+        let param_env = tcx.param_env(item_def_id);
         for field in fields {
             let field_ty = field.ty(tcx, substs);
             // We are currently checking the type this field came from, so it must be local.
             let field_span = tcx.hir().span_if_local(field.did).unwrap();
-            let param_env = tcx.param_env(field.did);
             if field_ty.needs_drop(tcx, param_env) {
                 struct_span_err!(
                     tcx.sess,
diff --git a/src/test/ui/recursion/recursion.rs b/src/test/ui/recursion/recursion.rs
index 9d939e13182..bf1eaef367d 100644
--- a/src/test/ui/recursion/recursion.rs
+++ b/src/test/ui/recursion/recursion.rs
@@ -1,4 +1,5 @@
 // build-fail
+// compile-flags:-C overflow-checks=off
 
 enum Nil {NilValue}
 struct Cons<T> {head:isize, tail:T}
diff --git a/src/test/ui/recursion/recursion.stderr b/src/test/ui/recursion/recursion.stderr
index 17293720a43..1a65b0e84f6 100644
--- a/src/test/ui/recursion/recursion.stderr
+++ b/src/test/ui/recursion/recursion.stderr
@@ -1,5 +1,5 @@
 error: reached the recursion limit while instantiating `test::<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Cons<Nil>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
-  --> $DIR/recursion.rs:14:1
+  --> $DIR/recursion.rs:15:1
    |
 LL | / fn test<T:Dot> (n:isize, i:isize, first:T, second:T) ->isize {
 LL | |   match n {    0 => {first.dot(second)}
diff --git a/src/test/ui/type-alias-impl-trait/issue-65918.rs b/src/test/ui/type-alias-impl-trait/issue-65918.rs
index 97efb85ef64..4ba778d53ac 100644
--- a/src/test/ui/type-alias-impl-trait/issue-65918.rs
+++ b/src/test/ui/type-alias-impl-trait/issue-65918.rs
@@ -1,3 +1,5 @@
+// ignore-test: This now ICEs again.
+
 // build-pass
 
 #![feature(type_alias_impl_trait)]