From 3b9ec8e0d92d87c7b3e73c4ec60de0d5dfd67a9b Mon Sep 17 00:00:00 2001 From: zetanumbers Date: Tue, 13 Feb 2024 12:31:41 +0300 Subject: [PATCH 1/4] Add simple async drop glue generation Explainer: https://zetanumbers.github.io/book/async-drop-design.html https://github.com/rust-lang/rust/pull/121801 --- tests/pass/async-drop.rs | 191 +++++++++++++++++++++++++++++ tests/pass/async-drop.stack.stdout | 22 ++++ tests/pass/async-drop.tree.stdout | 22 ++++ 3 files changed, 235 insertions(+) create mode 100644 tests/pass/async-drop.rs create mode 100644 tests/pass/async-drop.stack.stdout create mode 100644 tests/pass/async-drop.tree.stdout diff --git a/tests/pass/async-drop.rs b/tests/pass/async-drop.rs new file mode 100644 index 0000000000..f16206f3db --- /dev/null +++ b/tests/pass/async-drop.rs @@ -0,0 +1,191 @@ +//@revisions: stack tree +//@compile-flags: -Zmiri-strict-provenance +//@[tree]compile-flags: -Zmiri-tree-borrows +#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)] +#![allow(incomplete_features, dead_code)] + +// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests +use core::future::{async_drop_in_place, AsyncDrop, Future}; +use core::hint::black_box; +use core::mem::{self, ManuallyDrop}; +use core::pin::{pin, Pin}; +use core::task::{Context, Poll, Waker}; + +async fn test_async_drop(x: T) { + let mut x = mem::MaybeUninit::new(x); + let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); + test_idempotency(dtor).await; +} + +fn test_idempotency(mut x: Pin<&mut T>) -> impl Future + '_ +where + T: Future, +{ + core::future::poll_fn(move |cx| { + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + Poll::Ready(()) + }) +} + +fn main() { + let waker = Waker::noop(); + let mut cx = Context::from_waker(&waker); + + let i = 13; + let fut = pin!(async { + test_async_drop(Int(0)).await; + test_async_drop(AsyncInt(0)).await; + test_async_drop([AsyncInt(1), AsyncInt(2)]).await; + test_async_drop((AsyncInt(3), AsyncInt(4))).await; + test_async_drop(5).await; + let j = 42; + test_async_drop(&i).await; + test_async_drop(&j).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9))).await; + + let foo = AsyncInt(10); + test_async_drop(AsyncReference { foo: &foo }).await; + + let foo = AsyncInt(11); + test_async_drop(|| { + black_box(foo); + let foo = AsyncInt(10); + foo + }) + .await; + + test_async_drop(AsyncEnum::A(AsyncInt(12))).await; + test_async_drop(AsyncEnum::B(SyncInt(13))).await; + + test_async_drop(SyncInt(14)).await; + test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }) + .await; + + let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); + test_idempotency(async_drop_fut).await; + + let foo = AsyncInt(20); + test_async_drop(async || { + black_box(foo); + let foo = AsyncInt(19); + // Await point there, but this is async closure so it's fine + black_box(core::future::ready(())).await; + foo + }) + .await; + + test_async_drop(AsyncUnion { signed: 21 }).await; + }); + let res = fut.poll(&mut cx); + assert_eq!(res, Poll::Ready(())); +} + +struct AsyncInt(i32); + +impl AsyncDrop for AsyncInt { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncInt::Dropper::poll: {}", self.0); + } + } +} + +struct SyncInt(i32); + +impl Drop for SyncInt { + fn drop(&mut self) { + println!("SyncInt::drop: {}", self.0); + } +} + +struct SyncThenAsync { + i: i32, + a: AsyncInt, + b: SyncInt, + c: AsyncInt, +} + +impl Drop for SyncThenAsync { + fn drop(&mut self) { + println!("SyncThenAsync::drop: {}", self.i); + } +} + +struct AsyncReference<'a> { + foo: &'a AsyncInt, +} + +impl AsyncDrop for AsyncReference<'_> { + type Dropper<'a> = impl Future where Self: 'a; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncReference::Dropper::poll: {}", self.foo.0); + } + } +} + +struct Int(i32); + +struct AsyncStruct { + i: i32, + a: AsyncInt, + b: AsyncInt, +} + +impl AsyncDrop for AsyncStruct { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncStruct::Dropper::poll: {}", self.i); + } + } +} + +enum AsyncEnum { + A(AsyncInt), + B(SyncInt), +} + +impl AsyncDrop for AsyncEnum { + type Dropper<'a> = impl Future; + + 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)); + } + } +} + +// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions +union AsyncUnion { + signed: i32, + unsigned: u32, +} + +impl AsyncDrop for AsyncUnion { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe { + self.unsigned + }); + } + } +} diff --git a/tests/pass/async-drop.stack.stdout b/tests/pass/async-drop.stack.stdout new file mode 100644 index 0000000000..9cae4331ca --- /dev/null +++ b/tests/pass/async-drop.stack.stdout @@ -0,0 +1,22 @@ +AsyncInt::Dropper::poll: 0 +AsyncInt::Dropper::poll: 1 +AsyncInt::Dropper::poll: 2 +AsyncInt::Dropper::poll: 3 +AsyncInt::Dropper::poll: 4 +AsyncStruct::Dropper::poll: 6 +AsyncInt::Dropper::poll: 7 +AsyncInt::Dropper::poll: 8 +AsyncReference::Dropper::poll: 10 +AsyncInt::Dropper::poll: 11 +AsyncEnum(A)::Dropper::poll: 12 +SyncInt::drop: 12 +AsyncEnum(B)::Dropper::poll: 13 +AsyncInt::Dropper::poll: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::Dropper::poll: 16 +SyncInt::drop: 17 +AsyncInt::Dropper::poll: 18 +AsyncInt::Dropper::poll: 19 +AsyncInt::Dropper::poll: 20 +AsyncUnion::Dropper::poll: 21, 21 diff --git a/tests/pass/async-drop.tree.stdout b/tests/pass/async-drop.tree.stdout new file mode 100644 index 0000000000..9cae4331ca --- /dev/null +++ b/tests/pass/async-drop.tree.stdout @@ -0,0 +1,22 @@ +AsyncInt::Dropper::poll: 0 +AsyncInt::Dropper::poll: 1 +AsyncInt::Dropper::poll: 2 +AsyncInt::Dropper::poll: 3 +AsyncInt::Dropper::poll: 4 +AsyncStruct::Dropper::poll: 6 +AsyncInt::Dropper::poll: 7 +AsyncInt::Dropper::poll: 8 +AsyncReference::Dropper::poll: 10 +AsyncInt::Dropper::poll: 11 +AsyncEnum(A)::Dropper::poll: 12 +SyncInt::drop: 12 +AsyncEnum(B)::Dropper::poll: 13 +AsyncInt::Dropper::poll: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::Dropper::poll: 16 +SyncInt::drop: 17 +AsyncInt::Dropper::poll: 18 +AsyncInt::Dropper::poll: 19 +AsyncInt::Dropper::poll: 20 +AsyncUnion::Dropper::poll: 21, 21 From da5dfc9cf676191f990edaacd24b6a4ada8b8790 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 11:42:10 +0200 Subject: [PATCH 2/4] ensure the ICE-to-file logic does not affect our test --- tests/panic/mir-validation.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/panic/mir-validation.rs b/tests/panic/mir-validation.rs index fe618f6c81..5c1519f2eb 100644 --- a/tests/panic/mir-validation.rs +++ b/tests/panic/mir-validation.rs @@ -1,4 +1,5 @@ //! Ensure that the MIR validator runs on Miri's input. +//@rustc-env:RUSTC_ICE=0 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" From 019f5a2eaa6ac0536212e53e521e005783be4a5f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 21 Apr 2024 10:26:28 +0200 Subject: [PATCH 3/4] the mir-validation ICE test behaves strangely on Windows hosts let's just disable it there, this code is not platform-dependent anyway --- src/diagnostics.rs | 4 ++-- tests/panic/mir-validation.rs | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index d44d04e9bf..0c0ac4c603 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -291,7 +291,7 @@ pub fn report_error<'tcx, 'mir>( ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer ) => { - ecx.handle_ice(); // print interpreter backtrace + ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This validation error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e) @@ -308,7 +308,7 @@ pub fn report_error<'tcx, 'mir>( InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..), ) => "post-monomorphization error", _ => { - ecx.handle_ice(); // print interpreter backtrace + ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e) diff --git a/tests/panic/mir-validation.rs b/tests/panic/mir-validation.rs index 5c1519f2eb..f1d0ccc7d0 100644 --- a/tests/panic/mir-validation.rs +++ b/tests/panic/mir-validation.rs @@ -1,10 +1,13 @@ //! Ensure that the MIR validator runs on Miri's input. //@rustc-env:RUSTC_ICE=0 -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" -//@normalize-stderr-test: "\n +at [^\n]+" -> "" -//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" +//@normalize-stderr-test: "\n +[0-9]+:.+" -> "" +//@normalize-stderr-test: "\n +at .+" -> "" +//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\].*" -> "" //@normalize-stderr-test: "\n[ =]*note:.*" -> "" //@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId" +// Somehow on rustc Windows CI, the "Miri caused an ICE" message is not shown +// and we don't even get a regular panic; rustc aborts with a different exit code instead. +//@ignore-host-windows #![feature(custom_mir, core_intrinsics)] use core::intrinsics::mir::*; From 7a77fdac3d38acd1e99413495deadfea4ae5cf7c Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 21 Apr 2024 18:41:45 +0200 Subject: [PATCH 4/4] Stabilize generic `NonZero`. --- src/bin/miri.rs | 1 - src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index db2cd01ce0..0070d1f3eb 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,4 +1,3 @@ -#![feature(generic_nonzero)] #![feature(rustc_private, stmt_expr_attributes)] #![allow( clippy::manual_range_contains, diff --git a/src/lib.rs b/src/lib.rs index 2e19c9ff71..e1c0da9118 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ #![feature(cell_update)] #![feature(const_option)] #![feature(float_gamma)] -#![feature(generic_nonzero)] #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)]