Skip to content

AsyncDrop not called when type is inside Box #143658

Open
@beepster4096

Description

@beepster4096

I tried this code (modified tests\ui\async-await\async-drop\async-drop.rs):

#![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() {
    {
        // 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 mut first = Box::new(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)
}

I expected to see this happen: Test produces same output as before:

Foo::new() : 7
Foo::drop() : 7
Middle
Foo::new() : 10
Foo::async drop() : 10
Done

Instead, this happened: Test produces different output; Foo's async drop is never called:

Foo::new() : 7
Foo::drop() : 7
Middle
Foo::new() : 10
Foo::drop() : 10
Done

Meta

rustc --version --verbose:

rustc 1.90.0-nightly (a2d45f73c 2025-07-07)

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.F-async_drop`#![feature(async_drop)]`needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions