From 26b4adc3ea63df554278dab396c01707458d66f0 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Thu, 17 Jul 2025 12:34:26 +0700 Subject: [PATCH 1/2] AsyncDrop impl for Box --- compiler/rustc_feature/src/unstable.rs | 4 +- compiler/rustc_interface/src/passes.rs | 1 - compiler/rustc_metadata/src/creader.rs | 21 ---- compiler/rustc_metadata/src/rmeta/decoder.rs | 4 - compiler/rustc_passes/src/check_attr.rs | 3 +- compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/boxed.rs | 19 +++- library/alloc/src/lib.rs | 1 + library/core/src/future/async_drop.rs | 6 +- library/core/src/future/mod.rs | 2 +- src/tools/miri/tests/pass/async-drop.rs | 2 +- .../async-drop-future-from-future.rs | 2 +- .../async-drop-future-in-sync-context.rs | 2 +- .../async-drop/async-drop-glue-array.rs | 2 +- .../async-drop/async-drop-glue-generic.rs | 2 +- .../async-await/async-drop/async-drop-glue.rs | 2 +- .../async-drop/async-drop-initial.rs | 2 +- .../async-drop/async-drop-middle-drop.rs | 2 +- .../async-await/async-drop/async-drop-open.rs | 2 +- tests/ui/async-await/async-drop/async-drop.rs | 2 +- .../async-drop/auxiliary/async-drop-dep.rs | 2 +- .../async-drop/dependency-dropped.rs | 37 ------ ...dependency-dropped.with_feature.run.stdout | 1 - ...endency-dropped.without_feature.run.stdout | 1 - .../dependency-dropped.without_feature.stderr | 10 -- .../async-drop/deref-later-projection.rs | 2 +- .../elaborate-index-out-of-bounds.rs | 2 +- .../async-await/async-drop/ex-ice-132103.rs | 2 +- tests/ui/async-await/async-drop/inside_box.rs | 106 ++++++++++++++++++ .../async-drop/inside_box.run.stdout | 6 + .../async-drop/live-dead-storage3.rs | 2 +- .../async-drop/live-dead-storage4.rs | 2 +- .../async-await/async-drop/open-drop-error.rs | 2 +- .../async-drop/open-drop-error2.rs | 2 +- .../async-await/async-drop/type-parameter.rs | 2 +- .../async-await/async-drop/unexpected-sort.rs | 2 +- .../feature-gate-async-drop-lib.rs | 16 +++ .../feature-gate-async-drop-lib.stderr | 33 ++++++ .../feature-gates/feature-gate-async-drop.rs | 6 +- .../feature-gate-async-drop.stderr | 12 +- 40 files changed, 219 insertions(+), 111 deletions(-) delete mode 100644 tests/ui/async-await/async-drop/dependency-dropped.rs delete mode 100644 tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout delete mode 100644 tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout delete mode 100644 tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr create mode 100644 tests/ui/async-await/async-drop/inside_box.rs create mode 100644 tests/ui/async-await/async-drop/inside_box.run.stdout create mode 100644 tests/ui/feature-gates/feature-gate-async-drop-lib.rs create mode 100644 tests/ui/feature-gates/feature-gate-async-drop-lib.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index acc21f6c6d253..7ec11d5634207 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -388,8 +388,10 @@ declare_features! ( (unstable, associated_const_equality, "1.58.0", Some(92827)), /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), - /// Allows implementing `AsyncDrop`. + /// Enables async drop code generation (incomplete, async_drop, "1.88.0", Some(126482)), + /// Allows implementing `AsyncDrop`. For usage in standard library. + (incomplete, async_drop_lib, "1.88.0", Some(126482)), /// Allows async functions to be called from `dyn Trait`. (incomplete, async_fn_in_dyn_trait, "1.85.0", Some(133119)), /// Allows `#[track_caller]` on async functions. diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 3ba224723e365..ff9c29bf0d9ac 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -295,7 +295,6 @@ fn configure_and_expand( resolver.resolve_crate(&krate); CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate); - CStore::from_tcx(tcx).report_incompatible_async_drop_feature(tcx, &krate); krate } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 6bfb3769f2471..6cb3139f4dabc 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -461,27 +461,6 @@ impl CStore { } } - // Report about async drop types in dependency if async drop feature is disabled - pub fn report_incompatible_async_drop_feature(&self, tcx: TyCtxt<'_>, krate: &Crate) { - if tcx.features().async_drop() { - return; - } - for (_cnum, data) in self.iter_crate_data() { - if data.is_proc_macro_crate() { - continue; - } - if data.has_async_drops() { - let extern_crate = data.name(); - let local_crate = tcx.crate_name(LOCAL_CRATE); - tcx.dcx().emit_warn(errors::AsyncDropTypesInDependency { - span: krate.spans.inner_span.shrink_to_lo(), - extern_crate, - local_crate, - }); - } - } - } - pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 548c56a97bc05..26be56f281cdb 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -2022,10 +2022,6 @@ impl CrateMetadata { self.root.header.hash } - pub(crate) fn has_async_drops(&self) -> bool { - self.root.tables.adt_async_destructor.len > 0 - } - fn num_def_ids(&self) -> usize { self.root.tables.def_keys.size() } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 10c532b436aa1..b779e60310b29 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1648,7 +1648,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && let hir::ItemKind::Impl(impl_) = item.kind && let Some(trait_) = impl_.of_trait && let Some(def_id) = trait_.trait_def_id() - && self.tcx.is_lang_item(def_id, hir::LangItem::Drop) + && (self.tcx.is_lang_item(def_id, hir::LangItem::Drop) + || self.tcx.is_lang_item(def_id, hir::LangItem::AsyncDrop)) { return; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index acbed7a9eed81..a2c951b71a95f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -501,6 +501,7 @@ symbols! { async_closure, async_drop, async_drop_in_place, + async_drop_lib, async_fn, async_fn_in_dyn_trait, async_fn_in_trait, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f3d..6dd3f15c2740e 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -189,7 +189,7 @@ use core::clone::CloneToUninit; use core::cmp::Ordering; use core::error::{self, Error}; use core::fmt; -use core::future::Future; +use core::future::{AsyncDrop, Future}; use core::hash::{Hash, Hasher}; use core::marker::{Tuple, Unsize}; use core::mem::{self, SizedTypeProperties}; @@ -1669,6 +1669,23 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box { } } +#[unstable(feature = "async_drop_lib", issue = "126482")] +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> AsyncDrop for Box { + #[inline] + async fn drop(self: Pin<&mut Self>) { + // the T in the Box is dropped by the compiler before the destructor is run + + let ptr = self.0; + + unsafe { + let layout = Layout::for_value_raw(ptr.as_ptr()); + if layout.size() != 0 { + self.1.deallocate(From::from(ptr.cast()), layout); + } + } + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index c091e496c5090..1180cc6818d14 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -164,6 +164,7 @@ // tidy-alphabetical-start #![feature(allocator_internals)] #![feature(allow_internal_unstable)] +#![feature(async_drop_lib)] #![feature(cfg_sanitize)] #![feature(const_precise_live_drops)] #![feature(coroutine_trait)] diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index c48c3f2ba281e..f9a2895f43626 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -1,4 +1,4 @@ -#![unstable(feature = "async_drop", issue = "126482")] +#![unstable(feature = "async_drop_lib", issue = "126482")] #[allow(unused_imports)] use core::future::Future; @@ -24,7 +24,7 @@ use crate::task::{Context, Poll}; /// are `Copy` get implicitly duplicated by the compiler, making it very /// hard to predict when, and how often destructors will be executed. As such, /// these types cannot have destructors. -#[unstable(feature = "async_drop", issue = "126482")] +#[unstable(feature = "async_drop_lib", issue = "126482")] #[lang = "async_drop"] pub trait AsyncDrop { /// Executes the async destructor for this type. @@ -41,7 +41,7 @@ pub trait AsyncDrop { } /// Async drop. -#[unstable(feature = "async_drop", issue = "126482")] +#[unstable(feature = "async_drop_lib", issue = "126482")] #[lang = "async_drop_in_place"] pub async unsafe fn async_drop_in_place(_to_drop: *mut T) { // Code here does not matter - this is replaced by the diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 2b16a568b4031..f2ca88c1c296a 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -20,7 +20,7 @@ mod pending; mod poll_fn; mod ready; -#[unstable(feature = "async_drop", issue = "126482")] +#[unstable(feature = "async_drop_lib", issue = "126482")] pub use async_drop::{AsyncDrop, async_drop_in_place}; #[stable(feature = "into_future", since = "1.64.0")] pub use into_future::IntoFuture; diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs index 4fa84384d9bdd..a7b574b433509 100644 --- a/src/tools/miri/tests/pass/async-drop.rs +++ b/src/tools/miri/tests/pass/async-drop.rs @@ -6,7 +6,7 @@ // please consider modifying rustc's async drop test at // `tests/ui/async-await/async-drop/async-drop-initial.rs`. -#![feature(async_drop, impl_trait_in_assoc_type)] +#![feature(async_drop, async_drop_lib, impl_trait_in_assoc_type)] #![allow(incomplete_features, dead_code)] // FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests diff --git a/tests/ui/async-await/async-drop/async-drop-future-from-future.rs b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs index 44dcbd14f4886..7eafd4d28accd 100644 --- a/tests/ui/async-await/async-drop/async-drop-future-from-future.rs +++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs @@ -3,7 +3,7 @@ // Future `bar` with internal async drop `Foo` will have async drop itself. // And we trying to drop this future in sync context (`block_on` func) -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs index 417dce62dba90..919507e1a87ad 100644 --- a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs +++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs @@ -3,7 +3,7 @@ // Future `bar` with internal async drop `Foo` will have async drop itself. // And we trying to drop this future in sync context (`block_on` func) -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] //@ edition: 2021 diff --git a/tests/ui/async-await/async-drop/async-drop-glue-array.rs b/tests/ui/async-await/async-drop/async-drop-glue-array.rs index 21c08da2337ea..9152fefdcc84c 100644 --- a/tests/ui/async-await/async-drop/async-drop-glue-array.rs +++ b/tests/ui/async-await/async-drop/async-drop-glue-array.rs @@ -3,7 +3,7 @@ // struct `Foo` has both sync and async drop. // Struct `Complex` contains three `Foo` fields and has complex async drop glue. -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/async-drop-glue-generic.rs b/tests/ui/async-await/async-drop/async-drop-glue-generic.rs index 9e8ae13032423..7284caaa366a8 100644 --- a/tests/ui/async-await/async-drop/async-drop-glue-generic.rs +++ b/tests/ui/async-await/async-drop/async-drop-glue-generic.rs @@ -2,7 +2,7 @@ //@ check-run-results // struct `Foo` has both sync and async drop. -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/async-drop-glue.rs b/tests/ui/async-await/async-drop/async-drop-glue.rs index dc4bb527a51ca..c05da3f9288eb 100644 --- a/tests/ui/async-await/async-drop/async-drop-glue.rs +++ b/tests/ui/async-await/async-drop/async-drop-glue.rs @@ -3,7 +3,7 @@ // struct `Foo` has both sync and async drop. // Struct `Complex` contains three `Foo` fields and has complex async drop glue. -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/async-drop-initial.rs b/tests/ui/async-await/async-drop/async-drop-initial.rs index cd33c143fba01..5aebd893734cc 100644 --- a/tests/ui/async-await/async-drop/async-drop-initial.rs +++ b/tests/ui/async-await/async-drop/async-drop-initial.rs @@ -5,7 +5,7 @@ // 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)] +#![feature(async_drop, async_drop_lib, impl_trait_in_assoc_type)] #![allow(incomplete_features, dead_code)] //@ edition: 2021 diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop.rs b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs index 772a853fe1eff..b3a8cfc6cd8bb 100644 --- a/tests/ui/async-await/async-drop/async-drop-middle-drop.rs +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs @@ -3,7 +3,7 @@ // Test async drop of coroutine `bar` (with internal async drop), // stopped at the middle of execution, with AsyncDrop object Foo active. -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] //@ edition: 2021 diff --git a/tests/ui/async-await/async-drop/async-drop-open.rs b/tests/ui/async-await/async-drop/async-drop-open.rs index d14eff874aeac..495c51ad4b7c7 100644 --- a/tests/ui/async-await/async-drop/async-drop-open.rs +++ b/tests/ui/async-await/async-drop/async-drop-open.rs @@ -3,7 +3,7 @@ // struct `Foo` has both sync and async drop. // Struct `Complex` contains three `Foo` fields and one of them is moved out. -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/async-drop.rs b/tests/ui/async-await/async-drop/async-drop.rs index 2d9c5934be5d7..011d35e9ab37d 100644 --- a/tests/ui/async-await/async-drop/async-drop.rs +++ b/tests/ui/async-await/async-drop/async-drop.rs @@ -3,7 +3,7 @@ // struct `Foo` has both sync and async drop. // Sync version is called in sync context, async version is called in async function. -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs index 1729599f7b3f3..909a512e598ff 100644 --- a/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs +++ b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs @@ -1,6 +1,6 @@ //@ edition:2021 -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] pub struct HasDrop; diff --git a/tests/ui/async-await/async-drop/dependency-dropped.rs b/tests/ui/async-await/async-drop/dependency-dropped.rs deleted file mode 100644 index d7f415e19aadf..0000000000000 --- a/tests/ui/async-await/async-drop/dependency-dropped.rs +++ /dev/null @@ -1,37 +0,0 @@ -//@ run-pass -//@ check-run-results -//@ revisions: with_feature without_feature -//@ aux-build:async-drop-dep.rs -//@ edition:2021 - -#![cfg_attr(with_feature, feature(async_drop))] -//[without_feature]~^ WARN found async drop types in dependency `async_drop_dep`, but async_drop feature is disabled for `dependency_dropped` - -#![allow(incomplete_features)] - -extern crate async_drop_dep; - -use async_drop_dep::MongoDrop; -use std::pin::pin; -use std::task::{Context, Poll, Waker}; -use std::future::Future; - -async fn asyncdrop() { - let _ = MongoDrop::new().await; -} - -pub fn block_on(fut: impl Future) -> T { - let mut fut = pin!(fut); - let ctx = &mut Context::from_waker(Waker::noop()); - - loop { - match fut.as_mut().poll(ctx) { - Poll::Pending => {} - Poll::Ready(t) => break t, - } - } -} - -fn main() { - let _ = block_on(asyncdrop()); -} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout deleted file mode 100644 index 7aaf70c12d608..0000000000000 --- a/tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout +++ /dev/null @@ -1 +0,0 @@ -Async drop diff --git a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout deleted file mode 100644 index 80eeeefc2220f..0000000000000 --- a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout +++ /dev/null @@ -1 +0,0 @@ -Sync drop diff --git a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr deleted file mode 100644 index 96a4572055c60..0000000000000 --- a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: found async drop types in dependency `async_drop_dep`, but async_drop feature is disabled for `dependency_dropped` - --> $DIR/dependency-dropped.rs:7:1 - | -LL | #![cfg_attr(with_feature, feature(async_drop))] - | ^ - | - = 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/deref-later-projection.rs b/tests/ui/async-await/async-drop/deref-later-projection.rs index baf81daf766b6..ae84f963fc117 100644 --- a/tests/ui/async-await/async-drop/deref-later-projection.rs +++ b/tests/ui/async-await/async-drop/deref-later-projection.rs @@ -3,7 +3,7 @@ //@ build-pass //@ edition:2021 #![crate_type = "lib"] -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::{future::AsyncDrop, pin::Pin}; diff --git a/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs index bd0a95eb1e497..6892bc8a704b1 100644 --- a/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs +++ b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs @@ -2,7 +2,7 @@ // Ex-ICE: #140974 #![crate_type = "lib"] #![allow(incomplete_features)] -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] use core::future::AsyncDrop; async fn fun(_: HasIncompleteAsyncDrop) {} diff --git a/tests/ui/async-await/async-drop/ex-ice-132103.rs b/tests/ui/async-await/async-drop/ex-ice-132103.rs index 3d32cb14fb061..fc5b018e48834 100644 --- a/tests/ui/async-await/async-drop/ex-ice-132103.rs +++ b/tests/ui/async-await/async-drop/ex-ice-132103.rs @@ -3,7 +3,7 @@ //! Fixed when re-work async drop to shim drop glue coroutine scheme. //@ compile-flags: -Zvalidate-mir -Zinline-mir=yes //@ edition: 2018 -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use core::future::{async_drop_in_place, Future}; diff --git a/tests/ui/async-await/async-drop/inside_box.rs b/tests/ui/async-await/async-drop/inside_box.rs new file mode 100644 index 0000000000000..b9e9eb4b772c3 --- /dev/null +++ b/tests/ui/async-await/async-drop/inside_box.rs @@ -0,0 +1,106 @@ +//@ run-pass +//@ check-run-results + +#![feature(async_drop)] +#![feature(async_drop_lib)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + // added box here + let _ = Box::new(Foo::new(7)); + } + println!("Middle"); + block_on(bar(10)); + println!("Done") +} + +async fn bar(ident_base: usize) { + // added box here + let _ = Box::new(Foo::new(ident_base)); +} + +fn block_on(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/inside_box.run.stdout b/tests/ui/async-await/async-drop/inside_box.run.stdout new file mode 100644 index 0000000000000..cb7d0b0fea59d --- /dev/null +++ b/tests/ui/async-await/async-drop/inside_box.run.stdout @@ -0,0 +1,6 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::async drop() : 10 +Done diff --git a/tests/ui/async-await/async-drop/live-dead-storage3.rs b/tests/ui/async-await/async-drop/live-dead-storage3.rs index d9fba5759f73d..9bbde5376c402 100644 --- a/tests/ui/async-await/async-drop/live-dead-storage3.rs +++ b/tests/ui/async-await/async-drop/live-dead-storage3.rs @@ -3,7 +3,7 @@ //@ edition:2024 //@ check-pass -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] type BoxFuture = std::pin::Pin>>; diff --git a/tests/ui/async-await/async-drop/live-dead-storage4.rs b/tests/ui/async-await/async-drop/live-dead-storage4.rs index d927cb96674fc..92c3fd0299306 100644 --- a/tests/ui/async-await/async-drop/live-dead-storage4.rs +++ b/tests/ui/async-await/async-drop/live-dead-storage4.rs @@ -3,7 +3,7 @@ //@ edition:2024 //@ check-pass -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] #![allow(non_snake_case)] diff --git a/tests/ui/async-await/async-drop/open-drop-error.rs b/tests/ui/async-await/async-drop/open-drop-error.rs index 1d97eee521094..3f3c7d8f1b24a 100644 --- a/tests/ui/async-await/async-drop/open-drop-error.rs +++ b/tests/ui/async-await/async-drop/open-drop-error.rs @@ -1,7 +1,7 @@ //@ compile-flags: -Zmir-enable-passes=+DataflowConstProp //@ edition: 2021 //@ build-pass -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/open-drop-error2.rs b/tests/ui/async-await/async-drop/open-drop-error2.rs index b2a7b68190ec1..62e4b1728f47b 100644 --- a/tests/ui/async-await/async-drop/open-drop-error2.rs +++ b/tests/ui/async-await/async-drop/open-drop-error2.rs @@ -1,6 +1,6 @@ //@compile-flags: -Zvalidate-mir -Zinline-mir=yes --crate-type=lib -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::{ diff --git a/tests/ui/async-await/async-drop/type-parameter.rs b/tests/ui/async-await/async-drop/type-parameter.rs index dde5f9f8e6444..6e9af4a3e055a 100644 --- a/tests/ui/async-await/async-drop/type-parameter.rs +++ b/tests/ui/async-await/async-drop/type-parameter.rs @@ -1,7 +1,7 @@ //@ edition: 2024 // ex-ice: #140500 #![crate_type = "lib"] -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![expect(incomplete_features)] use std::future::AsyncDrop; struct A; diff --git a/tests/ui/async-await/async-drop/unexpected-sort.rs b/tests/ui/async-await/async-drop/unexpected-sort.rs index 659e21eb24119..b29f963561c19 100644 --- a/tests/ui/async-await/async-drop/unexpected-sort.rs +++ b/tests/ui/async-await/async-drop/unexpected-sort.rs @@ -3,7 +3,7 @@ #![crate_type = "lib"] #![allow(incomplete_features)] #![allow(non_camel_case_types)] -#![feature(async_drop)] +#![feature(async_drop_lib)] use std::future::AsyncDrop; struct a; impl Drop for a { //~ ERROR: not all trait items implemented, missing: `drop` diff --git a/tests/ui/feature-gates/feature-gate-async-drop-lib.rs b/tests/ui/feature-gates/feature-gate-async-drop-lib.rs new file mode 100644 index 0000000000000..c7abf1dbaff66 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop-lib.rs @@ -0,0 +1,16 @@ +//@ edition: 2021 + +use std::future::AsyncDrop; //~ ERROR use of unstable library feature `async_drop_lib` +use std::pin::Pin; + +struct Foo {} + +impl Drop for Foo { + fn drop(&mut self) {} +} + +impl AsyncDrop for Foo { //~ ERROR use of unstable library feature `async_drop_lib` + async fn drop(self: Pin<&mut Self>) {} //~ ERROR use of unstable library feature `async_drop_lib` +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-async-drop-lib.stderr b/tests/ui/feature-gates/feature-gate-async-drop-lib.stderr new file mode 100644 index 0000000000000..9397d425fa187 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop-lib.stderr @@ -0,0 +1,33 @@ +error[E0658]: use of unstable library feature `async_drop_lib` + --> $DIR/feature-gate-async-drop-lib.rs:3:5 + | +LL | use std::future::AsyncDrop; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 for more information + = help: add `#![feature(async_drop_lib)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `async_drop_lib` + --> $DIR/feature-gate-async-drop-lib.rs:13:5 + | +LL | async fn drop(self: Pin<&mut Self>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 for more information + = help: add `#![feature(async_drop_lib)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `async_drop_lib` + --> $DIR/feature-gate-async-drop-lib.rs:12:6 + | +LL | impl AsyncDrop for Foo { + | ^^^^^^^^^ + | + = note: see issue #126482 for more information + = help: add `#![feature(async_drop_lib)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-async-drop.rs b/tests/ui/feature-gates/feature-gate-async-drop.rs index 20511b1d8ee86..c7abf1dbaff66 100644 --- a/tests/ui/feature-gates/feature-gate-async-drop.rs +++ b/tests/ui/feature-gates/feature-gate-async-drop.rs @@ -1,6 +1,6 @@ //@ edition: 2021 -use std::future::AsyncDrop; //~ ERROR use of unstable library feature `async_drop` +use std::future::AsyncDrop; //~ ERROR use of unstable library feature `async_drop_lib` use std::pin::Pin; struct Foo {} @@ -9,8 +9,8 @@ impl Drop for Foo { fn drop(&mut self) {} } -impl AsyncDrop for Foo { //~ ERROR use of unstable library feature `async_drop` - async fn drop(self: Pin<&mut Self>) {} //~ ERROR use of unstable library feature `async_drop` +impl AsyncDrop for Foo { //~ ERROR use of unstable library feature `async_drop_lib` + async fn drop(self: Pin<&mut Self>) {} //~ ERROR use of unstable library feature `async_drop_lib` } fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-async-drop.stderr b/tests/ui/feature-gates/feature-gate-async-drop.stderr index 80f6228a16fe3..cd873d26dbbe2 100644 --- a/tests/ui/feature-gates/feature-gate-async-drop.stderr +++ b/tests/ui/feature-gates/feature-gate-async-drop.stderr @@ -1,31 +1,31 @@ -error[E0658]: use of unstable library feature `async_drop` +error[E0658]: use of unstable library feature `async_drop_lib` --> $DIR/feature-gate-async-drop.rs:3:5 | LL | use std::future::AsyncDrop; | ^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #126482 for more information - = help: add `#![feature(async_drop)]` to the crate attributes to enable + = help: add `#![feature(async_drop_lib)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: use of unstable library feature `async_drop` +error[E0658]: use of unstable library feature `async_drop_lib` --> $DIR/feature-gate-async-drop.rs:13:5 | LL | async fn drop(self: Pin<&mut Self>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #126482 for more information - = help: add `#![feature(async_drop)]` to the crate attributes to enable + = help: add `#![feature(async_drop_lib)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: use of unstable library feature `async_drop` +error[E0658]: use of unstable library feature `async_drop_lib` --> $DIR/feature-gate-async-drop.rs:12:6 | LL | impl AsyncDrop for Foo { | ^^^^^^^^^ | = note: see issue #126482 for more information - = help: add `#![feature(async_drop)]` to the crate attributes to enable + = help: add `#![feature(async_drop_lib)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 3 previous errors From a5631d1e2f8507ed6e328ad9dc0fb1904acd4b31 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Sat, 26 Jul 2025 15:48:13 +0700 Subject: [PATCH 2/2] Async drop support for dyn traits --- .../src/debuginfo/metadata.rs | 3 + compiler/rustc_codegen_ssa/src/mir/block.rs | 101 ++++- .../rustc_const_eval/src/interpret/call.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 2 + compiler/rustc_middle/src/ty/instance.rs | 24 ++ compiler/rustc_middle/src/ty/vtable.rs | 28 +- .../rustc_mir_transform/src/coroutine/drop.rs | 389 +++++++++++------- .../rustc_mir_transform/src/elaborate_drop.rs | 89 ++-- compiler/rustc_monomorphize/src/collector.rs | 16 + compiler/rustc_span/src/symbol.rs | 2 + library/alloc/src/boxed.rs | 23 +- library/core/src/lib.rs | 1 + library/std/src/lib.rs | 2 + tests/codegen-llvm/debug-vtable.rs | 31 +- .../virtual-function-elimination.rs | 10 +- .../async-drop/async-drop-box-allocator.rs | 2 +- .../async-await/async-drop/async-drop-box.rs | 2 +- .../async-drop/async-drop-box.run.stdout | 2 +- .../async-drop/async-drop-middle-drop-dyn.rs | 128 ++++++ .../async-drop-middle-drop-dyn.run.stdout | 5 + .../async-drop/async-drop-middle-drop-sync.rs | 57 +++ .../async-drop-middle-drop-sync.run.stdout | 3 + .../async-drop/async-without-sync.rs | 2 +- .../async-drop/foreign-fundamental.rs | 4 +- .../async-drop/foreign-fundamental.stderr | 11 +- .../feature-gate-async-drop-lib.stderr | 2 +- ...le-supertraits-modulo-binder-vtable.stderr | 2 + ...rtraits-modulo-normalization-vtable.stderr | 2 + .../ui/traits/vtable/multiple-markers.stderr | 4 + tests/ui/traits/vtable/vtable-diamond.stderr | 4 + .../vtable/vtable-dyn-incompatible.stderr | 1 + .../traits/vtable/vtable-multi-level.stderr | 15 + tests/ui/traits/vtable/vtable-multiple.stderr | 3 + tests/ui/traits/vtable/vtable-vacant.stderr | 2 + 34 files changed, 742 insertions(+), 231 deletions(-) create mode 100644 tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.rs create mode 100644 tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.run.stdout create mode 100644 tests/ui/async-await/async-drop/async-drop-middle-drop-sync.rs create mode 100644 tests/ui/async-await/async-drop/async-drop-middle-drop-sync.run.stdout diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 0e9dbfba658d2..67e01d8a8acaa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1475,6 +1475,9 @@ fn build_vtable_type_di_node<'ll, 'tcx>( ty::VtblEntry::MetadataDropInPlace => { ("drop_in_place".to_string(), void_pointer_type_di_node) } + ty::VtblEntry::MetadataAsyncDropInPlace => { + ("async_drop_in_place".to_string(), void_pointer_type_di_node) + } ty::VtblEntry::Method(_) => { // Note: This code does not try to give a proper name to each method // because their might be multiple methods with the same name diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e96590441fa42..cde642bd4f485 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -624,7 +624,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // \-------/ // let virtual_drop = Instance { - def: ty::InstanceKind::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function + def: ty::InstanceKind::Virtual( + drop_fn.def_id(), + ty::COMMON_VTABLE_ENTRIES_DROPINPLACE, + ), args: drop_fn.args, }; debug!("ty = {:?}", ty); @@ -678,6 +681,87 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) } + #[tracing::instrument(level = "trace", skip(self, helper, bx))] + fn codegen_async_drop_dyn( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + bx: &mut Bx, + source_info: &mir::SourceInfo, + dropee: mir::Place<'tcx>, + destination: mir::Place<'tcx>, + target: mir::BasicBlock, + unwind: mir::UnwindAction, + mergeable_succ: bool, + ) -> MergingSucc { + let ty = dropee.ty(self.mir, bx.tcx()).ty; + let ty = self.monomorphize(ty); + let drop_fn = Instance::resolve_async_drop_in_place_dyn(bx.tcx(), ty).unwrap(); + let place = self.codegen_place(bx, dropee.as_ref()); + + let (args1, args2); + let mut args = if let Some(llextra) = place.val.llextra { + args2 = [place.val.llval, llextra]; + &args2[..] + } else { + args1 = [place.val.llval]; + &args1[..] + }; + + let (drop_fn, fn_abi, drop_instance) = match ty.kind() { + ty::Dynamic(_, _, ty::Dyn) => { + let virtual_drop = Instance { + def: ty::InstanceKind::Virtual( + drop_fn.def_id(), + ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE, + ), + args: drop_fn.args, + }; + let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); + let vtable = args[1]; + // Truncate vtable off of args list + args = &args[..1]; + ( + meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE) + .get_optional_fn(bx, vtable, ty, fn_abi), + fn_abi, + virtual_drop, + ) + } + _ => bug!("Non-virtual call for async drop terminator (ty is not dyn or dyn*)"), + }; + // We generate a null check for the drop_fn. This saves a bunch of relocations being + // generated for no-op drops. + // FIXME: do we need it for dyn async drop? + { + let is_not_null = bx.append_sibling_block("is_not_null"); + let llty = bx.fn_ptr_backend_type(fn_abi); + let null = bx.const_null(llty); + let non_null = + bx.icmp(base::bin_op_to_icmp_predicate(mir::BinOp::Ne, false), drop_fn, null); + bx.cond_br(non_null, is_not_null, helper.llbb_with_cleanup(self, target)); + bx.switch_to_block(is_not_null); + self.set_debug_loc(bx, *source_info); + } + assert!(!fn_abi.ret.is_indirect()); + let mut llargs = Vec::new(); + let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs); + assert!(llargs.is_empty()); + + helper.do_call( + self, + bx, + fn_abi, + drop_fn, + args, + Some((return_dest, target)), + unwind, + &[], + Some(drop_instance), + CallKind::Normal, + false, + ) + } + fn codegen_assert_terminator( &mut self, helper: TerminatorCodegenHelper<'tcx>, @@ -898,6 +982,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (instance, mut llfn) = match *callee.layout.ty.kind() { ty::FnDef(def_id, generic_args) => { + if bx.tcx().is_lang_item(def_id, LangItem::AsyncDropInPlaceDyn) { + let mir::Operand::Move(dropee) = args[0].node else { + bug!(); + }; + return self.codegen_async_drop_dyn( + helper, + bx, + &terminator.source_info, + dropee, + destination, + target.unwrap(), + unwind, + mergeable_succ, + ); + } let instance = ty::Instance::expect_resolve( bx.tcx(), bx.typing_env(), diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index a0160d1188d08..9d7d6095b02b9 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -812,6 +812,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // only supports calling `VtblEntry::Method`; it would choke on a `MetadataDropInPlace`. So // instead we do the virtual call stuff ourselves. It's easier here than in `eval_fn_call` // since we can just get a place of the underlying type and use `mplace_to_ref`. + // FIXME: Support AsyncDrop (async_drop_in_place) here let place = match place.layout.ty.kind() { ty::Dynamic(data, _, ty::Dyn) => { // Dropping a trait object. Need to find actual drop fn. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 75c04b23ed6e8..b90bc8c7008df 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -193,6 +193,8 @@ language_item_table! { Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None; 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); + AsyncDropInPlaceDyn, sym::async_drop_in_place_dyn, async_drop_in_place_dyn_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropInPlaceSelf, sym::async_drop_in_place_self, async_drop_in_place_self_fn, Target::Fn, GenericRequirement::None; 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); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 3a51f79f12169..b2066ce3293d4 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -753,6 +753,30 @@ impl<'tcx> Instance<'tcx> { ) } + // async_drop_in_place, with return value converted into Pin>, for usage in vtable. + // May returns None for core lib compilation (before lang item definition in alloc lib). + pub fn resolve_async_drop_in_place_dyn( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + ) -> Option> { + // `async_drop_in_place::{closure}` is a special case, because such coroutine is its async drop future itself. + // To drop this coroutine we need to continue poll it. + // So, it async drop constructor function in vtable returns its address (from argument), boxed and pinned. + let (item, args) = if ty.is_async_drop_in_place_coroutine(tcx) { + (LangItem::AsyncDropInPlaceSelf, tcx.mk_args(&[])) + } else { + (LangItem::AsyncDropInPlaceDyn, tcx.mk_args(&[ty.into()])) + }; + let Some(def_id) = tcx.lang_items().get(item) else { return None }; + Some(Instance::expect_resolve( + tcx, + ty::TypingEnv::fully_monomorphized(), + def_id, + args, + ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP), + )) + } + pub fn resolve_async_drop_in_place_poll( tcx: TyCtxt<'tcx>, def_id: DefId, diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 6fc19c82342f9..66952276a8627 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -13,6 +13,8 @@ use crate::ty::{self, Instance, TraitRef, Ty, TyCtxt}; pub enum VtblEntry<'tcx> { /// destructor of this type (used in vtable header) MetadataDropInPlace, + /// async destructor of this type, function returns Pin>> (used in vtable header) + MetadataAsyncDropInPlace, /// layout size of this type (used in vtable header) MetadataSize, /// layout align of this type (used in vtable header) @@ -31,6 +33,7 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> { // so we implement this manually. match self { VtblEntry::MetadataDropInPlace => write!(f, "MetadataDropInPlace"), + VtblEntry::MetadataAsyncDropInPlace => write!(f, "MetadataAsyncDropInPlace"), VtblEntry::MetadataSize => write!(f, "MetadataSize"), VtblEntry::MetadataAlign => write!(f, "MetadataAlign"), VtblEntry::Vacant => write!(f, "Vacant"), @@ -42,13 +45,18 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> { // Needs to be associated with the `'tcx` lifetime impl<'tcx> TyCtxt<'tcx> { - pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] = - &[VtblEntry::MetadataDropInPlace, VtblEntry::MetadataSize, VtblEntry::MetadataAlign]; + pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] = &[ + VtblEntry::MetadataDropInPlace, + VtblEntry::MetadataAsyncDropInPlace, + VtblEntry::MetadataSize, + VtblEntry::MetadataAlign, + ]; } pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0; -pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 1; -pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2; +pub const COMMON_VTABLE_ENTRIES_ASYNCDROPINPLACE: usize = 1; +pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 2; +pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 3; // Note that we don't have access to a self type here, this has to be purely based on the trait (and // supertrait) definitions. That means we can't call into the same vtable_entries code since that @@ -129,6 +137,18 @@ pub(super) fn vtable_allocation_provider<'tcx>( Scalar::from_maybe_pointer(Pointer::null(), &tcx) } } + VtblEntry::MetadataAsyncDropInPlace => { + if tcx.features().async_drop() + && ty.needs_async_drop(tcx, ty::TypingEnv::fully_monomorphized()) + && let Some(instance) = ty::Instance::resolve_async_drop_in_place_dyn(tcx, ty) + { + let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT); + let fn_ptr = Pointer::from(fn_alloc_id); + Scalar::from_pointer(fn_ptr, &tcx) + } else { + Scalar::from_maybe_pointer(Pointer::null(), &tcx) + } + } VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size), VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size), VtblEntry::Vacant => continue, diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 1a314e029f4a7..cdc3404d346b0 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -1,5 +1,7 @@ //! Drops and async drops related logic for coroutine transformation pass +use rustc_span::source_map::Spanned; + use super::*; // Fix return Poll::Pending statement into Poll<()>::Pending for async drop function @@ -248,6 +250,197 @@ pub(super) fn has_expandable_async_drops<'tcx>( return false; } +// Compute Poll<> (aka Poll with void return) and add such a local +fn add_poll_unit_local<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + source_info: SourceInfo, +) -> (Place<'tcx>, Ty<'tcx>) { + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, source_info.span)); + let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + let poll_decl = LocalDecl::new(poll_enum, source_info.span); + (Place::from(body.local_decls.push(poll_decl)), poll_enum) +} + +fn prepare_yield_value<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + coroutine_kind: hir::CoroutineKind, + source_info: SourceInfo, +) -> Operand<'tcx> { + if matches!(coroutine_kind, CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) { + // For AsyncGen we need `yield Poll::Pending` + let full_yield_ty = body.yield_ty().unwrap(); + let ty::Adt(_poll_adt, args) = *full_yield_ty.kind() else { bug!() }; + let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { bug!() }; + let yield_ty = args.type_at(0); + Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + const_: Const::Unevaluated( + UnevaluatedConst::new( + tcx.require_lang_item(LangItem::AsyncGenPending, source_info.span), + tcx.mk_args(&[yield_ty.into()]), + ), + full_yield_ty, + ), + user_ty: None, + })) + } else { + // value needed only for return-yields or gen-coroutines, so just const false here + Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::from_bool(tcx, false), + })) + } +} + +/// Generate async drop polling +fn generate_async_drop_polling<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + bb: BasicBlock, + context_mut_ref: Ty<'tcx>, + coroutine_kind: hir::CoroutineKind, + is_dropline_bb: bool, +) -> BasicBlock { + let TerminatorKind::Drop { + place: _, + target, + unwind, + replace: _, + drop: dropline, + async_fut: Some(fut_local), + } = body[bb].terminator().kind + else { + bug!() + }; + let fut_place = Place::from(fut_local); + let fut_ty = fut_place.ty(&body.local_decls, tcx).ty; + + // poll-code: + // state_call_drop: + // #bb_pin: fut_pin = Pin::new_unchecked(&mut fut) + // #bb_call: rv = call fut.poll() (or future_drop_poll(fut) for internal future drops) + // #bb_check: match (rv) + // pending => return rv (yield) + // ready => *continue_bb|drop_bb* + + let source_info = body[bb].terminator.as_ref().unwrap().source_info; + let (poll_unit_place, poll_enum) = add_poll_unit_local(tcx, body, source_info); + + // First state-loop yield for mainline + let context_ref_place = + Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span))); + let arg = Rvalue::Use(Operand::Move(Place::from(CTX_ARG))); + body[bb].statements.push(Statement::new( + source_info, + StatementKind::Assign(Box::new((context_ref_place, arg))), + )); + // `kind` replaced later to Yield + let bb_yield = insert_term_block(body, TerminatorKind::Unreachable); + let (bb_pin, fut_pin_place) = + build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue); + let bb_check = + build_poll_switch(tcx, body, poll_enum, &poll_unit_place, &fut_pin_place, target, bb_yield); + let bb_call = build_poll_call( + tcx, + body, + &poll_unit_place, + bb_check, + &fut_pin_place, + fut_ty, + &context_ref_place, + unwind, + ); + + // Second state-loop yield for transition to dropline. + // When coroutine async drop started, we continue to poll this 'fut_place', + // but target block (where to go after) is changed to 'dropline', + // where other required drops will be executed. + // FIXME: Maybe, we can optimize it with bool flag like 'IWasAskedToDropTransition'. + // And perform 'if IWasAskedToDropTransition goto dropline else goto target' after poll-loop. + let mut bb_dropline_transition: Option = None; + let mut bb_dropline_yield: Option = None; + let mut bb_dropline_call: Option = None; + let mut dropline_context_ref: Option> = None; + + if !is_dropline_bb { + let context_ref_place2: Place<'_> = + Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span))); + // `kind` replaced later to Yield + let bb_drop_yield = insert_term_block(body, TerminatorKind::Unreachable); + let (bb_drop_pin, fut_pin_place2) = + build_pin_fut(tcx, body, fut_place, UnwindAction::Continue); + let bb_drop_switch = build_poll_switch( + tcx, + body, + poll_enum, + &poll_unit_place, + &fut_pin_place2, + dropline.unwrap(), + bb_drop_yield, + ); + let bb_drop_call = build_poll_call( + tcx, + body, + &poll_unit_place, + bb_drop_switch, + &fut_pin_place2, + fut_ty, + &context_ref_place2, + unwind, + ); + bb_dropline_transition = Some(bb_drop_pin); + bb_dropline_yield = Some(bb_drop_yield); + bb_dropline_call = Some(bb_drop_call); + dropline_context_ref = Some(context_ref_place2); + } + let value = prepare_yield_value(tcx, body, coroutine_kind, source_info); + + use rustc_middle::mir::AssertKind::ResumedAfterDrop; + let bb_panic = insert_panic_block(tcx, body, ResumedAfterDrop(coroutine_kind)); + + if is_dropline_bb { + body[bb_yield].terminator_mut().kind = TerminatorKind::Yield { + value: value.clone(), + resume: bb_panic, + resume_arg: context_ref_place, + drop: Some(bb_pin), + }; + } else { + body[bb_yield].terminator_mut().kind = TerminatorKind::Yield { + value: value.clone(), + resume: bb_pin, + resume_arg: context_ref_place, + drop: bb_dropline_transition, + }; + body[bb_dropline_yield.unwrap()].terminator_mut().kind = TerminatorKind::Yield { + value, + resume: bb_panic, + resume_arg: dropline_context_ref.unwrap(), + drop: bb_dropline_transition, + }; + } + // fix bb_pin -> bb_call link + if let TerminatorKind::Call { ref mut target, .. } = body[bb_pin].terminator_mut().kind { + *target = Some(bb_call); + } else { + bug!() + } + if !is_dropline_bb { + // fix bb_drop_pin -> bb_drop_call link + if let TerminatorKind::Call { ref mut target, .. } = + body[bb_dropline_transition.unwrap()].terminator_mut().kind + { + *target = bb_dropline_call; + } else { + bug!() + } + } + bb_pin +} + /// Expand Drop terminator for async drops into mainline poll-switch and dropline poll-switch pub(super) fn expand_async_drops<'tcx>( tcx: TyCtxt<'tcx>, @@ -278,14 +471,15 @@ pub(super) fn expand_async_drops<'tcx>( remove_asyncness(&mut body[bb]); continue; } - let TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } = + let TerminatorKind::Drop { place, target: _, unwind, replace: _, drop, async_fut } = body[bb].terminator().kind else { continue; }; + let dropee = place; - let place_ty = place.ty(&body.local_decls, tcx).ty; - if place_ty == coroutine_ty { + let dropee_ty = dropee.ty(&body.local_decls, tcx).ty; + if dropee_ty == coroutine_ty { remove_asyncness(&mut body[bb]); continue; } @@ -302,162 +496,41 @@ pub(super) fn expand_async_drops<'tcx>( continue; } - let fut_place = Place::from(fut_local); - let fut_ty = fut_place.ty(&body.local_decls, tcx).ty; - - // poll-code: - // state_call_drop: - // #bb_pin: fut_pin = Pin::new_unchecked(&mut fut) - // #bb_call: rv = call fut.poll() (or future_drop_poll(fut) for internal future drops) - // #bb_check: match (rv) - // pending => return rv (yield) - // ready => *continue_bb|drop_bb* - - let source_info = body[bb].terminator.as_ref().unwrap().source_info; - - // Compute Poll<> (aka Poll with void return) - let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, source_info.span)); - let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); - let poll_decl = LocalDecl::new(poll_enum, source_info.span); - let poll_unit_place = Place::from(body.local_decls.push(poll_decl)); - - // First state-loop yield for mainline - let context_ref_place = - Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span))); - let arg = Rvalue::Use(Operand::Move(Place::from(CTX_ARG))); - body[bb].statements.push(Statement::new( - source_info, - StatementKind::Assign(Box::new((context_ref_place, arg))), - )); - let yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield - let (pin_bb, fut_pin_place) = - build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue); - let switch_block = build_poll_switch( - tcx, - body, - poll_enum, - &poll_unit_place, - &fut_pin_place, - target, - yield_block, - ); - let call_bb = build_poll_call( + let bb_pin = generate_async_drop_polling( tcx, body, - &poll_unit_place, - switch_block, - &fut_pin_place, - fut_ty, - &context_ref_place, - unwind, + bb, + context_mut_ref, + coroutine_kind, + is_dropline_bb, ); - - // Second state-loop yield for transition to dropline (when coroutine async drop started) - let mut dropline_transition_bb: Option = None; - let mut dropline_yield_bb: Option = None; - let mut dropline_context_ref: Option> = None; - let mut dropline_call_bb: Option = None; - if !is_dropline_bb { - let context_ref_place2: Place<'_> = Place::from( - body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span)), - ); - let drop_yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield - let (pin_bb2, fut_pin_place2) = - build_pin_fut(tcx, body, fut_place, UnwindAction::Continue); - let drop_switch_block = build_poll_switch( + if dropee_ty.is_trait() { + let source_info = body[bb].terminator.as_ref().unwrap().source_info; + // dyn processing is a bit different: + // Drop terminator is replaced to Call terminator for 'async_drop_in_place_dyn' func + // (providing async drop future as a `Pin>`). + // Call terminator will be codegen'ed into vtable call to set async future to poll. + let async_drop_dyn_def_id = + tcx.require_lang_item(LangItem::AsyncDropInPlaceDyn, source_info.span); + let func = Operand::function_handle( tcx, - body, - poll_enum, - &poll_unit_place, - &fut_pin_place2, - drop.unwrap(), - drop_yield_block, + async_drop_dyn_def_id, + [dropee_ty.into()], + source_info.span, ); - let drop_call_bb = build_poll_call( - tcx, - body, - &poll_unit_place, - drop_switch_block, - &fut_pin_place2, - fut_ty, - &context_ref_place2, + body[bb].terminator_mut().kind = TerminatorKind::Call { + func, + args: [Spanned { node: Operand::Move(dropee), span: source_info.span }].into(), + destination: Place::from(fut_local), + target: Some(bb_pin), unwind, - ); - dropline_transition_bb = Some(pin_bb2); - dropline_yield_bb = Some(drop_yield_block); - dropline_context_ref = Some(context_ref_place2); - dropline_call_bb = Some(drop_call_bb); - } - - let value = - if matches!(coroutine_kind, CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) - { - // For AsyncGen we need `yield Poll::Pending` - let full_yield_ty = body.yield_ty().unwrap(); - let ty::Adt(_poll_adt, args) = *full_yield_ty.kind() else { bug!() }; - let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { bug!() }; - let yield_ty = args.type_at(0); - Operand::Constant(Box::new(ConstOperand { - span: source_info.span, - const_: Const::Unevaluated( - UnevaluatedConst::new( - tcx.require_lang_item(LangItem::AsyncGenPending, source_info.span), - tcx.mk_args(&[yield_ty.into()]), - ), - full_yield_ty, - ), - user_ty: None, - })) - } else { - // value needed only for return-yields or gen-coroutines, so just const here - Operand::Constant(Box::new(ConstOperand { - span: source_info.span, - user_ty: None, - const_: Const::from_bool(tcx, false), - })) - }; - - use rustc_middle::mir::AssertKind::ResumedAfterDrop; - let panic_bb = insert_panic_block(tcx, body, ResumedAfterDrop(coroutine_kind)); - - if is_dropline_bb { - body[yield_block].terminator_mut().kind = TerminatorKind::Yield { - value: value.clone(), - resume: panic_bb, - resume_arg: context_ref_place, - drop: Some(pin_bb), - }; - } else { - body[yield_block].terminator_mut().kind = TerminatorKind::Yield { - value: value.clone(), - resume: pin_bb, - resume_arg: context_ref_place, - drop: dropline_transition_bb, - }; - body[dropline_yield_bb.unwrap()].terminator_mut().kind = TerminatorKind::Yield { - value, - resume: panic_bb, - resume_arg: dropline_context_ref.unwrap(), - drop: dropline_transition_bb, + call_source: CallSource::Misc, + fn_span: source_info.span, }; - } - - if let TerminatorKind::Call { ref mut target, .. } = body[pin_bb].terminator_mut().kind { - *target = Some(call_bb); + continue; } else { - bug!() - } - if !is_dropline_bb { - if let TerminatorKind::Call { ref mut target, .. } = - body[dropline_transition_bb.unwrap()].terminator_mut().kind - { - *target = dropline_call_bb; - } else { - bug!() - } + body[bb].terminator_mut().kind = TerminatorKind::Goto { target: bb_pin }; } - - body[bb].terminator_mut().kind = TerminatorKind::Goto { target: pin_bb }; } } @@ -630,6 +703,7 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>( // Take the coroutine info out of the body, since the drop shim is // not a coroutine body itself; it just has its drop built out of it. let _ = body.coroutine.take(); + body.arg_count = 2; // Restoring context arg if it was removed for Gen coroutine FixReturnPendingVisitor { tcx }.visit_body(&mut body); @@ -675,15 +749,7 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>( body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info); make_coroutine_state_argument_indirect(tcx, &mut body); - - match transform.coroutine_kind { - // Iterator::next doesn't accept a pinned argument, - // unlike for all other coroutine kinds. - CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {} - _ => { - make_coroutine_state_argument_pinned(tcx, &mut body); - } - } + make_coroutine_state_argument_pinned(tcx, &mut body); // Make sure we remove dead blocks to remove // unrelated code from the resume part of the function @@ -715,6 +781,11 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>( body.basic_blocks = BasicBlocks::new(basic_blocks); body.var_debug_info.clear(); + // Async drop for coroutine has args for standart async poll. + // Restoring args (may be removed for Gen coroutine) + body.arg_count = 2; + body.local_decls[CTX_ARG].ty = Ty::new_task_context(tcx); + // Keeping return value and args body.local_decls.truncate(1 + body.arg_count); diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 4f3c53d761f10..a121a38ab674e 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -202,7 +202,7 @@ where } // Generates three blocks: - // * #1:pin_obj_bb: call Pin::new_unchecked(&mut obj) + // * #1:start_bb: call Pin::new_unchecked(&mut obj) // * #2:call_drop_bb: fut = call obj.() OR call async_drop_in_place(obj) // * #3:drop_term_bb: drop (obj, fut, ...) // We keep async drop unexpanded to poll-loop here, to expand it later, at StateTransform - @@ -221,7 +221,7 @@ where let tcx = self.tcx(); let span = self.source_info.span; - let pin_obj_bb = bb.unwrap_or_else(|| { + let start_bb = bb.unwrap_or_else(|| { self.elaborator.patch().new_block(BasicBlockData::new( Some(Terminator { // Temporary terminator, will be replaced by patch @@ -231,6 +231,55 @@ where false, )) }); + let fill_storage_deads = |patch: &mut MirPatch<'tcx>, local: Local| { + // StorageDead(fut) in self.succ block (at the begin) + patch.add_statement( + Location { block: succ, statement_index: 0 }, + StatementKind::StorageDead(local), + ); + // StorageDead(fut) in unwind block (at the begin) + if let Unwind::To(block) = unwind { + patch.add_statement( + Location { block, statement_index: 0 }, + StatementKind::StorageDead(local), + ); + } + // StorageDead(fut) in dropline block (at the begin) + if let Some(block) = dropline { + patch.add_statement( + Location { block, statement_index: 0 }, + StatementKind::StorageDead(local), + ); + } + }; + + if drop_ty.is_trait() { + assert!(!call_destructor_only); + // For dyn call we construct async drop future using Drop terminator, + // later expanded in codegen into vtable call + // fut_ty here is Pin>> + let drop_fn_def_id = tcx.require_lang_item(LangItem::AsyncDropInPlaceDyn, span); + let trait_args = tcx.mk_args(&[drop_ty.into()]); + let sig = tcx.fn_sig(drop_fn_def_id).instantiate(tcx, trait_args); + let sig = tcx.instantiate_bound_regions_with_erased(sig); + let fut_ty = sig.output(); + let fut = Place::from(self.new_temp(fut_ty)); + self.elaborator.patch().patch_terminator( + start_bb, + TerminatorKind::Drop { + place, + target: succ, + unwind: unwind.into_action(), + replace: false, + drop: dropline, + async_fut: Some(fut.local), + }, + ); + let term_loc = self.elaborator.terminator_loc(start_bb); + self.elaborator.patch().add_statement(term_loc, StatementKind::StorageLive(fut.local)); + fill_storage_deads(self.elaborator.patch(), fut.local); + return start_bb; + } let (fut_ty, drop_fn_def_id, trait_args) = if call_destructor_only { // Resolving obj.() @@ -270,7 +319,7 @@ where "AsyncDrop type without correct `async fn drop(...)`.", ); self.elaborator.patch().patch_terminator( - pin_obj_bb, + start_bb, TerminatorKind::Drop { place, target: succ, @@ -280,14 +329,13 @@ where async_fut: None, }, ); - return pin_obj_bb; + return start_bb; }; let drop_fn = Ty::new_fn_def(tcx, drop_fn_def_id, trait_args); let sig = drop_fn.fn_sig(tcx); let sig = tcx.instantiate_bound_regions_with_erased(sig); (sig.output(), drop_fn_def_id, trait_args) } else { - // Resolving async_drop_in_place function for drop_ty let drop_fn_def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, span); let trait_args = tcx.mk_args(&[drop_ty.into()]); let sig = tcx.fn_sig(drop_fn_def_id).instantiate(tcx, trait_args); @@ -297,11 +345,11 @@ where let fut = Place::from(self.new_temp(fut_ty)); - // #1:pin_obj_bb >>> obj_ref = &mut obj + // #1:start_bb >>> obj_ref = &mut obj let obj_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, drop_ty); let obj_ref_place = Place::from(self.new_temp(obj_ref_ty)); - let term_loc = self.elaborator.terminator_loc(pin_obj_bb); + let term_loc = self.elaborator.terminator_loc(start_bb); self.elaborator.patch().add_assign( term_loc, obj_ref_place, @@ -382,29 +430,9 @@ where }, ); - // StorageDead(fut) in self.succ block (at the begin) - self.elaborator.patch().add_statement( - Location { block: self.succ, statement_index: 0 }, - StatementKind::StorageDead(fut.local), - ); - // StorageDead(fut) in unwind block (at the begin) - if let Unwind::To(block) = unwind { - self.elaborator.patch().add_statement( - Location { block, statement_index: 0 }, - StatementKind::StorageDead(fut.local), - ); - } - // StorageDead(fut) in dropline block (at the begin) - if let Some(block) = dropline { - self.elaborator.patch().add_statement( - Location { block, statement_index: 0 }, - StatementKind::StorageDead(fut.local), - ); - } - - // #1:pin_obj_bb >>> call Pin::new_unchecked(&mut obj) + // #1:start_bb >>> call Pin::new_unchecked(&mut obj) self.elaborator.patch().patch_terminator( - pin_obj_bb, + start_bb, TerminatorKind::Call { func: pin_obj_new_unchecked_fn, args: [dummy_spanned(Operand::Move(obj_ref_place))].into(), @@ -415,7 +443,8 @@ where fn_span: span, }, ); - pin_obj_bb + fill_storage_deads(self.elaborator.patch(), fut.local); + start_bb } fn build_drop(&mut self, bb: BasicBlock) { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 26ca8518434b3..2c303f1ab1a6b 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -866,6 +866,18 @@ fn visit_drop_use<'tcx>( visit_instance_use(tcx, instance, is_direct_call, source, output); } +fn visit_async_drop_use<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + is_direct_call: bool, + source: Span, + output: &mut MonoItems<'tcx>, +) { + if let Some(instance) = Instance::resolve_async_drop_in_place_dyn(tcx, ty) { + visit_instance_use(tcx, instance, is_direct_call, source, output); + } +} + /// For every call of this function in the visitor, make sure there is a matching call in the /// `mentioned_items` pass! fn visit_fn_use<'tcx>( @@ -1152,6 +1164,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( .iter() .filter_map(|entry| match entry { VtblEntry::MetadataDropInPlace + | VtblEntry::MetadataAsyncDropInPlace | VtblEntry::MetadataSize | VtblEntry::MetadataAlign | VtblEntry::Vacant => None, @@ -1174,6 +1187,9 @@ fn create_mono_items_for_vtable_methods<'tcx>( if impl_ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) { visit_drop_use(tcx, impl_ty, false, source, output); } + if impl_ty.needs_async_drop(tcx, ty::TypingEnv::fully_monomorphized()) { + visit_async_drop_use(tcx, impl_ty, false, source, output); + } } /// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a2c951b71a95f..5f18da1771fd6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -501,6 +501,8 @@ symbols! { async_closure, async_drop, async_drop_in_place, + async_drop_in_place_dyn, + async_drop_in_place_self, async_drop_lib, async_fn, async_fn_in_dyn_trait, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 6dd3f15c2740e..0bdad39b8021a 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -189,7 +189,7 @@ use core::clone::CloneToUninit; use core::cmp::Ordering; use core::error::{self, Error}; use core::fmt; -use core::future::{AsyncDrop, Future}; +use core::future::{AsyncDrop, Future, async_drop_in_place}; use core::hash::{Hash, Hasher}; use core::marker::{Tuple, Unsize}; use core::mem::{self, SizedTypeProperties}; @@ -1686,6 +1686,27 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> AsyncDrop for Box { } } +/// Async drop for usage in vtable +#[unstable(feature = "async_drop_lib", issue = "126482")] +#[lang = "async_drop_in_place_dyn"] +pub unsafe fn async_drop_in_place_dyn( + to_drop: *mut T, +) -> Pin>> { + Box::pin(unsafe { async_drop_in_place(to_drop) }) +} + +/// Async drop for usage in vtable, for dropping `async_drop_in_place::{closure}` coroutine. +/// `async_drop_in_place::{closure}` is a special case, because to async drop such a coroutine, +/// we just need to continue poll it. +/// So vtable call should return argument as a return value. +#[unstable(feature = "async_drop_lib", issue = "126482")] +#[lang = "async_drop_in_place_self"] +pub unsafe fn async_drop_in_place_self( + to_drop: *mut dyn Future, +) -> Pin>> { + Box::into_pin(unsafe { Box::from_raw(to_drop) }) +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index d5bce6ad233e9..ef7ebc5db34e5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -138,6 +138,7 @@ #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(async_drop_lib)] #![feature(auto_traits)] #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 0c53753064724..ebc3ad3665ba4 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -256,6 +256,7 @@ #![deny(ffi_unwind_calls)] // std may use features in a platform-specific way #![allow(unused_features)] +#![allow(incomplete_features)] // // Features: #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] @@ -276,6 +277,7 @@ #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_experimental_arch)] +#![feature(async_drop_lib)] #![feature(autodiff)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] diff --git a/tests/codegen-llvm/debug-vtable.rs b/tests/codegen-llvm/debug-vtable.rs index 8a7b1cc3c4bcc..fccecc08db3e5 100644 --- a/tests/codegen-llvm/debug-vtable.rs +++ b/tests/codegen-llvm/debug-vtable.rs @@ -27,33 +27,36 @@ // NONMSVC: !DIGlobalVariable(name: "::{vtable}" // MSVC: !DIGlobalVariable(name: "impl$::vtable$" -// NONMSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]], -// MSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$::vtable_type$", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]], +// NONMSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", {{.*}} size: {{384|192}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]], +// MSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$::vtable_type$", {{.*}} size: {{384|192}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]], // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "async_drop_in_place", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}}) // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{256|128}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method5", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{320|160}}) // CHECK: ![[FOO_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", // NONMSVC: !DIGlobalVariable(name: ">::{vtable}" // MSVC: !DIGlobalVariable(name: "impl$ >::vtable$" -// NONMSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: ">::{vtable_type}", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], -// MSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$ >::vtable_type$", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], +// NONMSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: ">::{vtable_type}", {{.*}}, size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], +// MSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$ >::vtable_type$", {{.*}}, size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "async_drop_in_place", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{256|128}}) // NONMSVC: !DIGlobalVariable(name: "::{vtable}" // MSVC: !DIGlobalVariable(name: "impl$::vtable$" -// NONMSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], -// MSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$::vtable_type$", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], +// NONMSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "::{vtable_type}", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], +// MSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$::vtable_type$", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]], // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}}) -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "async_drop_in_place", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}}) +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}}) // NONMSVC: !DIGlobalVariable(name: ">)>>::{vtable}" // MSVC: !DIGlobalVariable(name: "impl$,assoc$ > > > > > > > > >::vtable$" diff --git a/tests/codegen-llvm/virtual-function-elimination.rs b/tests/codegen-llvm/virtual-function-elimination.rs index 26604478c11a2..93c31b2a1786e 100644 --- a/tests/codegen-llvm/virtual-function-elimination.rs +++ b/tests/codegen-llvm/virtual-function-elimination.rs @@ -62,24 +62,24 @@ impl V for S { } fn taking_t(t: &dyn T) -> i32 { - // CHECK: @llvm.type.checked.load({{.*}}, i32 24, metadata !"[[MANGLED_TYPE0:[0-9a-zA-Z_]+]]") + // CHECK: @llvm.type.checked.load({{.*}}, i32 32, metadata !"[[MANGLED_TYPE0:[0-9a-zA-Z_]+]]") t.used() } fn taking_rc_t(t: Rc) -> i32 { - // CHECK: @llvm.type.checked.load({{.*}}, i32 40, metadata !"[[MANGLED_TYPE0:[0-9a-zA-Z_]+]]") + // CHECK: @llvm.type.checked.load({{.*}}, i32 48, metadata !"[[MANGLED_TYPE0:[0-9a-zA-Z_]+]]") t.by_rc() } fn taking_u(u: &dyn U) -> i32 { - // CHECK: @llvm.type.checked.load({{.*}}, i32 64, metadata !"[[MANGLED_TYPE1:[0-9a-zA-Z_]+]]") - // CHECK: @llvm.type.checked.load({{.*}}, i32 24, metadata !"[[MANGLED_TYPE1:[0-9a-zA-Z_]+]]") + // CHECK: @llvm.type.checked.load({{.*}}, i32 72, metadata !"[[MANGLED_TYPE1:[0-9a-zA-Z_]+]]") // CHECK: @llvm.type.checked.load({{.*}}, i32 32, metadata !"[[MANGLED_TYPE1:[0-9a-zA-Z_]+]]") + // CHECK: @llvm.type.checked.load({{.*}}, i32 40, metadata !"[[MANGLED_TYPE1:[0-9a-zA-Z_]+]]") u.subtrait_used() + u.used() + u.used_through_sub_trait() } pub fn taking_v(v: &dyn V) -> i32 { - // CHECK: @llvm.type.checked.load({{.*}}, i32 24, metadata !"NtC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_28virtual_function_elimination1V") + // CHECK: @llvm.type.checked.load({{.*}}, i32 32, metadata !"NtC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_28virtual_function_elimination1V") v.public_function() } diff --git a/tests/ui/async-await/async-drop/async-drop-box-allocator.rs b/tests/ui/async-await/async-drop/async-drop-box-allocator.rs index 86ebf8a0ffd13..463e4c06a02c3 100644 --- a/tests/ui/async-await/async-drop/async-drop-box-allocator.rs +++ b/tests/ui/async-await/async-drop/async-drop-box-allocator.rs @@ -4,7 +4,7 @@ // It's used as the allocator of a `Box` which is conditionally moved out of. // Sync version is called in sync context, async version is called in async function. -#![feature(async_drop, allocator_api)] +#![feature(async_drop, async_drop_lib, allocator_api)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/async-drop-box.rs b/tests/ui/async-await/async-drop/async-drop-box.rs index 0a6ed412863a2..f8ec60ee5cc20 100644 --- a/tests/ui/async-await/async-drop/async-drop-box.rs +++ b/tests/ui/async-await/async-drop/async-drop-box.rs @@ -7,7 +7,7 @@ //@ known-bug: #143658 // async version is never actually called -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] use std::mem::ManuallyDrop; diff --git a/tests/ui/async-await/async-drop/async-drop-box.run.stdout b/tests/ui/async-await/async-drop/async-drop-box.run.stdout index a2dab2ba992b5..cb7d0b0fea59d 100644 --- a/tests/ui/async-await/async-drop/async-drop-box.run.stdout +++ b/tests/ui/async-await/async-drop/async-drop-box.run.stdout @@ -2,5 +2,5 @@ Foo::new() : 7 Foo::drop() : 7 Middle Foo::new() : 10 -Foo::drop() : 10 +Foo::async drop() : 10 Done diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.rs b/tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.rs new file mode 100644 index 0000000000000..3c58b653cb1b1 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.rs @@ -0,0 +1,128 @@ +//@ run-pass +//@ check-run-results +// Test async drop of coroutine `bar` (with internal async drop), +// stopped at the middle of execution, with AsyncDrop object Foo active. + +#![feature(async_drop, async_drop_lib)] +#![allow(incomplete_features)] + +//@ edition: 2021 + +use std::mem::ManuallyDrop; + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + println!("Foo::new({})", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("Foo::drop({})", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + println!("Foo::async drop({})", self.my_resource_handle); + } +} + +fn main() { + block_on_and_drop_in_the_middle(bar(10)); + println!("done") +} + +pub struct MiddleFuture { + first_call: bool, +} +impl MiddleFuture { + fn create() -> Box + Unpin> { + Box::new(MiddleFuture { first_call: true }) + } +} + +impl Drop for MiddleFuture { + fn drop(&mut self) { + println!("MiddleFuture::drop()"); + } +} + +impl AsyncDrop for MiddleFuture { + async fn drop(self: Pin<&mut Self>) { + println!("MiddleFuture::async drop()"); + } +} + +impl Future for MiddleFuture { + type Output = (); + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + if self.first_call { + println!("MiddleFuture first poll"); + self.first_call = false; + Poll::Pending + } else { + println!("MiddleFuture Ready"); + Poll::Ready(()) + } + } +} + +async fn bar(ident_base: usize) { + let middle = MiddleFuture::create(); + let mut _first = Foo::new(ident_base); + middle.await; // Hanging `bar` future before Foo drop +} + +fn block_on_and_drop_in_the_middle(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let poll1 = fut.as_mut().poll(&mut context); + assert!(poll1.is_pending()); + + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.run.stdout b/tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.run.stdout new file mode 100644 index 0000000000000..7941519898034 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop-dyn.run.stdout @@ -0,0 +1,5 @@ +Foo::new(10) +MiddleFuture first poll +MiddleFuture::async drop() +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop-sync.rs b/tests/ui/async-await/async-drop/async-drop-middle-drop-sync.rs new file mode 100644 index 0000000000000..1d755308aa96a --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop-sync.rs @@ -0,0 +1,57 @@ +//@ run-pass +//@ check-run-results +// Test async drop of coroutine `bar` (with internal async drop), +// stopped at the middle of execution, with AsyncDrop object Foo active. + +//#![feature(async_drop, async_drop_lib)] +#![allow(incomplete_features)] + +//@ edition: 2021 + +use std::{ + future::Future, + pin::{pin, Pin}, + task::{Context, Poll, Waker}, +}; + +fn main() { + bar(10); + println!("done") +} + +pub struct MiddleFuture { + first_call: bool, +} +impl Drop for MiddleFuture { + fn drop(&mut self) { + println!("MiddleFuture::drop()"); + } +} +impl MiddleFuture { + fn create() -> Box + Unpin> { + Box::new(MiddleFuture { first_call: true }) + } +} + +impl Future for MiddleFuture { + type Output = (); + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + if self.first_call { + println!("MiddleFuture first poll"); + self.first_call = false; + Poll::Pending + } else { + println!("MiddleFuture Ready"); + Poll::Ready(()) + } + } +} + +fn bar(_ident_base: usize) { + let middle = MiddleFuture::create(); + let waker = Waker::noop(); + let mut context = Context::from_waker(&waker); + let mut fut = pin!(middle); + let poll1 = fut.as_mut().poll(&mut context); + assert!(poll1.is_pending()); +} diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop-sync.run.stdout b/tests/ui/async-await/async-drop/async-drop-middle-drop-sync.run.stdout new file mode 100644 index 0000000000000..59220d586b81a --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop-sync.run.stdout @@ -0,0 +1,3 @@ +MiddleFuture first poll +MiddleFuture::drop() +done diff --git a/tests/ui/async-await/async-drop/async-without-sync.rs b/tests/ui/async-await/async-drop/async-without-sync.rs index 8a748636cc783..99366b7a438f7 100644 --- a/tests/ui/async-await/async-drop/async-without-sync.rs +++ b/tests/ui/async-await/async-drop/async-without-sync.rs @@ -1,5 +1,5 @@ //@ edition: 2024 -#![feature(async_drop)] +#![feature(async_drop, async_drop_lib)] #![allow(incomplete_features)] #![crate_type = "lib"] diff --git a/tests/ui/async-await/async-drop/foreign-fundamental.rs b/tests/ui/async-await/async-drop/foreign-fundamental.rs index 1c192fccd9f0c..635bdfa679dcb 100644 --- a/tests/ui/async-await/async-drop/foreign-fundamental.rs +++ b/tests/ui/async-await/async-drop/foreign-fundamental.rs @@ -1,7 +1,7 @@ //@ edition: 2018 -#![feature(async_drop)] -//~^ WARN the feature `async_drop` is incomplete +#![allow(incomplete_features)] +#![feature(async_drop, async_drop_lib)] use std::future::AsyncDrop; use std::pin::Pin; diff --git a/tests/ui/async-await/async-drop/foreign-fundamental.stderr b/tests/ui/async-await/async-drop/foreign-fundamental.stderr index 7b52329ac99d1..85951479a7b29 100644 --- a/tests/ui/async-await/async-drop/foreign-fundamental.stderr +++ b/tests/ui/async-await/async-drop/foreign-fundamental.stderr @@ -1,12 +1,3 @@ -warning: the feature `async_drop` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/foreign-fundamental.rs:3:12 - | -LL | #![feature(async_drop)] - | ^^^^^^^^^^ - | - = note: see issue #126482 for more information - = note: `#[warn(incomplete_features)]` on by default - error[E0120]: the `AsyncDrop` trait may only be implemented for local structs, enums, and unions --> $DIR/foreign-fundamental.rs:11:20 | @@ -19,6 +10,6 @@ error[E0120]: the `AsyncDrop` trait may only be implemented for local structs, e LL | impl AsyncDrop for Pin { | ^^^^^^^^ must be a struct, enum, or union in the current crate -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0120`. diff --git a/tests/ui/feature-gates/feature-gate-async-drop-lib.stderr b/tests/ui/feature-gates/feature-gate-async-drop-lib.stderr index 9397d425fa187..dc1f3a5bb7ef9 100644 --- a/tests/ui/feature-gates/feature-gate-async-drop-lib.stderr +++ b/tests/ui/feature-gates/feature-gate-async-drop-lib.stderr @@ -12,7 +12,7 @@ error[E0658]: use of unstable library feature `async_drop_lib` --> $DIR/feature-gate-async-drop-lib.rs:13:5 | LL | async fn drop(self: Pin<&mut Self>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #126482 for more information = help: add `#![feature(async_drop_lib)]` to the crate attributes to enable diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr index 24fa1650ca14c..b16ed436f400a 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), @@ -12,6 +13,7 @@ LL | type First = dyn for<'a> Trait<&'static (), &'a ()>; error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method( as Supertrait<&()>>::_print_numbers - shim(reify)), diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr index 04b1afae7beca..1e056adb3066b 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(<() as Supertrait<()>>::_print_numbers), @@ -12,6 +13,7 @@ LL | impl Trait for () {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method( as Supertrait<()>>::_print_numbers - shim(reify)), diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr index 35dd3c2516d8b..f486ce2fef02b 100644 --- a/tests/ui/traits/vtable/multiple-markers.stderr +++ b/tests/ui/traits/vtable/multiple-markers.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::method), @@ -11,6 +12,7 @@ LL | impl A for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::method), @@ -22,6 +24,7 @@ LL | impl B for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::method), @@ -33,6 +36,7 @@ LL | impl C for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::method), diff --git a/tests/ui/traits/vtable/vtable-diamond.stderr b/tests/ui/traits/vtable/vtable-diamond.stderr index 4644bf339b17d..4dbeb76207f37 100644 --- a/tests/ui/traits/vtable/vtable-diamond.stderr +++ b/tests/ui/traits/vtable/vtable-diamond.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), @@ -11,6 +12,7 @@ LL | impl A for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), @@ -23,6 +25,7 @@ LL | impl B for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), @@ -35,6 +38,7 @@ LL | impl C for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), diff --git a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr index c80a763998b1e..3d442da81e33f 100644 --- a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr +++ b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method( as Iterator>::next - shim(reify)), diff --git a/tests/ui/traits/vtable/vtable-multi-level.stderr b/tests/ui/traits/vtable/vtable-multi-level.stderr index 961900aa3d291..dcb01d23c2baf 100644 --- a/tests/ui/traits/vtable/vtable-multi-level.stderr +++ b/tests/ui/traits/vtable/vtable-multi-level.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), @@ -11,6 +12,7 @@ LL | impl A for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_b), @@ -22,6 +24,7 @@ LL | impl B for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), @@ -36,6 +39,7 @@ LL | impl C for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_d), @@ -47,6 +51,7 @@ LL | impl D for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_e), @@ -58,6 +63,7 @@ LL | impl E for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_d), @@ -72,6 +78,7 @@ LL | impl F for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), @@ -93,6 +100,7 @@ LL | impl G for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_h), @@ -104,6 +112,7 @@ LL | impl H for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_i), @@ -115,6 +124,7 @@ LL | impl I for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_h), @@ -129,6 +139,7 @@ LL | impl J for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_k), @@ -140,6 +151,7 @@ LL | impl K for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_l), @@ -151,6 +163,7 @@ LL | impl L for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_k), @@ -165,6 +178,7 @@ LL | impl M for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_h), @@ -186,6 +200,7 @@ LL | impl N for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), diff --git a/tests/ui/traits/vtable/vtable-multiple.stderr b/tests/ui/traits/vtable/vtable-multiple.stderr index bae44148baa87..7a66c765d815d 100644 --- a/tests/ui/traits/vtable/vtable-multiple.stderr +++ b/tests/ui/traits/vtable/vtable-multiple.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), @@ -11,6 +12,7 @@ LL | impl A for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_b), @@ -22,6 +24,7 @@ LL | impl B for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), diff --git a/tests/ui/traits/vtable/vtable-vacant.stderr b/tests/ui/traits/vtable/vtable-vacant.stderr index ed8466f7f2ecd..a73d5bae778d3 100644 --- a/tests/ui/traits/vtable/vtable-vacant.stderr +++ b/tests/ui/traits/vtable/vtable-vacant.stderr @@ -1,5 +1,6 @@ error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a1), @@ -12,6 +13,7 @@ LL | impl A for S {} error: vtable entries: [ MetadataDropInPlace, + MetadataAsyncDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a1),