diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 2c47c91d5192b..61e6133306066 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -133,7 +133,12 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option } fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl) + let result = tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl); + // Async drop in libstd/libcore would become insta-stable — catch that mistake. + if result.is_some() && tcx.features().staged_api() { + span_bug!(tcx.def_span(def_id), "don't use async drop in libstd, it becomes insta-stable"); + } + result } /// Given a `DefId` for an opaque type in return position, find its parent item's return diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 655262af8f36a..28c4421f94434 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -422,11 +422,8 @@ where fn build_drop(&mut self, bb: BasicBlock) { let drop_ty = self.place_ty(self.place); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup - && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + if !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup + && self.check_if_can_async_drop(drop_ty, false) { self.build_async_drop( self.place, @@ -452,6 +449,46 @@ where } } + /// Function to check if we can generate an async drop here + fn check_if_can_async_drop(&mut self, drop_ty: Ty<'tcx>, call_destructor_only: bool) -> bool { + let is_async_drop_feature_enabled = if self.tcx().features().async_drop() { + true + } else { + // Check if the type needing async drop comes from a dependency crate. + if let ty::Adt(adt_def, _) = drop_ty.kind() { + !adt_def.did().is_local() && adt_def.async_destructor(self.tcx()).is_some() + } else { + false + } + }; + + // Short-circuit before calling needs_async_drop/is_async_drop, as those + // require the `async_drop` lang item to exist (which may not be present + // in minimal/custom core environments like cranelift's mini_core). + if !is_async_drop_feature_enabled + || !self.elaborator.body().coroutine.is_some() + || !self.elaborator.allow_async_drops() + { + return false; + } + + let needs_async_drop = if call_destructor_only { + drop_ty.is_async_drop(self.tcx(), self.elaborator.typing_env()) + } else { + drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + }; + + // Async drop in libstd/libcore would become insta-stable — catch that mistake. + if needs_async_drop && self.tcx().features().staged_api() { + span_bug!( + self.source_info.span, + "don't use async drop in libstd, it becomes insta-stable" + ); + } + + needs_async_drop + } + /// This elaborates a single drop instruction, located at `bb`, and /// patches over it. /// @@ -1003,12 +1040,7 @@ where ) -> BasicBlock { debug!("destructor_call_block({:?}, {:?})", self, succ); let ty = self.place_ty(self.place); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !unwind.is_cleanup() - && ty.is_async_drop(self.tcx(), self.elaborator.typing_env()) - { + if !unwind.is_cleanup() && self.check_if_can_async_drop(ty, true) { self.build_async_drop(self.place, ty, None, succ, unwind, dropline, true) } else { self.destructor_call_block_sync((succ, unwind)) @@ -1078,12 +1110,7 @@ where let loop_block = self.elaborator.patch().new_block(loop_block); let place = tcx.mk_place_deref(ptr); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !unwind.is_cleanup() - && ety.needs_async_drop(self.tcx(), self.elaborator.typing_env()) - { + if !unwind.is_cleanup() && self.check_if_can_async_drop(ety, false) { self.build_async_drop( place, ety, @@ -1368,12 +1395,7 @@ where fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let drop_ty = self.place_ty(self.place); - if self.tcx().features().async_drop() - && self.elaborator.body().coroutine.is_some() - && self.elaborator.allow_async_drops() - && !unwind.is_cleanup() - && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) - { + if !unwind.is_cleanup() && self.check_if_can_async_drop(drop_ty, false) { self.build_async_drop( self.place, drop_ty, diff --git a/tests/ui/async-await/async-drop/async-drop-run-without-feature/async-drop-run-without-feature.rs b/tests/ui/async-await/async-drop/async-drop-run-without-feature/async-drop-run-without-feature.rs new file mode 100644 index 0000000000000..815a99ea712ff --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-run-without-feature/async-drop-run-without-feature.rs @@ -0,0 +1,18 @@ +//@ edition: 2024 +//@ run-pass +//@ compile-flags: -Z mir-opt-level=0 +//@ aux-crate: async_drop_crate_dep=async-drop-crate-dep.rs + +use std::{ //~ WARN found async drop types in dependency + pin::pin, + task::{Context, Waker}, +}; + +extern crate async_drop_crate_dep; + +fn main() { + let mut context = Context::from_waker(Waker::noop()); + let future = pin!(async { async_drop_crate_dep::run().await }); + // For some reason, putting this value into a variable is load-bearing. + let _x = future.poll(&mut context); +} diff --git a/tests/ui/async-await/async-drop/async-drop-run-without-feature/async-drop-run-without-feature.stderr b/tests/ui/async-await/async-drop/async-drop-run-without-feature/async-drop-run-without-feature.stderr new file mode 100644 index 0000000000000..70eca25edcbc0 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-run-without-feature/async-drop-run-without-feature.stderr @@ -0,0 +1,10 @@ +warning: found async drop types in dependency `async_drop_crate_dep`, but async_drop feature is disabled for `async_drop_run_without_feature` + --> $DIR/async-drop-run-without-feature.rs:6:1 + | +LL | use std::{ + | ^ + | + = help: if async drop type will be dropped in a crate without `feature(async_drop)`, sync Drop will be used + +warning: 1 warning emitted + diff --git a/tests/ui/async-await/async-drop/async-drop-run-without-feature/auxiliary/async-drop-crate-dep.rs b/tests/ui/async-await/async-drop/async-drop-run-without-feature/auxiliary/async-drop-crate-dep.rs new file mode 100644 index 0000000000000..9e10b2d39bcd5 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-run-without-feature/auxiliary/async-drop-crate-dep.rs @@ -0,0 +1,23 @@ +//@ edition: 2024 +#![feature(async_drop)] +use std::future::AsyncDrop; +use std::pin::Pin; + +pub async fn run() { + let _st = St; +} + +struct St; + +impl Drop for St { + fn drop(&mut self) {} +} + +impl AsyncDrop for St { + async fn drop(self: Pin<&mut Self>) { + // Removing this line makes the program panic "normally" (not abort). + nothing().await; + } +} + +async fn nothing() {}