Skip to content

Commit db6ee18

Browse files
rootroot
authored andcommitted
Fix multi-crate crash if dependency has enabled AsyncDrop
1 parent ddd36bd commit db6ee18

5 files changed

Lines changed: 102 additions & 24 deletions

File tree

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,12 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor>
133133
}
134134

135135
fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
136-
tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl)
136+
let result = tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl);
137+
// Async drop in libstd/libcore would become insta-stable — catch that mistake.
138+
if result.is_some() && tcx.features().staged_api() {
139+
span_bug!(tcx.def_span(def_id), "don't use async drop in libstd, it becomes insta-stable");
140+
}
141+
result
137142
}
138143

139144
/// Given a `DefId` for an opaque type in return position, find its parent item's return

compiler/rustc_mir_transform/src/elaborate_drop.rs

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -422,11 +422,8 @@ where
422422

423423
fn build_drop(&mut self, bb: BasicBlock) {
424424
let drop_ty = self.place_ty(self.place);
425-
if self.tcx().features().async_drop()
426-
&& self.elaborator.body().coroutine.is_some()
427-
&& self.elaborator.allow_async_drops()
428-
&& !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup
429-
&& drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env())
425+
if !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup
426+
&& self.check_if_can_async_drop(drop_ty, false)
430427
{
431428
self.build_async_drop(
432429
self.place,
@@ -452,6 +449,46 @@ where
452449
}
453450
}
454451

452+
/// Function to check if we can generate an async drop here
453+
fn check_if_can_async_drop(&mut self, drop_ty: Ty<'tcx>, call_destructor_only: bool) -> bool {
454+
let is_async_drop_feature_enabled = if self.tcx().features().async_drop() {
455+
true
456+
} else {
457+
// Check if the type needing async drop comes from a dependency crate.
458+
if let ty::Adt(adt_def, _) = drop_ty.kind() {
459+
!adt_def.did().is_local() && adt_def.async_destructor(self.tcx()).is_some()
460+
} else {
461+
false
462+
}
463+
};
464+
465+
// Short-circuit before calling needs_async_drop/is_async_drop, as those
466+
// require the `async_drop` lang item to exist (which may not be present
467+
// in minimal/custom core environments like cranelift's mini_core).
468+
if !is_async_drop_feature_enabled
469+
|| !self.elaborator.body().coroutine.is_some()
470+
|| !self.elaborator.allow_async_drops()
471+
{
472+
return false;
473+
}
474+
475+
let needs_async_drop = if call_destructor_only {
476+
drop_ty.is_async_drop(self.tcx(), self.elaborator.typing_env())
477+
} else {
478+
drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env())
479+
};
480+
481+
// Async drop in libstd/libcore would become insta-stable — catch that mistake.
482+
if needs_async_drop && self.tcx().features().staged_api() {
483+
span_bug!(
484+
self.source_info.span,
485+
"don't use async drop in libstd, it becomes insta-stable"
486+
);
487+
}
488+
489+
needs_async_drop
490+
}
491+
455492
/// This elaborates a single drop instruction, located at `bb`, and
456493
/// patches over it.
457494
///
@@ -1003,12 +1040,7 @@ where
10031040
) -> BasicBlock {
10041041
debug!("destructor_call_block({:?}, {:?})", self, succ);
10051042
let ty = self.place_ty(self.place);
1006-
if self.tcx().features().async_drop()
1007-
&& self.elaborator.body().coroutine.is_some()
1008-
&& self.elaborator.allow_async_drops()
1009-
&& !unwind.is_cleanup()
1010-
&& ty.is_async_drop(self.tcx(), self.elaborator.typing_env())
1011-
{
1043+
if !unwind.is_cleanup() && self.check_if_can_async_drop(ty, true) {
10121044
self.build_async_drop(self.place, ty, None, succ, unwind, dropline, true)
10131045
} else {
10141046
self.destructor_call_block_sync((succ, unwind))
@@ -1078,12 +1110,7 @@ where
10781110
let loop_block = self.elaborator.patch().new_block(loop_block);
10791111

10801112
let place = tcx.mk_place_deref(ptr);
1081-
if self.tcx().features().async_drop()
1082-
&& self.elaborator.body().coroutine.is_some()
1083-
&& self.elaborator.allow_async_drops()
1084-
&& !unwind.is_cleanup()
1085-
&& ety.needs_async_drop(self.tcx(), self.elaborator.typing_env())
1086-
{
1113+
if !unwind.is_cleanup() && self.check_if_can_async_drop(ety, false) {
10871114
self.build_async_drop(
10881115
place,
10891116
ety,
@@ -1368,12 +1395,7 @@ where
13681395

13691396
fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
13701397
let drop_ty = self.place_ty(self.place);
1371-
if self.tcx().features().async_drop()
1372-
&& self.elaborator.body().coroutine.is_some()
1373-
&& self.elaborator.allow_async_drops()
1374-
&& !unwind.is_cleanup()
1375-
&& drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env())
1376-
{
1398+
if !unwind.is_cleanup() && self.check_if_can_async_drop(drop_ty, false) {
13771399
self.build_async_drop(
13781400
self.place,
13791401
drop_ty,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ edition: 2024
2+
//@ run-pass
3+
//@ compile-flags: -Z mir-opt-level=0
4+
//@ aux-crate: async_drop_crate_dep=async-drop-crate-dep.rs
5+
6+
use std::{ //~ WARN found async drop types in dependency
7+
pin::pin,
8+
task::{Context, Waker},
9+
};
10+
11+
extern crate async_drop_crate_dep;
12+
13+
fn main() {
14+
let mut context = Context::from_waker(Waker::noop());
15+
let future = pin!(async { async_drop_crate_dep::run().await });
16+
// For some reason, putting this value into a variable is load-bearing.
17+
let _x = future.poll(&mut context);
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: found async drop types in dependency `async_drop_crate_dep`, but async_drop feature is disabled for `async_drop_run_without_feature`
2+
--> $DIR/async-drop-run-without-feature.rs:6:1
3+
|
4+
LL | use std::{
5+
| ^
6+
|
7+
= help: if async drop type will be dropped in a crate without `feature(async_drop)`, sync Drop will be used
8+
9+
warning: 1 warning emitted
10+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ edition: 2024
2+
#![feature(async_drop)]
3+
use std::future::AsyncDrop;
4+
use std::pin::Pin;
5+
6+
pub async fn run() {
7+
let _st = St;
8+
}
9+
10+
struct St;
11+
12+
impl Drop for St {
13+
fn drop(&mut self) {}
14+
}
15+
16+
impl AsyncDrop for St {
17+
async fn drop(self: Pin<&mut Self>) {
18+
// Removing this line makes the program panic "normally" (not abort).
19+
nothing().await;
20+
}
21+
}
22+
23+
async fn nothing() {}

0 commit comments

Comments
 (0)