diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 240bd20053b15..eee440a6dfed8 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -659,7 +659,14 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
             TerminatorKind::SwitchInt { discr, targets: _ } => {
                 self.consume_operand(loc, (discr, span), state);
             }
-            TerminatorKind::Drop { place, target: _, unwind: _, replace } => {
+            TerminatorKind::Drop {
+                place,
+                target: _,
+                unwind: _,
+                replace,
+                drop: _,
+                async_fut: _,
+            } => {
                 debug!(
                     "visit_terminator_drop \
                      loc: {:?} term: {:?} place: {:?} span: {:?}",
diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
index 0d1d8642bcacc..99dd0b2dd4664 100644
--- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
+++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
@@ -101,7 +101,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
             TerminatorKind::SwitchInt { discr, targets: _ } => {
                 self.consume_operand(location, discr);
             }
-            TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => {
+            TerminatorKind::Drop {
+                place: drop_place,
+                target: _,
+                unwind: _,
+                replace,
+                drop: _,
+                async_fut: _,
+            } => {
                 let write_kind =
                     if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop };
                 self.access_place(
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index f6144a25938ce..b801cfea24a2b 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -2086,8 +2086,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 }
             }
             TerminatorKind::Unreachable => {}
-            TerminatorKind::Drop { target, unwind, .. }
-            | TerminatorKind::Assert { target, unwind, .. } => {
+            TerminatorKind::Drop { target, unwind, drop, .. } => {
+                self.assert_iscleanup(block_data, target, is_cleanup);
+                self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
+                if let Some(drop) = drop {
+                    self.assert_iscleanup(block_data, drop, is_cleanup);
+                }
+            }
+            TerminatorKind::Assert { target, unwind, .. } => {
                 self.assert_iscleanup(block_data, target, is_cleanup);
                 self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
             }
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index ddd119e0c6108..5f631405a9a4a 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -441,7 +441,9 @@ pub(crate) fn codegen_terminator_call<'tcx>(
                     Err(instance) => Some(instance),
                 }
             }
-            InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => {
+            // We don't need AsyncDropGlueCtorShim here because it is not `noop func`,
+            // it is `func returning noop future`
+            InstanceKind::DropGlue(_, None) => {
                 // empty drop glue - a nop.
                 let dest = target.expect("Non terminating drop_in_place_real???");
                 let ret_block = fx.get_block(dest);
@@ -707,9 +709,8 @@ pub(crate) fn codegen_drop<'tcx>(
     let ty = drop_place.layout().ty;
     let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty);
 
-    if let ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) =
-        drop_instance.def
-    {
+    // AsyncDropGlueCtorShim can't be here
+    if let ty::InstanceKind::DropGlue(_, None) = drop_instance.def {
         // we don't actually need to drop anything
     } else {
         match ty.kind() {
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index adaa754491e56..e9c7186b03c9f 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -565,7 +565,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
             | TerminatorKind::CoroutineDrop => {
                 bug!("shouldn't exist at codegen {:?}", bb_data.terminator());
             }
-            TerminatorKind::Drop { place, target, unwind: _, replace: _ } => {
+            TerminatorKind::Drop { place, target, unwind: _, replace: _, drop, async_fut } => {
+                assert!(
+                    async_fut.is_none() && drop.is_none(),
+                    "Async Drop must be expanded or reset to sync before codegen"
+                );
                 let drop_place = codegen_place(fx, *place);
                 crate::abi::codegen_drop(fx, source_info, drop_place, *target);
             }
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index fd06c50eb8145..ebc11510e6609 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -374,7 +374,7 @@ fn exported_symbols_provider_local(
                     ));
                 }
                 MonoItem::Fn(Instance {
-                    def: InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)),
+                    def: InstanceKind::AsyncDropGlueCtorShim(_, ty),
                     args,
                 }) => {
                     // A little sanity-check
@@ -388,6 +388,16 @@ fn exported_symbols_provider_local(
                         },
                     ));
                 }
+                MonoItem::Fn(Instance { def: InstanceKind::AsyncDropGlue(_, ty), args: _ }) => {
+                    symbols.push((
+                        ExportedSymbol::AsyncDropGlue(ty),
+                        SymbolExportInfo {
+                            level: SymbolExportLevel::Rust,
+                            kind: SymbolExportKind::Text,
+                            used: false,
+                        },
+                    ));
+                }
                 _ => {
                     // Any other symbols don't qualify for sharing
                 }
@@ -411,6 +421,7 @@ fn upstream_monomorphizations_provider(
 
     let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
     let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn();
+    let async_drop_in_place_poll_fn_def_id = tcx.lang_items().async_drop_in_place_poll_fn();
 
     for &cnum in cnums.iter() {
         for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
@@ -429,8 +440,13 @@ fn upstream_monomorphizations_provider(
                     if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id {
                         (async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()]))
                     } else {
-                        // `drop_in_place` in place does not exist, don't try
-                        // to use it.
+                        continue;
+                    }
+                }
+                ExportedSymbol::AsyncDropGlue(ty) => {
+                    if let Some(poll_fn_def_id) = async_drop_in_place_poll_fn_def_id {
+                        (poll_fn_def_id, tcx.mk_args(&[ty.into()]))
+                    } else {
                         continue;
                     }
                 }
@@ -582,6 +598,13 @@ pub(crate) fn symbol_name_for_instance_in_crate<'tcx>(
                 instantiating_crate,
             )
         }
+        ExportedSymbol::AsyncDropGlue(ty) => {
+            rustc_symbol_mangling::symbol_name_for_instance_in_crate(
+                tcx,
+                Instance::resolve_async_drop_in_place_poll(tcx, ty),
+                instantiating_crate,
+            )
+        }
         ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(),
     }
 }
@@ -604,6 +627,7 @@ fn calling_convention_for_symbol<'tcx>(
         // AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the
         // target's default symbol decoration scheme.
         ExportedSymbol::AsyncDropGlueCtorShim(..) => None,
+        ExportedSymbol::AsyncDropGlue(..) => None,
         // NoDefId always follow the target's default symbol decoration scheme.
         ExportedSymbol::NoDefId(..) => None,
         // ThreadLocalShim always follow the target's default symbol decoration scheme.
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index d184ce3d61dea..950f19a6f0f4e 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -926,10 +926,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         let def = instance.map(|i| i.def);
 
-        if let Some(
-            ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None),
-        ) = def
-        {
+        // We don't need AsyncDropGlueCtorShim here because it is not `noop func`,
+        // it is `func returning noop future`
+        if let Some(ty::InstanceKind::DropGlue(_, None)) = def {
             // Empty drop glue; a no-op.
             let target = target.unwrap();
             return helper.funclet_br(self, bx, target, mergeable_succ);
@@ -1386,8 +1385,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 MergingSucc::False
             }
 
-            mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => self
-                .codegen_drop_terminator(
+            mir::TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } => {
+                assert!(
+                    async_fut.is_none() && drop.is_none(),
+                    "Async Drop must be expanded or reset to sync before codegen"
+                );
+                self.codegen_drop_terminator(
                     helper,
                     bx,
                     &terminator.source_info,
@@ -1395,7 +1398,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     target,
                     unwind,
                     mergeable_succ(),
-                ),
+                )
+            }
 
             mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, unwind } => self
                 .codegen_assert_terminator(
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 61a7ec13511c2..7c7daed525b2d 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -502,6 +502,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
             RemainderByZero(op) => RemainderByZero(eval_to_int(op)?),
             ResumedAfterReturn(coroutine_kind) => ResumedAfterReturn(*coroutine_kind),
             ResumedAfterPanic(coroutine_kind) => ResumedAfterPanic(*coroutine_kind),
+            ResumedAfterDrop(coroutine_kind) => ResumedAfterDrop(*coroutine_kind),
             MisalignedPointerDereference { required, found } => MisalignedPointerDereference {
                 required: eval_to_int(required)?,
                 found: eval_to_int(found)?,
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs
index 29f819cca1fb6..216800717fdaa 100644
--- a/compiler/rustc_const_eval/src/interpret/call.rs
+++ b/compiler/rustc_const_eval/src/interpret/call.rs
@@ -570,6 +570,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             | ty::InstanceKind::FnPtrAddrShim(..)
             | ty::InstanceKind::ThreadLocalShim(..)
             | ty::InstanceKind::AsyncDropGlueCtorShim(..)
+            | ty::InstanceKind::AsyncDropGlue(..)
+            | ty::InstanceKind::FutureDropPollShim(..)
             | ty::InstanceKind::Item(_) => {
                 // We need MIR for this fn.
                 // Note that this can be an intrinsic, if we are executing its fallback body.
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index ddf2d65914f6c..363ceee1970ee 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -539,7 +539,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 }
             }
 
-            Drop { place, target, unwind, replace: _ } => {
+            Drop { place, target, unwind, replace: _, drop, async_fut } => {
+                assert!(
+                    async_fut.is_none() && drop.is_none(),
+                    "Async Drop must be expanded or reset to sync in runtime MIR"
+                );
                 let place = self.eval_place(place)?;
                 let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
                 if let ty::InstanceKind::DropGlue(_, None) = instance.def {
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 90fab01ba2d45..ea95926d944ea 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -188,19 +188,10 @@ language_item_table! {
 
     Drop,                    sym::drop,                drop_trait,                 Target::Trait,          GenericRequirement::None;
     Destruct,                sym::destruct,            destruct_trait,             Target::Trait,          GenericRequirement::None;
-
-    AsyncDrop,               sym::async_drop,          async_drop_trait,           Target::Trait,          GenericRequirement::Exact(0);
-    AsyncDestruct,           sym::async_destruct,      async_destruct_trait,       Target::Trait,          GenericRequirement::Exact(0);
+    AsyncDrop,               sym::async_drop,          async_drop_trait,           Target::Trait,          GenericRequirement::None;
     AsyncDropInPlace,        sym::async_drop_in_place, async_drop_in_place_fn,     Target::Fn,             GenericRequirement::Exact(1);
-    SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
-    AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
-    AsyncDropSlice,          sym::async_drop_slice,    async_drop_slice_fn,        Target::Fn,             GenericRequirement::Exact(1);
-    AsyncDropChain,          sym::async_drop_chain,    async_drop_chain_fn,        Target::Fn,             GenericRequirement::Exact(2);
-    AsyncDropNoop,           sym::async_drop_noop,     async_drop_noop_fn,         Target::Fn,             GenericRequirement::Exact(0);
-    AsyncDropDeferredDropInPlace, sym::async_drop_deferred_drop_in_place, async_drop_deferred_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
-    AsyncDropFuse,           sym::async_drop_fuse,     async_drop_fuse_fn,         Target::Fn,             GenericRequirement::Exact(1);
-    AsyncDropDefer,          sym::async_drop_defer,    async_drop_defer_fn,        Target::Fn,             GenericRequirement::Exact(1);
-    AsyncDropEither,         sym::async_drop_either,   async_drop_either_fn,       Target::Fn,             GenericRequirement::Exact(3);
+    AsyncDropInPlacePoll,    sym::async_drop_in_place_poll, async_drop_in_place_poll_fn, Target::Closure,  GenericRequirement::Exact(1);
+    FutureDropPoll,          sym::future_drop_poll,    future_drop_poll_fn,        Target::Fn,             GenericRequirement::Exact(1);
 
     CoerceUnsized,           sym::coerce_unsized,      coerce_unsized_trait,       Target::Trait,          GenericRequirement::Minimum(1);
     DispatchFromDyn,         sym::dispatch_from_dyn,   dispatch_from_dyn_trait,    Target::Trait,          GenericRequirement::Minimum(1);
@@ -318,6 +309,10 @@ language_item_table! {
     PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None;
     PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None;
     PanicNullPointerDereference, sym::panic_null_pointer_dereference, panic_null_pointer_dereference, Target::Fn, GenericRequirement::None;
+    PanicCoroutineResumedDrop, sym::panic_const_coroutine_resumed_drop, panic_const_coroutine_resumed_drop, Target::Fn, GenericRequirement::None;
+    PanicAsyncFnResumedDrop, sym::panic_const_async_fn_resumed_drop, panic_const_async_fn_resumed_drop, Target::Fn, GenericRequirement::None;
+    PanicAsyncGenFnResumedDrop, sym::panic_const_async_gen_fn_resumed_drop, panic_const_async_gen_fn_resumed_drop, Target::Fn, GenericRequirement::None;
+    PanicGenFnNoneDrop, sym::panic_const_gen_fn_none_drop, panic_const_gen_fn_none_drop, Target::Fn, GenericRequirement::None;
     /// libstd panic entry point. Necessary for const eval to be able to catch it
     BeginPanic,              sym::begin_panic,         begin_panic_fn,             Target::Fn,             GenericRequirement::None;
 
@@ -330,7 +325,6 @@ language_item_table! {
 
     ExchangeMalloc,          sym::exchange_malloc,     exchange_malloc_fn,         Target::Fn,             GenericRequirement::None;
     DropInPlace,             sym::drop_in_place,       drop_in_place_fn,           Target::Fn,             GenericRequirement::Minimum(1);
-    FallbackSurfaceDrop,     sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn,             GenericRequirement::None;
     AllocLayout,             sym::alloc_layout,        alloc_layout,               Target::Struct,         GenericRequirement::None;
 
     /// For all binary crates without `#![no_main]`, Rust will generate a "main" function.
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 02fd7367e2f29..5f5e9e45612a7 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -34,11 +34,9 @@ pub(crate) fn check_legal_trait_for_method_call(
     receiver: Option<Span>,
     expr_span: Span,
     trait_id: DefId,
-    body_id: DefId,
+    _body_id: DefId,
 ) -> Result<(), ErrorGuaranteed> {
-    if tcx.is_lang_item(trait_id, LangItem::Drop)
-        && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id)
-    {
+    if tcx.is_lang_item(trait_id, LangItem::Drop) {
         let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
             errors::ExplicitDestructorCallSugg::Snippet {
                 lo: expr_span.shrink_to_lo(),
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index 0b3c0be1a4e1a..3d27e587b6cb4 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -1,7 +1,11 @@
+middle_assert_async_resume_after_drop = `async fn` resumed after async drop
+
 middle_assert_async_resume_after_panic = `async fn` resumed after panicking
 
 middle_assert_async_resume_after_return = `async fn` resumed after completion
 
+middle_assert_coroutine_resume_after_drop = coroutine resumed after async drop
+
 middle_assert_coroutine_resume_after_panic = coroutine resumed after panicking
 
 middle_assert_coroutine_resume_after_return = coroutine resumed after completion
@@ -9,6 +13,8 @@ middle_assert_coroutine_resume_after_return = coroutine resumed after completion
 middle_assert_divide_by_zero =
     attempt to divide `{$val}` by zero
 
+middle_assert_gen_resume_after_drop = `gen` fn or block cannot be further iterated on after it async dropped
+
 middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further iterated on after it panicked
 
 middle_assert_misaligned_ptr_deref =
diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs
index 0bfbd39879747..0a4c028f5918e 100644
--- a/compiler/rustc_middle/src/middle/exported_symbols.rs
+++ b/compiler/rustc_middle/src/middle/exported_symbols.rs
@@ -44,6 +44,7 @@ pub enum ExportedSymbol<'tcx> {
     Generic(DefId, GenericArgsRef<'tcx>),
     DropGlue(Ty<'tcx>),
     AsyncDropGlueCtorShim(Ty<'tcx>),
+    AsyncDropGlue(Ty<'tcx>),
     ThreadLocalShim(DefId),
     NoDefId(ty::SymbolName<'tcx>),
 }
@@ -63,6 +64,9 @@ impl<'tcx> ExportedSymbol<'tcx> {
             ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
                 tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty))
             }
+            ExportedSymbol::AsyncDropGlue(ty) => {
+                tcx.symbol_name(ty::Instance::resolve_async_drop_in_place_poll(tcx, ty))
+            }
             ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance {
                 def: ty::InstanceKind::ThreadLocalShim(def_id),
                 args: ty::GenericArgs::empty(),
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 4dfb362f3a22b..a068e2b8ddf12 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -200,7 +200,13 @@ pub struct CoroutineInfo<'tcx> {
     /// Coroutine drop glue. This field is populated after the state transform pass.
     pub coroutine_drop: Option<Body<'tcx>>,
 
-    /// The layout of a coroutine. This field is populated after the state transform pass.
+    /// Coroutine async drop glue.
+    pub coroutine_drop_async: Option<Body<'tcx>>,
+
+    /// When coroutine has sync drop, this is async proxy calling `coroutine_drop` sync impl.
+    pub coroutine_drop_proxy_async: Option<Body<'tcx>>,
+
+    /// The layout of a coroutine. Produced by the state transformation.
     pub coroutine_layout: Option<CoroutineLayout<'tcx>>,
 
     /// If this is a coroutine then record the type of source expression that caused this coroutine
@@ -220,6 +226,8 @@ impl<'tcx> CoroutineInfo<'tcx> {
             yield_ty: Some(yield_ty),
             resume_ty: Some(resume_ty),
             coroutine_drop: None,
+            coroutine_drop_async: None,
+            coroutine_drop_proxy_async: None,
             coroutine_layout: None,
         }
     }
@@ -587,6 +595,26 @@ impl<'tcx> Body<'tcx> {
         self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop.as_ref())
     }
 
+    #[inline]
+    pub fn coroutine_drop_async(&self) -> Option<&Body<'tcx>> {
+        self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop_async.as_ref())
+    }
+
+    #[inline]
+    pub fn coroutine_requires_async_drop(&self) -> bool {
+        self.coroutine_drop_async().is_some()
+    }
+
+    #[inline]
+    pub fn future_drop_poll(&self) -> Option<&Body<'tcx>> {
+        self.coroutine.as_ref().and_then(|coroutine| {
+            coroutine
+                .coroutine_drop_async
+                .as_ref()
+                .or(coroutine.coroutine_drop_proxy_async.as_ref())
+        })
+    }
+
     #[inline]
     pub fn coroutine_kind(&self) -> Option<CoroutineKind> {
         self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind)
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 1ffe958dbdd0e..6b413a7383a5e 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -530,6 +530,8 @@ impl<'tcx> CodegenUnit<'tcx> {
                             | InstanceKind::CloneShim(..)
                             | InstanceKind::ThreadLocalShim(..)
                             | InstanceKind::FnPtrAddrShim(..)
+                            | InstanceKind::AsyncDropGlue(..)
+                            | InstanceKind::FutureDropPollShim(..)
                             | InstanceKind::AsyncDropGlueCtorShim(..) => None,
                         }
                     }
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 5a038b27337cf..2ead0f6a53369 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -253,9 +253,7 @@ fn dump_path<'tcx>(
             }));
             s
         }
-        ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => {
-            // Unfortunately, pretty-printed typed are not very filename-friendly.
-            // We dome some filtering.
+        ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
             let mut s = ".".to_owned();
             s.extend(ty.to_string().chars().filter_map(|c| match c {
                 ' ' => None,
@@ -264,6 +262,34 @@ fn dump_path<'tcx>(
             }));
             s
         }
+        ty::InstanceKind::AsyncDropGlue(_, ty) => {
+            let ty::Coroutine(_, args) = ty.kind() else {
+                bug!();
+            };
+            let ty = args.first().unwrap().expect_ty();
+            let mut s = ".".to_owned();
+            s.extend(ty.to_string().chars().filter_map(|c| match c {
+                ' ' => None,
+                ':' | '<' | '>' => Some('_'),
+                c => Some(c),
+            }));
+            s
+        }
+        ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
+            let mut s = ".".to_owned();
+            s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
+                ' ' => None,
+                ':' | '<' | '>' => Some('_'),
+                c => Some(c),
+            }));
+            s.push('.');
+            s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
+                ' ' => None,
+                ':' | '<' | '>' => Some('_'),
+                c => Some(c),
+            }));
+            s
+        }
         _ => String::new(),
     };
 
@@ -1050,7 +1076,13 @@ impl<'tcx> TerminatorKind<'tcx> {
             Call { target: None, unwind: _, .. } => vec![],
             Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
             Yield { drop: None, .. } => vec!["resume".into()],
-            Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
+            Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => {
+                vec!["return".into(), "unwind".into(), "drop".into()]
+            }
+            Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => {
+                vec!["return".into(), "unwind".into()]
+            }
+            Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into()],
             Drop { unwind: _, .. } => vec!["return".into()],
             Assert { unwind: UnwindAction::Cleanup(_), .. } => {
                 vec!["success".into(), "unwind".into()]
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 707c8d04d557f..71ba2917b9b6a 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -77,6 +77,8 @@ pub enum MirPhase {
     ///   exception is fields of packed structs. In analysis MIR, `Drop(P)` for a `P` that might be
     ///   misaligned for this reason implicitly moves `P` to a temporary before dropping. Runtime
     ///   MIR has no such rules, and dropping a misaligned place is simply UB.
+    /// - Async drops: after drop elaboration some drops may become async (`drop`, `async_fut` fields).
+    ///   StateTransform pass will expand those async drops or reset to sync.
     /// - Unwinding: in analysis MIR, unwinding from a function which may not unwind aborts. In
     ///   runtime MIR, this is UB.
     /// - Retags: If `-Zmir-emit-retag` is enabled, analysis MIR has "implicit" retags in the same
@@ -766,7 +768,34 @@ pub enum TerminatorKind<'tcx> {
     /// The `replace` flag indicates whether this terminator was created as part of an assignment.
     /// This should only be used for diagnostic purposes, and does not have any operational
     /// meaning.
-    Drop { place: Place<'tcx>, target: BasicBlock, unwind: UnwindAction, replace: bool },
+    ///
+    /// Async drop processing:
+    /// In compiler/rustc_mir_build/src/build/scope.rs we detect possible async drop:
+    ///   drop of object with `needs_async_drop`.
+    /// Async drop later, in StateTransform pass, may be expanded into additional yield-point
+    ///   for poll-loop of async drop future.
+    /// So we need prepared 'drop' target block in the similar way as for `Yield` terminator
+    ///   (see `drops.build_mir::<CoroutineDrop>` in scopes.rs).
+    /// In compiler/rustc_mir_transform/src/elaborate_drops.rs for object implementing `AsyncDrop` trait
+    ///   we need to prepare async drop feature - resolve `AsyncDrop::drop` and codegen call.
+    /// `async_fut` is set to the corresponding local.
+    /// For coroutine drop we don't need this logic because coroutine drop works with the same
+    ///   layout object as coroutine itself. So `async_fut` will be `None` for coroutine drop.
+    /// Both `drop` and `async_fut` fields are only used in compiler/rustc_mir_transform/src/coroutine.rs,
+    ///   StateTransform pass. In `expand_async_drops` async drops are expanded
+    ///   into one or two yield points with poll ready/pending switch.
+    /// When a coroutine has any internal async drop, the coroutine drop function will be async
+    ///   (generated by `create_coroutine_drop_shim_async`, not `create_coroutine_drop_shim`).
+    Drop {
+        place: Place<'tcx>,
+        target: BasicBlock,
+        unwind: UnwindAction,
+        replace: bool,
+        /// Cleanup to be done if the coroutine is dropped at this suspend point (for async drop).
+        drop: Option<BasicBlock>,
+        /// Prepared async future local (for async drop)
+        async_fut: Option<Local>,
+    },
 
     /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
     /// the referred to function. The operand types must match the argument types of the function.
@@ -1037,6 +1066,7 @@ pub enum AssertKind<O> {
     RemainderByZero(O),
     ResumedAfterReturn(CoroutineKind),
     ResumedAfterPanic(CoroutineKind),
+    ResumedAfterDrop(CoroutineKind),
     MisalignedPointerDereference { required: O, found: O },
     NullPointerDereference,
 }
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index b2c51ad88645c..ca9438b1ed64e 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -208,6 +208,16 @@ impl<O> AssertKind<O> {
                 LangItem::PanicGenFnNonePanic
             }
             NullPointerDereference => LangItem::PanicNullPointerDereference,
+            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop,
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+                LangItem::PanicAsyncFnResumedDrop
+            }
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
+                LangItem::PanicAsyncGenFnResumedDrop
+            }
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
+                LangItem::PanicGenFnNoneDrop
+            }
 
             BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
                 bug!("Unexpected AssertKind")
@@ -298,6 +308,18 @@ impl<O> AssertKind<O> {
             ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
             }
+            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
+                write!(f, "\"coroutine resumed after async drop\"")
+            }
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+                write!(f, "\"`async fn` resumed after async drop\"")
+            }
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
+                write!(f, "\"`async gen fn` resumed after async drop\"")
+            }
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
+                write!(f, "\"`gen fn` resumed after drop\"")
+            }
         }
     }
 
@@ -345,6 +367,19 @@ impl<O> AssertKind<O> {
                 middle_assert_coroutine_resume_after_panic
             }
             NullPointerDereference => middle_assert_null_ptr_deref,
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
+                middle_assert_async_resume_after_drop
+            }
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
+                todo!()
+            }
+            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
+                middle_assert_gen_resume_after_drop
+            }
+            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
+                middle_assert_coroutine_resume_after_drop
+            }
+
             MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
         }
     }
@@ -377,7 +412,10 @@ impl<O> AssertKind<O> {
                 add!("left", format!("{left:#?}"));
                 add!("right", format!("{right:#?}"));
             }
-            ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {}
+            ResumedAfterReturn(_)
+            | ResumedAfterPanic(_)
+            | NullPointerDereference
+            | ResumedAfterDrop(_) => {}
             MisalignedPointerDereference { required, found } => {
                 add!("required", format!("{required:#?}"));
                 add!("found", format!("{found:#?}"));
@@ -457,7 +495,7 @@ mod helper {
         #[cfg_attr(not(bootstrap), define_opaque(Successors))]
         pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
             let target = self.target_for_value(value);
-            (&[]).into_iter().copied().chain(Some(target))
+            (&[]).into_iter().copied().chain(Some(target).into_iter().chain(None))
         }
     }
 
@@ -467,13 +505,23 @@ mod helper {
         pub fn successors(&self) -> Successors<'_> {
             use self::TerminatorKind::*;
             match *self {
+                // 3-successors for async drop: target, unwind, dropline (parent coroutine drop)
+                Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => {
+                    slice::from_ref(t)
+                        .into_iter()
+                        .copied()
+                        .chain(Some(u).into_iter().chain(Some(d)))
+                }
+                // 2-successors
                 Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
                 | Yield { resume: ref t, drop: Some(u), .. }
-                | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
+                | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. }
+                | Drop { target: ref t, unwind: _, drop: Some(u), .. }
                 | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
                 | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
-                    slice::from_ref(t).into_iter().copied().chain(Some(u))
+                    slice::from_ref(t).into_iter().copied().chain(Some(u).into_iter().chain(None))
                 }
+                // single successor
                 Goto { target: ref t }
                 | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
                 | Call { target: Some(ref t), unwind: _, .. }
@@ -481,23 +529,33 @@ mod helper {
                 | Drop { target: ref t, unwind: _, .. }
                 | Assert { target: ref t, unwind: _, .. }
                 | FalseUnwind { real_target: ref t, unwind: _ } => {
-                    slice::from_ref(t).into_iter().copied().chain(None)
+                    slice::from_ref(t).into_iter().copied().chain(None.into_iter().chain(None))
                 }
+                // No successors
                 UnwindResume
                 | UnwindTerminate(_)
                 | CoroutineDrop
                 | Return
                 | Unreachable
                 | TailCall { .. }
-                | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
+                | Call { target: None, unwind: _, .. } => {
+                    (&[]).into_iter().copied().chain(None.into_iter().chain(None))
+                }
+                // Multiple successors
                 InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
-                    targets.iter().copied().chain(Some(u))
+                    targets.iter().copied().chain(Some(u).into_iter().chain(None))
+                }
+                InlineAsm { ref targets, unwind: _, .. } => {
+                    targets.iter().copied().chain(None.into_iter().chain(None))
                 }
-                InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None),
-                SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None),
-                FalseEdge { ref real_target, imaginary_target } => {
-                    slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target))
+                SwitchInt { ref targets, .. } => {
+                    targets.targets.iter().copied().chain(None.into_iter().chain(None))
                 }
+                // FalseEdge
+                FalseEdge { ref real_target, imaginary_target } => slice::from_ref(real_target)
+                    .into_iter()
+                    .copied()
+                    .chain(Some(imaginary_target).into_iter().chain(None)),
             }
         }
 
@@ -506,16 +564,31 @@ mod helper {
         pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
             use self::TerminatorKind::*;
             match *self {
+                // 3-successors for async drop: target, unwind, dropline (parent coroutine drop)
+                Drop {
+                    target: ref mut t,
+                    unwind: UnwindAction::Cleanup(ref mut u),
+                    drop: Some(ref mut d),
+                    ..
+                } => slice::from_mut(t).into_iter().chain(Some(u).into_iter().chain(Some(d))),
+                // 2-successors
                 Call {
                     target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), ..
                 }
                 | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
-                | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
+                | Drop {
+                    target: ref mut t,
+                    unwind: UnwindAction::Cleanup(ref mut u),
+                    drop: None,
+                    ..
+                }
+                | Drop { target: ref mut t, unwind: _, drop: Some(ref mut u), .. }
                 | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
                 | FalseUnwind {
                     real_target: ref mut t,
                     unwind: UnwindAction::Cleanup(ref mut u),
-                } => slice::from_mut(t).into_iter().chain(Some(u)),
+                } => slice::from_mut(t).into_iter().chain(Some(u).into_iter().chain(None)),
+                // single successor
                 Goto { target: ref mut t }
                 | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
                 | Call { target: Some(ref mut t), unwind: _, .. }
@@ -523,22 +596,33 @@ mod helper {
                 | Drop { target: ref mut t, unwind: _, .. }
                 | Assert { target: ref mut t, unwind: _, .. }
                 | FalseUnwind { real_target: ref mut t, unwind: _ } => {
-                    slice::from_mut(t).into_iter().chain(None)
+                    slice::from_mut(t).into_iter().chain(None.into_iter().chain(None))
                 }
+                // No successors
                 UnwindResume
                 | UnwindTerminate(_)
                 | CoroutineDrop
                 | Return
                 | Unreachable
                 | TailCall { .. }
-                | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
+                | Call { target: None, unwind: _, .. } => {
+                    (&mut []).into_iter().chain(None.into_iter().chain(None))
+                }
+                // Multiple successors
                 InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
-                    targets.iter_mut().chain(Some(u))
+                    targets.iter_mut().chain(Some(u).into_iter().chain(None))
+                }
+                InlineAsm { ref mut targets, unwind: _, .. } => {
+                    targets.iter_mut().chain(None.into_iter().chain(None))
+                }
+                SwitchInt { ref mut targets, .. } => {
+                    targets.targets.iter_mut().chain(None.into_iter().chain(None))
                 }
-                InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None),
-                SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None),
+                // FalseEdge
                 FalseEdge { ref mut real_target, ref mut imaginary_target } => {
-                    slice::from_mut(real_target).into_iter().chain(Some(imaginary_target))
+                    slice::from_mut(real_target)
+                        .into_iter()
+                        .chain(Some(imaginary_target).into_iter().chain(None))
                 }
             }
         }
@@ -671,8 +755,10 @@ impl<'tcx> TerminatorKind<'tcx> {
 
             Goto { target } => TerminatorEdges::Single(target),
 
+            // FIXME: Maybe we need also TerminatorEdges::Trio for async drop
+            // (target + unwind + dropline)
             Assert { target, unwind, expected: _, msg: _, cond: _ }
-            | Drop { target, unwind, place: _, replace: _ }
+            | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ }
             | FalseUnwind { real_target: target, unwind } => match unwind {
                 UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
                 UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 3c83d962900ae..c8f258297727a 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -353,17 +353,21 @@ macro_rules! make_mir_visitor {
                             coroutine_closure_def_id: _def_id,
                             receiver_by_ref: _,
                         }
-                        | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, None)
                         | ty::InstanceKind::DropGlue(_def_id, None) => {}
 
                         ty::InstanceKind::FnPtrShim(_def_id, ty)
                         | ty::InstanceKind::DropGlue(_def_id, Some(ty))
                         | ty::InstanceKind::CloneShim(_def_id, ty)
                         | ty::InstanceKind::FnPtrAddrShim(_def_id, ty)
-                        | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, Some(ty)) => {
+                        | ty::InstanceKind::AsyncDropGlue(_def_id, ty)
+                        | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, ty) => {
                             // FIXME(eddyb) use a better `TyContext` here.
                             self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
                         }
+                        ty::InstanceKind::FutureDropPollShim(_def_id, proxy_ty, impl_ty) => {
+                            self.visit_ty($(& $mutability)? *proxy_ty, TyContext::Location(location));
+                            self.visit_ty($(& $mutability)? *impl_ty, TyContext::Location(location));
+                        }
                     }
                     self.visit_args(callee_args, location);
                 }
@@ -515,7 +519,14 @@ macro_rules! make_mir_visitor {
                         self.visit_operand(discr, location);
                     }
 
-                    TerminatorKind::Drop { place, target: _, unwind: _, replace: _ } => {
+                    TerminatorKind::Drop {
+                        place,
+                        target: _,
+                        unwind: _,
+                        replace: _,
+                        drop: _,
+                        async_fut: _,
+                    } => {
                         self.visit_place(
                             place,
                             PlaceContext::MutatingUse(MutatingUseContext::Drop),
@@ -628,7 +639,7 @@ macro_rules! make_mir_visitor {
                     OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
                         self.visit_operand(op, location);
                     }
-                    ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {
+                    ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference | ResumedAfterDrop(_) => {
                         // Nothing to visit
                     }
                     MisalignedPointerDereference { required, found } => {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 0d5fba3cc69b4..f3d245f26ed70 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1575,6 +1575,10 @@ rustc_queries! {
     query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` is `Unpin`", env.value }
     }
+    /// Query backing `Ty::is_async_drop`.
+    query is_async_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
+        desc { "computing whether `{}` is `AsyncDrop`", env.value }
+    }
     /// Query backing `Ty::needs_drop`.
     query needs_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
         desc { "computing whether `{}` needs drop", env.value }
@@ -1607,6 +1611,14 @@ rustc_queries! {
         cache_on_disk_if { true }
     }
 
+    /// A list of types where the ADT requires async drop if and only if any of
+    /// those types require async drop. If the ADT is known to always need async drop
+    /// then `Err(AlwaysRequiresDrop)` is returned.
+    query adt_async_drop_tys(def_id: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
+        desc { |tcx| "computing when `{}` needs async drop", tcx.def_path_str(def_id) }
+        cache_on_disk_if { true }
+    }
+
     /// A list of types where the ADT requires drop if and only if any of those types
     /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor`
     /// is considered to not be significant. A drop is significant if it is implemented
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 162ca1f4af850..efabb60b0d265 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -694,7 +694,6 @@ macro_rules! bidirectional_lang_item_map {
 
 bidirectional_lang_item_map! {
 // tidy-alphabetical-start
-    AsyncDestruct,
     AsyncFn,
     AsyncFnKindHelper,
     AsyncFnKindUpvars,
@@ -1692,6 +1691,10 @@ impl<'tcx> TyCtxt<'tcx> {
         self.coroutine_kind(def_id).is_some()
     }
 
+    pub fn is_templated_coroutine(self, def_id: DefId) -> bool {
+        Some(def_id) == self.lang_items().async_drop_in_place_poll_fn()
+    }
+
     /// Returns the movability of the coroutine of `def_id`, or panics
     /// if given a `def_id` that is not a coroutine.
     pub fn coroutine_movability(self, def_id: DefId) -> hir::Movability {
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 55ebd15248c61..475945cfa2b70 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -147,6 +147,9 @@ pub enum InstanceKind<'tcx> {
     /// native support.
     ThreadLocalShim(DefId),
 
+    /// Proxy shim for async drop of future (def_id, proxy_cor_ty, impl_cor_ty)
+    FutureDropPollShim(DefId, Ty<'tcx>, Ty<'tcx>),
+
     /// `core::ptr::drop_in_place::<T>`.
     ///
     /// The `DefId` is for `core::ptr::drop_in_place`.
@@ -173,7 +176,13 @@ pub enum InstanceKind<'tcx> {
     ///
     /// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty`
     /// is the type `T`.
-    AsyncDropGlueCtorShim(DefId, Option<Ty<'tcx>>),
+    AsyncDropGlueCtorShim(DefId, Ty<'tcx>),
+
+    /// `core::future::async_drop::async_drop_in_place::<'_, T>::{closure}`.
+    ///
+    /// async_drop_in_place poll function implementation (for generated coroutine).
+    /// `Ty` here is `async_drop_in_place<T>::{closure}` coroutine type, not just `T`
+    AsyncDropGlue(DefId, Ty<'tcx>),
 }
 
 impl<'tcx> Instance<'tcx> {
@@ -221,7 +230,9 @@ impl<'tcx> Instance<'tcx> {
                 .upstream_monomorphizations_for(def)
                 .and_then(|monos| monos.get(&self.args).cloned()),
             InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args),
-            InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => {
+            InstanceKind::AsyncDropGlue(_, _) => None,
+            InstanceKind::FutureDropPollShim(_, _, _) => None,
+            InstanceKind::AsyncDropGlueCtorShim(_, _) => {
                 tcx.upstream_async_drop_glue_for(self.args)
             }
             _ => None,
@@ -248,6 +259,8 @@ impl<'tcx> InstanceKind<'tcx> {
             | InstanceKind::DropGlue(def_id, _)
             | InstanceKind::CloneShim(def_id, _)
             | InstanceKind::FnPtrAddrShim(def_id, _)
+            | InstanceKind::FutureDropPollShim(def_id, _, _)
+            | InstanceKind::AsyncDropGlue(def_id, _)
             | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id,
         }
     }
@@ -257,7 +270,9 @@ impl<'tcx> InstanceKind<'tcx> {
         match self {
             ty::InstanceKind::Item(def) => Some(def),
             ty::InstanceKind::DropGlue(def_id, Some(_))
-            | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_))
+            | InstanceKind::AsyncDropGlueCtorShim(def_id, _)
+            | InstanceKind::AsyncDropGlue(def_id, _)
+            | InstanceKind::FutureDropPollShim(def_id, ..)
             | InstanceKind::ThreadLocalShim(def_id) => Some(def_id),
             InstanceKind::VTableShim(..)
             | InstanceKind::ReifyShim(..)
@@ -267,7 +282,6 @@ impl<'tcx> InstanceKind<'tcx> {
             | InstanceKind::ClosureOnceShim { .. }
             | ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
             | InstanceKind::DropGlue(..)
-            | InstanceKind::AsyncDropGlueCtorShim(..)
             | InstanceKind::CloneShim(..)
             | InstanceKind::FnPtrAddrShim(..) => None,
         }
@@ -292,7 +306,9 @@ impl<'tcx> InstanceKind<'tcx> {
         let def_id = match *self {
             ty::InstanceKind::Item(def) => def,
             ty::InstanceKind::DropGlue(_, Some(_)) => return false,
-            ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => return false,
+            ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => return ty.is_coroutine(),
+            ty::InstanceKind::FutureDropPollShim(_, _, _) => return false,
+            ty::InstanceKind::AsyncDropGlue(_, _) => return false,
             ty::InstanceKind::ThreadLocalShim(_) => return false,
             _ => return true,
         };
@@ -325,11 +341,12 @@ impl<'tcx> InstanceKind<'tcx> {
             | InstanceKind::FnPtrAddrShim(..)
             | InstanceKind::FnPtrShim(..)
             | InstanceKind::DropGlue(_, Some(_))
-            | InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => false,
+            | InstanceKind::FutureDropPollShim(..)
+            | InstanceKind::AsyncDropGlue(_, _) => false,
+            InstanceKind::AsyncDropGlueCtorShim(_, _) => false,
             InstanceKind::ClosureOnceShim { .. }
             | InstanceKind::ConstructCoroutineInClosureShim { .. }
             | InstanceKind::DropGlue(..)
-            | InstanceKind::AsyncDropGlueCtorShim(..)
             | InstanceKind::Item(_)
             | InstanceKind::Intrinsic(..)
             | InstanceKind::ReifyShim(..)
@@ -406,8 +423,11 @@ pub fn fmt_instance(
         InstanceKind::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
         InstanceKind::CloneShim(_, ty) => write!(f, " - shim({ty})"),
         InstanceKind::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"),
-        InstanceKind::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"),
-        InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
+        InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => {
+            write!(f, " - dropshim({proxy_ty}-{impl_ty})")
+        }
+        InstanceKind::AsyncDropGlue(_, ty) => write!(f, " - shim({ty})"),
+        InstanceKind::AsyncDropGlueCtorShim(_, ty) => write!(f, " - shim(Some({ty}))"),
     }
 }
 
@@ -425,6 +445,59 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
     }
 }
 
+// async_drop_in_place<T>::coroutine.poll, when T is a standart coroutine,
+// should be resolved to this coroutine's future_drop_poll (through FutureDropPollShim proxy).
+// async_drop_in_place<async_drop_in_place<T>::coroutine>::coroutine.poll,
+// when T is a standart coroutine, should be resolved to this coroutine's future_drop_poll.
+// async_drop_in_place<async_drop_in_place<T>::coroutine>::coroutine.poll,
+// when T is not a coroutine, should be resolved to the innermost
+// async_drop_in_place<T>::coroutine's poll function (through FutureDropPollShim proxy)
+fn resolve_async_drop_poll<'tcx>(tcx: TyCtxt<'tcx>, mut cor_ty: Ty<'tcx>) -> Instance<'tcx> {
+    let first_cor = cor_ty;
+    let ty::Coroutine(_, proxy_args) = first_cor.kind() else {
+        bug!();
+    };
+    let async_drop_in_place_poll = tcx.lang_items().async_drop_in_place_poll_fn().unwrap();
+    let mut child_ty = cor_ty;
+    loop {
+        if let ty::Coroutine(child_def, child_args) = child_ty.kind() {
+            cor_ty = child_ty;
+            if *child_def == async_drop_in_place_poll {
+                child_ty = child_args.first().unwrap().expect_ty();
+                continue;
+            } else {
+                return Instance {
+                    def: ty::InstanceKind::FutureDropPollShim(
+                        async_drop_in_place_poll,
+                        first_cor,
+                        cor_ty,
+                    ),
+                    args: proxy_args,
+                };
+            }
+        } else {
+            let ty::Coroutine(_, child_args) = cor_ty.kind() else {
+                bug!();
+            };
+            if first_cor != cor_ty {
+                return Instance {
+                    def: ty::InstanceKind::FutureDropPollShim(
+                        async_drop_in_place_poll,
+                        first_cor,
+                        cor_ty,
+                    ),
+                    args: proxy_args,
+                };
+            } else {
+                return Instance {
+                    def: ty::InstanceKind::AsyncDropGlue(async_drop_in_place_poll, cor_ty),
+                    args: child_args,
+                };
+            }
+        }
+    }
+}
+
 impl<'tcx> Instance<'tcx> {
     pub fn new(def_id: DefId, args: GenericArgsRef<'tcx>) -> Instance<'tcx> {
         assert!(
@@ -736,6 +809,12 @@ impl<'tcx> Instance<'tcx> {
         )
     }
 
+    pub fn resolve_async_drop_in_place_poll(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> {
+        let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlacePoll, None);
+        let args = tcx.mk_args(&[ty.into()]);
+        Instance::expect_resolve(tcx, ty::TypingEnv::fully_monomorphized(), def_id, args, DUMMY_SP)
+    }
+
     #[instrument(level = "debug", skip(tcx), ret)]
     pub fn fn_once_adapter_instance(
         tcx: TyCtxt<'tcx>,
@@ -800,6 +879,9 @@ impl<'tcx> Instance<'tcx> {
         };
 
         if tcx.is_lang_item(trait_item_id, coroutine_callable_item) {
+            if Some(coroutine_def_id) == tcx.lang_items().async_drop_in_place_poll_fn() {
+                return Some(resolve_async_drop_poll(tcx, rcvr_args.type_at(0)));
+            }
             let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind()
             else {
                 bug!()
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 80f1bd7c6f464..5354a6b74c7cd 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1716,11 +1716,14 @@ impl<'tcx> TyCtxt<'tcx> {
             | ty::InstanceKind::Virtual(..)
             | ty::InstanceKind::ClosureOnceShim { .. }
             | ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
+            | ty::InstanceKind::FutureDropPollShim(..)
             | ty::InstanceKind::DropGlue(..)
             | ty::InstanceKind::CloneShim(..)
             | ty::InstanceKind::ThreadLocalShim(..)
             | ty::InstanceKind::FnPtrAddrShim(..)
             | ty::InstanceKind::AsyncDropGlueCtorShim(..) => self.mir_shims(instance),
+            // async drop glue should be processed specifically, as a templated coroutine
+            ty::InstanceKind::AsyncDropGlue(_, _ty) => todo!(),
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 27ee363f1c142..caaa674e2ae6e 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -4,7 +4,6 @@
 
 use std::assert_matches::debug_assert_matches;
 use std::borrow::Cow;
-use std::iter;
 use std::ops::{ControlFlow, Range};
 
 use hir::def::{CtorKind, DefKind};
@@ -18,7 +17,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate};
 use tracing::instrument;
-use ty::util::{AsyncDropGlueMorphology, IntTypeExt};
+use ty::util::IntTypeExt;
 
 use super::GenericParamDefKind;
 use crate::infer::canonical::Canonical;
@@ -1045,10 +1044,6 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         self.discriminant_ty(interner)
     }
 
-    fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
-        self.async_destructor_ty(interner)
-    }
-
     fn has_unsafe_fields(self) -> bool {
         Ty::has_unsafe_fields(self)
     }
@@ -1559,125 +1554,6 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    /// Returns the type of the async destructor of this type.
-    pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        match self.async_drop_glue_morphology(tcx) {
-            AsyncDropGlueMorphology::Noop => {
-                return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
-                    .instantiate_identity();
-            }
-            AsyncDropGlueMorphology::DeferredDropInPlace => {
-                let drop_in_place =
-                    Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace)
-                        .instantiate(tcx, &[self.into()]);
-                return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
-                    .instantiate(tcx, &[drop_in_place.into()]);
-            }
-            AsyncDropGlueMorphology::Custom => (),
-        }
-
-        match *self.kind() {
-            ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
-                let assoc_items = tcx
-                    .associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None));
-                Ty::new_projection(tcx, assoc_items[0], [self])
-            }
-
-            ty::Array(elem_ty, _) | ty::Slice(elem_ty) => {
-                let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice)
-                    .instantiate(tcx, &[elem_ty.into()]);
-                Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
-                    .instantiate(tcx, &[dtor.into()])
-            }
-
-            ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self
-                .adt_async_destructor_ty(
-                    tcx,
-                    adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))),
-                ),
-            ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)),
-            ty::Closure(_, args) => {
-                self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys()))
-            }
-            ty::CoroutineClosure(_, args) => self
-                .adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())),
-
-            ty::Adt(adt_def, _) => {
-                assert!(adt_def.is_union());
-
-                let surface_drop = self.surface_async_dropper_ty(tcx).unwrap();
-
-                Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
-                    .instantiate(tcx, &[surface_drop.into()])
-            }
-
-            ty::Bound(..)
-            | ty::Foreign(_)
-            | ty::Placeholder(_)
-            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-                bug!("`async_destructor_ty` applied to unexpected type: {self:?}")
-            }
-
-            _ => bug!("`async_destructor_ty` is not yet implemented for type: {self:?}"),
-        }
-    }
-
-    fn adt_async_destructor_ty<I>(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx>
-    where
-        I: Iterator + ExactSizeIterator,
-        I::Item: IntoIterator<Item = Ty<'tcx>>,
-    {
-        debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom);
-
-        let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
-        let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain);
-
-        let noop =
-            Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity();
-        let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither);
-
-        let variants_dtor = variants
-            .into_iter()
-            .map(|variant| {
-                variant
-                    .into_iter()
-                    .map(|ty| defer.instantiate(tcx, &[ty.into()]))
-                    .reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()]))
-                    .unwrap_or(noop)
-            })
-            .reduce(|other, matched| {
-                either.instantiate(tcx, &[other.into(), matched.into(), self.into()])
-            })
-            .unwrap();
-
-        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 {
-            variants_dtor
-        };
-
-        Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
-            .instantiate(tcx, &[dtor.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(
-        tcx: TyCtxt<'tcx>,
-        lang_item: LangItem,
-    ) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
-        tcx.fn_sig(tcx.require_lang_item(lang_item, None))
-            .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap())
-    }
-
     /// Returns the type of metadata for (potentially wide) pointers to this type,
     /// or the struct tail if the metadata type cannot be determined.
     pub fn ptr_metadata_ty_or_tail(
@@ -2021,6 +1897,13 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
+    pub fn is_templated_coroutine(self, tcx: TyCtxt<'_>) -> bool {
+        match self.kind() {
+            ty::Coroutine(def, ..) => tcx.is_templated_coroutine(*def),
+            _ => false,
+        }
+    }
+
     /// Returns `true` when the outermost type cannot be further normalized,
     /// resolved, or instantiated. This includes all primitive types, but also
     /// things like ADTs and trait objects, since even if their arguments or
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index e4863896fc8b3..16290efe4f3f7 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -468,25 +468,6 @@ impl<'tcx> TyCtxt<'tcx> {
         Some(ty::AsyncDestructor { impl_did: dtor_candidate? })
     }
 
-    /// Returns async drop glue morphology for a definition. To get async drop
-    /// glue morphology for a type see [`Ty::async_drop_glue_morphology`].
-    //
-    // FIXME: consider making this a query
-    pub fn async_drop_glue_morphology(self, did: DefId) -> AsyncDropGlueMorphology {
-        let ty: Ty<'tcx> = self.type_of(did).instantiate_identity();
-
-        // Async drop glue morphology is an internal detail, so
-        // using `TypingMode::PostAnalysis` probably should be fine.
-        let typing_env = ty::TypingEnv::fully_monomorphized();
-        if ty.needs_async_drop(self, typing_env) {
-            AsyncDropGlueMorphology::Custom
-        } else if ty.needs_drop(self, typing_env) {
-            AsyncDropGlueMorphology::DeferredDropInPlace
-        } else {
-            AsyncDropGlueMorphology::Noop
-        }
-    }
-
     /// Returns the set of types that are required to be alive in
     /// order to run the destructor of `def` (see RFCs 769 and
     /// 1238).
@@ -1114,18 +1095,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
     }
 }
 
-/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async
-/// drop glue for types not using async drop.
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum AsyncDropGlueMorphology {
-    /// Async destructor simply does nothing
-    Noop,
-    /// Async destructor simply runs `drop_in_place`
-    DeferredDropInPlace,
-    /// Async destructor has custom logic
-    Custom,
-}
-
 impl<'tcx> Ty<'tcx> {
     /// Returns the `Size` for primitive types (bool, uint, int, char, float).
     pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {
@@ -1295,16 +1264,17 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    /// Get morphology of the async drop glue, needed for types which do not
-    /// use async drop. To get async drop glue morphology for a definition see
-    /// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor`
-    /// type construction.
-    //
-    // FIXME: implement optimization to not instantiate a certain morphology of
-    // async drop glue too soon to allow per type optimizations, see array case
-    // for more info. Perhaps then remove this method and use `needs_(async_)drop`
-    // instead.
-    pub fn async_drop_glue_morphology(self, tcx: TyCtxt<'tcx>) -> AsyncDropGlueMorphology {
+    /// Checks whether values of this type `T` implement the `AsyncDrop` trait.
+    pub fn is_async_drop(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
+        !self.is_trivially_not_async_drop()
+            && tcx.is_async_drop_raw(typing_env.as_query_input(self))
+    }
+
+    /// Fast path helper for testing if a type is `AsyncDrop`.
+    ///
+    /// Returning true means the type is known to be `!AsyncDrop`. Returning
+    /// `false` means nothing -- could be `AsyncDrop`, might not be.
+    fn is_trivially_not_async_drop(self) -> bool {
         match self.kind() {
             ty::Int(_)
             | ty::Uint(_)
@@ -1316,46 +1286,26 @@ impl<'tcx> Ty<'tcx> {
             | ty::Ref(..)
             | ty::RawPtr(..)
             | ty::FnDef(..)
-            | ty::FnPtr(..)
-            | ty::Infer(ty::FreshIntTy(_))
-            | ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop,
-
+            | ty::Error(_)
+            | ty::FnPtr(..) => true,
             // FIXME(unsafe_binders):
             ty::UnsafeBinder(_) => todo!(),
-
-            ty::Tuple(tys) if tys.is_empty() => AsyncDropGlueMorphology::Noop,
-            ty::Adt(adt_def, _) if adt_def.is_manually_drop() => AsyncDropGlueMorphology::Noop,
-
-            // Foreign types can never have destructors.
-            ty::Foreign(_) => AsyncDropGlueMorphology::Noop,
-
-            // FIXME: implement dynamic types async drops
-            ty::Error(_) | ty::Dynamic(..) => AsyncDropGlueMorphology::DeferredDropInPlace,
-
-            ty::Tuple(_) | ty::Array(_, _) | ty::Slice(_) => {
-                // Assume worst-case scenario, because we can instantiate async
-                // destructors in different orders:
-                //
-                // 1. Instantiate [T; N] with T = String and N = 0
-                // 2. Instantiate <[String; 0] as AsyncDestruct>::Destructor
-                //
-                // And viceversa, thus we cannot rely on String not using async
-                // drop or array having zero (0) elements
-                AsyncDropGlueMorphology::Custom
-            }
-            ty::Pat(ty, _) => ty.async_drop_glue_morphology(tcx),
-
-            ty::Adt(adt_def, _) => tcx.async_drop_glue_morphology(adt_def.did()),
-
-            ty::Closure(did, _)
-            | ty::CoroutineClosure(did, _)
-            | ty::Coroutine(did, _)
-            | ty::CoroutineWitness(did, _) => tcx.async_drop_glue_morphology(*did),
-
-            ty::Alias(..) | ty::Param(_) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(_) => {
-                // No specifics, but would usually mean forwarding async drop glue
-                AsyncDropGlueMorphology::Custom
+            ty::Tuple(fields) => fields.iter().all(Self::is_trivially_not_async_drop),
+            ty::Pat(elem_ty, _) | ty::Slice(elem_ty) | ty::Array(elem_ty, _) => {
+                elem_ty.is_trivially_not_async_drop()
             }
+            ty::Adt(..)
+            | ty::Bound(..)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Dynamic(..)
+            | ty::Foreign(_)
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Infer(_)
+            | ty::Alias(..)
+            | ty::Param(_)
+            | ty::Placeholder(_) => false,
         }
     }
 
@@ -1401,9 +1351,6 @@ impl<'tcx> Ty<'tcx> {
     /// (Note that this implies that if `ty` has an async destructor attached,
     /// then `needs_async_drop` will definitely return `true` for `ty`.)
     ///
-    /// When constructing `AsyncDestruct::Destructor` type, use
-    /// [`Ty::async_drop_glue_morphology`] instead.
-    //
     // FIXME(zetanumbers): Note that this method is used to check eligible types
     // in unions.
     #[inline]
@@ -1638,6 +1585,46 @@ pub fn needs_drop_components_with_async<'tcx>(
     }
 }
 
+pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool {
+    match *ty.kind() {
+        ty::Bool
+        | ty::Char
+        | ty::Int(_)
+        | ty::Uint(_)
+        | ty::Float(_)
+        | ty::Infer(ty::IntVar(_))
+        | ty::Infer(ty::FloatVar(_))
+        | ty::Str
+        | ty::RawPtr(_, _)
+        | ty::Ref(..)
+        | ty::FnDef(..)
+        | ty::FnPtr(..)
+        | ty::Never
+        | ty::Foreign(_) => true,
+
+        ty::Alias(..)
+        | ty::Dynamic(..)
+        | ty::Error(_)
+        | ty::Bound(..)
+        | ty::Param(_)
+        | ty::Placeholder(_)
+        | ty::Infer(_)
+        | ty::UnsafeBinder(_) => false,
+
+        // Not trivial because they have components, and instead of looking inside,
+        // we'll just perform trait selection.
+        ty::Closure(..)
+        | ty::CoroutineClosure(..)
+        | ty::Coroutine(..)
+        | ty::CoroutineWitness(..)
+        | ty::Adt(..) => false,
+
+        ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => is_trivially_const_drop(ty),
+
+        ty::Tuple(tys) => tys.iter().all(|ty| is_trivially_const_drop(ty)),
+    }
+}
+
 /// Does the equivalent of
 /// ```ignore (illustrative)
 /// let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
index 19669021eefb4..59e03fa79a050 100644
--- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
@@ -69,6 +69,8 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
                     target: self.parse_return_to(args[1])?,
                     unwind: self.parse_unwind_action(args[2])?,
                     replace: false,
+                    drop: None,
+                    async_fut: None,
                 })
             },
             @call(mir_call, args) => {
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index f9791776f71e5..5a97b08db28d6 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -762,6 +762,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         target: success,
                         unwind: UnwindAction::Continue,
                         replace: false,
+                        drop: None,
+                        async_fut: None,
                     },
                 );
                 this.diverge_from(block);
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index e42336a1dbbcc..79225c87d61fd 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -405,6 +405,8 @@ impl DropTree {
                         unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
                         place: drop_node.data.local.into(),
                         replace: false,
+                        drop: None,
+                        async_fut: None,
                     };
                     cfg.terminate(block, drop_node.data.source_info, terminator);
                 }
@@ -848,6 +850,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                 target: next,
                                 unwind: UnwindAction::Continue,
                                 replace: false,
+                                drop: None,
+                                async_fut: None,
                             },
                         );
                         block = next;
@@ -1371,6 +1375,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 target: assign,
                 unwind: UnwindAction::Cleanup(assign_unwind),
                 replace: true,
+                drop: None,
+                async_fut: None,
             },
         );
         self.diverge_from(block);
@@ -1510,6 +1516,8 @@ fn build_scope_drops<'tcx>(
                         target: next,
                         unwind: UnwindAction::Continue,
                         replace: false,
+                        drop: None,
+                        async_fut: None,
                     },
                 );
                 block = next;
diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
index f5ffc42d52ab0..18165b0b9bd08 100644
--- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
@@ -376,7 +376,14 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
         // the result of `is_unwind_dead`.
         let mut edges = terminator.edges();
         if self.skip_unreachable_unwind
-            && let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind
+            && let mir::TerminatorKind::Drop {
+                target,
+                unwind,
+                place,
+                replace: _,
+                drop: _,
+                async_fut: _,
+            } = terminator.kind
             && matches!(unwind, mir::UnwindAction::Cleanup(_))
             && self.is_unwind_dead(place, state)
         {
diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
index b33326cb873df..a414d120e68b5 100644
--- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
+++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs
@@ -83,7 +83,9 @@ fn add_move_for_packed_drop<'tcx>(
     is_cleanup: bool,
 ) {
     debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc);
-    let TerminatorKind::Drop { ref place, target, unwind, replace } = terminator.kind else {
+    let TerminatorKind::Drop { ref place, target, unwind, replace, drop, async_fut } =
+        terminator.kind
+    else {
         unreachable!();
     };
 
@@ -106,6 +108,8 @@ fn add_move_for_packed_drop<'tcx>(
             target: storage_dead_block,
             unwind,
             replace,
+            drop,
+            async_fut,
         },
     );
 }
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 04d96f117072f..c41c4abec7117 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1077,7 +1077,8 @@ fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         let (target, unwind, source_info) = match block_data.terminator() {
             Terminator {
                 source_info,
-                kind: TerminatorKind::Drop { place, target, unwind, replace: _ },
+                kind:
+                    TerminatorKind::Drop { place, target, unwind, replace: _, drop: _, async_fut: _ },
             } => {
                 if let Some(local) = place.as_local()
                     && local == SELF_ARG
@@ -1365,6 +1366,8 @@ fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock {
         target: return_block,
         unwind: UnwindAction::Continue,
         replace: false,
+        drop: None,
+        async_fut: None,
     };
     let source_info = SourceInfo::outermost(body.span);
 
diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs
index 0d8cf524661c8..a824f82d7edce 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drop.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs
@@ -229,6 +229,8 @@ where
                         target: self.succ,
                         unwind: self.unwind.into_action(),
                         replace: false,
+                        drop: None,
+                        async_fut: None,
                     },
                 );
             }
@@ -755,6 +757,8 @@ where
                 target: loop_block,
                 unwind: unwind.into_action(),
                 replace: false,
+                drop: None,
+                async_fut: None,
             },
         );
 
@@ -992,6 +996,8 @@ where
             target,
             unwind: unwind.into_action(),
             replace: false,
+            drop: None,
+            async_fut: None,
         };
         self.new_block(unwind, block)
     }
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 530c72ca549a6..8f11085082fd3 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -328,7 +328,9 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> {
         // This function should mirror what `collect_drop_flags` does.
         for (bb, data) in self.body.basic_blocks.iter_enumerated() {
             let terminator = data.terminator();
-            let TerminatorKind::Drop { place, target, unwind, replace } = terminator.kind else {
+            let TerminatorKind::Drop { place, target, unwind, replace, drop: _, async_fut: _ } =
+                terminator.kind
+            else {
                 continue;
             };
 
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 0ab24e48d443c..85ead1782eb1b 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -413,7 +413,15 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
 
             let term = blk.terminator();
             let caller_attrs = tcx.codegen_fn_attrs(self.caller_def_id());
-            if let TerminatorKind::Drop { ref place, target, unwind, replace: _ } = term.kind {
+            if let TerminatorKind::Drop {
+                ref place,
+                target,
+                unwind,
+                replace: _,
+                drop: _,
+                async_fut: _,
+            } = term.kind
+            {
                 work_list.push(target);
 
                 // If the place doesn't actually need dropping, treat it like a regular goto.
@@ -726,6 +734,20 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>(
             debug!("still needs substitution");
             return Err("implementation limitation -- HACK for dropping polymorphic type");
         }
+        InstanceKind::AsyncDropGlue(_, ty) | InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
+            return if ty.still_further_specializable() {
+                Err("still needs substitution")
+            } else {
+                Ok(())
+            };
+        }
+        InstanceKind::FutureDropPollShim(_, ty, ty2) => {
+            return if ty.still_further_specializable() || ty2.still_further_specializable() {
+                Err("still needs substitution")
+            } else {
+                Ok(())
+            };
+        }
 
         // This cannot result in an immediate cycle since the callee MIR is a shim, which does
         // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we
@@ -739,8 +761,7 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>(
         | InstanceKind::DropGlue(..)
         | InstanceKind::CloneShim(..)
         | InstanceKind::ThreadLocalShim(..)
-        | InstanceKind::FnPtrAddrShim(..)
-        | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()),
+        | InstanceKind::FnPtrAddrShim(..) => return Ok(()),
     }
 
     if inliner.tcx().is_constructor(callee_def_id) {
@@ -1345,8 +1366,8 @@ fn try_instance_mir<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: InstanceKind<'tcx>,
 ) -> Result<&'tcx Body<'tcx>, &'static str> {
-    if let ty::InstanceKind::DropGlue(_, Some(ty))
-    | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) = instance
+    if let ty::InstanceKind::DropGlue(_, Some(ty)) | ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) =
+        instance
         && let ty::Adt(def, args) = ty.kind()
     {
         let fields = def.all_fields();
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index a40768300f5d0..292278800f8a2 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -95,7 +95,10 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
                 InstanceKind::FnPtrAddrShim(..) => {
                     continue;
                 }
-                InstanceKind::DropGlue(..) | InstanceKind::AsyncDropGlueCtorShim(..) => {
+                InstanceKind::DropGlue(..)
+                | InstanceKind::FutureDropPollShim(..)
+                | InstanceKind::AsyncDropGlue(..)
+                | InstanceKind::AsyncDropGlueCtorShim(..) => {
                     // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
                     // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
                     // needs some more analysis.
diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
index 29a9133abe93d..496335ee3c12f 100644
--- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
+++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
@@ -134,6 +134,8 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> {
                     target: _,
                     unwind: _,
                     replace: _,
+                    drop: _,
+                    async_fut: _,
                 } = &terminator.kind
                 && place_has_common_prefix(dropped_place, self.place)
             {
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index c9771467e499c..4557013bc125b 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -23,8 +23,6 @@ use crate::{
     instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
 };
 
-mod async_destructor_ctor;
-
 pub(super) fn provide(providers: &mut Providers) {
     providers.mir_shims = make_shim;
 }
@@ -129,8 +127,14 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<
         ty::InstanceKind::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance),
         ty::InstanceKind::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
         ty::InstanceKind::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
-        ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) => {
-            async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty)
+        ty::InstanceKind::FutureDropPollShim(_def_id, _proxy_ty, _impl_ty) => {
+            todo!()
+        }
+        ty::InstanceKind::AsyncDropGlue(_def_id, _ty) => {
+            todo!()
+        }
+        ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, _ty) => {
+            bug!("AsyncDropGlueCtorShim in re-working ({:?})", instance)
         }
         ty::InstanceKind::Virtual(..) => {
             bug!("InstanceKind::Virtual ({:?}) is for direct calls only", instance)
@@ -621,6 +625,8 @@ impl<'tcx> CloneShimBuilder<'tcx> {
                     target: unwind,
                     unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
                     replace: false,
+                    drop: None,
+                    async_fut: None,
                 },
                 /* is_cleanup */ true,
             );
@@ -886,6 +892,8 @@ fn build_call_shim<'tcx>(
                 target: BasicBlock::new(2),
                 unwind: UnwindAction::Continue,
                 replace: false,
+                drop: None,
+                async_fut: None,
             },
             false,
         );
@@ -903,6 +911,8 @@ fn build_call_shim<'tcx>(
                 target: BasicBlock::new(4),
                 unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
                 replace: false,
+                drop: None,
+                async_fut: None,
             },
             /* is_cleanup */ true,
         );
diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
deleted file mode 100644
index 94b1b4b1855b5..0000000000000
--- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
+++ /dev/null
@@ -1,639 +0,0 @@
-use std::iter;
-
-use itertools::Itertools;
-use rustc_abi::{FieldIdx, VariantIdx};
-use rustc_const_eval::interpret;
-use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::LangItem;
-use rustc_index::{Idx, IndexVec};
-use rustc_middle::mir::*;
-use rustc_middle::ty::adjustment::PointerCoercion;
-use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr};
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::{bug, span_bug};
-use rustc_span::source_map::respan;
-use rustc_span::{Span, Symbol};
-use rustc_target::spec::PanicStrategy;
-use tracing::debug;
-
-use super::{local_decls_for_sig, new_body};
-
-pub(super) fn build_async_destructor_ctor_shim<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    def_id: DefId,
-    ty: Option<Ty<'tcx>>,
-) -> Body<'tcx> {
-    debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
-
-    AsyncDestructorCtorShimBuilder::new(tcx, def_id, ty).build()
-}
-
-/// Builder for async_drop_in_place shim. Functions as a stack machine
-/// to build up an expression using combinators. Stack contains pairs
-/// of locals and types. Combinator is a not yet instantiated pair of a
-/// function and a type, is considered to be an operator which consumes
-/// operands from the stack by instantiating its function and its type
-/// with operand types and moving locals into the function call. Top
-/// pair is considered to be the last operand.
-// FIXME: add mir-opt tests
-struct AsyncDestructorCtorShimBuilder<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    def_id: DefId,
-    self_ty: Option<Ty<'tcx>>,
-    span: Span,
-    source_info: SourceInfo,
-    typing_env: ty::TypingEnv<'tcx>,
-
-    stack: Vec<Operand<'tcx>>,
-    last_bb: BasicBlock,
-    top_cleanup_bb: Option<BasicBlock>,
-
-    locals: IndexVec<Local, LocalDecl<'tcx>>,
-    bbs: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
-}
-
-#[derive(Clone, Copy)]
-enum SurfaceDropKind {
-    Async,
-    Sync,
-}
-
-impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
-    const SELF_PTR: Local = Local::from_u32(1);
-    const INPUT_COUNT: usize = 1;
-    const MAX_STACK_LEN: usize = 2;
-
-    fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Option<Ty<'tcx>>) -> Self {
-        let args = if let Some(ty) = self_ty {
-            tcx.mk_args(&[ty.into()])
-        } else {
-            ty::GenericArgs::identity_for_item(tcx, def_id)
-        };
-        let sig = tcx.fn_sig(def_id).instantiate(tcx, args);
-        let sig = tcx.instantiate_bound_regions_with_erased(sig);
-        let span = tcx.def_span(def_id);
-
-        let source_info = SourceInfo::outermost(span);
-
-        debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT);
-        let locals = local_decls_for_sig(&sig, span);
-
-        // Usual case: noop() + unwind resume + return
-        let mut bbs = IndexVec::with_capacity(3);
-        let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);
-        AsyncDestructorCtorShimBuilder {
-            tcx,
-            def_id,
-            self_ty,
-            span,
-            source_info,
-            typing_env,
-
-            stack: Vec::with_capacity(Self::MAX_STACK_LEN),
-            last_bb: bbs.push(BasicBlockData::new(None, false)),
-            top_cleanup_bb: match tcx.sess.panic_strategy() {
-                PanicStrategy::Unwind => {
-                    // Don't drop input arg because it's just a pointer
-                    Some(bbs.push(BasicBlockData {
-                        statements: Vec::new(),
-                        terminator: Some(Terminator {
-                            source_info,
-                            kind: TerminatorKind::UnwindResume,
-                        }),
-                        is_cleanup: true,
-                    }))
-                }
-                PanicStrategy::Abort => None,
-            },
-
-            locals,
-            bbs,
-        }
-    }
-
-    fn build(self) -> Body<'tcx> {
-        let (tcx, Some(self_ty)) = (self.tcx, self.self_ty) else {
-            return self.build_zst_output();
-        };
-        match self_ty.async_drop_glue_morphology(tcx) {
-            AsyncDropGlueMorphology::Noop => span_bug!(
-                self.span,
-                "async drop glue shim generator encountered type with noop async drop glue morphology"
-            ),
-            AsyncDropGlueMorphology::DeferredDropInPlace => {
-                return self.build_deferred_drop_in_place();
-            }
-            AsyncDropGlueMorphology::Custom => (),
-        }
-
-        let surface_drop_kind = || {
-            let adt_def = self_ty.ty_adt_def()?;
-            if adt_def.async_destructor(tcx).is_some() {
-                Some(SurfaceDropKind::Async)
-            } else if adt_def.destructor(tcx).is_some() {
-                Some(SurfaceDropKind::Sync)
-            } else {
-                None
-            }
-        };
-
-        match self_ty.kind() {
-            ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty),
-            ty::Slice(elem_ty) => self.build_slice(false, *elem_ty),
-
-            ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()),
-            ty::Adt(adt_def, args) if adt_def.is_struct() => {
-                let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args));
-                self.build_chain(surface_drop_kind(), field_tys)
-            }
-            ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()),
-            ty::CoroutineClosure(_, args) => {
-                self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter())
-            }
-
-            ty::Adt(adt_def, args) if adt_def.is_enum() => {
-                self.build_enum(*adt_def, *args, surface_drop_kind())
-            }
-
-            ty::Adt(adt_def, _) => {
-                assert!(adt_def.is_union());
-                match surface_drop_kind().unwrap() {
-                    SurfaceDropKind::Async => self.build_fused_async_surface(),
-                    SurfaceDropKind::Sync => self.build_fused_sync_surface(),
-                }
-            }
-
-            ty::Bound(..)
-            | ty::Foreign(_)
-            | ty::Placeholder(_)
-            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_))
-            | ty::Param(_)
-            | ty::Alias(..) => {
-                bug!("Building async destructor for unexpected type: {self_ty:?}")
-            }
-
-            _ => {
-                bug!(
-                    "Building async destructor constructor shim is not yet implemented for type: {self_ty:?}"
-                )
-            }
-        }
-    }
-
-    fn build_enum(
-        mut self,
-        adt_def: ty::AdtDef<'tcx>,
-        args: ty::GenericArgsRef<'tcx>,
-        surface_drop: Option<SurfaceDropKind>,
-    ) -> Body<'tcx> {
-        let tcx = self.tcx;
-
-        let surface = match surface_drop {
-            None => None,
-            Some(kind) => {
-                self.put_self();
-                Some(match kind {
-                    SurfaceDropKind::Async => self.combine_async_surface(),
-                    SurfaceDropKind::Sync => self.combine_sync_surface(),
-                })
-            }
-        };
-
-        let mut other = None;
-        for (variant_idx, discr) in adt_def.discriminants(tcx) {
-            let variant = adt_def.variant(variant_idx);
-
-            let mut chain = None;
-            for (field_idx, field) in variant.fields.iter_enumerated() {
-                let field_ty = field.ty(tcx, args);
-                self.put_variant_field(variant.name, variant_idx, field_idx, field_ty);
-                let defer = self.combine_defer(field_ty);
-                chain = Some(match chain {
-                    None => defer,
-                    Some(chain) => self.combine_chain(chain, defer),
-                })
-            }
-            let variant_dtor = chain.unwrap_or_else(|| self.put_noop());
-
-            other = Some(match other {
-                None => variant_dtor,
-                Some(other) => {
-                    self.put_self();
-                    self.put_discr(discr);
-                    self.combine_either(other, variant_dtor)
-                }
-            });
-        }
-        let variants_dtor = other.unwrap_or_else(|| self.put_noop());
-
-        let dtor = match surface {
-            None => variants_dtor,
-            Some(surface) => self.combine_chain(surface, variants_dtor),
-        };
-        self.combine_fuse(dtor);
-        self.return_()
-    }
-
-    fn build_chain<I>(mut self, surface_drop: Option<SurfaceDropKind>, elem_tys: I) -> Body<'tcx>
-    where
-        I: Iterator<Item = Ty<'tcx>> + ExactSizeIterator,
-    {
-        let surface = match surface_drop {
-            None => None,
-            Some(kind) => {
-                self.put_self();
-                Some(match kind {
-                    SurfaceDropKind::Async => self.combine_async_surface(),
-                    SurfaceDropKind::Sync => self.combine_sync_surface(),
-                })
-            }
-        };
-
-        let mut chain = None;
-        for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) {
-            self.put_field(field_idx, field_ty);
-            let defer = self.combine_defer(field_ty);
-            chain = Some(match chain {
-                None => defer,
-                Some(chain) => self.combine_chain(chain, defer),
-            })
-        }
-        let chain = chain.unwrap_or_else(|| self.put_noop());
-
-        let dtor = match surface {
-            None => chain,
-            Some(surface) => self.combine_chain(surface, chain),
-        };
-        self.combine_fuse(dtor);
-        self.return_()
-    }
-
-    fn build_zst_output(mut self) -> Body<'tcx> {
-        self.put_zst_output();
-        self.return_()
-    }
-
-    fn build_deferred_drop_in_place(mut self) -> Body<'tcx> {
-        self.put_self();
-        let deferred = self.combine_deferred_drop_in_place();
-        self.combine_fuse(deferred);
-        self.return_()
-    }
-
-    fn build_fused_async_surface(mut self) -> Body<'tcx> {
-        self.put_self();
-        let surface = self.combine_async_surface();
-        self.combine_fuse(surface);
-        self.return_()
-    }
-
-    fn build_fused_sync_surface(mut self) -> Body<'tcx> {
-        self.put_self();
-        let surface = self.combine_sync_surface();
-        self.combine_fuse(surface);
-        self.return_()
-    }
-
-    fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> {
-        if is_array {
-            self.put_array_as_slice(elem_ty)
-        } else {
-            self.put_self()
-        }
-        let dtor = self.combine_slice(elem_ty);
-        self.combine_fuse(dtor);
-        self.return_()
-    }
-
-    fn put_zst_output(&mut self) {
-        let return_ty = self.locals[RETURN_PLACE].ty;
-        self.put_operand(Operand::Constant(Box::new(ConstOperand {
-            span: self.span,
-            user_ty: None,
-            const_: Const::zero_sized(return_ty),
-        })));
-    }
-
-    /// Puts `to_drop: *mut Self` on top of the stack.
-    fn put_self(&mut self) {
-        self.put_operand(Operand::Copy(Self::SELF_PTR.into()))
-    }
-
-    /// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]`
-    /// on top of the stack.
-    fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) {
-        let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty));
-        self.put_temp_rvalue(Rvalue::Cast(
-            CastKind::PointerCoercion(PointerCoercion::Unsize, CoercionSource::Implicit),
-            Operand::Copy(Self::SELF_PTR.into()),
-            slice_ptr_ty,
-        ))
-    }
-
-    /// If given Self is a struct puts `to_drop: *mut FieldTy` on top
-    /// of the stack.
-    fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) {
-        let place = Place {
-            local: Self::SELF_PTR,
-            projection: self
-                .tcx
-                .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]),
-        };
-        self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place))
-    }
-
-    /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
-    /// the stack.
-    fn put_variant_field(
-        &mut self,
-        variant_sym: Symbol,
-        variant: VariantIdx,
-        field: FieldIdx,
-        field_ty: Ty<'tcx>,
-    ) {
-        let place = Place {
-            local: Self::SELF_PTR,
-            projection: self.tcx.mk_place_elems(&[
-                PlaceElem::Deref,
-                PlaceElem::Downcast(Some(variant_sym), variant),
-                PlaceElem::Field(field, field_ty),
-            ]),
-        };
-        self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place))
-    }
-
-    /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
-    /// the stack.
-    fn put_discr(&mut self, discr: Discr<'tcx>) {
-        let (size, _) = discr.ty.int_size_and_signed(self.tcx);
-        self.put_operand(Operand::const_from_scalar(
-            self.tcx,
-            discr.ty,
-            interpret::Scalar::from_uint(discr.val, size),
-            self.span,
-        ));
-    }
-
-    /// Puts `x: RvalueType` on top of the stack.
-    fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) {
-        let last_bb = &mut self.bbs[self.last_bb];
-        debug_assert!(last_bb.terminator.is_none());
-        let source_info = self.source_info;
-
-        let local_ty = rvalue.ty(&self.locals, self.tcx);
-        // We need to create a new local to be able to "consume" it with
-        // a combinator
-        let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info));
-        last_bb.statements.extend_from_slice(&[
-            Statement { source_info, kind: StatementKind::StorageLive(local) },
-            Statement {
-                source_info,
-                kind: StatementKind::Assign(Box::new((local.into(), rvalue))),
-            },
-        ]);
-
-        self.put_operand(Operand::Move(local.into()));
-    }
-
-    /// Puts operand on top of the stack.
-    fn put_operand(&mut self, operand: Operand<'tcx>) {
-        if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
-            let source_info = self.source_info;
-            match &operand {
-                Operand::Copy(_) | Operand::Constant(_) => {
-                    *top_cleanup_bb = self.bbs.push(BasicBlockData {
-                        statements: Vec::new(),
-                        terminator: Some(Terminator {
-                            source_info,
-                            kind: TerminatorKind::Goto { target: *top_cleanup_bb },
-                        }),
-                        is_cleanup: true,
-                    });
-                }
-                Operand::Move(place) => {
-                    let local = place.as_local().unwrap();
-                    *top_cleanup_bb = self.bbs.push(BasicBlockData {
-                        statements: Vec::new(),
-                        terminator: Some(Terminator {
-                            source_info,
-                            kind: if self.locals[local].ty.needs_drop(self.tcx, self.typing_env) {
-                                TerminatorKind::Drop {
-                                    place: local.into(),
-                                    target: *top_cleanup_bb,
-                                    unwind: UnwindAction::Terminate(
-                                        UnwindTerminateReason::InCleanup,
-                                    ),
-                                    replace: false,
-                                }
-                            } else {
-                                TerminatorKind::Goto { target: *top_cleanup_bb }
-                            },
-                        }),
-                        is_cleanup: true,
-                    });
-                }
-            };
-        }
-        self.stack.push(operand);
-    }
-
-    /// Puts `noop: async_drop::Noop` on top of the stack
-    fn put_noop(&mut self) -> Ty<'tcx> {
-        self.apply_combinator(0, LangItem::AsyncDropNoop, &[])
-    }
-
-    fn combine_async_surface(&mut self) -> Ty<'tcx> {
-        self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.unwrap().into()])
-    }
-
-    fn combine_sync_surface(&mut self) -> Ty<'tcx> {
-        self.apply_combinator(
-            1,
-            LangItem::AsyncDropSurfaceDropInPlace,
-            &[self.self_ty.unwrap().into()],
-        )
-    }
-
-    fn combine_deferred_drop_in_place(&mut self) -> Ty<'tcx> {
-        self.apply_combinator(
-            1,
-            LangItem::AsyncDropDeferredDropInPlace,
-            &[self.self_ty.unwrap().into()],
-        )
-    }
-
-    fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()])
-    }
-
-    fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()])
-    }
-
-    fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()])
-    }
-
-    fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> {
-        self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()])
-    }
-
-    fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> {
-        self.apply_combinator(
-            4,
-            LangItem::AsyncDropEither,
-            &[other.into(), matched.into(), self.self_ty.unwrap().into()],
-        )
-    }
-
-    fn return_(mut self) -> Body<'tcx> {
-        let last_bb = &mut self.bbs[self.last_bb];
-        debug_assert!(last_bb.terminator.is_none());
-        let source_info = self.source_info;
-
-        let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else {
-            span_bug!(
-                self.span,
-                "async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}",
-                self.stack.len(),
-            )
-        };
-        #[cfg(debug_assertions)]
-        if let Some(ty) = self.self_ty {
-            debug_assert_eq!(
-                output.ty(&self.locals, self.tcx),
-                ty.async_destructor_ty(self.tcx),
-                "output async destructor types did not match for type: {ty:?}",
-            );
-        }
-
-        let dead_storage = match &output {
-            Operand::Move(place) => Some(Statement {
-                source_info,
-                kind: StatementKind::StorageDead(place.as_local().unwrap()),
-            }),
-            _ => None,
-        };
-
-        last_bb.statements.extend(
-            iter::once(Statement {
-                source_info,
-                kind: StatementKind::Assign(Box::new((RETURN_PLACE.into(), Rvalue::Use(output)))),
-            })
-            .chain(dead_storage),
-        );
-
-        last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return });
-
-        let source = MirSource::from_instance(ty::InstanceKind::AsyncDropGlueCtorShim(
-            self.def_id,
-            self.self_ty,
-        ));
-        new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span)
-    }
-
-    fn apply_combinator(
-        &mut self,
-        arity: usize,
-        function: LangItem,
-        args: &[ty::GenericArg<'tcx>],
-    ) -> Ty<'tcx> {
-        let function = self.tcx.require_lang_item(function, Some(self.span));
-        let operands_split = self
-            .stack
-            .len()
-            .checked_sub(arity)
-            .expect("async destructor ctor shim combinator tried to consume too many items");
-        let operands = &self.stack[operands_split..];
-
-        let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied());
-        let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap();
-        #[cfg(debug_assertions)]
-        operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| {
-            let operand_ty = operand.ty(&self.locals, self.tcx);
-            if operand_ty == *expected_ty {
-                return;
-            }
-
-            // If projection of Discriminant then compare with `Ty::discriminant_ty`
-            if let ty::Alias(ty::Projection, ty::AliasTy { args, def_id, .. }) = expected_ty.kind()
-                && self.tcx.is_lang_item(*def_id, LangItem::Discriminant)
-                && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty
-            {
-                return;
-            }
-
-            span_bug!(
-                self.span,
-                "Operand type and combinator argument type are not equal.
-    operand_ty: {:?}
-    argument_ty: {:?}
-",
-                operand_ty,
-                expected_ty
-            );
-        });
-
-        let target = self.bbs.push(BasicBlockData {
-            statements: operands
-                .iter()
-                .rev()
-                .filter_map(|o| {
-                    if let Operand::Move(Place { local, projection }) = o {
-                        assert!(projection.is_empty());
-                        Some(Statement {
-                            source_info: self.source_info,
-                            kind: StatementKind::StorageDead(*local),
-                        })
-                    } else {
-                        None
-                    }
-                })
-                .collect(),
-            terminator: None,
-            is_cleanup: false,
-        });
-
-        let dest_ty = func_sig.output();
-        let dest =
-            self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable());
-
-        let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
-            for _ in 0..arity {
-                *top_cleanup_bb =
-                    self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap();
-            }
-            UnwindAction::Cleanup(*top_cleanup_bb)
-        } else {
-            UnwindAction::Unreachable
-        };
-
-        let last_bb = &mut self.bbs[self.last_bb];
-        debug_assert!(last_bb.terminator.is_none());
-        last_bb.statements.push(Statement {
-            source_info: self.source_info,
-            kind: StatementKind::StorageLive(dest),
-        });
-        last_bb.terminator = Some(Terminator {
-            source_info: self.source_info,
-            kind: TerminatorKind::Call {
-                func: Operand::Constant(Box::new(ConstOperand {
-                    span: self.span,
-                    user_ty: None,
-                    const_: Const::Val(ConstValue::ZeroSized, func_ty),
-                })),
-                destination: dest.into(),
-                target: Some(target),
-                unwind,
-                call_source: CallSource::Misc,
-                fn_span: self.span,
-                args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(),
-            },
-        });
-
-        self.put_operand(Operand::Move(dest.into()));
-        self.last_bb = target;
-
-        dest_ty
-    }
-}
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index e7930f0a1e3f6..4647839b95c2a 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -373,9 +373,12 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                     );
                 }
             }
-            TerminatorKind::Drop { target, unwind, .. } => {
+            TerminatorKind::Drop { target, unwind, drop, .. } => {
                 self.check_edge(location, *target, EdgeKind::Normal);
                 self.check_unwind_edge(location, *unwind);
+                if let Some(drop) = drop {
+                    self.check_edge(location, *drop, EdgeKind::Normal);
+                }
             }
             TerminatorKind::Call { func, args, .. }
             | TerminatorKind::TailCall { func, args, .. } => {
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 6e676ac6b8d53..b0c1fc08dd284 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -929,14 +929,16 @@ fn visit_instance_use<'tcx>(
         ty::InstanceKind::ThreadLocalShim(..) => {
             bug!("{:?} being reified", instance);
         }
-        ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => {
+        ty::InstanceKind::DropGlue(_, None) => {
             // Don't need to emit noop drop glue if we are calling directly.
             if !is_direct_call {
                 output.push(create_fn_mono_item(tcx, instance, source));
             }
         }
         ty::InstanceKind::DropGlue(_, Some(_))
-        | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_))
+        | ty::InstanceKind::FutureDropPollShim(..)
+        | ty::InstanceKind::AsyncDropGlue(_, _)
+        | ty::InstanceKind::AsyncDropGlueCtorShim(_, _)
         | ty::InstanceKind::VTableShim(..)
         | ty::InstanceKind::ReifyShim(..)
         | ty::InstanceKind::ClosureOnceShim { .. }
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index b1b6f10e0fe2c..c3ee09a4e17c4 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -643,6 +643,8 @@ fn characteristic_def_id_of_mono_item<'tcx>(
                 | ty::InstanceKind::CloneShim(..)
                 | ty::InstanceKind::ThreadLocalShim(..)
                 | ty::InstanceKind::FnPtrAddrShim(..)
+                | ty::InstanceKind::FutureDropPollShim(..)
+                | ty::InstanceKind::AsyncDropGlue(..)
                 | ty::InstanceKind::AsyncDropGlueCtorShim(..) => return None,
             };
 
@@ -795,7 +797,9 @@ fn mono_item_visibility<'tcx>(
     let def_id = match instance.def {
         InstanceKind::Item(def_id)
         | InstanceKind::DropGlue(def_id, Some(_))
-        | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id,
+        | InstanceKind::FutureDropPollShim(def_id, _, _)
+        | InstanceKind::AsyncDropGlue(def_id, _)
+        | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id,
 
         // We match the visibility of statics here
         InstanceKind::ThreadLocalShim(def_id) => {
@@ -811,7 +815,6 @@ fn mono_item_visibility<'tcx>(
         | InstanceKind::ClosureOnceShim { .. }
         | InstanceKind::ConstructCoroutineInClosureShim { .. }
         | InstanceKind::DropGlue(..)
-        | InstanceKind::AsyncDropGlueCtorShim(..)
         | InstanceKind::CloneShim(..)
         | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden,
     };
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 384a304c4a9d6..972ee47085504 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -251,11 +251,6 @@ where
         goal: Goal<I, Self>,
     ) -> Result<Candidate<I>, NoSolution>;
 
-    fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, D>,
-        goal: Goal<I, Self>,
-    ) -> Result<Candidate<I>, NoSolution>;
-
     fn consider_builtin_destruct_candidate(
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
@@ -454,9 +449,6 @@ where
                 Some(TraitSolverLangItem::DiscriminantKind) => {
                     G::consider_builtin_discriminant_kind_candidate(self, goal)
                 }
-                Some(TraitSolverLangItem::AsyncDestruct) => {
-                    G::consider_builtin_async_destruct_candidate(self, goal)
-                }
                 Some(TraitSolverLangItem::Destruct) => {
                     G::consider_builtin_destruct_candidate(self, goal)
                 }
diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
index 0b61c368d8e8d..5ad6e1dd27539 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -336,13 +336,6 @@ where
         unreachable!("DiscriminantKind is not const")
     }
 
-    fn consider_builtin_async_destruct_candidate(
-        _ecx: &mut EvalCtxt<'_, D>,
-        _goal: Goal<I, Self>,
-    ) -> Result<Candidate<I>, NoSolution> {
-        unreachable!("AsyncDestruct is not const")
-    }
-
     fn consider_builtin_destruct_candidate(
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index de6d21da0f592..89e4162910814 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -846,66 +846,6 @@ where
         })
     }
 
-    fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, D>,
-        goal: Goal<I, Self>,
-    ) -> Result<Candidate<I>, NoSolution> {
-        let self_ty = goal.predicate.self_ty();
-        let async_destructor_ty = match self_ty.kind() {
-            ty::Bool
-            | ty::Char
-            | ty::Int(..)
-            | ty::Uint(..)
-            | ty::Float(..)
-            | ty::Array(..)
-            | ty::RawPtr(..)
-            | ty::Ref(..)
-            | ty::FnDef(..)
-            | ty::FnPtr(..)
-            | ty::Closure(..)
-            | ty::CoroutineClosure(..)
-            | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
-            | ty::Never
-            | ty::Adt(_, _)
-            | ty::Str
-            | ty::Slice(_)
-            | ty::Tuple(_)
-            | ty::Error(_) => self_ty.async_destructor_ty(ecx.cx()),
-
-            ty::UnsafeBinder(_) => {
-                // FIXME(unsafe_binders): Instantiate the binder with placeholders I guess.
-                todo!()
-            }
-
-            // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid
-            // alias. In case there's a where-bound further constraining this alias it is preferred over
-            // this impl candidate anyways. It's still a bit scuffed.
-            ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
-                return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
-                    ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                });
-            }
-
-            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
-            | ty::Foreign(..)
-            | ty::Bound(..) => panic!(
-                "unexpected self ty `{:?}` when normalizing `<T as AsyncDestruct>::AsyncDestructor`",
-                goal.predicate.self_ty()
-            ),
-
-            ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => panic!(
-                "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}"
-            ),
-        };
-
-        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
-            ecx.eq(goal.param_env, goal.predicate.term, async_destructor_ty.into())
-                .expect("expected goal term to be fully unconstrained");
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        })
-    }
-
     fn consider_builtin_destruct_candidate(
         _ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index d42c9980f4633..fbb0d025c5d2f 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -571,19 +571,6 @@ where
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
 
-    fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, D>,
-        goal: Goal<I, Self>,
-    ) -> Result<Candidate<I>, NoSolution> {
-        if goal.predicate.polarity != ty::PredicatePolarity::Positive {
-            return Err(NoSolution);
-        }
-
-        // `AsyncDestruct` is automatically implemented for every type.
-        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
-            .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
-    }
-
     fn consider_builtin_destruct_candidate(
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 664bd4ad0a252..52ea0c0516b48 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -57,6 +57,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
         &mut self,
         actual_target: Target,
         def_id: LocalDefId,
+        cor_def_id: Option<LocalDefId>,
         attrs: &'ast [ast::Attribute],
         item_span: Span,
         generics: Option<&'ast ast::Generics>,
@@ -73,6 +74,18 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
                         generics,
                         actual_target,
                     );
+                    // We need to register LangItem::AsyncDropInPlacePoll
+                    // for async_drop_in_place<T>::{closure}
+                    if cor_def_id.is_some() && lang_item == LangItem::AsyncDropInPlace {
+                        self.collect_item_extended(
+                            LangItem::AsyncDropInPlacePoll,
+                            cor_def_id.unwrap(),
+                            item_span,
+                            attr_span,
+                            generics,
+                            actual_target,
+                        );
+                    }
                 }
                 // Known lang item with attribute on incorrect target.
                 Some(lang_item) => {
@@ -288,10 +301,18 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
                 unreachable!("macros should have been expanded")
             }
         };
+        let cor_def_id = if let ast::ItemKind::Fn(box ast::Fn { sig, .. }) = &i.kind
+            && let Some(kind) = sig.header.coroutine_kind
+        {
+            self.resolver.node_id_to_def_id.get(&kind.closure_id()).copied()
+        } else {
+            None
+        };
 
         self.check_for_lang(
             target,
             self.resolver.node_id_to_def_id[&i.id],
+            cor_def_id,
             &i.attrs,
             i.span,
             i.opt_generics(),
@@ -307,6 +328,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
             self.check_for_lang(
                 Target::Variant,
                 self.resolver.node_id_to_def_id[&variant.id],
+                None,
                 &variant.attrs,
                 variant.span,
                 None,
@@ -349,6 +371,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
         self.check_for_lang(
             target,
             self.resolver.node_id_to_def_id[&i.id],
+            None,
             &i.attrs,
             i.span,
             generics,
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 6f6115af96c87..9f3ebe639eeb7 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -9,6 +9,7 @@
 #![cfg_attr(doc, recursion_limit = "256")] // FIXME(nnethercote): will be removed by #124141
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
+#![feature(box_patterns)]
 #![feature(let_chains)]
 #![feature(map_try_insert)]
 #![feature(rustdoc_internals)]
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 240f6f7fe45a1..0b89cb9fb821c 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -630,12 +630,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         matches!(instance.def, ty::InstanceKind::DropGlue(_, None))
     }
 
-    fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool {
-        let tables = self.0.borrow_mut();
-        let instance = tables.instances[def];
-        matches!(instance.def, ty::InstanceKind::AsyncDropGlueCtorShim(_, None))
-    }
-
     fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance {
         let mut tables = self.0.borrow_mut();
         let def_id = tables[def_id];
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index 61b769bce0831..42b3e59b73ab9 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -494,6 +494,9 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
             AssertKind::ResumedAfterPanic(coroutine) => {
                 stable_mir::mir::AssertMessage::ResumedAfterPanic(coroutine.stable(tables))
             }
+            AssertKind::ResumedAfterDrop(coroutine) => {
+                stable_mir::mir::AssertMessage::ResumedAfterDrop(coroutine.stable(tables))
+            }
             AssertKind::MisalignedPointerDereference { required, found } => {
                 stable_mir::mir::AssertMessage::MisalignedPointerDereference {
                     required: required.stable(tables),
@@ -648,13 +651,18 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
             mir::TerminatorKind::UnwindTerminate(_) => TerminatorKind::Abort,
             mir::TerminatorKind::Return => TerminatorKind::Return,
             mir::TerminatorKind::Unreachable => TerminatorKind::Unreachable,
-            mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => {
-                TerminatorKind::Drop {
-                    place: place.stable(tables),
-                    target: target.as_usize(),
-                    unwind: unwind.stable(tables),
-                }
-            }
+            mir::TerminatorKind::Drop {
+                place,
+                target,
+                unwind,
+                replace: _,
+                drop: _,
+                async_fut: _,
+            } => TerminatorKind::Drop {
+                place: place.stable(tables),
+                target: target.as_usize(),
+                unwind: unwind.stable(tables),
+            },
             mir::TerminatorKind::Call {
                 func,
                 args,
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 1ba25aa0e971a..6b7abf59ae7c4 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -813,6 +813,8 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
             | ty::InstanceKind::DropGlue(..)
             | ty::InstanceKind::CloneShim(..)
             | ty::InstanceKind::FnPtrShim(..)
+            | ty::InstanceKind::FutureDropPollShim(..)
+            | ty::InstanceKind::AsyncDropGlue(..)
             | ty::InstanceKind::AsyncDropGlueCtorShim(..) => {
                 stable_mir::mir::mono::InstanceKind::Shim
             }
diff --git a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
index 33d950bb951cd..b0dfd4d75c890 100644
--- a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
+++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs
@@ -189,9 +189,6 @@ pub trait Context {
     /// Check if this is an empty DropGlue shim.
     fn is_empty_drop_shim(&self, def: InstanceDef) -> bool;
 
-    /// Check if this is an empty AsyncDropGlueCtor shim.
-    fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool;
-
     /// Convert a non-generic crate item into an instance.
     /// This function will panic if the item is generic.
     fn mono_instance(&self, def_id: DefId) -> Instance;
diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs
index b23605454246e..660cd7db0800d 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/body.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs
@@ -267,6 +267,7 @@ pub enum AssertMessage {
     RemainderByZero(Operand),
     ResumedAfterReturn(CoroutineKind),
     ResumedAfterPanic(CoroutineKind),
+    ResumedAfterDrop(CoroutineKind),
     MisalignedPointerDereference { required: Operand, found: Operand },
     NullPointerDereference,
 }
@@ -320,6 +321,22 @@ impl AssertMessage {
                 _,
             )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"),
 
+            AssertMessage::ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
+                Ok("coroutine resumed after async drop")
+            }
+            AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+                CoroutineDesugaring::Async,
+                _,
+            )) => Ok("`async fn` resumed after async drop"),
+            AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+                CoroutineDesugaring::Gen,
+                _,
+            )) => Ok("`async gen fn` resumed after async drop"),
+            AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared(
+                CoroutineDesugaring::AsyncGen,
+                _,
+            )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after async drop"),
+
             AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"),
             AssertMessage::MisalignedPointerDereference { .. } => {
                 Ok("misaligned pointer dereference")
diff --git a/compiler/rustc_smir/src/stable_mir/mir/mono.rs b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
index 0c3e4fb7c90b9..f5239cccae1eb 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/mono.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/mono.rs
@@ -163,10 +163,7 @@ impl Instance {
     /// When generating code for a Drop terminator, users can ignore an empty drop glue.
     /// These shims are only needed to generate a valid Drop call done via VTable.
     pub fn is_empty_shim(&self) -> bool {
-        self.kind == InstanceKind::Shim
-            && with(|cx| {
-                cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def)
-            })
+        self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def))
     }
 
     /// Try to constant evaluate the instance into a constant with the given type.
diff --git a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
index 439ebe978e591..7b96add854ad0 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs
@@ -312,7 +312,9 @@ fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::R
         AssertMessage::NullPointerDereference => {
             write!(writer, "\"null pointer dereference occurred\"")
         }
-        AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => {
+        AssertMessage::ResumedAfterReturn(_)
+        | AssertMessage::ResumedAfterPanic(_)
+        | AssertMessage::ResumedAfterDrop(_) => {
             write!(writer, "{}", msg.description().unwrap())
         }
     }
diff --git a/compiler/rustc_smir/src/stable_mir/mir/visit.rs b/compiler/rustc_smir/src/stable_mir/mir/visit.rs
index 786693ea98d36..e21dc11eea9ca 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/visit.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/visit.rs
@@ -372,7 +372,8 @@ macro_rules! make_mir_visitor {
                     }
                     AssertMessage::ResumedAfterReturn(_)
                     | AssertMessage::ResumedAfterPanic(_)
-                    | AssertMessage::NullPointerDereference => {
+                    | AssertMessage::NullPointerDereference
+                    | AssertMessage::ResumedAfterDrop(_) => {
                         //nothing to visit
                     }
                     AssertMessage::MisalignedPointerDereference { required, found } => {
diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs
index 641bac88ad025..2030c74fecb74 100644
--- a/compiler/rustc_span/src/def_id.rs
+++ b/compiler/rustc_span/src/def_id.rs
@@ -297,6 +297,20 @@ impl DefId {
         }
     }
 
+    // For templated coroutine we have non-local async_drop_in_place<T>::coroutine def_id
+    #[inline]
+    #[track_caller]
+    pub fn expect_local_or_templated<F>(self, is_templated: F) -> DefId
+    where
+        F: FnOnce(DefId) -> bool,
+    {
+        assert!(
+            self.is_local() || is_templated(self),
+            "DefId::expect_local_or_templated: `{self:?}` isn't local or templated"
+        );
+        self
+    }
+
     #[inline]
     pub fn is_crate_root(self) -> bool {
         self.index == CRATE_DEF_INDEX
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 31847ae3b4658..4acb08190ce4c 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -500,17 +500,9 @@ symbols! {
         async_call_mut,
         async_call_once,
         async_closure,
-        async_destruct,
         async_drop,
-        async_drop_chain,
-        async_drop_defer,
-        async_drop_deferred_drop_in_place,
-        async_drop_either,
-        async_drop_fuse,
         async_drop_in_place,
-        async_drop_noop,
-        async_drop_slice,
-        async_drop_surface_drop_in_place,
+        async_drop_in_place_poll,
         async_fn,
         async_fn_in_dyn_trait,
         async_fn_in_trait,
@@ -980,7 +972,6 @@ symbols! {
         fadd_fast,
         fake_variadic,
         fallback,
-        fallback_surface_drop,
         fdiv_algebraic,
         fdiv_fast,
         feature,
@@ -1061,6 +1052,7 @@ symbols! {
         fundamental,
         fused_iterator,
         future,
+        future_drop_poll,
         future_output,
         future_trait,
         gdb_script_file,
@@ -1518,14 +1510,18 @@ symbols! {
         panic_cannot_unwind,
         panic_const_add_overflow,
         panic_const_async_fn_resumed,
+        panic_const_async_fn_resumed_drop,
         panic_const_async_fn_resumed_panic,
         panic_const_async_gen_fn_resumed,
+        panic_const_async_gen_fn_resumed_drop,
         panic_const_async_gen_fn_resumed_panic,
         panic_const_coroutine_resumed,
+        panic_const_coroutine_resumed_drop,
         panic_const_coroutine_resumed_panic,
         panic_const_div_by_zero,
         panic_const_div_overflow,
         panic_const_gen_fn_none,
+        panic_const_gen_fn_none_drop,
         panic_const_gen_fn_none_panic,
         panic_const_mul_overflow,
         panic_const_neg_overflow,
@@ -2051,7 +2047,6 @@ symbols! {
         suggestion,
         super_let,
         supertrait_item_shadowing,
-        surface_async_drop_in_place,
         sym,
         sync,
         synthetic,
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 2802e8918073f..db102abda7fa3 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -63,10 +63,17 @@ pub(super) fn mangle<'tcx>(
         .print_def_path(
             def_id,
             if let ty::InstanceKind::DropGlue(_, _)
-            | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) = instance.def
+            | ty::InstanceKind::AsyncDropGlueCtorShim(_, _)
+            | ty::InstanceKind::FutureDropPollShim(_, _, _) = instance.def
             {
                 // Add the name of the dropped type to the symbol name
                 &*instance.args
+            } else if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def {
+                let ty::Coroutine(_, cor_args) = ty.kind() else {
+                    bug!();
+                };
+                let drop_ty = cor_args.first().unwrap().expect_ty();
+                tcx.mk_args(&[GenericArg::from(drop_ty)])
             } else {
                 &[]
             },
@@ -99,6 +106,10 @@ pub(super) fn mangle<'tcx>(
         _ => {}
     }
 
+    if let ty::InstanceKind::FutureDropPollShim(..) = instance.def {
+        let _ = printer.write_str("{{drop-shim}}");
+    }
+
     printer.path.finish(hash)
 }
 
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 99d44bcd7eb80..32324ad8c99ba 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -58,11 +58,17 @@ pub(super) fn mangle<'tcx>(
         ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: false, .. } => {
             Some("by_ref")
         }
-
+        ty::InstanceKind::FutureDropPollShim(_, _, _) => Some("drop"),
         _ => None,
     };
 
-    if let Some(shim_kind) = shim_kind {
+    if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def {
+        let ty::Coroutine(_, cor_args) = ty.kind() else {
+            bug!();
+        };
+        let drop_ty = cor_args.first().unwrap().expect_ty();
+        cx.print_def_path(def_id, tcx.mk_args(&[GenericArg::from(drop_ty)])).unwrap()
+    } else if let Some(shim_kind) = shim_kind {
         cx.path_append_ns(|cx| cx.print_def_path(def_id, args), 'S', 0, shim_kind).unwrap()
     } else {
         cx.print_def_path(def_id, args).unwrap()
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 349569d750e08..c7f17b512cafc 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1051,43 +1051,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         | ty::Infer(..)
                         | ty::Error(_) => false,
                     }
-                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncDestruct) {
-                    match self_ty.kind() {
-                        ty::Bool
-                        | ty::Char
-                        | ty::Int(_)
-                        | ty::Uint(_)
-                        | ty::Float(_)
-                        | ty::Adt(..)
-                        | ty::Str
-                        | ty::Array(..)
-                        | ty::Slice(_)
-                        | ty::RawPtr(..)
-                        | ty::Ref(..)
-                        | ty::FnDef(..)
-                        | ty::FnPtr(..)
-                        | ty::UnsafeBinder(_)
-                        | ty::Dynamic(..)
-                        | ty::Closure(..)
-                        | ty::CoroutineClosure(..)
-                        | ty::Coroutine(..)
-                        | ty::CoroutineWitness(..)
-                        | ty::Pat(..)
-                        | ty::Never
-                        | ty::Tuple(..)
-                        | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
-
-                        // type parameters, opaques, and unnormalized projections don't have
-                        // a known async destructor and may need to be normalized further or rely
-                        // on param env for async destructor projections
-                        ty::Param(_)
-                        | ty::Foreign(_)
-                        | ty::Alias(..)
-                        | ty::Bound(..)
-                        | ty::Placeholder(..)
-                        | ty::Infer(_)
-                        | ty::Error(_) => false,
-                    }
                 } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) {
                     let tail = selcx.tcx().struct_tail_raw(
                         self_ty,
@@ -1541,11 +1504,6 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
         assert_eq!(discriminant_def_id, item_def_id);
 
         (self_ty.discriminant_ty(tcx).into(), PredicateObligations::new())
-    } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) {
-        let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
-        assert_eq!(destructor_def_id, item_def_id);
-
-        (self_ty.async_destructor_ty(tcx).into(), PredicateObligations::new())
     } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) {
         let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
         assert_eq!(metadata_def_id, item_def_id);
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index cf6d2bc151fb0..e4f1c3b62b068 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -79,9 +79,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             } else if tcx.is_lang_item(def_id, LangItem::DiscriminantKind) {
                 // `DiscriminantKind` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
-            } else if tcx.is_lang_item(def_id, LangItem::AsyncDestruct) {
-                // `AsyncDestruct` is automatically implemented for every type.
-                candidates.vec.push(BuiltinCandidate { has_nested: false });
             } else if tcx.is_lang_item(def_id, LangItem::PointeeTrait) {
                 // `Pointee` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 48d5a4a0fcb0d..83724a3625220 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -550,8 +550,10 @@ fn fn_abi_new_uncached<'tcx>(
         extra_args
     };
 
-    let is_drop_in_place =
-        determined_fn_def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::DropInPlace));
+    let is_drop_in_place = determined_fn_def_id.is_some_and(|def_id| {
+        tcx.is_lang_item(def_id, LangItem::DropInPlace)
+            || tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace)
+    });
 
     let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, &'tcx FnAbiError<'tcx>> {
         let span = tracing::debug_span!("arg_of");
diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs
index 20646cf9a826c..bb2c4172b0877 100644
--- a/compiler/rustc_ty_utils/src/common_traits.rs
+++ b/compiler/rustc_ty_utils/src/common_traits.rs
@@ -29,6 +29,13 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, T
     is_item_raw(tcx, query, LangItem::Unpin)
 }
 
+fn is_async_drop_raw<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
+) -> bool {
+    is_item_raw(tcx, query, LangItem::AsyncDrop)
+}
+
 fn is_item_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
@@ -46,6 +53,7 @@ pub(crate) fn provide(providers: &mut Providers) {
         is_sized_raw,
         is_freeze_raw,
         is_unpin_raw,
+        is_async_drop_raw,
         ..*providers
     };
 }
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 66c18bed5e71a..4d9aad6f40041 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -5,7 +5,6 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError};
-use rustc_middle::ty::util::AsyncDropGlueMorphology;
 use rustc_middle::ty::{
     self, ClosureKind, GenericArgsRef, Instance, PseudoCanonicalInput, TyCtxt, TypeVisitableExt,
 };
@@ -41,20 +40,26 @@ fn resolve_instance_raw<'tcx>(
             if ty.needs_drop(tcx, typing_env) {
                 debug!(" => nontrivial drop glue");
                 match *ty.kind() {
+                    ty::Coroutine(coroutine_def_id, ..) => {
+                        // FIXME: sync drop of coroutine with async drop (generate both versions?)
+                        // Currently just ignored
+                        if tcx.optimized_mir(coroutine_def_id).coroutine_drop_async().is_some() {
+                            ty::InstanceKind::DropGlue(def_id, None)
+                        } else {
+                            ty::InstanceKind::DropGlue(def_id, Some(ty))
+                        }
+                    }
                     ty::Closure(..)
                     | ty::CoroutineClosure(..)
-                    | ty::Coroutine(..)
                     | ty::Tuple(..)
                     | ty::Adt(..)
                     | ty::Dynamic(..)
                     | ty::Array(..)
                     | ty::Slice(..)
-                    | ty::UnsafeBinder(..) => {}
+                    | ty::UnsafeBinder(..) => ty::InstanceKind::DropGlue(def_id, Some(ty)),
                     // Drop shims can only be built from ADTs.
                     _ => return Ok(None),
                 }
-
-                ty::InstanceKind::DropGlue(def_id, Some(ty))
             } else {
                 debug!(" => trivial drop glue");
                 ty::InstanceKind::DropGlue(def_id, None)
@@ -62,7 +67,7 @@ fn resolve_instance_raw<'tcx>(
         } else if tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) {
             let ty = args.type_at(0);
 
-            if ty.async_drop_glue_morphology(tcx) != AsyncDropGlueMorphology::Noop {
+            if ty.needs_async_drop(tcx, typing_env) {
                 match *ty.kind() {
                     ty::Closure(..)
                     | ty::CoroutineClosure(..)
@@ -76,11 +81,14 @@ fn resolve_instance_raw<'tcx>(
                     _ => return Ok(None),
                 }
                 debug!(" => nontrivial async drop glue ctor");
-                ty::InstanceKind::AsyncDropGlueCtorShim(def_id, Some(ty))
+                ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty)
             } else {
                 debug!(" => trivial async drop glue ctor");
-                ty::InstanceKind::AsyncDropGlueCtorShim(def_id, None)
+                ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty)
             }
+        } else if tcx.is_lang_item(def_id, LangItem::AsyncDropInPlacePoll) {
+            let ty = args.type_at(0);
+            ty::InstanceKind::AsyncDropGlue(def_id, ty)
         } else {
             debug!(" => free item");
             ty::InstanceKind::Item(def_id)
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index 52955ec59a4a0..c3b04c20f4b67 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -42,11 +42,11 @@ fn needs_async_drop_raw<'tcx>(
     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.typing_env, adt_has_async_dtor, false, false)
-        .filter(filter_array_elements(tcx, query.typing_env))
+        .filter(filter_array_elements_async(tcx, query.typing_env))
         .next()
         .is_some();
 
-    debug!("needs_drop_raw({:?}) = {:?}", query, res);
+    debug!("needs_async_drop_raw({:?}) = {:?}", query, res);
     res
 }
 
@@ -66,6 +66,18 @@ fn filter_array_elements<'tcx>(
         Err(AlwaysRequiresDrop) => true,
     }
 }
+fn filter_array_elements_async<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
+) -> impl Fn(&Result<Ty<'tcx>, AlwaysRequiresDrop>) -> bool {
+    move |ty| match ty {
+        Ok(ty) => match *ty.kind() {
+            ty::Array(elem, _) => tcx.needs_async_drop_raw(typing_env.as_query_input(elem)),
+            _ => true,
+        },
+        Err(AlwaysRequiresDrop) => true,
+    }
+}
 
 fn has_significant_drop_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -414,6 +426,27 @@ fn adt_drop_tys<'tcx>(
     .collect::<Result<Vec<_>, _>>()
     .map(|components| tcx.mk_type_list(&components))
 }
+
+fn adt_async_drop_tys<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
+    // This is for the "adt_async_drop_tys" query, that considers all `AsyncDrop` impls.
+    let adt_has_dtor =
+        |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant);
+    // `tcx.type_of(def_id)` identical to `tcx.make_adt(def, identity_args)`
+    drop_tys_helper(
+        tcx,
+        tcx.type_of(def_id).instantiate_identity(),
+        ty::TypingEnv::non_body_analysis(tcx, def_id),
+        adt_has_dtor,
+        false,
+        false,
+    )
+    .collect::<Result<Vec<_>, _>>()
+    .map(|components| tcx.mk_type_list(&components))
+}
+
 // If `def_id` refers to a generic ADT, the queries above and below act as if they had been handed
 // a `tcx.make_ty(def, identity_args)` and as such it is legal to instantiate the generic parameters
 // of the ADT into the outputted `ty`s.
@@ -458,6 +491,7 @@ pub(crate) fn provide(providers: &mut Providers) {
         needs_async_drop_raw,
         has_significant_drop_raw,
         adt_drop_tys,
+        adt_async_drop_tys,
         adt_significant_drop_tys,
         list_significant_drop_tys,
         ..*providers
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 59c2d3c2fc8d9..77d7e33870390 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -151,7 +151,6 @@ pub trait Ty<I: Interner<Ty = Self>>:
 
     fn discriminant_ty(self, interner: I) -> I::Ty;
 
-    fn async_destructor_ty(self, interner: I) -> I::Ty;
     fn is_known_rigid(self) -> bool {
         self.kind().is_known_rigid()
     }
diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs
index 65f7cdf8f922b..699dd82fb22c9 100644
--- a/compiler/rustc_type_ir/src/lang_items.rs
+++ b/compiler/rustc_type_ir/src/lang_items.rs
@@ -2,7 +2,6 @@
 /// representation of `LangItem`s used in the underlying compiler implementation.
 pub enum TraitSolverLangItem {
     // tidy-alphabetical-start
-    AsyncDestruct,
     AsyncFn,
     AsyncFnKindHelper,
     AsyncFnKindUpvars,
diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs
deleted file mode 100644
index f1778a4d782af..0000000000000
--- a/library/core/src/future/async_drop.rs
+++ /dev/null
@@ -1,284 +0,0 @@
-#![unstable(feature = "async_drop", issue = "126482")]
-
-use crate::fmt;
-use crate::future::{Future, IntoFuture};
-use crate::intrinsics::discriminant_value;
-use crate::marker::{DiscriminantKind, PhantomPinned};
-use crate::mem::MaybeUninit;
-use crate::pin::Pin;
-use crate::task::{Context, Poll, ready};
-
-/// Asynchronously drops a value by running `AsyncDrop::async_drop`
-/// on a value and its fields recursively.
-#[unstable(feature = "async_drop", issue = "126482")]
-pub fn async_drop<T>(value: T) -> AsyncDropOwning<T> {
-    AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned }
-}
-
-/// A future returned by the [`async_drop`].
-#[unstable(feature = "async_drop", issue = "126482")]
-pub struct AsyncDropOwning<T> {
-    value: MaybeUninit<T>,
-    dtor: Option<AsyncDropInPlace<T>>,
-    _pinned: PhantomPinned,
-}
-
-#[unstable(feature = "async_drop", issue = "126482")]
-impl<T> fmt::Debug for AsyncDropOwning<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("AsyncDropOwning").finish_non_exhaustive()
-    }
-}
-
-#[unstable(feature = "async_drop", issue = "126482")]
-impl<T> Future for AsyncDropOwning<T> {
-    type Output = ();
-
-    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        // SAFETY: Self is pinned thus it is ok to store references to self
-        unsafe {
-            let this = self.get_unchecked_mut();
-            let dtor = Pin::new_unchecked(
-                this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())),
-            );
-            // AsyncDestuctors are idempotent so Self gets idempotency as well
-            dtor.poll(cx)
-        }
-    }
-}
-
-#[lang = "async_drop_in_place"]
-#[allow(unconditional_recursion)]
-// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed?
-unsafe fn async_drop_in_place_raw<T: ?Sized>(
-    to_drop: *mut T,
-) -> <T as AsyncDestruct>::AsyncDestructor {
-    // Code here does not matter - this is replaced by the
-    // real async drop glue constructor by the compiler.
-
-    // SAFETY: see comment above
-    unsafe { async_drop_in_place_raw(to_drop) }
-}
-
-/// Creates the asynchronous destructor of the pointed-to value.
-///
-/// # Safety
-///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes.
-///
-/// * `to_drop` must be properly aligned, even if `T` has size 0.
-///
-/// * `to_drop` must be nonnull, even if `T` has size 0.
-///
-/// * The value `to_drop` points to must be valid for async dropping,
-///   which may mean it must uphold additional invariants. These
-///   invariants depend on the type of the value being dropped. For
-///   instance, when dropping a Box, the box's pointer to the heap must
-///   be valid.
-///
-/// * While `async_drop_in_place` is executing or the returned async
-///   destructor is alive, the only way to access parts of `to_drop`
-///   is through the `self: Pin<&mut Self>` references supplied to
-///   the `AsyncDrop::async_drop` methods that `async_drop_in_place`
-///   or `AsyncDropInPlace<T>::poll` invokes. This usually means the
-///   returned future stores the `to_drop` pointer and user is required
-///   to guarantee that dropped value doesn't move.
-///
-#[unstable(feature = "async_drop", issue = "126482")]
-pub unsafe fn async_drop_in_place<T: ?Sized>(to_drop: *mut T) -> AsyncDropInPlace<T> {
-    // SAFETY: `async_drop_in_place_raw` has the same safety requirements
-    unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) }
-}
-
-/// A future returned by the [`async_drop_in_place`].
-#[unstable(feature = "async_drop", issue = "126482")]
-pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor);
-
-#[unstable(feature = "async_drop", issue = "126482")]
-impl<T: ?Sized> fmt::Debug for AsyncDropInPlace<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("AsyncDropInPlace").finish_non_exhaustive()
-    }
-}
-
-#[unstable(feature = "async_drop", issue = "126482")]
-impl<T: ?Sized> Future for AsyncDropInPlace<T> {
-    type Output = ();
-
-    #[inline(always)]
-    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        // SAFETY: This code simply forwards poll call to the inner future
-        unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx)
-    }
-}
-
-// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as
-//   with Drop impls
-/// Custom code within the asynchronous destructor.
-#[unstable(feature = "async_drop", issue = "126482")]
-#[lang = "async_drop"]
-pub trait AsyncDrop {
-    /// A future returned by the [`AsyncDrop::async_drop`] to be part
-    /// of the async destructor.
-    #[unstable(feature = "async_drop", issue = "126482")]
-    type Dropper<'a>: Future<Output = ()>
-    where
-        Self: 'a;
-
-    /// Constructs the asynchronous destructor for this type.
-    #[unstable(feature = "async_drop", issue = "126482")]
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>;
-}
-
-#[lang = "async_destruct"]
-#[rustc_deny_explicit_impl]
-#[rustc_do_not_implement_via_object]
-trait AsyncDestruct {
-    type AsyncDestructor: Future<Output = ()>;
-}
-
-/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify
-/// generation of the code for `async_drop_in_place_raw`
-#[lang = "surface_async_drop_in_place"]
-async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) {
-    // SAFETY: We call this from async drop `async_drop_in_place_raw`
-    //   which has the same safety requirements
-    unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await }
-}
-
-/// Basically calls `Drop::drop` with pointer. Used to simplify generation
-/// of the code for `async_drop_in_place_raw`
-#[allow(drop_bounds)]
-#[lang = "async_drop_surface_drop_in_place"]
-async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
-    // SAFETY: We call this from async drop `async_drop_in_place_raw`
-    //   which has the same safety requirements
-    unsafe { crate::ops::fallback_surface_drop(&mut *ptr) }
-}
-
-/// Wraps a future to continue outputting `Poll::Ready(())` once after
-/// wrapped future completes by returning `Poll::Ready(())` on poll. This
-/// is useful for constructing async destructors to guarantee this
-/// "fuse" property
-//
-// FIXME: Consider optimizing combinators to not have to use fuse in majority
-// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for
-// async functions and blocks with the unit return type. However current layout
-// optimizations currently encode `None` case into the async block's discriminant.
-struct Fuse<T> {
-    inner: Option<T>,
-}
-
-#[lang = "async_drop_fuse"]
-fn fuse<T>(inner: T) -> Fuse<T> {
-    Fuse { inner: Some(inner) }
-}
-
-impl<T> Future for Fuse<T>
-where
-    T: Future<Output = ()>,
-{
-    type Output = ();
-
-    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        // SAFETY: pin projection into `self.inner`
-        unsafe {
-            let this = self.get_unchecked_mut();
-            if let Some(inner) = &mut this.inner {
-                ready!(Pin::new_unchecked(inner).poll(cx));
-                this.inner = None;
-            }
-        }
-        Poll::Ready(())
-    }
-}
-
-/// Async destructor for arrays and slices.
-#[lang = "async_drop_slice"]
-async unsafe fn slice<T>(s: *mut [T]) {
-    let len = s.len();
-    let ptr = s.as_mut_ptr();
-    for i in 0..len {
-        // SAFETY: we iterate over elements of `s` slice
-        unsafe { async_drop_in_place_raw(ptr.add(i)).await }
-    }
-}
-
-/// Constructs a chain of two futures, which awaits them sequentially as
-/// a future.
-#[lang = "async_drop_chain"]
-async fn chain<F, G>(first: F, last: G)
-where
-    F: IntoFuture<Output = ()>,
-    G: IntoFuture<Output = ()>,
-{
-    first.await;
-    last.await;
-}
-
-/// Basically a lazy version of `async_drop_in_place`. Returns a future
-/// that would call `AsyncDrop::async_drop` on a first poll.
-///
-/// # Safety
-///
-/// Same as `async_drop_in_place` except is lazy to avoid creating
-/// multiple mutable references.
-#[lang = "async_drop_defer"]
-async unsafe fn defer<T: ?Sized>(to_drop: *mut T) {
-    // SAFETY: same safety requirements as `async_drop_in_place`
-    unsafe { async_drop_in_place(to_drop) }.await
-}
-
-/// If `T`'s discriminant is equal to the stored one then awaits `M`
-/// otherwise awaits the `O`.
-///
-/// # Safety
-///
-/// Users should carefully manage the returned future, since it would
-/// try creating an immutable reference from `this` and get pointee's
-/// discriminant.
-// FIXME(zetanumbers): Send and Sync impls
-#[lang = "async_drop_either"]
-async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>(
-    other: O,
-    matched: M,
-    this: *mut T,
-    discr: <T as DiscriminantKind>::Discriminant,
-) {
-    // SAFETY: Guaranteed by the safety section of this funtion's documentation
-    if unsafe { discriminant_value(&*this) } == discr {
-        drop(other);
-        matched.await
-    } else {
-        drop(matched);
-        other.await
-    }
-}
-
-#[lang = "async_drop_deferred_drop_in_place"]
-async unsafe fn deferred_drop_in_place<T>(to_drop: *mut T) {
-    // SAFETY: same safety requirements as with drop_in_place (implied by
-    // function's name)
-    unsafe { crate::ptr::drop_in_place(to_drop) }
-}
-
-/// Used for noop async destructors. We don't use [`core::future::Ready`]
-/// because it panics after its second poll, which could be potentially
-/// bad if that would happen during the cleanup.
-#[derive(Clone, Copy)]
-struct Noop;
-
-#[lang = "async_drop_noop"]
-fn noop() -> Noop {
-    Noop
-}
-
-impl Future for Noop {
-    type Output = ();
-
-    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
-        Poll::Ready(())
-    }
-}
diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs
index 65c0171c88d5b..518c9bbf6223c 100644
--- a/library/core/src/future/mod.rs
+++ b/library/core/src/future/mod.rs
@@ -12,7 +12,6 @@
 use crate::ptr::NonNull;
 use crate::task::Context;
 
-mod async_drop;
 mod future;
 mod into_future;
 mod join;
@@ -20,8 +19,6 @@ mod pending;
 mod poll_fn;
 mod ready;
 
-#[unstable(feature = "async_drop", issue = "126482")]
-pub use async_drop::{AsyncDrop, AsyncDropInPlace, async_drop, async_drop_in_place};
 #[stable(feature = "into_future", since = "1.64.0")]
 pub use into_future::IntoFuture;
 #[stable(feature = "future_readiness_fns", since = "1.48.0")]
diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs
index e024b7fb4d301..5d040804a8d1c 100644
--- a/library/core/src/ops/drop.rs
+++ b/library/core/src/ops/drop.rs
@@ -240,10 +240,3 @@ pub trait Drop {
     #[stable(feature = "rust1", since = "1.0.0")]
     fn drop(&mut self);
 }
-
-/// Fallback function to call surface level `Drop::drop` function
-#[allow(drop_bounds)]
-#[lang = "fallback_surface_drop"]
-pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) {
-    <T as Drop>::drop(x)
-}
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index 627a875d9f724..1658f0e5a3692 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -176,7 +176,6 @@ pub use self::deref::Receiver;
 pub use self::deref::{Deref, DerefMut};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::drop::Drop;
-pub(crate) use self::drop::fallback_surface_drop;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::function::{Fn, FnMut, FnOnce};
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs
index 33ad59916e391..83a45436b3050 100644
--- a/library/core/src/panicking.rs
+++ b/library/core/src/panicking.rs
@@ -155,30 +155,26 @@ pub const fn panic(expr: &'static str) -> ! {
 // reducing binary size impact.
 macro_rules! panic_const {
     ($($lang:ident = $message:expr,)+) => {
-        pub mod panic_const {
-            use super::*;
-
-            $(
-                /// This is a panic called with a message that's a result of a MIR-produced Assert.
-                //
-                // never inline unless panic_immediate_abort to avoid code
-                // bloat at the call sites as much as possible
-                #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
-                #[cfg_attr(feature = "panic_immediate_abort", inline)]
-                #[track_caller]
-                #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable
-                #[lang = stringify!($lang)]
-                pub const fn $lang() -> ! {
-                    // Use Arguments::new_const instead of format_args!("{expr}") to potentially
-                    // reduce size overhead. The format_args! macro uses str's Display trait to
-                    // write expr, which calls Formatter::pad, which must accommodate string
-                    // truncation and padding (even though none is used here). Using
-                    // Arguments::new_const may allow the compiler to omit Formatter::pad from the
-                    // output binary, saving up to a few kilobytes.
-                    panic_fmt(fmt::Arguments::new_const(&[$message]));
-                }
-            )+
-        }
+        $(
+            /// This is a panic called with a message that's a result of a MIR-produced Assert.
+            //
+            // never inline unless panic_immediate_abort to avoid code
+            // bloat at the call sites as much as possible
+            #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
+            #[cfg_attr(feature = "panic_immediate_abort", inline)]
+            #[track_caller]
+            #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable
+            #[lang = stringify!($lang)]
+            pub const fn $lang() -> ! {
+                // Use Arguments::new_const instead of format_args!("{expr}") to potentially
+                // reduce size overhead. The format_args! macro uses str's Display trait to
+                // write expr, which calls Formatter::pad, which must accommodate string
+                // truncation and padding (even though none is used here). Using
+                // Arguments::new_const may allow the compiler to omit Formatter::pad from the
+                // output binary, saving up to a few kilobytes.
+                panic_fmt(fmt::Arguments::new_const(&[$message]));
+            }
+        )+
     }
 }
 
@@ -186,25 +182,37 @@ macro_rules! panic_const {
 // slightly different forms. It's not clear if there's a good way to deduplicate without adding
 // special cases to the compiler (e.g., a const generic function wouldn't have a single definition
 // shared across crates, which is exactly what we want here).
-panic_const! {
-    panic_const_add_overflow = "attempt to add with overflow",
-    panic_const_sub_overflow = "attempt to subtract with overflow",
-    panic_const_mul_overflow = "attempt to multiply with overflow",
-    panic_const_div_overflow = "attempt to divide with overflow",
-    panic_const_rem_overflow = "attempt to calculate the remainder with overflow",
-    panic_const_neg_overflow = "attempt to negate with overflow",
-    panic_const_shr_overflow = "attempt to shift right with overflow",
-    panic_const_shl_overflow = "attempt to shift left with overflow",
-    panic_const_div_by_zero = "attempt to divide by zero",
-    panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero",
-    panic_const_coroutine_resumed = "coroutine resumed after completion",
-    panic_const_async_fn_resumed = "`async fn` resumed after completion",
-    panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion",
-    panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion",
-    panic_const_coroutine_resumed_panic = "coroutine resumed after panicking",
-    panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking",
-    panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking",
-    panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking",
+pub mod panic_const {
+    use super::*;
+    panic_const! {
+        panic_const_add_overflow = "attempt to add with overflow",
+        panic_const_sub_overflow = "attempt to subtract with overflow",
+        panic_const_mul_overflow = "attempt to multiply with overflow",
+        panic_const_div_overflow = "attempt to divide with overflow",
+        panic_const_rem_overflow = "attempt to calculate the remainder with overflow",
+        panic_const_neg_overflow = "attempt to negate with overflow",
+        panic_const_shr_overflow = "attempt to shift right with overflow",
+        panic_const_shl_overflow = "attempt to shift left with overflow",
+        panic_const_div_by_zero = "attempt to divide by zero",
+        panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero",
+        panic_const_coroutine_resumed = "coroutine resumed after completion",
+        panic_const_async_fn_resumed = "`async fn` resumed after completion",
+        panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion",
+        panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion",
+        panic_const_coroutine_resumed_panic = "coroutine resumed after panicking",
+        panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking",
+        panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking",
+        panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking",
+    }
+    // Separated panic constants list for async drop feature
+    // (May be joined when the corresponding lang items will be in the bootstrap)
+    #[cfg(not(bootstrap))]
+    panic_const! {
+        panic_const_coroutine_resumed_drop = "coroutine resumed after async drop",
+        panic_const_async_fn_resumed_drop = "`async fn` resumed after async drop",
+        panic_const_async_gen_fn_resumed_drop = "`async gen fn` resumed after async drop",
+        panic_const_gen_fn_none_drop = "`gen fn` resumed after async drop",
+    }
 }
 
 /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller.
diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs
deleted file mode 100644
index 6d556b77795d9..0000000000000
--- a/src/tools/miri/tests/pass/async-drop.rs
+++ /dev/null
@@ -1,199 +0,0 @@
-//@revisions: stack tree
-//@compile-flags: -Zmiri-strict-provenance
-//@[tree]compile-flags: -Zmiri-tree-borrows
-
-// WARNING: If you would ever want to modify this test,
-// please consider modifying rustc's async drop test at
-// `tests/ui/async-await/async-drop.rs`.
-
-#![feature(async_drop, impl_trait_in_assoc_type)]
-#![allow(incomplete_features, dead_code)]
-
-// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
-use core::future::{AsyncDrop, Future, async_drop_in_place};
-use core::hint::black_box;
-use core::mem::{self, ManuallyDrop};
-use core::pin::{Pin, pin};
-use core::task::{Context, Poll, Waker};
-
-async fn test_async_drop<T>(x: T) {
-    let mut x = mem::MaybeUninit::new(x);
-    let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) });
-    test_idempotency(dtor).await;
-}
-
-fn test_idempotency<T>(mut x: Pin<&mut T>) -> impl Future<Output = ()> + '_
-where
-    T: Future<Output = ()>,
-{
-    core::future::poll_fn(move |cx| {
-        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
-        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
-        Poll::Ready(())
-    })
-}
-
-fn main() {
-    let waker = Waker::noop();
-    let mut cx = Context::from_waker(&waker);
-
-    let i = 13;
-    let fut = pin!(async {
-        test_async_drop(Int(0)).await;
-        test_async_drop(AsyncInt(0)).await;
-        test_async_drop([AsyncInt(1), AsyncInt(2)]).await;
-        test_async_drop((AsyncInt(3), AsyncInt(4))).await;
-        test_async_drop(5).await;
-        let j = 42;
-        test_async_drop(&i).await;
-        test_async_drop(&j).await;
-        test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await;
-        test_async_drop(ManuallyDrop::new(AsyncInt(9))).await;
-
-        let foo = AsyncInt(10);
-        test_async_drop(AsyncReference { foo: &foo }).await;
-
-        let foo = AsyncInt(11);
-        test_async_drop(|| {
-            black_box(foo);
-            let foo = AsyncInt(10);
-            foo
-        })
-        .await;
-
-        test_async_drop(AsyncEnum::A(AsyncInt(12))).await;
-        test_async_drop(AsyncEnum::B(SyncInt(13))).await;
-
-        test_async_drop(SyncInt(14)).await;
-        test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) })
-            .await;
-
-        let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19)));
-        test_idempotency(async_drop_fut).await;
-
-        let foo = AsyncInt(20);
-        test_async_drop(async || {
-            black_box(foo);
-            let foo = AsyncInt(19);
-            // Await point there, but this is async closure so it's fine
-            black_box(core::future::ready(())).await;
-            foo
-        })
-        .await;
-
-        test_async_drop(AsyncUnion { signed: 21 }).await;
-    });
-    let res = fut.poll(&mut cx);
-    assert_eq!(res, Poll::Ready(()));
-}
-
-struct AsyncInt(i32);
-
-impl AsyncDrop for AsyncInt {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!("AsyncInt::Dropper::poll: {}", self.0);
-        }
-    }
-}
-
-struct SyncInt(i32);
-
-impl Drop for SyncInt {
-    fn drop(&mut self) {
-        println!("SyncInt::drop: {}", self.0);
-    }
-}
-
-struct SyncThenAsync {
-    i: i32,
-    a: AsyncInt,
-    b: SyncInt,
-    c: AsyncInt,
-}
-
-impl Drop for SyncThenAsync {
-    fn drop(&mut self) {
-        println!("SyncThenAsync::drop: {}", self.i);
-    }
-}
-
-struct AsyncReference<'a> {
-    foo: &'a AsyncInt,
-}
-
-impl AsyncDrop for AsyncReference<'_> {
-    type Dropper<'a>
-        = impl Future<Output = ()>
-    where
-        Self: 'a;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!("AsyncReference::Dropper::poll: {}", self.foo.0);
-        }
-    }
-}
-
-struct Int(i32);
-
-struct AsyncStruct {
-    i: i32,
-    a: AsyncInt,
-    b: AsyncInt,
-}
-
-impl AsyncDrop for AsyncStruct {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!("AsyncStruct::Dropper::poll: {}", self.i);
-        }
-    }
-}
-
-enum AsyncEnum {
-    A(AsyncInt),
-    B(SyncInt),
-}
-
-impl AsyncDrop for AsyncEnum {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            let new_self = match &*self {
-                AsyncEnum::A(foo) => {
-                    println!("AsyncEnum(A)::Dropper::poll: {}", foo.0);
-                    AsyncEnum::B(SyncInt(foo.0))
-                }
-                AsyncEnum::B(foo) => {
-                    println!("AsyncEnum(B)::Dropper::poll: {}", foo.0);
-                    AsyncEnum::A(AsyncInt(foo.0))
-                }
-            };
-            mem::forget(mem::replace(&mut *self, new_self));
-        }
-    }
-}
-
-// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions
-union AsyncUnion {
-    signed: i32,
-    unsigned: u32,
-}
-
-impl AsyncDrop for AsyncUnion {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe {
-                self.unsigned
-            });
-        }
-    }
-}
diff --git a/tests/crashes/128695.rs b/tests/crashes/128695.rs
deleted file mode 100644
index 661f427dc0e99..0000000000000
--- a/tests/crashes/128695.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@ known-bug: rust-lang/rust#128695
-//@ edition: 2021
-
-use core::pin::{pin, Pin};
-
-fn main() {
-    let fut = pin!(async {
-        let async_drop_fut = pin!(core::future::async_drop(async {}));
-        (async_drop_fut).await;
-    });
-}
diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop.rs
deleted file mode 100644
index b1af81423cea5..0000000000000
--- a/tests/ui/async-await/async-drop.rs
+++ /dev/null
@@ -1,222 +0,0 @@
-//@ run-pass
-//@ check-run-results
-
-// WARNING: If you would ever want to modify this test,
-// please consider modifying miri's async drop test at
-// `src/tools/miri/tests/pass/async-drop.rs`.
-
-#![feature(async_drop, impl_trait_in_assoc_type)]
-#![allow(incomplete_features, dead_code)]
-
-//@ edition: 2021
-
-// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
-use core::future::{async_drop_in_place, AsyncDrop, Future};
-use core::hint::black_box;
-use core::mem::{self, ManuallyDrop};
-use core::pin::{pin, Pin};
-use core::task::{Context, Poll, Waker};
-
-async fn test_async_drop<T>(x: T, _size: usize) {
-    let mut x = mem::MaybeUninit::new(x);
-    let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) });
-
-    // FIXME(zetanumbers): This check fully depends on the layout of
-    // the coroutine state, since async destructor combinators are just
-    // async functions.
-    #[cfg(target_pointer_width = "64")]
-    assert_eq!(
-        mem::size_of_val(&*dtor),
-        _size,
-        "sizes did not match for async destructor of type {}",
-        core::any::type_name::<T>(),
-    );
-
-    test_idempotency(dtor).await;
-}
-
-fn test_idempotency<T>(mut x: Pin<&mut T>) -> impl Future<Output = ()> + '_
-where
-    T: Future<Output = ()>,
-{
-    core::future::poll_fn(move |cx| {
-        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
-        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
-        Poll::Ready(())
-    })
-}
-
-fn main() {
-    let waker = Waker::noop();
-    let mut cx = Context::from_waker(&waker);
-
-    let i = 13;
-    let fut = pin!(async {
-        test_async_drop(Int(0), 0).await;
-        // FIXME(#63818): niches in coroutines are disabled.
-        // Some of these sizes should be smaller, as indicated in comments.
-        test_async_drop(AsyncInt(0), /*104*/ 112).await;
-        test_async_drop([AsyncInt(1), AsyncInt(2)], /*152*/ 168).await;
-        test_async_drop((AsyncInt(3), AsyncInt(4)), /*488*/ 528).await;
-        test_async_drop(5, 0).await;
-        let j = 42;
-        test_async_drop(&i, 0).await;
-        test_async_drop(&j, 0).await;
-        test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, /*1688*/ 1792).await;
-        test_async_drop(ManuallyDrop::new(AsyncInt(9)), 0).await;
-
-        let foo = AsyncInt(10);
-        test_async_drop(AsyncReference { foo: &foo }, /*104*/ 112).await;
-
-        let foo = AsyncInt(11);
-        test_async_drop(
-            || {
-                black_box(foo);
-                let foo = AsyncInt(10);
-                foo
-            },
-            /*120*/ 136,
-        )
-        .await;
-
-        test_async_drop(AsyncEnum::A(AsyncInt(12)), /*680*/ 736).await;
-        test_async_drop(AsyncEnum::B(SyncInt(13)), /*680*/ 736).await;
-
-        test_async_drop(SyncInt(14), /*16*/ 24).await;
-        test_async_drop(
-            SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) },
-            /*3064*/ 3296,
-        )
-        .await;
-
-        let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19)));
-        test_idempotency(async_drop_fut).await;
-
-        let foo = AsyncInt(20);
-        test_async_drop(
-            async || {
-                black_box(foo);
-                let foo = AsyncInt(19);
-                // Await point there, but this is async closure so it's fine
-                black_box(core::future::ready(())).await;
-                foo
-            },
-            /*120*/ 136,
-        )
-        .await;
-
-        test_async_drop(AsyncUnion { signed: 21 }, /*32*/ 40).await;
-    });
-    let res = fut.poll(&mut cx);
-    assert_eq!(res, Poll::Ready(()));
-}
-
-struct AsyncInt(i32);
-
-impl AsyncDrop for AsyncInt {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!("AsyncInt::Dropper::poll: {}", self.0);
-        }
-    }
-}
-
-struct SyncInt(i32);
-
-impl Drop for SyncInt {
-    fn drop(&mut self) {
-        println!("SyncInt::drop: {}", self.0);
-    }
-}
-
-struct SyncThenAsync {
-    i: i32,
-    a: AsyncInt,
-    b: SyncInt,
-    c: AsyncInt,
-}
-
-impl Drop for SyncThenAsync {
-    fn drop(&mut self) {
-        println!("SyncThenAsync::drop: {}", self.i);
-    }
-}
-
-struct AsyncReference<'a> {
-    foo: &'a AsyncInt,
-}
-
-impl AsyncDrop for AsyncReference<'_> {
-    type Dropper<'a> = impl Future<Output = ()> where Self: 'a;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!("AsyncReference::Dropper::poll: {}", self.foo.0);
-        }
-    }
-}
-
-struct Int(i32);
-
-struct AsyncStruct {
-    i: i32,
-    a: AsyncInt,
-    b: AsyncInt,
-}
-
-impl AsyncDrop for AsyncStruct {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!("AsyncStruct::Dropper::poll: {}", self.i);
-        }
-    }
-}
-
-enum AsyncEnum {
-    A(AsyncInt),
-    B(SyncInt),
-}
-
-impl AsyncDrop for AsyncEnum {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            let new_self = match &*self {
-                AsyncEnum::A(foo) => {
-                    println!("AsyncEnum(A)::Dropper::poll: {}", foo.0);
-                    AsyncEnum::B(SyncInt(foo.0))
-                }
-                AsyncEnum::B(foo) => {
-                    println!("AsyncEnum(B)::Dropper::poll: {}", foo.0);
-                    AsyncEnum::A(AsyncInt(foo.0))
-                }
-            };
-            mem::forget(mem::replace(&mut *self, new_self));
-        }
-    }
-}
-
-// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions
-union AsyncUnion {
-    signed: i32,
-    unsigned: u32,
-}
-
-impl AsyncDrop for AsyncUnion {
-    type Dropper<'a> = impl Future<Output = ()>;
-
-    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
-        async move {
-            println!(
-                "AsyncUnion::Dropper::poll: {}, {}",
-                unsafe { self.signed },
-                unsafe { self.unsigned },
-            );
-        }
-    }
-}
diff --git a/tests/ui/async-await/async-drop.run.stdout b/tests/ui/async-await/async-drop.run.stdout
deleted file mode 100644
index 9cae4331caf92..0000000000000
--- a/tests/ui/async-await/async-drop.run.stdout
+++ /dev/null
@@ -1,22 +0,0 @@
-AsyncInt::Dropper::poll: 0
-AsyncInt::Dropper::poll: 1
-AsyncInt::Dropper::poll: 2
-AsyncInt::Dropper::poll: 3
-AsyncInt::Dropper::poll: 4
-AsyncStruct::Dropper::poll: 6
-AsyncInt::Dropper::poll: 7
-AsyncInt::Dropper::poll: 8
-AsyncReference::Dropper::poll: 10
-AsyncInt::Dropper::poll: 11
-AsyncEnum(A)::Dropper::poll: 12
-SyncInt::drop: 12
-AsyncEnum(B)::Dropper::poll: 13
-AsyncInt::Dropper::poll: 13
-SyncInt::drop: 14
-SyncThenAsync::drop: 15
-AsyncInt::Dropper::poll: 16
-SyncInt::drop: 17
-AsyncInt::Dropper::poll: 18
-AsyncInt::Dropper::poll: 19
-AsyncInt::Dropper::poll: 20
-AsyncUnion::Dropper::poll: 21, 21
diff --git a/tests/crashes/132103.rs b/tests/ui/async-await/async-drop/ex-ice-132103.rs
similarity index 73%
rename from tests/crashes/132103.rs
rename to tests/ui/async-await/async-drop/ex-ice-132103.rs
index 5bf4792c44c9f..0163d0f97f612 100644
--- a/tests/crashes/132103.rs
+++ b/tests/ui/async-await/async-drop/ex-ice-132103.rs
@@ -1,6 +1,8 @@
-//@ known-bug: #132103
+//! This test used to ICE: rust-lang/rust#132103
+//! Fixed when re-work async drop to templated coroutine scheme.
 //@compile-flags: -Zvalidate-mir --edition=2018 -Zinline-mir=yes
 use core::future::{async_drop_in_place, Future};
+//~^ ERROR unresolved import `core::future::async_drop_in_place` [E0432]
 use core::mem::{self};
 use core::pin::pin;
 use core::task::{Context, Waker};
diff --git a/tests/ui/async-await/async-drop/ex-ice-132103.stderr b/tests/ui/async-await/async-drop/ex-ice-132103.stderr
new file mode 100644
index 0000000000000..1febe56c1669b
--- /dev/null
+++ b/tests/ui/async-await/async-drop/ex-ice-132103.stderr
@@ -0,0 +1,9 @@
+error[E0432]: unresolved import `core::future::async_drop_in_place`
+  --> $DIR/ex-ice-132103.rs:4:20
+   |
+LL | use core::future::{async_drop_in_place, Future};
+   |                    ^^^^^^^^^^^^^^^^^^^ no `async_drop_in_place` in `future`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0432`.