| 
 | 1 | +//@revisions: stack tree  | 
 | 2 | +//@compile-flags: -Zmiri-strict-provenance  | 
 | 3 | +//@[tree]compile-flags: -Zmiri-tree-borrows  | 
 | 4 | +#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)]  | 
 | 5 | +#![allow(incomplete_features, dead_code)]  | 
 | 6 | + | 
 | 7 | +// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests  | 
 | 8 | +use core::future::{async_drop_in_place, AsyncDrop, Future};  | 
 | 9 | +use core::hint::black_box;  | 
 | 10 | +use core::mem::{self, ManuallyDrop};  | 
 | 11 | +use core::pin::{pin, Pin};  | 
 | 12 | +use core::task::{Context, Poll, Waker};  | 
 | 13 | + | 
 | 14 | +async fn test_async_drop<T>(x: T) {  | 
 | 15 | +    let mut x = mem::MaybeUninit::new(x);  | 
 | 16 | +    let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) });  | 
 | 17 | +    test_idempotency(dtor).await;  | 
 | 18 | +}  | 
 | 19 | + | 
 | 20 | +fn test_idempotency<T>(mut x: Pin<&mut T>) -> impl Future<Output = ()> + '_  | 
 | 21 | +where  | 
 | 22 | +    T: Future<Output = ()>,  | 
 | 23 | +{  | 
 | 24 | +    core::future::poll_fn(move |cx| {  | 
 | 25 | +        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));  | 
 | 26 | +        assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));  | 
 | 27 | +        Poll::Ready(())  | 
 | 28 | +    })  | 
 | 29 | +}  | 
 | 30 | + | 
 | 31 | +fn main() {  | 
 | 32 | +    let waker = Waker::noop();  | 
 | 33 | +    let mut cx = Context::from_waker(&waker);  | 
 | 34 | + | 
 | 35 | +    let i = 13;  | 
 | 36 | +    let fut = pin!(async {  | 
 | 37 | +        test_async_drop(Int(0)).await;  | 
 | 38 | +        test_async_drop(AsyncInt(0)).await;  | 
 | 39 | +        test_async_drop([AsyncInt(1), AsyncInt(2)]).await;  | 
 | 40 | +        test_async_drop((AsyncInt(3), AsyncInt(4))).await;  | 
 | 41 | +        test_async_drop(5).await;  | 
 | 42 | +        let j = 42;  | 
 | 43 | +        test_async_drop(&i).await;  | 
 | 44 | +        test_async_drop(&j).await;  | 
 | 45 | +        test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await;  | 
 | 46 | +        test_async_drop(ManuallyDrop::new(AsyncInt(9))).await;  | 
 | 47 | + | 
 | 48 | +        let foo = AsyncInt(10);  | 
 | 49 | +        test_async_drop(AsyncReference { foo: &foo }).await;  | 
 | 50 | + | 
 | 51 | +        let foo = AsyncInt(11);  | 
 | 52 | +        test_async_drop(|| {  | 
 | 53 | +            black_box(foo);  | 
 | 54 | +            let foo = AsyncInt(10);  | 
 | 55 | +            foo  | 
 | 56 | +        })  | 
 | 57 | +        .await;  | 
 | 58 | + | 
 | 59 | +        test_async_drop(AsyncEnum::A(AsyncInt(12))).await;  | 
 | 60 | +        test_async_drop(AsyncEnum::B(SyncInt(13))).await;  | 
 | 61 | + | 
 | 62 | +        test_async_drop(SyncInt(14)).await;  | 
 | 63 | +        test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) })  | 
 | 64 | +            .await;  | 
 | 65 | + | 
 | 66 | +        let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19)));  | 
 | 67 | +        test_idempotency(async_drop_fut).await;  | 
 | 68 | + | 
 | 69 | +        let foo = AsyncInt(20);  | 
 | 70 | +        test_async_drop(async || {  | 
 | 71 | +            black_box(foo);  | 
 | 72 | +            let foo = AsyncInt(19);  | 
 | 73 | +            // Await point there, but this is async closure so it's fine  | 
 | 74 | +            black_box(core::future::ready(())).await;  | 
 | 75 | +            foo  | 
 | 76 | +        })  | 
 | 77 | +        .await;  | 
 | 78 | + | 
 | 79 | +        test_async_drop(AsyncUnion { signed: 21 }).await;  | 
 | 80 | +    });  | 
 | 81 | +    let res = fut.poll(&mut cx);  | 
 | 82 | +    assert_eq!(res, Poll::Ready(()));  | 
 | 83 | +}  | 
 | 84 | + | 
 | 85 | +struct AsyncInt(i32);  | 
 | 86 | + | 
 | 87 | +impl AsyncDrop for AsyncInt {  | 
 | 88 | +    type Dropper<'a> = impl Future<Output = ()>;  | 
 | 89 | + | 
 | 90 | +    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {  | 
 | 91 | +        async move {  | 
 | 92 | +            println!("AsyncInt::Dropper::poll: {}", self.0);  | 
 | 93 | +        }  | 
 | 94 | +    }  | 
 | 95 | +}  | 
 | 96 | + | 
 | 97 | +struct SyncInt(i32);  | 
 | 98 | + | 
 | 99 | +impl Drop for SyncInt {  | 
 | 100 | +    fn drop(&mut self) {  | 
 | 101 | +        println!("SyncInt::drop: {}", self.0);  | 
 | 102 | +    }  | 
 | 103 | +}  | 
 | 104 | + | 
 | 105 | +struct SyncThenAsync {  | 
 | 106 | +    i: i32,  | 
 | 107 | +    a: AsyncInt,  | 
 | 108 | +    b: SyncInt,  | 
 | 109 | +    c: AsyncInt,  | 
 | 110 | +}  | 
 | 111 | + | 
 | 112 | +impl Drop for SyncThenAsync {  | 
 | 113 | +    fn drop(&mut self) {  | 
 | 114 | +        println!("SyncThenAsync::drop: {}", self.i);  | 
 | 115 | +    }  | 
 | 116 | +}  | 
 | 117 | + | 
 | 118 | +struct AsyncReference<'a> {  | 
 | 119 | +    foo: &'a AsyncInt,  | 
 | 120 | +}  | 
 | 121 | + | 
 | 122 | +impl AsyncDrop for AsyncReference<'_> {  | 
 | 123 | +    type Dropper<'a> = impl Future<Output = ()> where Self: 'a;  | 
 | 124 | + | 
 | 125 | +    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {  | 
 | 126 | +        async move {  | 
 | 127 | +            println!("AsyncReference::Dropper::poll: {}", self.foo.0);  | 
 | 128 | +        }  | 
 | 129 | +    }  | 
 | 130 | +}  | 
 | 131 | + | 
 | 132 | +struct Int(i32);  | 
 | 133 | + | 
 | 134 | +struct AsyncStruct {  | 
 | 135 | +    i: i32,  | 
 | 136 | +    a: AsyncInt,  | 
 | 137 | +    b: AsyncInt,  | 
 | 138 | +}  | 
 | 139 | + | 
 | 140 | +impl AsyncDrop for AsyncStruct {  | 
 | 141 | +    type Dropper<'a> = impl Future<Output = ()>;  | 
 | 142 | + | 
 | 143 | +    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {  | 
 | 144 | +        async move {  | 
 | 145 | +            println!("AsyncStruct::Dropper::poll: {}", self.i);  | 
 | 146 | +        }  | 
 | 147 | +    }  | 
 | 148 | +}  | 
 | 149 | + | 
 | 150 | +enum AsyncEnum {  | 
 | 151 | +    A(AsyncInt),  | 
 | 152 | +    B(SyncInt),  | 
 | 153 | +}  | 
 | 154 | + | 
 | 155 | +impl AsyncDrop for AsyncEnum {  | 
 | 156 | +    type Dropper<'a> = impl Future<Output = ()>;  | 
 | 157 | + | 
 | 158 | +    fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> {  | 
 | 159 | +        async move {  | 
 | 160 | +            let new_self = match &*self {  | 
 | 161 | +                AsyncEnum::A(foo) => {  | 
 | 162 | +                    println!("AsyncEnum(A)::Dropper::poll: {}", foo.0);  | 
 | 163 | +                    AsyncEnum::B(SyncInt(foo.0))  | 
 | 164 | +                }  | 
 | 165 | +                AsyncEnum::B(foo) => {  | 
 | 166 | +                    println!("AsyncEnum(B)::Dropper::poll: {}", foo.0);  | 
 | 167 | +                    AsyncEnum::A(AsyncInt(foo.0))  | 
 | 168 | +                }  | 
 | 169 | +            };  | 
 | 170 | +            mem::forget(mem::replace(&mut *self, new_self));  | 
 | 171 | +        }  | 
 | 172 | +    }  | 
 | 173 | +}  | 
 | 174 | + | 
 | 175 | +// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions  | 
 | 176 | +union AsyncUnion {  | 
 | 177 | +    signed: i32,  | 
 | 178 | +    unsigned: u32,  | 
 | 179 | +}  | 
 | 180 | + | 
 | 181 | +impl AsyncDrop for AsyncUnion {  | 
 | 182 | +    type Dropper<'a> = impl Future<Output = ()>;  | 
 | 183 | + | 
 | 184 | +    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {  | 
 | 185 | +        async move {  | 
 | 186 | +            println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe {  | 
 | 187 | +                self.unsigned  | 
 | 188 | +            });  | 
 | 189 | +        }  | 
 | 190 | +    }  | 
 | 191 | +}  | 
0 commit comments