about summary refs log tree commit diff
path: root/library/core
diff options
context:
space:
mode:
Diffstat (limited to 'library/core')
-rw-r--r--library/core/src/future/async_drop.rs271
-rw-r--r--library/core/src/future/mod.rs6
-rw-r--r--library/core/src/ops/drop.rs8
-rw-r--r--library/core/src/ops/mod.rs3
4 files changed, 288 insertions, 0 deletions
diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs
new file mode 100644
index 00000000000..0eb8d7bb328
--- /dev/null
+++ b/library/core/src/future/async_drop.rs
@@ -0,0 +1,271 @@
+#![unstable(feature = "async_drop", issue = "none")]
+
+use crate::fmt;
+use crate::future::{Future, IntoFuture};
+use crate::intrinsics::discriminant_value;
+use crate::marker::{DiscriminantKind, PhantomPinned};
+use crate::mem::MaybeUninit;
+use crate::pin::Pin;
+use crate::task::{ready, Context, Poll};
+
+/// Asynchronously drops a value by running `AsyncDrop::async_drop`
+/// on a value and its fields recursively.
+#[unstable(feature = "async_drop", issue = "none")]
+pub fn async_drop<T>(value: T) -> AsyncDropOwning<T> {
+    AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned }
+}
+
+/// A future returned by the [`async_drop`].
+#[unstable(feature = "async_drop", issue = "none")]
+pub struct AsyncDropOwning<T> {
+    value: MaybeUninit<T>,
+    dtor: Option<AsyncDropInPlace<T>>,
+    _pinned: PhantomPinned,
+}
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T> fmt::Debug for AsyncDropOwning<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("AsyncDropOwning").finish_non_exhaustive()
+    }
+}
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T> Future for AsyncDropOwning<T> {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // SAFETY: Self is pinned thus it is ok to store references to self
+        unsafe {
+            let this = self.get_unchecked_mut();
+            let dtor = Pin::new_unchecked(
+                this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())),
+            );
+            // AsyncDestuctors are idempotent so Self gets idempotency as well
+            dtor.poll(cx)
+        }
+    }
+}
+
+#[lang = "async_drop_in_place"]
+#[allow(unconditional_recursion)]
+// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed?
+unsafe fn async_drop_in_place_raw<T: ?Sized>(
+    to_drop: *mut T,
+) -> <T as AsyncDestruct>::AsyncDestructor {
+    // Code here does not matter - this is replaced by the
+    // real async drop glue constructor by the compiler.
+
+    // SAFETY: see comment above
+    unsafe { async_drop_in_place_raw(to_drop) }
+}
+
+/// Creates the asynchronous destructor of the pointed-to value.
+///
+/// # Safety
+///
+/// Behavior is undefined if any of the following conditions are violated:
+///
+/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes.
+///
+/// * `to_drop` must be properly aligned, even if `T` has size 0.
+///
+/// * `to_drop` must be nonnull, even if `T` has size 0.
+///
+/// * The value `to_drop` points to must be valid for async dropping,
+///   which may mean it must uphold additional invariants. These
+///   invariants depend on the type of the value being dropped. For
+///   instance, when dropping a Box, the box's pointer to the heap must
+///   be valid.
+///
+/// * While `async_drop_in_place` is executing or the returned async
+///   destructor is alive, the only way to access parts of `to_drop`
+///   is through the `self: Pin<&mut Self>` references supplied to
+///   the `AsyncDrop::async_drop` methods that `async_drop_in_place`
+///   or `AsyncDropInPlace<T>::poll` invokes. This usually means the
+///   returned future stores the `to_drop` pointer and user is required
+///   to guarantee that dropped value doesn't move.
+///
+#[unstable(feature = "async_drop", issue = "none")]
+pub unsafe fn async_drop_in_place<T: ?Sized>(to_drop: *mut T) -> AsyncDropInPlace<T> {
+    // SAFETY: `async_drop_in_place_raw` has the same safety requirements
+    unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) }
+}
+
+/// A future returned by the [`async_drop_in_place`].
+#[unstable(feature = "async_drop", issue = "none")]
+pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor);
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T: ?Sized> fmt::Debug for AsyncDropInPlace<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("AsyncDropInPlace").finish_non_exhaustive()
+    }
+}
+
+#[unstable(feature = "async_drop", issue = "none")]
+impl<T: ?Sized> Future for AsyncDropInPlace<T> {
+    type Output = ();
+
+    #[inline(always)]
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // SAFETY: This code simply forwards poll call to the inner future
+        unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx)
+    }
+}
+
+// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as
+//   with Drop impls
+/// Custom code within the asynchronous destructor.
+#[unstable(feature = "async_drop", issue = "none")]
+#[lang = "async_drop"]
+pub trait AsyncDrop {
+    /// A future returned by the [`AsyncDrop::async_drop`] to be part
+    /// of the async destructor.
+    #[unstable(feature = "async_drop", issue = "none")]
+    type Dropper<'a>: Future<Output = ()>
+    where
+        Self: 'a;
+
+    /// Constructs the asynchronous destructor for this type.
+    #[unstable(feature = "async_drop", issue = "none")]
+    fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>;
+}
+
+#[lang = "async_destruct"]
+#[rustc_deny_explicit_impl(implement_via_object = false)]
+trait AsyncDestruct {
+    type AsyncDestructor: Future<Output = ()>;
+}
+
+/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify
+/// generation of the code for `async_drop_in_place_raw`
+#[lang = "surface_async_drop_in_place"]
+async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) {
+    // SAFETY: We call this from async drop `async_drop_in_place_raw`
+    //   which has the same safety requirements
+    unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await }
+}
+
+/// Basically calls `Drop::drop` with pointer. Used to simplify generation
+/// of the code for `async_drop_in_place_raw`
+#[allow(drop_bounds)]
+#[lang = "async_drop_surface_drop_in_place"]
+async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
+    // SAFETY: We call this from async drop `async_drop_in_place_raw`
+    //   which has the same safety requirements
+    unsafe { crate::ops::fallback_surface_drop(&mut *ptr) }
+}
+
+/// Wraps a future to continue outputing `Poll::Ready(())` once after
+/// wrapped future completes by returning `Poll::Ready(())` on poll. This
+/// is useful for constructing async destructors to guarantee this
+/// "fuse" property
+struct Fuse<T> {
+    inner: Option<T>,
+}
+
+#[lang = "async_drop_fuse"]
+fn fuse<T>(inner: T) -> Fuse<T> {
+    Fuse { inner: Some(inner) }
+}
+
+impl<T> Future for Fuse<T>
+where
+    T: Future<Output = ()>,
+{
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // SAFETY: pin projection into `self.inner`
+        unsafe {
+            let this = self.get_unchecked_mut();
+            if let Some(inner) = &mut this.inner {
+                ready!(Pin::new_unchecked(inner).poll(cx));
+                this.inner = None;
+            }
+        }
+        Poll::Ready(())
+    }
+}
+
+/// Async destructor for arrays and slices.
+#[lang = "async_drop_slice"]
+async unsafe fn slice<T>(s: *mut [T]) {
+    let len = s.len();
+    let ptr = s.as_mut_ptr();
+    for i in 0..len {
+        // SAFETY: we iterate over elements of `s` slice
+        unsafe { async_drop_in_place_raw(ptr.add(i)).await }
+    }
+}
+
+/// Construct a chain of two futures, which awaits them sequentially as
+/// a future.
+#[lang = "async_drop_chain"]
+async fn chain<F, G>(first: F, last: G)
+where
+    F: IntoFuture<Output = ()>,
+    G: IntoFuture<Output = ()>,
+{
+    first.await;
+    last.await;
+}
+
+/// Basically a lazy version of `async_drop_in_place`. Returns a future
+/// that would call `AsyncDrop::async_drop` on a first poll.
+///
+/// # Safety
+///
+/// Same as `async_drop_in_place` except is lazy to avoid creating
+/// multiple mutable refernces.
+#[lang = "async_drop_defer"]
+async unsafe fn defer<T: ?Sized>(to_drop: *mut T) {
+    // SAFETY: same safety requirements as `async_drop_in_place`
+    unsafe { async_drop_in_place(to_drop) }.await
+}
+
+/// If `T`'s discriminant is equal to the stored one then awaits `M`
+/// otherwise awaits the `O`.
+///
+/// # Safety
+///
+/// User should carefully manage returned future, since it would
+/// try creating an immutable referece from `this` and get pointee's
+/// discriminant.
+// FIXME(zetanumbers): Send and Sync impls
+#[lang = "async_drop_either"]
+async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>(
+    other: O,
+    matched: M,
+    this: *mut T,
+    discr: <T as DiscriminantKind>::Discriminant,
+) {
+    // SAFETY: Guaranteed by the safety section of this funtion's documentation
+    if unsafe { discriminant_value(&*this) } == discr {
+        drop(other);
+        matched.await
+    } else {
+        drop(matched);
+        other.await
+    }
+}
+
+/// Used for noop async destructors. We don't use [`core::future::Ready`]
+/// because it panics after its second poll, which could be potentially
+/// bad if that would happen during the cleanup.
+#[derive(Clone, Copy)]
+struct Noop;
+
+#[lang = "async_drop_noop"]
+fn noop() -> Noop {
+    Noop
+}
+
+impl Future for Noop {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
+        Poll::Ready(())
+    }
+}
diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs
index 0f77a2d8343..c3bd18e30aa 100644
--- a/library/core/src/future/mod.rs
+++ b/library/core/src/future/mod.rs
@@ -12,6 +12,8 @@
 use crate::ptr::NonNull;
 use crate::task::Context;
 
+#[cfg(not(bootstrap))]
+mod async_drop;
 mod future;
 mod into_future;
 mod join;
@@ -36,6 +38,10 @@ pub use ready::{ready, Ready};
 #[stable(feature = "future_poll_fn", since = "1.64.0")]
 pub use poll_fn::{poll_fn, PollFn};
 
+#[cfg(not(bootstrap))]
+#[unstable(feature = "async_drop", issue = "none")]
+pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace};
+
 /// This type is needed because:
 ///
 /// a) Coroutines cannot implement `for<'a, 'b> Coroutine<&'a mut Context<'b>>`, so we need to pass
diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs
index 34dfa9e4c51..1325d90e4f3 100644
--- a/library/core/src/ops/drop.rs
+++ b/library/core/src/ops/drop.rs
@@ -238,3 +238,11 @@ pub trait Drop {
     #[stable(feature = "rust1", since = "1.0.0")]
     fn drop(&mut self);
 }
+
+/// Fallback function to call surface level `Drop::drop` function
+#[cfg(not(bootstrap))]
+#[allow(drop_bounds)]
+#[lang = "fallback_surface_drop"]
+pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) {
+    <T as Drop>::drop(x)
+}
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index ac808bec50e..81d5e5c949e 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -174,6 +174,9 @@ pub use self::deref::Receiver;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::drop::Drop;
 
+#[cfg(not(bootstrap))]
+pub(crate) use self::drop::fallback_surface_drop;
+
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::function::{Fn, FnMut, FnOnce};