about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs4
-rw-r--r--compiler/rustc_middle/src/query/erase.rs2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs17
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs10
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs9
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs26
-rw-r--r--compiler/rustc_middle/src/ty/util.rs196
-rw-r--r--compiler/rustc_ty_utils/src/common_traits.rs21
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs16
10 files changed, 196 insertions, 110 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 0083da2a1e4..149e7737e30 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -112,6 +112,7 @@ pub fn provide(providers: &mut Providers) {
     wfcheck::provide(providers);
     *providers = Providers {
         adt_destructor,
+        adt_async_destructor,
         region_scope_tree,
         collect_return_position_impl_trait_in_trait_tys,
         compare_impl_const: compare_impl_item::compare_impl_const_raw,
@@ -124,6 +125,10 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor>
     tcx.calculate_dtor(def_id.to_def_id(), dropck::check_drop_impl)
 }
 
+fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
+    tcx.calculate_async_dtor(def_id.to_def_id(), dropck::check_drop_impl)
+}
+
 /// Given a `DefId` for an opaque type in return position, find its parent item's return
 /// expressions.
 fn get_owner_return_paths(
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 4cad317ce80..f6b9c7ed992 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -287,6 +287,10 @@ provide! { tcx, def_id, other, cdata,
         let _ = cdata;
         tcx.calculate_dtor(def_id, |_,_| Ok(()))
     }
+    adt_async_destructor => {
+        let _ = cdata;
+        tcx.calculate_async_dtor(def_id, |_,_| Ok(()))
+    }
     associated_item_def_ids => {
         tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index))
     }
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 1e36f034cc2..f98dbf8a0bd 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -238,6 +238,7 @@ trivial! {
     Option<rustc_hir::CoroutineKind>,
     Option<rustc_hir::HirId>,
     Option<rustc_middle::middle::stability::DeprecationEntry>,
+    Option<rustc_middle::ty::AsyncDestructor>,
     Option<rustc_middle::ty::Destructor>,
     Option<rustc_middle::ty::ImplTraitInTraitData>,
     Option<rustc_middle::ty::ScalarInt>,
@@ -295,6 +296,7 @@ trivial! {
     rustc_middle::ty::AssocItem,
     rustc_middle::ty::AssocItemContainer,
     rustc_middle::ty::Asyncness,
+    rustc_middle::ty::AsyncDestructor,
     rustc_middle::ty::BoundVariableKind,
     rustc_middle::ty::DeducedParamAttrs,
     rustc_middle::ty::Destructor,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 6ad4b7c40fb..d72ad09f954 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -703,6 +703,11 @@ rustc_queries! {
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
     }
+    query adt_async_destructor(key: DefId) -> Option<ty::AsyncDestructor> {
+        desc { |tcx| "computing `AsyncDrop` impl for `{}`", tcx.def_path_str(key) }
+        cache_on_disk_if { key.is_local() }
+        separate_provide_extern
+    }
 
     query adt_sized_constraint(key: DefId) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
         desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) }
@@ -1343,18 +1348,14 @@ rustc_queries! {
     query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` is `Unpin`", env.value }
     }
-    /// Query backing `Ty::has_surface_async_drop`.
-    query has_surface_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-        desc { "computing whether `{}` has `AsyncDrop` implementation", env.value }
-    }
-    /// Query backing `Ty::has_surface_drop`.
-    query has_surface_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-        desc { "computing whether `{}` has `Drop` implementation", env.value }
-    }
     /// Query backing `Ty::needs_drop`.
     query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` needs drop", env.value }
     }
+    /// Query backing `Ty::needs_async_drop`.
+    query needs_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+        desc { "computing whether `{}` needs async drop", env.value }
+    }
     /// Query backing `Ty::has_significant_drop_raw`.
     query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` has a significant drop", env.value }
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 8e946bc8b31..988ce35484d 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -24,7 +24,9 @@ use std::hash::{Hash, Hasher};
 use std::ops::Range;
 use std::str;
 
-use super::{Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr};
+use super::{
+    AsyncDestructor, Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr,
+};
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
 pub struct AdtFlags(u16);
@@ -577,6 +579,12 @@ impl<'tcx> AdtDef<'tcx> {
         tcx.adt_destructor(self.did())
     }
 
+    // FIXME(zetanumbers): consider supporting this method in same places where
+    // `destructor` is referenced
+    pub fn async_destructor(self, tcx: TyCtxt<'tcx>) -> Option<AsyncDestructor> {
+        tcx.adt_async_destructor(self.did())
+    }
+
     /// Returns a type such that `Self: Sized` if and only if that type is `Sized`,
     /// or `None` if the type is always sized.
     pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index f24074cb472..90c154233da 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1172,6 +1172,15 @@ pub struct Destructor {
     pub constness: hir::Constness,
 }
 
+// FIXME: consider combining this definition with regular `Destructor`
+#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)]
+pub struct AsyncDestructor {
+    /// The `DefId` of the async destructor future constructor
+    pub ctor: DefId,
+    /// The `DefId` of the async destructor future type
+    pub future: DefId,
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 pub struct VariantFlags(u8);
 bitflags::bitflags! {
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 5cf96d29837..00f603a8818 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1991,7 +1991,7 @@ impl<'tcx> Ty<'tcx> {
             ty::Adt(adt_def, _) => {
                 assert!(adt_def.is_union());
 
-                let surface_drop = self.surface_async_dropper_ty(tcx, param_env).unwrap();
+                let surface_drop = self.surface_async_dropper_ty(tcx).unwrap();
 
                 Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
                     .instantiate(tcx, &[surface_drop.into()])
@@ -2041,7 +2041,7 @@ impl<'tcx> Ty<'tcx> {
             })
             .unwrap();
 
-        let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx, param_env) {
+        let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx) {
             Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain)
                 .instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()])
         } else {
@@ -2052,21 +2052,13 @@ impl<'tcx> Ty<'tcx> {
             .instantiate(tcx, &[dtor.into()])
     }
 
-    fn surface_async_dropper_ty(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-    ) -> Option<Ty<'tcx>> {
-        if self.has_surface_async_drop(tcx, param_env) {
-            Some(LangItem::SurfaceAsyncDropInPlace)
-        } else if self.has_surface_drop(tcx, param_env) {
-            Some(LangItem::AsyncDropSurfaceDropInPlace)
-        } else {
-            None
-        }
-        .map(|dropper| {
-            Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()])
-        })
+    fn surface_async_dropper_ty(self, tcx: TyCtxt<'tcx>) -> Option<Ty<'tcx>> {
+        let adt_def = self.ty_adt_def()?;
+        let dropper = adt_def
+            .async_destructor(tcx)
+            .map(|_| LangItem::SurfaceAsyncDropInPlace)
+            .or_else(|| adt_def.destructor(tcx).map(|_| LangItem::AsyncDropSurfaceDropInPlace))?;
+        Some(Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()]))
     }
 
     fn async_destructor_combinator(
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index cacaa859d52..905ada3832d 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -4,8 +4,8 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use crate::query::{IntoQueryParam, Providers};
 use crate::ty::layout::{FloatExt, IntegerExt};
 use crate::ty::{
-    self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
-    TypeVisitableExt, Upcast,
+    self, Asyncness, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder,
+    TypeSuperFoldable, TypeVisitableExt, Upcast,
 };
 use crate::ty::{GenericArgKind, GenericArgsRef};
 use rustc_apfloat::Float as _;
@@ -382,6 +382,45 @@ impl<'tcx> TyCtxt<'tcx> {
         Some(ty::Destructor { did, constness })
     }
 
+    /// Calculate the async destructor of a given type.
+    pub fn calculate_async_dtor(
+        self,
+        adt_did: DefId,
+        validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>,
+    ) -> Option<ty::AsyncDestructor> {
+        let async_drop_trait = self.lang_items().async_drop_trait()?;
+        self.ensure().coherent_trait(async_drop_trait).ok()?;
+
+        let ty = self.type_of(adt_did).instantiate_identity();
+        let mut dtor_candidate = None;
+        self.for_each_relevant_impl(async_drop_trait, ty, |impl_did| {
+            if validate(self, impl_did).is_err() {
+                // Already `ErrorGuaranteed`, no need to delay a span bug here.
+                return;
+            }
+
+            let [future, ctor] = self.associated_item_def_ids(impl_did) else {
+                self.dcx().span_delayed_bug(
+                    self.def_span(impl_did),
+                    "AsyncDrop impl without async_drop function or Dropper type",
+                );
+                return;
+            };
+
+            if let Some((_, _, old_impl_did)) = dtor_candidate {
+                self.dcx()
+                    .struct_span_err(self.def_span(impl_did), "multiple async drop impls found")
+                    .with_span_note(self.def_span(old_impl_did), "other impl here")
+                    .delay_as_bug();
+            }
+
+            dtor_candidate = Some((*future, *ctor, impl_did));
+        });
+
+        let (future, ctor, _) = dtor_candidate?;
+        Some(ty::AsyncDestructor { future, ctor })
+    }
+
     /// Returns the set of types that are required to be alive in
     /// order to run the destructor of `def` (see RFCs 769 and
     /// 1238).
@@ -1303,72 +1342,19 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    /// Checks whether values of this type `T` implements the `AsyncDrop`
-    /// trait.
-    pub fn has_surface_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
-        self.could_have_surface_async_drop() && tcx.has_surface_async_drop_raw(param_env.and(self))
-    }
-
-    /// Fast path helper for testing if a type has `AsyncDrop`
-    /// implementation.
-    ///
-    /// Returning `false` means the type is known to not have `AsyncDrop`
-    /// implementation. Returning `true` means nothing -- could be
-    /// `AsyncDrop`, might not be.
-    fn could_have_surface_async_drop(self) -> bool {
-        !self.is_async_destructor_trivially_noop()
-            && !matches!(
-                self.kind(),
-                ty::Tuple(_)
-                    | ty::Slice(_)
-                    | ty::Array(_, _)
-                    | ty::Closure(..)
-                    | ty::CoroutineClosure(..)
-                    | ty::Coroutine(..)
-            )
-    }
-
-    /// Checks whether values of this type `T` implements the `Drop`
-    /// trait.
-    pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
-        self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self))
-    }
-
-    /// Fast path helper for testing if a type has `Drop` implementation.
-    ///
-    /// Returning `false` means the type is known to not have `Drop`
-    /// implementation. Returning `true` means nothing -- could be
-    /// `Drop`, might not be.
-    fn could_have_surface_drop(self) -> bool {
-        !self.is_async_destructor_trivially_noop()
-            && !matches!(
-                self.kind(),
-                ty::Tuple(_)
-                    | ty::Slice(_)
-                    | ty::Array(_, _)
-                    | ty::Closure(..)
-                    | ty::CoroutineClosure(..)
-                    | ty::Coroutine(..)
-            )
-    }
-
     /// Checks whether values of this type `T` implement has noop async destructor.
     //
     // FIXME: implement optimization to make ADTs, which do not need drop,
-    // to skip fields or to have noop async destructor.
+    // to skip fields or to have noop async destructor, use `needs_(async_)drop`
     pub fn is_async_destructor_noop(
         self,
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> bool {
+        // TODO: check on the most generic version of your type
         self.is_async_destructor_trivially_noop()
-            || if let ty::Adt(adt_def, _) = self.kind() {
-                (adt_def.is_union() || adt_def.is_payloadfree())
-                    && !self.has_surface_async_drop(tcx, param_env)
-                    && !self.has_surface_drop(tcx, param_env)
-            } else {
-                false
-            }
+            || self.needs_async_drop(tcx, param_env)
+            || self.needs_drop(tcx, param_env)
     }
 
     /// Fast path helper for testing if a type has noop async destructor.
@@ -1391,7 +1377,34 @@ impl<'tcx> Ty<'tcx> {
             | ty::FnPtr(_) => true,
             ty::Tuple(tys) => tys.is_empty(),
             ty::Adt(adt_def, _) => adt_def.is_manually_drop(),
-            _ => false,
+            ty::Bool => todo!(),
+            ty::Char => todo!(),
+            ty::Int(_) => todo!(),
+            ty::Uint(_) => todo!(),
+            ty::Float(_) => todo!(),
+            ty::Adt(_, _) => todo!(),
+            ty::Foreign(_) => todo!(),
+            ty::Str => todo!(),
+            ty::Array(_, _) => todo!(),
+            ty::Pat(_, _) => todo!(),
+            ty::Slice(_) => todo!(),
+            ty::RawPtr(_, _) => todo!(),
+            ty::Ref(_, _, _) => todo!(),
+            ty::FnDef(_, _) => todo!(),
+            ty::FnPtr(_) => todo!(),
+            ty::Dynamic(_, _, _) => todo!(),
+            ty::Closure(_, _) => todo!(),
+            ty::CoroutineClosure(_, _) => todo!(),
+            ty::Coroutine(_, _) => todo!(),
+            ty::CoroutineWitness(_, _) => todo!(),
+            ty::Never => todo!(),
+            ty::Tuple(_) => todo!(),
+            ty::Alias(_, _) => todo!(),
+            ty::Param(_) => todo!(),
+            ty::Bound(_, _) => todo!(),
+            ty::Placeholder(_) => todo!(),
+            ty::Infer(_) => todo!(),
+            ty::Error(_) => todo!(),
         }
     }
 
@@ -1430,6 +1443,42 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
+    /// If `ty.needs_async_drop(...)` returns `true`, then `ty` is definitely
+    /// non-copy and *might* have a async destructor attached; if it returns
+    /// `false`, then `ty` definitely has no async destructor (i.e., no async
+    /// drop glue).
+    ///
+    /// (Note that this implies that if `ty` has an async destructor attached,
+    /// then `needs_async_drop` will definitely return `true` for `ty`.)
+    ///
+    /// Note that this method is used to check eligible types in unions.
+    #[inline]
+    pub fn needs_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+        // Avoid querying in simple cases.
+        match needs_drop_components(tcx, self) {
+            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.
+                // If normalization fails, we just use `query_ty`.
+                debug_assert!(!param_env.has_infer());
+                let query_ty = tcx
+                    .try_normalize_erasing_regions(param_env, query_ty)
+                    .unwrap_or_else(|_| tcx.erase_regions(query_ty));
+
+                tcx.needs_async_drop_raw(param_env.and(query_ty))
+            }
+        }
+    }
+
     /// Checks if `ty` has a significant drop.
     ///
     /// Note that this method can return false even if `ty` has a destructor
@@ -1598,6 +1647,7 @@ impl<'tcx> ExplicitSelf<'tcx> {
     }
 }
 
+// FIXME(zetanumbers): make specifying asyncness explicit
 /// 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.
@@ -1605,6 +1655,17 @@ pub fn needs_drop_components<'tcx>(
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
 ) -> Result<SmallVec<[Ty<'tcx>; 2]>, AlwaysRequiresDrop> {
+    needs_drop_components_with_async(tcx, ty, Asyncness::No)
+}
+
+/// 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_with_async<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+    asyncness: Asyncness,
+) -> Result<SmallVec<[Ty<'tcx>; 2]>, AlwaysRequiresDrop> {
     match *ty.kind() {
         ty::Infer(ty::FreshIntTy(_))
         | ty::Infer(ty::FreshFloatTy(_))
@@ -1623,11 +1684,18 @@ pub fn needs_drop_components<'tcx>(
         // Foreign types can never have destructors.
         ty::Foreign(..) => Ok(SmallVec::new()),
 
-        ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop),
+        // FIXME(zetanumbers): Temporary workaround for async drop of dynamic types
+        ty::Dynamic(..) | ty::Error(_) => {
+            if asyncness.is_async() {
+                Ok(SmallVec::new())
+            } else {
+                Err(AlwaysRequiresDrop)
+            }
+        }
 
-        ty::Pat(ty, _) | ty::Slice(ty) => needs_drop_components(tcx, ty),
+        ty::Pat(ty, _) | ty::Slice(ty) => needs_drop_components_with_async(tcx, ty, asyncness),
         ty::Array(elem_ty, size) => {
-            match needs_drop_components(tcx, elem_ty) {
+            match needs_drop_components_with_async(tcx, elem_ty, asyncness) {
                 Ok(v) if v.is_empty() => Ok(v),
                 res => match size.try_to_target_usize(tcx) {
                     // Arrays of size zero don't need drop, even if their element
@@ -1643,7 +1711,7 @@ pub fn needs_drop_components<'tcx>(
         }
         // If any field needs drop, then the whole tuple does.
         ty::Tuple(fields) => fields.iter().try_fold(SmallVec::new(), move |mut acc, elem| {
-            acc.extend(needs_drop_components(tcx, elem)?);
+            acc.extend(needs_drop_components_with_async(tcx, elem, asyncness)?);
             Ok(acc)
         }),
 
diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs
index cb95239e991..51b908881eb 100644
--- a/compiler/rustc_ty_utils/src/common_traits.rs
+++ b/compiler/rustc_ty_utils/src/common_traits.rs
@@ -22,17 +22,6 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
     is_item_raw(tcx, query, LangItem::Unpin)
 }
 
-fn has_surface_async_drop_raw<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
-) -> bool {
-    is_item_raw(tcx, query, LangItem::AsyncDrop)
-}
-
-fn has_surface_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-    is_item_raw(tcx, query, LangItem::Drop)
-}
-
 fn is_item_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -45,13 +34,5 @@ fn is_item_raw<'tcx>(
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
-    *providers = Providers {
-        is_copy_raw,
-        is_sized_raw,
-        is_freeze_raw,
-        is_unpin_raw,
-        has_surface_async_drop_raw,
-        has_surface_drop_raw,
-        ..*providers
-    };
+    *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers };
 }
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index 72ee1a48249..7fc3543ff66 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -30,6 +30,21 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>
     res
 }
 
+fn needs_async_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+    // If we don't know a type doesn't need async drop, for example if it's a
+    // type parameter without a `Copy` bound, then we conservatively return that
+    // it needs async drop.
+    let adt_has_async_dtor =
+        |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant);
+    let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false)
+        .filter(filter_array_elements(tcx, query.param_env))
+        .next()
+        .is_some();
+
+    debug!("needs_drop_raw({:?}) = {:?}", query, res);
+    res
+}
+
 /// HACK: in order to not mistakenly assume that `[PhantomData<T>; N]` requires drop glue
 /// we check the element type for drop glue. The correct fix would be looking at the
 /// entirety of the code around `needs_drop_components` and this file and come up with
@@ -389,6 +404,7 @@ fn adt_significant_drop_tys(
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         needs_drop_raw,
+        needs_async_drop_raw,
         has_significant_drop_raw,
         adt_drop_tys,
         adt_significant_drop_tys,