-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MCP: Low level components for async drop #727
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. Concerns or objections to the proposal should be discussed on Zulip and formally registered here by adding a comment with the following syntax:
Concerns can be lifted with:
See documentation at https://forge.rust-lang.org cc @rust-lang/compiler @rust-lang/compiler-contributors |
@rustbot second on the impl stuff Should in parallel find a T-lang shepherd for the unstable (non-internal) lang features and lang items |
@rustbot label -final-comment-period +major-change-accepted |
…-obk Add simple async drop glue generation This is a prototype of the async drop glue generation for some simple types. Async drop glue is intended to behave very similar to the regular drop glue except for being asynchronous. Currently it does not execute synchronous drops but only calls user implementations of `AsyncDrop::async_drop` associative function and awaits the returned future. It is not complete as it only recurses into arrays, slices, tuples, and structs and does not have same sensible restrictions as the old `Drop` trait implementation like having the same bounds as the type definition, while code assumes their existence (requires a future work). This current design uses a workaround as it does not create any custom async destructor state machine types for ADTs, but instead uses types defined in the std library called future combinators (deferred_async_drop, chain, ready_unit). Also I recommend reading my [explainer](https://zetanumbers.github.io/book/async-drop-design.html). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727) work. Feature completeness: - [x] `AsyncDrop` trait - [ ] `async_drop_in_place_raw`/async drop glue generation support for - [x] Trivially destructible types (integers, bools, floats, string slices, pointers, references, etc.) - [x] Arrays and slices (array pointer is unsized into slice pointer) - [x] ADTs (enums, structs, unions) - [x] tuple-like types (tuples, closures) - [ ] Dynamic types (`dyn Trait`, see explainer's [proposed design](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#async-drop-glue-for-dyn-trait)) - [ ] coroutines (rust-lang#123948) - [x] Async drop glue includes sync drop glue code - [x] Cleanup branch generation for `async_drop_in_place_raw` - [ ] Union rejects non-trivially async destructible fields - [ ] `AsyncDrop` implementation requires same bounds as type definition - [ ] Skip trivially destructible fields (optimization) - [ ] New [`TyKind::AdtAsyncDestructor`](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#adt-async-destructor-types) and get rid of combinators - [ ] [Synchronously undroppable types](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#exclusively-async-drop) - [ ] Automatic async drop at the end of the scope in async context
Add simple async drop glue generation This is a prototype of the async drop glue generation for some simple types. Async drop glue is intended to behave very similar to the regular drop glue except for being asynchronous. Currently it does not execute synchronous drops but only calls user implementations of `AsyncDrop::async_drop` associative function and awaits the returned future. It is not complete as it only recurses into arrays, slices, tuples, and structs and does not have same sensible restrictions as the old `Drop` trait implementation like having the same bounds as the type definition, while code assumes their existence (requires a future work). This current design uses a workaround as it does not create any custom async destructor state machine types for ADTs, but instead uses types defined in the std library called future combinators (deferred_async_drop, chain, ready_unit). Also I recommend reading my [explainer](https://zetanumbers.github.io/book/async-drop-design.html). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727) work. Feature completeness: - [x] `AsyncDrop` trait - [ ] `async_drop_in_place_raw`/async drop glue generation support for - [x] Trivially destructible types (integers, bools, floats, string slices, pointers, references, etc.) - [x] Arrays and slices (array pointer is unsized into slice pointer) - [x] ADTs (enums, structs, unions) - [x] tuple-like types (tuples, closures) - [ ] Dynamic types (`dyn Trait`, see explainer's [proposed design](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#async-drop-glue-for-dyn-trait)) - [ ] coroutines (rust-lang#123948) - [x] Async drop glue includes sync drop glue code - [x] Cleanup branch generation for `async_drop_in_place_raw` - [ ] Union rejects non-trivially async destructible fields - [ ] `AsyncDrop` implementation requires same bounds as type definition - [ ] Skip trivially destructible fields (optimization) - [ ] New [`TyKind::AdtAsyncDestructor`](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#adt-async-destructor-types) and get rid of combinators - [ ] [Synchronously undroppable types](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#exclusively-async-drop) - [ ] Automatic async drop at the end of the scope in async context
Add simple async drop glue generation This is a prototype of the async drop glue generation for some simple types. Async drop glue is intended to behave very similar to the regular drop glue except for being asynchronous. Currently it does not execute synchronous drops but only calls user implementations of `AsyncDrop::async_drop` associative function and awaits the returned future. It is not complete as it only recurses into arrays, slices, tuples, and structs and does not have same sensible restrictions as the old `Drop` trait implementation like having the same bounds as the type definition, while code assumes their existence (requires a future work). This current design uses a workaround as it does not create any custom async destructor state machine types for ADTs, but instead uses types defined in the std library called future combinators (deferred_async_drop, chain, ready_unit). Also I recommend reading my [explainer](https://zetanumbers.github.io/book/async-drop-design.html). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727) work. Feature completeness: - [x] `AsyncDrop` trait - [ ] `async_drop_in_place_raw`/async drop glue generation support for - [x] Trivially destructible types (integers, bools, floats, string slices, pointers, references, etc.) - [x] Arrays and slices (array pointer is unsized into slice pointer) - [x] ADTs (enums, structs, unions) - [x] tuple-like types (tuples, closures) - [ ] Dynamic types (`dyn Trait`, see explainer's [proposed design](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#async-drop-glue-for-dyn-trait)) - [ ] coroutines (rust-lang/rust#123948) - [x] Async drop glue includes sync drop glue code - [x] Cleanup branch generation for `async_drop_in_place_raw` - [ ] Union rejects non-trivially async destructible fields - [ ] `AsyncDrop` implementation requires same bounds as type definition - [ ] Skip trivially destructible fields (optimization) - [ ] New [`TyKind::AdtAsyncDestructor`](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#adt-async-destructor-types) and get rid of combinators - [ ] [Synchronously undroppable types](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#exclusively-async-drop) - [ ] Automatic async drop at the end of the scope in async context
Add simple async drop glue generation This is a prototype of the async drop glue generation for some simple types. Async drop glue is intended to behave very similar to the regular drop glue except for being asynchronous. Currently it does not execute synchronous drops but only calls user implementations of `AsyncDrop::async_drop` associative function and awaits the returned future. It is not complete as it only recurses into arrays, slices, tuples, and structs and does not have same sensible restrictions as the old `Drop` trait implementation like having the same bounds as the type definition, while code assumes their existence (requires a future work). This current design uses a workaround as it does not create any custom async destructor state machine types for ADTs, but instead uses types defined in the std library called future combinators (deferred_async_drop, chain, ready_unit). Also I recommend reading my [explainer](https://zetanumbers.github.io/book/async-drop-design.html). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727) work. Feature completeness: - [x] `AsyncDrop` trait - [ ] `async_drop_in_place_raw`/async drop glue generation support for - [x] Trivially destructible types (integers, bools, floats, string slices, pointers, references, etc.) - [x] Arrays and slices (array pointer is unsized into slice pointer) - [x] ADTs (enums, structs, unions) - [x] tuple-like types (tuples, closures) - [ ] Dynamic types (`dyn Trait`, see explainer's [proposed design](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#async-drop-glue-for-dyn-trait)) - [ ] coroutines (rust-lang/rust#123948) - [x] Async drop glue includes sync drop glue code - [x] Cleanup branch generation for `async_drop_in_place_raw` - [ ] Union rejects non-trivially async destructible fields - [ ] `AsyncDrop` implementation requires same bounds as type definition - [ ] Skip trivially destructible fields (optimization) - [ ] New [`TyKind::AdtAsyncDestructor`](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#adt-async-destructor-types) and get rid of combinators - [ ] [Synchronously undroppable types](https://github.com/zetanumbers/posts/blob/main/async-drop-design.md#exclusively-async-drop) - [ ] Automatic async drop at the end of the scope in async context
Support ?Trait bounds in supertraits and dyn Trait under a feature gate This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example: ```rust #![feature(allow_maybe_polarity)] ... trait Trait1 : ?Trait { ... } // ok fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok ``` Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](rust-lang#120706 (comment)). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727)
Support ?Trait bounds in supertraits and dyn Trait under a feature gate This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example: ```rust #![feature(allow_maybe_polarity)] ... trait Trait1 : ?Trait { ... } // ok fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok ``` Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](rust-lang#120706 (comment)). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727)
Support ?Trait bounds in supertraits and dyn Trait under a feature gate This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example: ```rust #![feature(allow_maybe_polarity)] ... trait Trait1 : ?Trait { ... } // ok fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok ``` Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](rust-lang#120706 (comment)). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727)
Support ?Trait bounds in supertraits and dyn Trait under a feature gate This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example: ```rust #![feature(allow_maybe_polarity)] ... trait Trait1 : ?Trait { ... } // ok fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok ``` Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](rust-lang#120706 (comment)). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727)
Support ?Trait bounds in supertraits and dyn Trait under a feature gate This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example: ```rust #![feature(allow_maybe_polarity)] ... trait Trait1 : ?Trait { ... } // ok fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok ``` Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](rust-lang/rust#120706 (comment)). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727)
Support ?Trait bounds in supertraits and dyn Trait under a feature gate This patch allows `maybe` polarity bounds under a feature gate. The only language change here is that corresponding hard errors are replaced by feature gates. Example: ```rust #![feature(allow_maybe_polarity)] ... trait Trait1 : ?Trait { ... } // ok fn foo(_: Box<(dyn Trait2 + ?Trait)>) {} // ok fn bar<T: ?Sized + ?Trait>(_: &T) {} // ok ``` Maybe bounds still don't do anything (except for `Sized` trait), however this patch will allow us to [experiment with default auto traits](rust-lang/rust#120706 (comment)). This is a part of the [MCP: Low level components for async drop](rust-lang/compiler-team#727)
Proposal
"Async drop"
is one of the features on Async WG roadmap 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.
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 moreefficient (first convert original
T1
intoT2
that can be dropped using inline state, thenasync-drop
T2
using apoll
-like interface,T1
may be the same asT2
and the conversion maybe trivial).
PRs in progress: rust-lang/rust#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 thatneed to be dropped synchronously or asynchronously.
"Manual" async dropping with
should work after this component is implemented.
PRs in progress: rust-lang/rust#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 thecorresponding
AsyncDrop
impls and drop glue and generateyield
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
orSyncDrop
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, byresults of this experiment).
Surface syntax for making async drops visible by humans (
let async
,await
blocks, etc) is NOTyet 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: rust-lang/rust#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 providea 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 similarfunctions, 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 theproposal.
Handles for scoped tasks are an example of such unforgettable types.
auto trait Forgettable {}
(could also be namedLeak
) is added to libcore, tying theunforgettable 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 inpost-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, byresults 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
, orSyncDrop
(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: rust-lang/rust#120706,
rust-lang/rust#121676.
Literature
(Apologies to those not included, I know there is more.)
Async drop and async cancellation
Leak / Forgettable / linear types
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 are as follows:
@rustbot second
.-C flag
, then full team check-off is required.@rfcbot fcp merge
on either the MCP or the PR.You can read more about Major Change Proposals on forge.
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.
The text was updated successfully, but these errors were encountered: