diff options
| author | Andrew Zhogin <andrew.zhogin@gmail.com> | 2024-08-26 16:45:15 +0300 |
|---|---|---|
| committer | Andrew Zhogin <andrew.zhogin@gmail.com> | 2025-04-28 16:23:13 +0700 |
| commit | c366756a8537ef998d32c1ef57098d5aae7ca92f (patch) | |
| tree | 9e2bb71eee25b90a0ddee1b15ed8ba339d5f0d9e /tests | |
| parent | 52c1838fa712ee60d35b0d8cb6d4df3225430176 (diff) | |
| download | rust-c366756a8537ef998d32c1ef57098d5aae7ca92f.tar.gz rust-c366756a8537ef998d32c1ef57098d5aae7ca92f.zip | |
AsyncDrop implementation using shim codegen of async_drop_in_place::{closure}, scoped async drop added.
Diffstat (limited to 'tests')
30 files changed, 1145 insertions, 89 deletions
diff --git a/tests/crashes/128695.rs b/tests/crashes/128695.rs deleted file mode 100644 index 661f427dc0e..00000000000 --- a/tests/crashes/128695.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: rust-lang/rust#128695 -//@ edition: 2021 - -use core::pin::{pin, Pin}; - -fn main() { - let fut = pin!(async { - let async_drop_fut = pin!(core::future::async_drop(async {})); - (async_drop_fut).await; - }); -} diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir index 0c8a17ff70b..b43af549b23 100644 --- a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir +++ b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir @@ -68,14 +68,18 @@ yields () } bb10: { - drop(_1) -> [return: bb11, unwind: bb12]; + drop(_1) -> [return: bb11, unwind: bb13, drop: bb12]; } bb11: { return; } - bb12 (cleanup): { + bb12: { + coroutine_drop; + } + + bb13 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir index 9070c95bca4..5623b6d64e9 100644 --- a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir @@ -51,14 +51,18 @@ yields () } bb6: { - drop(_1) -> [return: bb7, unwind: bb8]; + drop(_1) -> [return: bb7, unwind: bb9, drop: bb8]; } bb7: { return; } - bb8 (cleanup): { + bb8: { + coroutine_drop; + } + + bb9 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir index 8a584853e00..4d484b16b50 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir index c5f538e5ecd..ace780f773e 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir index c6721085eb2..f50ad689f44 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir index e295f9b3cf1..62d8adeedcb 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } 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 new file mode 100644 index 00000000000..44dcbd14f48 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs @@ -0,0 +1,101 @@ +//@ run-pass +//@ check-run-results +// 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)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +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(bar(10)); + println!("done") +} + +async fn baz(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); + baz(ident_base + 1).await; +} + +fn block_on<F>(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>) { + 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-future-from-future.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout new file mode 100644 index 00000000000..c2663b3f238 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout @@ -0,0 +1,5 @@ +Foo::new(10) +Foo::new(11) +Foo::async drop(11) +Foo::async drop(10) +done 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 new file mode 100644 index 00000000000..417dce62dba --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs @@ -0,0 +1,82 @@ +//@ run-pass +//@ check-run-results +// 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)] +#![allow(incomplete_features)] + +//@ edition: 2021 + +use std::{ + future::{Future, 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(bar(10)); + println!("done") +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +fn block_on<F>(fut: F) -> F::Output +where + F: Future, +{ + let mut fut = pin!(fut); + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + 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>) { + 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-future-in-sync-context.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout new file mode 100644 index 00000000000..ad0b083ca09 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout @@ -0,0 +1,3 @@ +Foo::new(10) +Foo::async drop(10) +done 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 new file mode 100644 index 00000000000..21c08da2337 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-array.rs @@ -0,0 +1,112 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and has complex async drop glue. + +#![feature(async_drop)] +#![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() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) { + let _vec: [Foo; 4] = [ + Foo::new(ident_base), + Foo::new(ident_base + 1), + Foo::new(ident_base + 2), + Foo::new(ident_base + 3) + ]; +} + +fn block_on<F>(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>) { + 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-glue-array.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout new file mode 100644 index 00000000000..56b61103cc4 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout @@ -0,0 +1,12 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::new() : 13 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Foo::async drop() : 13 +Done 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 new file mode 100644 index 00000000000..9e8ae130324 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-generic.rs @@ -0,0 +1,111 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. + +#![feature(async_drop)] +#![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() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(6, 10)); + } + println!("Done") +} + +async fn bar<T>(_arg: T, ident_base: usize) { + let _vec: [Foo; 4] = [ + Foo::new(ident_base), + Foo::new(ident_base + 1), + Foo::new(ident_base + 2), + Foo::new(ident_base + 3) + ]; +} + +fn block_on<F>(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>) { + 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-glue-generic.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout new file mode 100644 index 00000000000..56b61103cc4 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout @@ -0,0 +1,12 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::new() : 13 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Foo::async drop() : 13 +Done diff --git a/tests/ui/async-await/async-drop/async-drop-glue.rs b/tests/ui/async-await/async-drop/async-drop-glue.rs new file mode 100644 index 00000000000..dc4bb527a51 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue.rs @@ -0,0 +1,124 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and has complex async drop glue. + +#![feature(async_drop)] +#![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, +} + +#[allow(dead_code)] +struct Complex { + field1: Foo, + field2: Foo, + field3: Foo, +} + +impl Complex { + fn new(my_resource_handle: usize) -> Self { + myprintln("Complex::new()", my_resource_handle); + let field1 = Foo::new(my_resource_handle); + let field2 = Foo::new(my_resource_handle + 1); + let field3 = Foo::new(my_resource_handle + 2); + Complex { field1, field2, field3 } + } +} + +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() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) { + let _complex = Complex::new(ident_base); +} + +fn block_on<F>(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>) { + 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-glue.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue.run.stdout new file mode 100644 index 00000000000..e21a9dd34fa --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue.run.stdout @@ -0,0 +1,11 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Complex::new() : 10 +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Done diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop/async-drop-initial.rs index b1af81423ce..80b34840c8b 100644 --- a/tests/ui/async-await/async-drop.rs +++ b/tests/ui/async-await/async-drop/async-drop-initial.rs @@ -52,21 +52,20 @@ fn main() { let i = 13; let fut = pin!(async { - test_async_drop(Int(0), 0).await; - // FIXME(#63818): niches in coroutines are disabled. - // Some of these sizes should be smaller, as indicated in comments. - test_async_drop(AsyncInt(0), /*104*/ 112).await; - test_async_drop([AsyncInt(1), AsyncInt(2)], /*152*/ 168).await; - test_async_drop((AsyncInt(3), AsyncInt(4)), /*488*/ 528).await; - test_async_drop(5, 0).await; + test_async_drop(Int(0), 16).await; + test_async_drop(AsyncInt(0), 32).await; + test_async_drop([AsyncInt(1), AsyncInt(2)], 104).await; + test_async_drop((AsyncInt(3), AsyncInt(4)), 120).await; + test_async_drop(5, 16).await; let j = 42; - test_async_drop(&i, 0).await; - test_async_drop(&j, 0).await; - test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, /*1688*/ 1792).await; - test_async_drop(ManuallyDrop::new(AsyncInt(9)), 0).await; + test_async_drop(&i, 16).await; + test_async_drop(&j, 16).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, 168).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9)), 16).await; let foo = AsyncInt(10); - test_async_drop(AsyncReference { foo: &foo }, /*104*/ 112).await; + test_async_drop(AsyncReference { foo: &foo }, 32).await; + let _ = ManuallyDrop::new(foo); let foo = AsyncInt(11); test_async_drop( @@ -75,21 +74,22 @@ fn main() { let foo = AsyncInt(10); foo }, - /*120*/ 136, + 48, ) .await; - test_async_drop(AsyncEnum::A(AsyncInt(12)), /*680*/ 736).await; - test_async_drop(AsyncEnum::B(SyncInt(13)), /*680*/ 736).await; + test_async_drop(AsyncEnum::A(AsyncInt(12)), 104).await; + test_async_drop(AsyncEnum::B(SyncInt(13)), 104).await; - test_async_drop(SyncInt(14), /*16*/ 24).await; + test_async_drop(SyncInt(14), 16).await; test_async_drop( SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }, - /*3064*/ 3296, + 120, ) .await; - let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); + let mut ptr19 = mem::MaybeUninit::new(AsyncInt(19)); + let async_drop_fut = pin!(unsafe { async_drop_in_place(ptr19.as_mut_ptr()) }); test_idempotency(async_drop_fut).await; let foo = AsyncInt(20); @@ -101,11 +101,11 @@ fn main() { black_box(core::future::ready(())).await; foo }, - /*120*/ 136, + 48, ) .await; - test_async_drop(AsyncUnion { signed: 21 }, /*32*/ 40).await; + test_async_drop(AsyncUnion { signed: 21 }, 32).await; }); let res = fut.poll(&mut cx); assert_eq!(res, Poll::Ready(())); @@ -113,13 +113,14 @@ fn main() { struct AsyncInt(i32); +impl Drop for AsyncInt { + fn drop(&mut self) { + println!("AsyncInt::drop: {}", self.0); + } +} impl AsyncDrop for AsyncInt { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncInt::Dropper::poll: {}", self.0); - } + async fn drop(self: Pin<&mut Self>) { + println!("AsyncInt::Dropper::poll: {}", self.0); } } @@ -148,13 +149,15 @@ struct AsyncReference<'a> { foo: &'a AsyncInt, } -impl AsyncDrop for AsyncReference<'_> { - type Dropper<'a> = impl Future<Output = ()> where Self: 'a; +impl Drop for AsyncReference<'_> { + fn drop(&mut self) { + println!("AsyncReference::drop: {}", self.foo.0); + } +} - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncReference::Dropper::poll: {}", self.foo.0); - } +impl AsyncDrop for AsyncReference<'_> { + async fn drop(self: Pin<&mut Self>) { + println!("AsyncReference::Dropper::poll: {}", self.foo.0); } } @@ -166,13 +169,15 @@ struct AsyncStruct { b: AsyncInt, } -impl AsyncDrop for AsyncStruct { - type Dropper<'a> = impl Future<Output = ()>; +impl Drop for AsyncStruct { + fn drop(&mut self) { + println!("AsyncStruct::drop: {}", self.i); + } +} - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncStruct::Dropper::poll: {}", self.i); - } +impl AsyncDrop for AsyncStruct { + async fn drop(self: Pin<&mut Self>) { + println!("AsyncStruct::Dropper::poll: {}", self.i); } } @@ -181,23 +186,34 @@ enum AsyncEnum { B(SyncInt), } +impl Drop for AsyncEnum { + fn drop(&mut self) { + let new_self = match self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::drop: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::drop: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } +} impl AsyncDrop for AsyncEnum { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - let new_self = match &*self { - AsyncEnum::A(foo) => { - println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); - AsyncEnum::B(SyncInt(foo.0)) - } - AsyncEnum::B(foo) => { - println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); - AsyncEnum::A(AsyncInt(foo.0)) - } - }; - mem::forget(mem::replace(&mut *self, new_self)); - } + async fn drop(mut self: Pin<&mut Self>) { + let new_self = match &*self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); } } @@ -207,16 +223,21 @@ union AsyncUnion { unsigned: u32, } +impl Drop for AsyncUnion { + fn drop(&mut self) { + println!( + "AsyncUnion::drop: {}, {}", + unsafe { self.signed }, + unsafe { self.unsigned }, + ); + } +} impl AsyncDrop for AsyncUnion { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!( - "AsyncUnion::Dropper::poll: {}, {}", - unsafe { self.signed }, - unsafe { self.unsigned }, - ); - } + async fn drop(self: Pin<&mut Self>) { + println!( + "AsyncUnion::Dropper::poll: {}, {}", + unsafe { self.signed }, + unsafe { self.unsigned }, + ); } } diff --git a/tests/ui/async-await/async-drop.run.stdout b/tests/ui/async-await/async-drop/async-drop-initial.run.stdout index 9cae4331caf..9cae4331caf 100644 --- a/tests/ui/async-await/async-drop.run.stdout +++ b/tests/ui/async-await/async-drop/async-drop-initial.run.stdout 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 new file mode 100644 index 00000000000..772a853fe1e --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs @@ -0,0 +1,110 @@ +//@ 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)] +#![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 Future for MiddleFuture { + type Output = (); + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { + 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 { first_call: true }; + let mut _first = Foo::new(ident_base); + middle.await; // Hanging `bar` future before Foo drop +} + +fn block_on_and_drop_in_the_middle<F>(fut_unpin: F) -> F::Output +where + F: Future<Output = ()>, +{ + 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>) { + 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.run.stdout b/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout new file mode 100644 index 00000000000..60df7674d0a --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout @@ -0,0 +1,4 @@ +Foo::new(10) +MiddleFuture first poll +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-open.rs b/tests/ui/async-await/async-drop/async-drop-open.rs new file mode 100644 index 00000000000..d14eff874ae --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-open.rs @@ -0,0 +1,127 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and one of them is moved out. + +#![feature(async_drop)] +#![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, +} + +#[allow(dead_code)] +struct Complex { + field1: Foo, + field2: Foo, + field3: Foo, +} + +impl Complex { + fn new(my_resource_handle: usize) -> Self { + myprintln("Complex::new()", my_resource_handle); + let field1 = Foo::new(my_resource_handle); + let field2 = Foo::new(my_resource_handle + 1); + let field3 = Foo::new(my_resource_handle + 2); + Complex { field1, field2, field3 } + } +} + +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() { + { + let _ = Foo::new(7); + } + println!("Middle"); + // Inside field1 and field3 of Complex must be dropped (as async drop) + // field2 must be dropped here (as sync drop) + { + let _field2 = block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) -> Foo { + let complex = Complex::new(ident_base); + complex.field2 +} + +fn block_on<F>(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>) { + 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-open.run.stdout b/tests/ui/async-await/async-drop/async-drop-open.run.stdout new file mode 100644 index 00000000000..e72dfd8a743 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-open.run.stdout @@ -0,0 +1,11 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Complex::new() : 10 +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::async drop() : 10 +Foo::async drop() : 12 +Foo::drop() : 11 +Done diff --git a/tests/ui/async-await/async-drop/async-drop.rs b/tests/ui/async-await/async-drop/async-drop.rs new file mode 100644 index 00000000000..2d9c5934be5 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop.rs @@ -0,0 +1,105 @@ +//@ run-pass +//@ check-run-results +// 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)] +#![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() { + { + let _ = Foo::new(7); + } + println!("Middle"); + block_on(bar(10)); + println!("Done") +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +fn block_on<F>(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>) { + 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.run.stdout b/tests/ui/async-await/async-drop/async-drop.run.stdout new file mode 100644 index 00000000000..cb7d0b0fea5 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop.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/crashes/132103.rs b/tests/ui/async-await/async-drop/ex-ice-132103.rs index e2d8378efe6..3d32cb14fb0 100644 --- a/tests/crashes/132103.rs +++ b/tests/ui/async-await/async-drop/ex-ice-132103.rs @@ -1,6 +1,11 @@ -//@ known-bug: #132103 +//@ run-pass +//! This test used to ICE: rust-lang/rust#132103 +//! Fixed when re-work async drop to shim drop glue coroutine scheme. //@ compile-flags: -Zvalidate-mir -Zinline-mir=yes //@ edition: 2018 +#![feature(async_drop)] +#![allow(incomplete_features)] + use core::future::{async_drop_in_place, Future}; use core::mem::{self}; use core::pin::pin; @@ -18,5 +23,5 @@ fn main() { let fut = pin!(async { test_async_drop(test_async_drop(0)).await; }); - fut.poll(&mut cx); + let _ = fut.poll(&mut cx); } diff --git a/tests/ui/async-await/async-drop/ex-ice1.rs b/tests/ui/async-await/async-drop/ex-ice1.rs new file mode 100644 index 00000000000..f514c57097a --- /dev/null +++ b/tests/ui/async-await/async-drop/ex-ice1.rs @@ -0,0 +1,13 @@ +//! This test used to ICE: rust-lang/rust#128695 +//! Fixed when re-work async drop to shim drop glue coroutine scheme. +//@ edition: 2021 + +use core::pin::{pin, Pin}; + +fn main() { + let fut = pin!(async { + let async_drop_fut = pin!(core::future::async_drop(async {})); //~ ERROR: expected function, found module `core::future::async_drop` + //~^ ERROR: module `async_drop` is private + (async_drop_fut).await; + }); +} diff --git a/tests/ui/async-await/async-drop/ex-ice1.stderr b/tests/ui/async-await/async-drop/ex-ice1.stderr new file mode 100644 index 00000000000..68533edeb08 --- /dev/null +++ b/tests/ui/async-await/async-drop/ex-ice1.stderr @@ -0,0 +1,19 @@ +error[E0423]: expected function, found module `core::future::async_drop` + --> $DIR/ex-ice1.rs:9:35 + | +LL | let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not a function + +error[E0603]: module `async_drop` is private + --> $DIR/ex-ice1.rs:9:49 + | +LL | let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^ private module + | +note: the module `async_drop` is defined here + --> $SRC_DIR/core/src/future/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0423, E0603. +For more information about an error, try `rustc --explain E0423`. diff --git a/tests/ui/feature-gates/feature-gate-async-drop.rs b/tests/ui/feature-gates/feature-gate-async-drop.rs new file mode 100644 index 00000000000..20511b1d8ee --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop.rs @@ -0,0 +1,16 @@ +//@ edition: 2021 + +use std::future::AsyncDrop; //~ ERROR use of unstable library feature `async_drop` +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` + async fn drop(self: Pin<&mut Self>) {} //~ ERROR use of unstable library feature `async_drop` +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-async-drop.stderr b/tests/ui/feature-gates/feature-gate-async-drop.stderr new file mode 100644 index 00000000000..e795c3a3422 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop.stderr @@ -0,0 +1,33 @@ +error[E0658]: use of unstable library feature `async_drop` + --> $DIR/feature-gate-async-drop.rs:3:5 + | +LL | use std::future::AsyncDrop; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 <https://github.com/rust-lang/rust/issues/126482> for more information + = help: add `#![feature(async_drop)]` 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` + --> $DIR/feature-gate-async-drop.rs:13:5 + | +LL | async fn drop(self: Pin<&mut Self>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 <https://github.com/rust-lang/rust/issues/126482> for more information + = help: add `#![feature(async_drop)]` 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` + --> $DIR/feature-gate-async-drop.rs:12:6 + | +LL | impl AsyncDrop for Foo { + | ^^^^^^^^^ + | + = note: see issue #126482 <https://github.com/rust-lang/rust/issues/126482> for more information + = help: add `#![feature(async_drop)]` 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`. |
