# Proposal ["Async drop"](https://rust-lang.github.io/async-fundamentals-initiative/roadmap/async_drop.html) is one of the features on [Async WG roadmap](https://github.com/orgs/rust-lang/projects/28) that is tentatively scheduled for 2024-2025. The feature would allow to perform future awaits (at higher level) aka coroutine suspensions (at lower level) in destructors when variables go out of scope, or in similar language constructions like `defer`/`finally` blocks. The surface level, user-visible design for this feature is a contentious question (see the literature list below). The underlying mechanisms, however, are more or less shared between those surface designs. More than that, majority of implementation work lies in those shared parts, like code generation. The work on the surface level features is more about selecting a specific design, and resolving backward compatibility and migration issues, than about implementation issues (with exception of borrow checking for defer/finally blocks, perhaps). So, we suggest implementing those common components in rustc, and providing some minimal surface area to make them usable from the outside. **_The components implemented as a result of this MCP must be sufficient for enabling implementation of a working and sound library for scoped tasks and/or structured concurrency, which is a major selling point for async drop related features._** ## Component 1: Async drop trait The main trait in libcore providing the compiler with code to execute, and tying async drop to the type system from one side. The trait will be looking approximately like this, further details (what the `AsyncDropFuture` type is, in particular) are clarified by implementation. ```rust #[lang = "async_drop"] trait AsyncDrop { type AsyncDropFuture: Future; fn async_drop(*mut Self) -> AsyncDropFuture; } ``` The goal is to support something that works and is functionally correct, but not necessarily optimally efficient. For example, additional futures may be created and support for types that want to keep the drop future state inline may be not provided, some type erasure or boxing may also be possible. A two-step protocol similar to `IntoFuture` could be later implemented to make drops more efficient (first convert original `T1` into `T2` that can be dropped using inline state, then async-drop `T2` using a `poll`-like interface, `T1` may be the same as `T2` and the conversion may be trivial). PRs in progress: https://github.com/rust-lang/rust/pull/121801 ## Component 2: Async drop glue Implement automatic generation of async drop code for structures, enums, coroutines, and complex built-in types that either have their own `AsyncDrop` implementations, or have nested fields that need to be dropped synchronously or asynchronously. "Manual" async dropping with ```rust std::ptr::async_drop_in_place(ptr).await ``` should work after this component is implemented. PRs in progress: https://github.com/rust-lang/rust/pull/121801 ## Component 3: Calling async drop glue at the end of scope Values of types implementing `AsyncDrop` or having nested fields implementing it should run the corresponding `AsyncDrop` impls and drop glue and generate `yield`s at the destruction points. These async drops are *NOT* yet tied to the type system from the opposite side than `AsyncDrop` trait, there are effectively no `AsyncDrop` or `SyncDrop` *bounds*. Async drops can be attempted in any code, including generic code, regardless of bounds, and will result in a post-monomorphization errors when called in inappropriate contexts. `*Drop` bounds are an invasive change that we should be well motivated first (for example, by results of this experiment). Surface syntax for making async drops visible by humans (`let async`, `await` blocks, etc) is *NOT* yet provided. It is useful for humans, but its design is contentious, and it is not strictly necessary for implementing a working scoped task / structured concurrency library. PRs in progress: https://github.com/rust-lang/rust/pull/123948 ## Component 4: Support for suspending tasks that are currently unwinding We need to implement the "catch-suspend-resume-rethrow" protocol that would allow to catch a panic currently unwinding the stack, clear thread-local components of that panic so other tasks later running on the same thread don't see it as panicking, package the caught panic payload into a coroutine for suspension, and then unpackage and rethrow it after that coroutine's resumption. Same or similar mechanism can work in both async destructors, or `defer`/`finally` blocks to provide a reliable async cleanup on unwinding. Branches in progress: *no work is happening right now* ## Component 5: `Forgettable` trait "Unforgettable" types are types that cannot be passed to `mem::forget`, `Rc::new` and similar functions, and that are guaranteed to be either destroyed using (possibly async) drop or destructured, unless `unsafe` code is involved. The trait only really makes sense for non-`'static` types, see more details in the [proposal](https://github.com/zetanumbers/posts/blob/main/myosotis.md). Handles for scoped tasks are an example of such unforgettable types. `auto trait Forgettable {}` (could also be named `Leak`) is added to libcore, tying the unforgettable types to the type system from one side. Unforgettable types are *NOT* yet tied to the type system from the opposite side than `Forgettable` trait, there are effectively no `Forgettable` *bounds*. `mem::forget`, `Rc::new` and similar functions are marked with something like a `#[rustc_forgettable]` attribute, and passing unforgettable types to them will result in post-monomorphization errors. Post-monomorphization checking will still accept code that needs to be accepted, and error on code that needs to be rejected, which is a minimum that is strictly necessary for implementing a working scoped task / structured concurrency library. `Forgettable` bounds are an invasive change that we should be well motivated first (for example, by results of this experiment). Branches in progress: https://github.com/zetanumbers/rust/tree/postmono_forgettable Proposals: https://github.com/zetanumbers/posts/blob/main/myosotis.md ## Component 6: Default bounds for potential new auto traits We'd like to experiment with a mechanism for adding new auto traits like `Leak`, or `SyncDrop` (or some others, specific traits don't matter here), to give a definitive answer to the question of whether we can do it in practice or not. For example, the `Leak` trait could be implicitly added to *all* bound lists on the current edition, and then added more conservatively and user-friendly using some heuristic on the next edition. There may be difficulties with backward compatibility due to cycles in trait solver, or with longer compilation times, or with comping up with a good heuristic for the next edition. PRs in progress: https://github.com/rust-lang/rust/pull/120706, https://github.com/rust-lang/rust/pull/121676. ## Literature (Apologies to those not included, I know there is more.) ### Async drop and async cancellation - https://smallcultfollowing.com/babysteps/blog/2022/06/13/async-cancellation-a-case-study-of-pub-sub-in-mini-redis/ - https://smallcultfollowing.com/babysteps/blog/2022/01/27/panics-vs-cancellation-part-1/ - https://blog.yoshuawuyts.com/async-cancellation-2/ - https://blog.yoshuawuyts.com/async-cancellation-1/ - https://without.boats/blog/asynchronous-clean-up/ - https://without.boats/blog/poll-drop/ - ["Add Drop::poll_drop_ready for asynchronous destructors"](https://github.com/rust-lang/rfcs/pull/2958) - https://sabrinajewson.org/blog/async-drop - https://tmandry.gitlab.io/blog/posts/2023-03-01-scoped-tasks/ - https://theincredibleholk.org/blog/2023/11/14/a-mechanism-for-async-cancellation/ - https://theincredibleholk.org/blog/2023/11/08/cancellation-async-state-machines/ - https://theincredibleholk.org/blog/2022/03/25/perspectives-on-async-cancellation/ - https://www.ncameron.org/blog/defer-blocks-and-async-drop/ - https://rust-lang.github.io/async-fundamentals-initiative/roadmap/async_drop.html - https://internals.rust-lang.org/t/a-defer-discussion/20387 - https://internals.rust-lang.org/t/pre-pre-pre-rfc-implicit-code-control-and-defer-statements/20071 - ["Design meeting 2024-02-08: async Drop"](https://hackmd.io/qsCxElt6SM-riz2pMdKBNA) ### Leak / Forgettable / linear types - https://github.com/zetanumbers/posts/blob/main/myosotis.md - https://blog.yoshuawuyts.com/linear-types-one-pager/ - https://blog.yoshuawuyts.com/linearity-and-control/ - https://smallcultfollowing.com/babysteps/blog/2023/03/16/must-move-types/ - https://without.boats/blog/changing-the-rules-of-rust/ - https://without.boats/blog/generic-trait-methods-and-new-auto-traits/ - https://without.boats/blog/the-scoped-task-trilemma/ # Mentors or Reviewers Design - @traviscross @yoshuawuyts, process - @petrochenkov, code review - some MIR/codegen experts are needed to review changes in MIR transforms/passes. # Process The main points of the [Major Change Process][MCP] are as follows: * [x] File an issue describing the proposal. * [ ] A compiler team member or contributor who is knowledgeable in the area can **second** by writing `@rustbot second`. * Finding a "second" suffices for internal changes. If however, you are proposing a new public-facing feature, such as a `-C flag`, then full team check-off is required. * Compiler team members can initiate a check-off via `@rfcbot fcp merge` on either the MCP or the PR. * [ ] Once an MCP is seconded, the Final Comment Period begins. If no objections are raised after 10 days, the MCP is considered **approved**. You can read [more about Major Change Proposals on forge][MCP]. [MCP]: https://forge.rust-lang.org/compiler/mcp.html # Comments **This issue is not meant to be used for technical discussion. There is a Zulip stream for that. Use this issue to leave procedural comments, such as volunteering to review, indicating that you second the proposal (or third, etc), or raising a concern that you would like to be addressed.**