Fix multi-crate crash if dependency has enabled AsyncDrop

This commit is contained in:
root
2026-03-04 00:40:47 -08:00
parent ddd36bd570
commit db6ee18fd0
5 changed files with 102 additions and 24 deletions
+6 -1
View File
@@ -133,7 +133,12 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor>
}
fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
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
@@ -422,11 +422,8 @@ fn build_async_drop(
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 @@ fn build_drop(&mut self, bb: BasicBlock) {
}
}
/// 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 @@ fn destructor_call_block(
) -> 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 @@ fn drop_loop(
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 @@ fn drop_block_simple(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBloc
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,
@@ -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);
}
@@ -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
@@ -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() {}