Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
68 changes: 45 additions & 23 deletions compiler/rustc_mir_transform/src/elaborate_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
///
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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() {}
Loading