Skip to content

Commit 6c871da

Browse files
committed
Auto merge of rust-lang#123948 - azhogin:azhogin/async-drop, r=oli-obk
Async drop codegen Async drop implementation using templated coroutine for async drop glue generation. Scopes changes to generate `async_drop_in_place()` awaits, when async droppable objects are out-of-scope in async context. Implementation details: https://github.com/azhogin/posts/blob/main/async-drop-impl.md New fields in Drop terminator (drop & async_fut). Processing in codegen/miri must validate that those fields are empty (in full version async Drop terminator will be expanded at StateTransform pass or reverted to sync version). Changes in terminator visiting to consider possible new successor (drop field). ResumedAfterDrop messages for panic when coroutine is resumed after it is started to be async drop'ed. Lang item for generated coroutine for async function async_drop_in_place. `async fn async_drop_in_place<T>()::{{closure0}}`. Scopes processing for generate async drop preparations. Async drop is a hidden Yield, so potentially async drops require the same dropline preparation as for Yield terminators. Processing in StateTransform: async drops are expanded into yield-point. Generation of async drop of coroutine itself added. Shims for AsyncDropGlueCtorShim, AsyncDropGlue and FutureDropPoll. ```rust #[lang = "async_drop"] pub trait AsyncDrop { #[allow(async_fn_in_trait)] async fn drop(self: Pin<&mut Self>); } impl Drop for Foo { fn drop(&mut self) { println!("Foo::drop({})", self.my_resource_handle); } } impl AsyncDrop for Foo { async fn drop(self: Pin<&mut Self>) { println!("Foo::async drop({})", self.my_resource_handle); } } ``` First async drop glue implementation re-worked to use the same drop elaboration code as for sync drop. `async_drop_in_place` changed to be `async fn`. So both `async_drop_in_place` ctor and produced coroutine have their lang items (`AsyncDropInPlace`/`AsyncDropInPlacePoll`) and shim instances (`AsyncDropGlueCtorShim`/`AsyncDropGlue`). ``` pub async unsafe fn async_drop_in_place<T: ?Sized>(_to_drop: *mut T) { } ``` AsyncDropGlue shim generation uses `elaborate_drops::elaborate_drop` to produce drop ladder (in the similar way as for sync drop glue) and then `coroutine::StateTransform` to convert function into coroutine poll. AsyncDropGlue coroutine's layout can't be calculated for generic T, it requires known final dropee type to be generated (in StateTransform). So, `templated coroutine` was introduced here (`templated_coroutine_layout(...)` etc). Such approach overrides the first implementation using mixing language-level futures in rust-lang#121801.
2 parents 7d5108c + 8a6bb70 commit 6c871da

File tree

5 files changed

+92
-324
lines changed

5 files changed

+92
-324
lines changed

core/src/future/async_drop.rs

+39-272
Original file line numberDiff line numberDiff line change
@@ -1,284 +1,51 @@
11
#![unstable(feature = "async_drop", issue = "126482")]
22

3-
use crate::fmt;
4-
use crate::future::{Future, IntoFuture};
5-
use crate::intrinsics::discriminant_value;
6-
use crate::marker::{DiscriminantKind, PhantomPinned};
7-
use crate::mem::MaybeUninit;
8-
use crate::pin::Pin;
9-
use crate::task::{Context, Poll, ready};
10-
11-
/// Asynchronously drops a value by running `AsyncDrop::async_drop`
12-
/// on a value and its fields recursively.
13-
#[unstable(feature = "async_drop", issue = "126482")]
14-
pub fn async_drop<T>(value: T) -> AsyncDropOwning<T> {
15-
AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned }
16-
}
17-
18-
/// A future returned by the [`async_drop`].
19-
#[unstable(feature = "async_drop", issue = "126482")]
20-
pub struct AsyncDropOwning<T> {
21-
value: MaybeUninit<T>,
22-
dtor: Option<AsyncDropInPlace<T>>,
23-
_pinned: PhantomPinned,
24-
}
25-
26-
#[unstable(feature = "async_drop", issue = "126482")]
27-
impl<T> fmt::Debug for AsyncDropOwning<T> {
28-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29-
f.debug_struct("AsyncDropOwning").finish_non_exhaustive()
30-
}
31-
}
32-
33-
#[unstable(feature = "async_drop", issue = "126482")]
34-
impl<T> Future for AsyncDropOwning<T> {
35-
type Output = ();
36-
37-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
38-
// SAFETY: Self is pinned thus it is ok to store references to self
39-
unsafe {
40-
let this = self.get_unchecked_mut();
41-
let dtor = Pin::new_unchecked(
42-
this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())),
43-
);
44-
// AsyncDestuctors are idempotent so Self gets idempotency as well
45-
dtor.poll(cx)
46-
}
47-
}
48-
}
3+
#[allow(unused_imports)]
4+
use core::future::Future;
495

50-
#[lang = "async_drop_in_place"]
51-
#[allow(unconditional_recursion)]
52-
// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed?
53-
unsafe fn async_drop_in_place_raw<T: ?Sized>(
54-
to_drop: *mut T,
55-
) -> <T as AsyncDestruct>::AsyncDestructor {
56-
// Code here does not matter - this is replaced by the
57-
// real async drop glue constructor by the compiler.
58-
59-
// SAFETY: see comment above
60-
unsafe { async_drop_in_place_raw(to_drop) }
61-
}
6+
#[allow(unused_imports)]
7+
use crate::pin::Pin;
8+
#[allow(unused_imports)]
9+
use crate::task::{Context, Poll};
6210

63-
/// Creates the asynchronous destructor of the pointed-to value.
64-
///
65-
/// # Safety
66-
///
67-
/// Behavior is undefined if any of the following conditions are violated:
68-
///
69-
/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes.
70-
///
71-
/// * `to_drop` must be properly aligned, even if `T` has size 0.
11+
/// Async version of Drop trait.
7212
///
73-
/// * `to_drop` must be nonnull, even if `T` has size 0.
13+
/// When a value is no longer needed, Rust will run a "destructor" on that value.
14+
/// The most common way that a value is no longer needed is when it goes out of
15+
/// scope. Destructors may still run in other circumstances, but we're going to
16+
/// focus on scope for the examples here. To learn about some of those other cases,
17+
/// please see [the reference] section on destructors.
7418
///
75-
/// * The value `to_drop` points to must be valid for async dropping,
76-
/// which may mean it must uphold additional invariants. These
77-
/// invariants depend on the type of the value being dropped. For
78-
/// instance, when dropping a Box, the box's pointer to the heap must
79-
/// be valid.
19+
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
8020
///
81-
/// * While `async_drop_in_place` is executing or the returned async
82-
/// destructor is alive, the only way to access parts of `to_drop`
83-
/// is through the `self: Pin<&mut Self>` references supplied to
84-
/// the `AsyncDrop::async_drop` methods that `async_drop_in_place`
85-
/// or `AsyncDropInPlace<T>::poll` invokes. This usually means the
86-
/// returned future stores the `to_drop` pointer and user is required
87-
/// to guarantee that dropped value doesn't move.
21+
/// ## `Copy` and ([`Drop`]|`AsyncDrop`) are exclusive
8822
///
89-
#[unstable(feature = "async_drop", issue = "126482")]
90-
pub unsafe fn async_drop_in_place<T: ?Sized>(to_drop: *mut T) -> AsyncDropInPlace<T> {
91-
// SAFETY: `async_drop_in_place_raw` has the same safety requirements
92-
unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) }
93-
}
94-
95-
/// A future returned by the [`async_drop_in_place`].
96-
#[unstable(feature = "async_drop", issue = "126482")]
97-
pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor);
98-
99-
#[unstable(feature = "async_drop", issue = "126482")]
100-
impl<T: ?Sized> fmt::Debug for AsyncDropInPlace<T> {
101-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102-
f.debug_struct("AsyncDropInPlace").finish_non_exhaustive()
103-
}
104-
}
105-
106-
#[unstable(feature = "async_drop", issue = "126482")]
107-
impl<T: ?Sized> Future for AsyncDropInPlace<T> {
108-
type Output = ();
109-
110-
#[inline(always)]
111-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
112-
// SAFETY: This code simply forwards poll call to the inner future
113-
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx)
114-
}
115-
}
116-
117-
// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as
118-
// with Drop impls
119-
/// Custom code within the asynchronous destructor.
23+
/// You cannot implement both [`Copy`] and ([`Drop`]|`AsyncDrop`) on the same type. Types that
24+
/// are `Copy` get implicitly duplicated by the compiler, making it very
25+
/// hard to predict when, and how often destructors will be executed. As such,
26+
/// these types cannot have destructors.
27+
#[cfg(not(bootstrap))]
12028
#[unstable(feature = "async_drop", issue = "126482")]
12129
#[lang = "async_drop"]
12230
pub trait AsyncDrop {
123-
/// A future returned by the [`AsyncDrop::async_drop`] to be part
124-
/// of the async destructor.
125-
#[unstable(feature = "async_drop", issue = "126482")]
126-
type Dropper<'a>: Future<Output = ()>
127-
where
128-
Self: 'a;
129-
130-
/// Constructs the asynchronous destructor for this type.
131-
#[unstable(feature = "async_drop", issue = "126482")]
132-
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>;
133-
}
134-
135-
#[lang = "async_destruct"]
136-
#[rustc_deny_explicit_impl]
137-
#[rustc_do_not_implement_via_object]
138-
trait AsyncDestruct {
139-
type AsyncDestructor: Future<Output = ()>;
140-
}
141-
142-
/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify
143-
/// generation of the code for `async_drop_in_place_raw`
144-
#[lang = "surface_async_drop_in_place"]
145-
async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) {
146-
// SAFETY: We call this from async drop `async_drop_in_place_raw`
147-
// which has the same safety requirements
148-
unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await }
149-
}
150-
151-
/// Basically calls `Drop::drop` with pointer. Used to simplify generation
152-
/// of the code for `async_drop_in_place_raw`
153-
#[allow(drop_bounds)]
154-
#[lang = "async_drop_surface_drop_in_place"]
155-
async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
156-
// SAFETY: We call this from async drop `async_drop_in_place_raw`
157-
// which has the same safety requirements
158-
unsafe { crate::ops::fallback_surface_drop(&mut *ptr) }
159-
}
160-
161-
/// Wraps a future to continue outputting `Poll::Ready(())` once after
162-
/// wrapped future completes by returning `Poll::Ready(())` on poll. This
163-
/// is useful for constructing async destructors to guarantee this
164-
/// "fuse" property
165-
//
166-
// FIXME: Consider optimizing combinators to not have to use fuse in majority
167-
// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for
168-
// async functions and blocks with the unit return type. However current layout
169-
// optimizations currently encode `None` case into the async block's discriminant.
170-
struct Fuse<T> {
171-
inner: Option<T>,
172-
}
173-
174-
#[lang = "async_drop_fuse"]
175-
fn fuse<T>(inner: T) -> Fuse<T> {
176-
Fuse { inner: Some(inner) }
177-
}
178-
179-
impl<T> Future for Fuse<T>
180-
where
181-
T: Future<Output = ()>,
182-
{
183-
type Output = ();
184-
185-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
186-
// SAFETY: pin projection into `self.inner`
187-
unsafe {
188-
let this = self.get_unchecked_mut();
189-
if let Some(inner) = &mut this.inner {
190-
ready!(Pin::new_unchecked(inner).poll(cx));
191-
this.inner = None;
192-
}
193-
}
194-
Poll::Ready(())
195-
}
196-
}
197-
198-
/// Async destructor for arrays and slices.
199-
#[lang = "async_drop_slice"]
200-
async unsafe fn slice<T>(s: *mut [T]) {
201-
let len = s.len();
202-
let ptr = s.as_mut_ptr();
203-
for i in 0..len {
204-
// SAFETY: we iterate over elements of `s` slice
205-
unsafe { async_drop_in_place_raw(ptr.add(i)).await }
206-
}
207-
}
208-
209-
/// Constructs a chain of two futures, which awaits them sequentially as
210-
/// a future.
211-
#[lang = "async_drop_chain"]
212-
async fn chain<F, G>(first: F, last: G)
213-
where
214-
F: IntoFuture<Output = ()>,
215-
G: IntoFuture<Output = ()>,
216-
{
217-
first.await;
218-
last.await;
219-
}
220-
221-
/// Basically a lazy version of `async_drop_in_place`. Returns a future
222-
/// that would call `AsyncDrop::async_drop` on a first poll.
223-
///
224-
/// # Safety
225-
///
226-
/// Same as `async_drop_in_place` except is lazy to avoid creating
227-
/// multiple mutable references.
228-
#[lang = "async_drop_defer"]
229-
async unsafe fn defer<T: ?Sized>(to_drop: *mut T) {
230-
// SAFETY: same safety requirements as `async_drop_in_place`
231-
unsafe { async_drop_in_place(to_drop) }.await
232-
}
233-
234-
/// If `T`'s discriminant is equal to the stored one then awaits `M`
235-
/// otherwise awaits the `O`.
236-
///
237-
/// # Safety
238-
///
239-
/// Users should carefully manage the returned future, since it would
240-
/// try creating an immutable reference from `this` and get pointee's
241-
/// discriminant.
242-
// FIXME(zetanumbers): Send and Sync impls
243-
#[lang = "async_drop_either"]
244-
async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>(
245-
other: O,
246-
matched: M,
247-
this: *mut T,
248-
discr: <T as DiscriminantKind>::Discriminant,
249-
) {
250-
// SAFETY: Guaranteed by the safety section of this funtion's documentation
251-
if unsafe { discriminant_value(&*this) } == discr {
252-
drop(other);
253-
matched.await
254-
} else {
255-
drop(matched);
256-
other.await
257-
}
258-
}
259-
260-
#[lang = "async_drop_deferred_drop_in_place"]
261-
async unsafe fn deferred_drop_in_place<T>(to_drop: *mut T) {
262-
// SAFETY: same safety requirements as with drop_in_place (implied by
263-
// function's name)
264-
unsafe { crate::ptr::drop_in_place(to_drop) }
265-
}
266-
267-
/// Used for noop async destructors. We don't use [`core::future::Ready`]
268-
/// because it panics after its second poll, which could be potentially
269-
/// bad if that would happen during the cleanup.
270-
#[derive(Clone, Copy)]
271-
struct Noop;
272-
273-
#[lang = "async_drop_noop"]
274-
fn noop() -> Noop {
275-
Noop
276-
}
277-
278-
impl Future for Noop {
279-
type Output = ();
280-
281-
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
282-
Poll::Ready(())
283-
}
31+
/// Executes the async destructor for this type.
32+
///
33+
/// This method is called implicitly when the value goes out of scope,
34+
/// and cannot be called explicitly.
35+
///
36+
/// When this method has been called, `self` has not yet been deallocated.
37+
/// That only happens after the method is over.
38+
///
39+
/// # Panics
40+
#[allow(async_fn_in_trait)]
41+
async fn drop(self: Pin<&mut Self>);
42+
}
43+
44+
/// Async drop.
45+
#[cfg(not(bootstrap))]
46+
#[unstable(feature = "async_drop", issue = "126482")]
47+
#[lang = "async_drop_in_place"]
48+
pub async unsafe fn async_drop_in_place<T: ?Sized>(_to_drop: *mut T) {
49+
// Code here does not matter - this is replaced by the
50+
// real implementation by the compiler.
28451
}

core/src/future/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ mod pending;
2020
mod poll_fn;
2121
mod ready;
2222

23+
#[cfg(not(bootstrap))]
2324
#[unstable(feature = "async_drop", issue = "126482")]
24-
pub use async_drop::{AsyncDrop, AsyncDropInPlace, async_drop, async_drop_in_place};
25+
pub use async_drop::{AsyncDrop, async_drop_in_place};
2526
#[stable(feature = "into_future", since = "1.64.0")]
2627
pub use into_future::IntoFuture;
2728
#[stable(feature = "future_readiness_fns", since = "1.48.0")]

core/src/ops/drop.rs

-7
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,3 @@ pub trait Drop {
240240
#[stable(feature = "rust1", since = "1.0.0")]
241241
fn drop(&mut self);
242242
}
243-
244-
/// Fallback function to call surface level `Drop::drop` function
245-
#[allow(drop_bounds)]
246-
#[lang = "fallback_surface_drop"]
247-
pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) {
248-
<T as Drop>::drop(x)
249-
}

core/src/ops/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ pub use self::deref::Receiver;
176176
pub use self::deref::{Deref, DerefMut};
177177
#[stable(feature = "rust1", since = "1.0.0")]
178178
pub use self::drop::Drop;
179-
pub(crate) use self::drop::fallback_surface_drop;
180179
#[stable(feature = "rust1", since = "1.0.0")]
181180
pub use self::function::{Fn, FnMut, FnOnce};
182181
#[stable(feature = "rust1", since = "1.0.0")]

0 commit comments

Comments
 (0)