blob: 41a6214a7cec878057ae0079323478a69ecad4ed [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#![cfg_attr(not(test), no_std)]
#![feature(negative_impls)]
//! Traits for memory management operations on wrapped C++ objects, based on
//! moveit.
//!
//! # Comparison with moveit
//!
//! ## Non-destructive move
//!
//! Unlike moveit, C++ moves are never Rust moves.
//!
//! This is important because it dramatically simplifies the mental model and
//! code involved. Everything to do with DerefMove can be removed. Furthermore,
//! it makes it *more useful*.
//!
//! In particular, imagine trying to write a *Rust* implementation of the
//! following C++ function:
//!
//! ```c++
//! void Swap(CxxClass& x, CxxClass& y) {
//! CxxClass tmp = std::move(x);
//! x = std::move(y);
//! y = std::move(tmp);
//! }
//! ```
//!
//! With destructive moves we're in a bind: any move from x destroys its source,
//! leaving us with nothing to assign to. C++ moves are non-destructive. Traits
//! modeling C++ moves, therefore, in order to be most compatible, should also
//! be non-destructive.
//!
//! Here is an implementation using the traits in this file, in pure Rust:
//!
//! ```
//! fn swap(mut x: Pin<&mut CxxClass>, mut y: Pin<&mut CxxClass>) {
//! emplace!{ let mut tmp = mov!(x.as_mut()); }
//! x.assign(mov!(y.as_mut()));
//! y.assign(mov!(tmp));
//! }
//! ```
//!
//! ## Curried `Ctor` traits
//!
//! Rather than `CopyCtor::copy_ctor` and `MoveCtor::move_ctor`, this crate
//! embraces `Ctor` itself as the single unique way to initialize everything.
//! And so copy-constructible types are not things which implement `CopyCtor`,
//! but rather, things which implement `CtorNew<&T>`. Similarly,
//! move-constructible things implement `CtorNew<RvalueReference<'_, T>>`.
//!
//! ## Blanket impls
//!
//! So that Rust types can be used against C++ functions (even templated ones),
//! the copy and move traits have blanket impls for all suitable Unpin types.
//!
//! More significantly, we take the position that all Unpin types are their own
//! `Ctor`. This makes `Ctor`-based initialization nearly a perfect superset of
//! normal initialization rules: for a non-self-referential Rust type (an Unpin
//! type), it looks the same as normal, but for C++-like !Unpin types we use
//! custom `Ctor` values to initialize in-place.
//!
//! Blanket implementing based on `Unpin` means that we're treating `T : Unpin`
//! as effectively meaning "T is safe to deal with by value", which is close to,
//! but not quite, what it means. (There are some types which are !Unpin but
//! would be safe to treat as normal rust types. Unfortunately, there is no way
//! to detect them.)
//!
//! ## Overload trait
//!
//! This module also includes a prototype alternative API which more directly
//! models overloaded constructors: `T : CtorNew<(A, B, C)>` is roughly
//! equivalent to the C++ "T has a constructor taking arguments of type A, B,
//! and C".
//!
//! ## StackBox
//!
//! Moveit needs a StackBox type which is comparable to Box, except stored on
//! the stack -- in particular, which implements OuterDrop and the like.
//!
//! Since DerefMove is not used here, we instead are able to export only a
//! Pin<&mut T> to users.
//!
//! One still needs something similar to `Slot` or `StackBox`: a `MaybeUninit`
//! type which will run drop. This isn't exposed to users directly if they are
//! simply storing in a local variable.
//!
//! ## Structs and the `ctor!` macro
//!
//! `ctor` adds a `ctor!` macro to make it easy to initialize a struct
//! that contains non-trivially-relocatable fields.
//!
//! ## Features
//!
//! This library requires the following unstable features enabled in users:
//!
//! **negative_impls:**
//! This is used to allow trait coherence checking for `!Unpin` types. A
//! "blanket impl for all `Unpin` types is used to make them constructible with
//! `ctor`, and Rust believes this may conflict with types that use
//! `PhantomPinned`. It knows no conflict exists if, instead, types impl
//! `!Unpin`.
extern crate alloc;
use alloc::boxed::Box;
use core::marker::{PhantomData, Unpin};
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ops::{Deref, DerefMut};
use core::pin::Pin;
pub use ctor_proc_macros::*;
/// The string constant for #[must_use] to describe why you must use a `Ctor`.
macro_rules! must_use_ctor {
() => {
"A Ctor is not invoked unless emplaced, using e.g. `emplace!{}`, or `Box::emplace()`."
};
}
/// The string constant for #[must_use] to describe why you must use a
/// `Ctor`/`Assign` source.
macro_rules! must_use_ctor_assign {
($name:literal) => {
concat!(
$name,
" is not invoked unless emplaced, using e.g. `emplace!{}`, or `Box::emplace()`, or",
" unless assigned from, using `.assign()`."
)
};
}
// ======================
// ~Unchanged from moveit
// ======================
#[must_use = must_use_ctor!()]
pub trait Ctor {
type Output;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<Self::Output>>);
/// Returns a chained Ctor, which will invoke `f` after construction.
///
/// for example, these two snippets are equivalent:
///
/// ```
/// emplace! { let x = y; }
/// x.mutating_method();
/// ```
///
/// ```
/// emplace! { let x = y.ctor_then(|mut inited| inited.mutating_method()); }
/// ```
fn ctor_then<F: FnOnce(Pin<&mut Self::Output>)>(self, f: F) -> CtorThen<Self, F>
where
Self: Sized,
{
CtorThen { ctor: self, f }
}
}
pub trait Emplace<T>: Sized {
fn emplace<C: Ctor<Output = T>>(c: C) -> Pin<Self>;
}
impl<T> Emplace<T> for Box<T> {
fn emplace<C: Ctor<Output = T>>(ctor: C) -> Pin<Box<T>> {
let mut uninit = Box::new(MaybeUninit::<T>::uninit());
unsafe {
let pinned = Pin::new_unchecked(&mut *uninit);
ctor.ctor(pinned);
Pin::new_unchecked(Box::from_raw(Box::into_raw(uninit).cast::<T>()))
}
}
}
#[must_use = must_use_ctor!()]
pub struct FnCtor<Output, F: FnOnce(Pin<&mut MaybeUninit<Output>>)>(pub F, PhantomData<fn(Output)>);
impl<Output, F: FnOnce(Pin<&mut MaybeUninit<Output>>)> FnCtor<Output, F> {
pub fn new(f: F) -> Self {
Self(f, PhantomData)
}
}
impl<Output, F: FnOnce(Pin<&mut MaybeUninit<Output>>)> Ctor for FnCtor<Output, F> {
type Output = Output;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<Output>>) {
self.0(dest);
}
}
/// !Unpin to override the blanket Ctor impl.
impl<Output, F> !Unpin for FnCtor<Output, F> {}
/// Copy type.
///
/// This creates a new `P::Target` by copying -- either copy-construction
/// (construction from `&*P`), or copy-assignment (assignment from &*P`). It can
/// also be used directly as a `Ctor`.
///
/// Note: this does not actually copy `P` until it is used.
#[must_use = must_use_ctor_assign!("Copy")]
pub struct Copy<P: Deref>(P);
impl<Output: for<'a> CtorNew<&'a Output>, P: Deref<Target = Output>> Ctor for Copy<P> {
type Output = Output;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<Output>>) {
Output::ctor_new(&*self.0).ctor(dest);
}
}
/// !Unpin to override the blanket Ctor impl.
impl<P> !Unpin for Copy<P> {}
/// Returns a `Copy` which can be used as a `CtorNew` or `Assign` source, or as
/// a `Ctor` directly.
///
/// Note: this does not actually copy the parameter until it is used.
pub fn copy<T: for<'a> CtorNew<&'a T>, P: Deref<Target = T>>(src: P) -> Copy<P> {
Copy(src)
}
// ================================
// DerefMut based move construction
// ================================
/// Rvalue Reference (move-reference) type.
///
/// This creates a new `T` by moving -- either move-construction (construction
/// from `RvalueReference(&*P)`), or move-assignment (assignment from
/// `RvalueReference(&*P)`).
///
/// Note: this does not actually move until it is used.
#[must_use = must_use_ctor_assign!("RvalueReference")]
#[repr(transparent)]
pub struct RvalueReference<'a, T>(pub Pin<&'a mut T>);
impl<T> RvalueReference<'_, T> {
pub fn as_const(&self) -> ConstRvalueReference<'_, T> {
ConstRvalueReference(&*self.0)
}
pub fn as_mut(&mut self) -> Pin<&mut T> {
self.0.as_mut()
}
pub fn get_ref(&self) -> &T {
// It would be nice to return &'a T, but that would not be sound, and as a
// result Pin makes it impossible (in safe code). Consider:
//
// let my_pin: Pin<&mut T> = ...;
// let my_borrow = RvalueReference(my_pin.as_mut()).get_ref();
//
// The lifetime of my_borrow CANNOT be 'a, but instead MUST be scoped to the
// reborrowed lifetime of the pin, or else it would violate the aliasing
// rules by coexisting with my_pin. Thus, get_ref must return &T, not
// &'a T. (For the same reason, as_const returns a ConstRvalueReference
// whose lifetime is bound by self, not 'a.)
&*self.0
}
}
impl<T> Deref for RvalueReference<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get_ref()
}
}
impl<'a, T> Ctor for RvalueReference<'a, T>
where
T: CtorNew<Self>,
{
type Output = T;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<T>>) {
T::ctor_new(self).ctor(dest);
}
}
/// Converts to an RvalueReference.
///
/// Do not use this trait directly, instead, cast to an RvalueReference using
/// the `mov!()` macro.
pub trait DerefRvalueReference: Deref
where
Self::Target: Sized,
{
fn deref_rvalue_reference(&mut self) -> RvalueReference<'_, Self::Target>;
}
impl<T> DerefRvalueReference for Pin<T>
where
T: DerefMut,
Self::Target: Sized,
{
fn deref_rvalue_reference(&mut self) -> RvalueReference<'_, Self::Target> {
RvalueReference(self.as_mut())
}
}
impl<T> DerefRvalueReference for &mut T
where
T: Unpin,
Self::Target: Sized,
{
fn deref_rvalue_reference(&mut self) -> RvalueReference<'_, Self::Target> {
RvalueReference(Pin::new(self))
}
}
/// !Unpin to override the blanket `Ctor` impl.
impl<'a, T> !Unpin for RvalueReference<'a, T> {}
/// Const rvalue reference (move-reference) type. Usually not very helpful.
///
/// This implicitly converts to `T` by const-moving -- either
/// const-move-construction (construction from `ConstRvalueReference(&x)`), or
/// const-move-assignment (assignment from `ConstRvalueReference(&x)`).
#[must_use = must_use_ctor_assign!("ConstRvalueReference")]
#[repr(transparent)]
pub struct ConstRvalueReference<'a, T>(pub &'a T);
impl<'a, T> ConstRvalueReference<'a, T> {
pub fn get_ref(&mut self) -> &'a T {
self.0
}
}
impl<T> Deref for ConstRvalueReference<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a, T> Ctor for ConstRvalueReference<'a, T>
where
T: CtorNew<Self>,
{
type Output = T;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<T>>) {
T::ctor_new(self).ctor(dest);
}
}
/// !Unpin to override the blanket `Ctor` impl.
impl<'a, T> !Unpin for ConstRvalueReference<'a, T> {}
/// Creates a "to-be-moved" pointer for `src`.
///
/// In other words, this is analogous to C++ `std::move`, except that this can
/// directly create an `RvalueReference<T>` out of e.g. a `Pin<Box<T>>`. The
/// resulting `RvalueReference` has the lifetime of a temporary, after which the
/// parameter is destroyed.
///
/// The resulting `RvalueReference` can be used as a `CtorNew` or `Assign`
/// source, or as a `Ctor` directly.
///
/// Note: this does not actually move the parameter until it is used.
#[macro_export]
macro_rules! mov {
($p:expr) => {
$crate::DerefRvalueReference::deref_rvalue_reference(&mut { $p })
};
}
#[macro_export]
macro_rules! const_mov {
($p:expr) => {
$crate::ConstRvalueReference(&*{ $p })
};
}
// =============
// Blanket impls
// =============
//
// For interop with C++ templates that may need to take Rust types, we will want
// blanket impls for Rust types where it is safe to do so.
/// All Rust types are their own constructor, if it is known to be safe (i.e.
/// Unpin).
///
/// (Note that this is an approximation: some types are safe to deal with by
/// value, as long as they have never been pinned. In `ctor`, we assume
/// that *anything* which is `!Unpin` is only safe to initialize via a manually
/// provided `Ctor`.)
///
/// (Note: to avoid overlapping impls, every type that implements `Ctor` by hand
/// must be `!Unpin`.)
///
/// This allows code to safely accept direct initialization of `Unpin` Rust
/// values, while also accepting `Ctor`-based initialization for `!Unpin`
/// values: use `Ctor`-based initialization for both, and as a special case,
/// `Ctor`-based initialization looks identical to direct initialization when
/// the value is `Unpin`. For example, the `ctor!` macro looks like direct
/// initialization for everything `Unpin`.
///
/// A contrasting design might be to have a separate initialization syntax for
/// direct vs `Ctor`-based initialization. However, that would still likely need
/// to be restricted to `Unpin` types for safety.
impl<T: Unpin> Ctor for T {
type Output = T;
unsafe fn ctor(self, mut dest: Pin<&mut MaybeUninit<Self>>) {
dest.as_mut_ptr().write(self);
}
}
/// Constructs via a Rust move.
#[must_use = must_use_ctor!()]
pub struct RustMoveCtor<T>(T);
impl<T> !Unpin for RustMoveCtor<T> {}
impl<T> Ctor for RustMoveCtor<T> {
type Output = T;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<T>>) {
Pin::into_inner_unchecked(dest).as_mut_ptr().write(self.0);
}
}
/// All Rust types are C++-default-constructible if safe (i.e. Unpin + Default).
impl<T: Unpin + Default + Sized> CtorNew<()> for T {
type CtorType = RustMoveCtor<Self>;
fn ctor_new(_: ()) -> Self::CtorType {
RustMoveCtor(Default::default())
}
}
/// All Unpin Rust types are C++-copyable if they are Rust-cloneable.
///
/// (Unpin is required for safety; otherwise, this would violate the Pin
/// guarantee.)
impl<T: Unpin + Clone> CtorNew<&T> for T {
type CtorType = RustMoveCtor<Self>;
fn ctor_new(src: &Self) -> Self::CtorType {
RustMoveCtor(src.clone())
}
}
// ===========
// ctor_then()
// ===========
/// A `Ctor` which constructs using `self.ctor`, and then invokes `f` on the
/// resulting object.
///
/// This struct is created by the `ctor_then` method on `Ctor`. See its
/// documentation for more.
#[must_use = must_use_ctor!()]
pub struct CtorThen<C: Ctor, F: FnOnce(Pin<&mut C::Output>)> {
ctor: C,
f: F,
}
impl<C: Ctor, F: FnOnce(Pin<&mut C::Output>)> Ctor for CtorThen<C, F> {
type Output = C::Output;
unsafe fn ctor(self, mut dest: Pin<&mut MaybeUninit<Self::Output>>) {
self.ctor.ctor(dest.as_mut());
let dest = Pin::new_unchecked(Pin::into_inner_unchecked(dest).assume_init_mut());
(self.f)(dest)
}
}
impl<C: Ctor, F: FnOnce(Pin<&mut C::Output>)> !Unpin for CtorThen<C, F> {}
// ========
// emplace!
// ========
//
// The emplace!{} macro is now a little simpler, as it doesn't require StackBox
// in the public interface: it can use &mut.
/// Emplace a constructor into a local or temporary.
/// Syntax: `emplace! { let mut varname = expr() }`, where `expr()` evaluates to
/// a `Ctor<Output=T>`. `varname` will be a `Pin<&mut T>`.
///
/// If the emplaced value will just be used for the duration of one statement,
/// the `emplace!(ctor)` syntax can be used instead, to emplace the ctor into a
/// temporary. e.g. `foo(emplace!(ctor))` to pass a pinned reference to the
/// emplaced value to a function.
#[macro_export]
macro_rules! emplace {
($expr:expr) => {
$crate::Slot::unsafe_new().unsafe_construct($expr).unsafe_as_pin_unchecked()
};
(@emplace_one let [$($mut_:tt)?] $var:ident [$($type_:tt)*]= $expr:expr;) => {
let mut $var = $crate::Slot::unsafe_new();
$var.unsafe_construct($expr);
let $($mut_)* $var $($type_)* = $var.unsafe_as_pin_unchecked();
};
// Base case for repeated lets: empty emplace.
() => {};
// Recursive case: let [mut] x [: T] = ...;
// There are four different combinations of mutability and explicit type parameter, we have to
// match all of them.
(let mut $var:ident : $t:ty = $expr:expr; $($remaining_lets:tt)*) => {
$crate::emplace! {@emplace_one let [mut] $var [:$t] = $expr;}
$crate::emplace! {$($remaining_lets)*};
};
(let mut $var:ident = $expr:expr; $($remaining_lets:tt)*) => {
$crate::emplace! {@emplace_one let [mut] $var []= $expr;}
$crate::emplace! {$($remaining_lets)*};
};
(let $var:ident : $t:ty = $expr:expr; $($remaining_lets:tt)*) => {
$crate::emplace! {@emplace_one let [] $var [:$t] = $expr;}
$crate::emplace! {$($remaining_lets)*};
};
(let $var:ident = $expr:expr; $($remaining_lets:tt)*) => {
$crate::emplace! {@emplace_one let [] $var [] = $expr;}
$crate::emplace! {$($remaining_lets)*};
};
}
// ====
// Slot
// ====
//
// Alternate design: we could expose without the is_initialized flag, which
// would require that all users initialize the type before drop. We may still
// need such a type for interop, since that can be repr(transparent). But it is
// *exceptionally* dangerous, as it requires that all code paths will initialize
// before drop. For example, it means that Ctor is not allowed to panic.
//
// Hypothesis: at least for local variables and reasoning, rustc will be able to
// elide the bool and be equally performant, while also being substantially
// safer.
/// A pinned optional type, which can store in-place constructed objects.
///
/// To create a slot safely, it must be constructed in place, using (for
/// example) the `emplace!` macro. It then can operate as a structurally pinned
/// variant of `Option`, allowing for pinned access to the interior.
///
/// Examples:
///
/// ```
/// // Slots can be used to implement a "slotted return value".
/// fn slotted_return(slot: Pin<&mut Slot<u32>>) -> Pin<&mut u32> {
/// slot.replace(42)
/// }
///
/// emplace! {let slot = Slot::uninit(); }
/// let rv = slotted_return(slot);
/// assert_eq!(*rv, 42);
/// ```
///
/// ```
/// // Slots can also be used for plain output parameters.
/// fn slotted_out_param(slot: Pin<&mut Slot<u32>>) {
/// slot.replace(42);
/// }
///
/// emplace! {let mut slot = Slot::uninit(); }
/// slotted_out_param(slot.as_mut());
/// assert_eq!(*slot.as_opt().unwrap(), 42);
/// ```
pub struct Slot<T> {
is_initialized: bool,
maybe_uninit: MaybeUninit<T>,
}
impl<T> Drop for Slot<T> {
fn drop(&mut self) {
unsafe { Pin::new_unchecked(self) }.clear()
}
}
impl<T> !Unpin for Slot<T> {}
impl<T> Slot<T> {
pub fn uninit() -> impl Ctor<Output = Self> {
RustMoveCtor(Self::unsafe_new())
}
pub fn new(value: impl Ctor<Output = T>) -> impl Ctor<Output = Self> {
RustMoveCtor(Self::unsafe_new()).ctor_then(|slot| {
slot.replace(value);
})
}
pub fn clear(self: Pin<&mut Self>) {
if self.is_initialized {
let Self { is_initialized, maybe_uninit } = unsafe { Pin::into_inner_unchecked(self) };
unsafe {
core::ptr::drop_in_place(maybe_uninit.as_mut_ptr());
}
*is_initialized = false;
}
}
pub fn replace(mut self: Pin<&mut Self>, value: impl Ctor<Output = T>) -> Pin<&mut T> {
self.as_mut().clear();
{
let Self { is_initialized, maybe_uninit } =
unsafe { Pin::into_inner_unchecked(self.as_mut()) };
unsafe {
value.ctor(Pin::new_unchecked(maybe_uninit));
}
*is_initialized = true;
}
self.as_opt_mut().unwrap()
}
pub fn as_opt_mut(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
if self.is_initialized {
Some(unsafe { Pin::into_inner_unchecked(self) }.unsafe_as_pin_unchecked())
} else {
None
}
}
pub fn as_opt(&self) -> Option<&T> {
if self.is_initialized {
Some(unsafe { self.maybe_uninit.assume_init_ref() })
} else {
None
}
}
}
// Hidden: these are only for use by safety macros.
#[doc(hidden)]
impl<T> Slot<T> {
pub fn unsafe_new() -> Self {
Slot { is_initialized: false, maybe_uninit: MaybeUninit::uninit() }
}
// The following two functions are not marked `unsafe` so that they can be
// called from a single expression in a macro, without an `unsafe{}` block
// which would also cover macro parameters.
//
// One alternative would be an immediately-invoked lambda, but this forces type
// inference, which leads to confusing error messages when it isn't
// inferrable. And, since this is a lambda, the type is not known concretely
// to begin with!
//
// We could instead use a generic function with `impl Ctor` and the like, but
// this function would be unsafe... leading back to the very original
// problem!
//
// So here we bite the bullet: these functions should ideally be marked
// "unsafe", but, seeing as they are only invoked from a macro, and that
// macro needs to invoke expressions in calls to these functions, they are
// only *named* "unsafe_foo" instead.
/// Safety: must not have already been constructed, as that would violate
/// the pin guarantee.
pub fn unsafe_construct(&mut self, ctor: impl Ctor<Output = T>) -> &mut Self {
unsafe { ctor.ctor(Pin::new_unchecked(&mut self.maybe_uninit)) };
self.is_initialized = true;
self
}
/// Safety: pin guarantee, assumes init.
pub fn unsafe_as_pin_unchecked(&mut self) -> Pin<&mut T> {
unsafe { Pin::new_unchecked(self.maybe_uninit.assume_init_mut()) }
}
}
#[doc(hidden)]
pub mod macro_internal {
use super::*;
pub use core::mem::MaybeUninit;
pub use core::pin::Pin;
/// Workaround for more_qualified_paths.
/// Instead of `<Foo as Bar>::Assoc { ... }`, which requires that feature,
/// we can use `Identity<<Foo as Bar>::Assoc> { ... }`.
///
/// See https://github.com/rust-lang/rust/issues/86935#issuecomment-1146670057
///
/// TODO(jeanpierreda): Delete this when the feature is stabilized.
pub type Identity<T> = T;
/// Trait which causes compilation error if a `#[recursively_pinned]` struct
/// impls `Drop`.
///
/// Idea from https://docs.rs/pin-project/latest/pin_project/attr.pin_project.html#safety
pub trait DoNotImplDrop {}
#[allow(drop_bounds)]
impl<T: Drop> DoNotImplDrop for T {}
/// Drops a pointer when dropped.
///
/// Safety: this will drop the pointer, so this is only safe if no other
/// code will access the pointer afterwards. This includes drop: the
/// pointer must itself be in a ManuallyDrop.
pub struct UnsafeDropGuard<T>(*mut T);
impl<T> Drop for UnsafeDropGuard<T> {
fn drop(&mut self) {
unsafe { core::ptr::drop_in_place(self.0) };
}
}
/// Initializes a field, and returns a drop guard which will drop that
/// field.
///
/// Intended use: when initializing a struct field-by-field, each
/// initialized field should be guarded in case of panic. Once the
/// struct is completely initialized, the drop guards can be forgotten
/// with `std::mem::forget()`. See the `ctor!` macro, where this is used.
///
/// Safety: the field must satisfy the Pin guarantee.
pub unsafe fn init_field<T>(field: *mut T, ctor: impl Ctor<Output = T>) -> impl Drop {
// safety: MaybeUninit<T> is the same layout as T, the caller guarantees it's
// pinned.
let maybe_uninit = field as *mut MaybeUninit<T>;
let pinned = Pin::new_unchecked(&mut *maybe_uninit);
Ctor::ctor(ctor, pinned);
UnsafeDropGuard(field)
}
pub fn require_recursively_pinned<_T: RecursivelyPinned>() {}
}
// =====================
// #[recursively_pinned]
// =====================
/// The `RecursivelyPinned` trait asserts that when the struct is pinned, every
/// field is also pinned.
///
/// This trait is automatically implemented for any `#[recursively_pinned]`
/// struct.
///
/// ## Safety
///
/// Only use if you never directly access fields of a pinned object. For
/// example, with the pin-project crate, all fields should be marked `#[pin]`.
pub unsafe trait RecursivelyPinned {
/// An associated type with the same fields, minus any which are not
/// initialized by the `ctor!()` macro.
///
/// For example, the following struct `CtorOnly` can be constructed by value
/// using `ctor!()`, but not using normal Rust initialization.
/// Effectively, the struct is forced into only ever existing in a
/// pinned state.
///
/// ```
/// // (Alternatively, `#[non_exhaustive]` may be used instead of the private field.)
/// pub struct CtorOnly {
/// pub field: i32,
/// _must_construct_using_ctor: [(); 0],
/// }
///
/// // The same struct, but without the private field.
/// // (Alternatively, without `#[non_exhaustive]`.)
/// pub struct CtorOnlyFields {
/// pub field: i32,
/// }
///
/// unsafe impl RecursivelyPinned for CtorOnly {
/// type CtorInitializedFields = CtorOnlyFields;
/// }
/// ```
///
/// By using `CtorInitializedFields` paired with a private field (or
/// `#[non_exhaustive]`), the following code is now invalid:
///
/// ```ignore
/// # // TODO(jeanpierreda): make this tested, somehow.
/// // Fails to compile: did not specify _must_construct_using_ctor, and cannot,
/// // because it is private
/// let x = CtorOnly {field: 3};
/// ```
///
/// While construction using `ctor!()` works fine:
///
/// ```ignore
/// emplace!{let x = ctor!(CtorOnly {field: 3})}
/// ```
///
/// The size and layout of `CtorInitializedFields` is ignored; it only
/// affects which field names are required for complete `ctor!()`
/// initialization. Any fields left out of the `CtorInitializedFields` type
/// will not be initialized, so they should generally be zero-sized.
type CtorInitializedFields;
}
/// The drop trait for `#[recursively_pinned(PinnedDrop)]` types.
///
/// It is never valid to implement `Drop` for a recursively-pinned type, as this
/// would be unsound: the `&mut self` in `drop` would allow the pin guarantee to
/// be violated.
///
/// Instead, if such a struct is to implement drop, it must pass `PinnedDrop` to
/// `recursively_pinned`, and implement the `PinnedDrop` trait.
///
/// See also the [analogous `pin_project` feature](https://docs.rs/pin-project/latest/pin_project/attr.pinned_drop.html)
pub trait PinnedDrop {
/// Run the drop logic for self.
///
/// ## Safety
///
/// If called from anywhere other than the automatically-generated
/// `Drop::drop`, the behavior is undefined.
///
/// To manually drop the value, use `ManuallyDrop` or use
/// `std::ptr::drop_in_place` (etc.) instead.
unsafe fn pinned_drop(self: Pin<&mut Self>);
}
// =====
// ctor!
// =====
/// The `ctor!` macro evaluates to a `Ctor` for a Rust struct, with
/// user-specified fields.
///
/// (This was inspired by, but takes no code from, https://crates.io/crates/project-uninit.)
///
/// Example use:
///
/// ```
/// fn new() -> impl Ctor<Output=MyStruct> {
/// ctor!(MyStruct {field_1: default_ctor(), field_2: 42})
/// }
///
/// // Actually invoke the Ctor to create a new MyStruct:
/// emplace! { let mut my_struct = MyStruct::new(); }
/// ```
#[macro_export]
macro_rules! ctor {
// Note: we're using `ident (::ident)*` for the type names because neither `ident` nor `path`
// really work perfectly -- `ident` is great, except that `foo::bar` isn't an ident. `path` is
// great, except that e.g. parentheses can't follow a path. (This is not fixable: FnOnce(T) is
// also a path, so parens can't follow paths due to the ambiguous parse). Thus... we use ident,
// and manually reconstruct the path.
//
// TODO(jeanpierreda): support <X as Y> and foo<Z> in paths.
//
// tt is used for the field names, because this allows for both integer fields for tuple
// structs, and named fields for non-tuple structs.
( $t:ident $(:: $ts:ident)* {$( $name:tt: $sub_ctor:expr ),* $(,)?} ) => {
{
use $t $(:: $ts)* as Type;
$crate::FnCtor::new(|x: $crate::macro_internal::Pin<&mut $crate::macro_internal::MaybeUninit<Type>>| {
// Unused if Type is fieldless.
#[allow(unused_imports)] use ::core::ptr::addr_of_mut;
struct DropGuard;
let drop_guard = DropGuard;
let x_mut = unsafe{$crate::macro_internal::Pin::into_inner_unchecked(x)}.as_mut_ptr();
let _ = &x_mut; // silence unused_variables warning if Type is fieldless.
// Enforce that the ctor!() expression resembles a valid direct initialization
// expression, by using the names in a conventional literal.
// For structs, this fails to compile unless the names are fully exhaustive.
// For unions, it fails to compile unless precisely one name is used.
// In both cases, this ensures that we only compile when expressions corresponding
// to normal init are used, with unsurprising semantics.
let _ = |x: Type| {
let _ = &x; // silence unused_variables warning if Type is fieldless.
// If this fails to compile, not every field was specified in the ctor! invocation.
// The `panic!(...)` allows us to avoid moving out of x, while still pretending to
// fill in each field.
#[allow(unreachable_code, unused_unsafe)] $crate::macro_internal::Identity::<
<Type as $crate::RecursivelyPinned>::CtorInitializedFields> {
// unsafe {} block is in case this is a *union* literal, rather than
// a struct literal.
$($name: panic!("{}", unsafe {&x.$name} as *const _ as usize),)*
};
};
// Enforce that the type is RecursivelyPinned.
$crate::macro_internal::require_recursively_pinned::<Type>();
$(
let sub_ctor = $sub_ctor;
let field_drop = unsafe {
$crate::macro_internal::init_field(
// safety: this is almost verbatim the same as the second example at
// https://doc.rust-lang.org/nightly/std/ptr/macro.addr_of_mut.html
addr_of_mut!((*x_mut).$name),
sub_ctor)
};
let drop_guard = (drop_guard, field_drop);
)*
::core::mem::forget(drop_guard);
})
}
};
// Unit struct ctor.
($t:ident $(:: $ts:ident)*) => {$crate::ctor!($t $(:: $ts)* { })};
// Conventional tuple struct syntax (with parens, no integer names) supported for < 8 fields.
// Otherwise, use MyTupleStruct{0: ..., 1: ...} syntax, which works for any number of fields.
// Generated as so:
/* python3 -c 'for i in range(8):
ctor_ins = ", ".join(f"$ctor_{j}:expr" for j in range(i))
ctor_outs = ", ".join(f"{j}: $ctor_{j}" for j in range(i))
print(f" ($t:ident $(:: $ts:ident)* ({ctor_ins})) => {{$crate::ctor!($t $(:: $ts)* {{ {ctor_outs} }})}};")' */
($t:ident $(:: $ts:ident)* ()) => {$crate::ctor!($t $(:: $ts)* { })};
($t:ident $(:: $ts:ident)* ($ctor_0:expr)) => {$crate::ctor!($t $(:: $ts)* { 0: $ctor_0 })};
($t:ident $(:: $ts:ident)* ($ctor_0:expr, $ctor_1:expr)) => {$crate::ctor!($t $(:: $ts)* { 0: $ctor_0, 1: $ctor_1 })};
($t:ident $(:: $ts:ident)* ($ctor_0:expr, $ctor_1:expr, $ctor_2:expr)) => {$crate::ctor!($t $(:: $ts)* { 0: $ctor_0, 1: $ctor_1, 2: $ctor_2 })};
($t:ident $(:: $ts:ident)* ($ctor_0:expr, $ctor_1:expr, $ctor_2:expr, $ctor_3:expr)) => {$crate::ctor!($t $(:: $ts)* { 0: $ctor_0, 1: $ctor_1, 2: $ctor_2, 3: $ctor_3 })};
($t:ident $(:: $ts:ident)* ($ctor_0:expr, $ctor_1:expr, $ctor_2:expr, $ctor_3:expr, $ctor_4:expr)) => {$crate::ctor!($t $(:: $ts)* { 0: $ctor_0, 1: $ctor_1, 2: $ctor_2, 3: $ctor_3, 4: $ctor_4 })};
($t:ident $(:: $ts:ident)* ($ctor_0:expr, $ctor_1:expr, $ctor_2:expr, $ctor_3:expr, $ctor_4:expr, $ctor_5:expr)) => {$crate::ctor!($t $(:: $ts)* { 0: $ctor_0, 1: $ctor_1, 2: $ctor_2, 3: $ctor_3, 4: $ctor_4, 5: $ctor_5 })};
($t:ident $(:: $ts:ident)* ($ctor_0:expr, $ctor_1:expr, $ctor_2:expr, $ctor_3:expr, $ctor_4:expr, $ctor_5:expr, $ctor_6:expr)) => {$crate::ctor!($t $(:: $ts)* { 0: $ctor_0, 1: $ctor_1, 2: $ctor_2, 3: $ctor_3, 4: $ctor_4, 5: $ctor_5, 6: $ctor_6 })};
}
// ==========
// CtorNew traits
// ==========
//
// Finally, we introduce some new traits for assignment.
/// Destroy-then-reconstruct. Sidesteps `operator=`, instead reconstructing
/// in-place.
///
/// If the object cannot be destroyed/reconstructed in place (e.g. it is a base
/// class subobject), the behavior is undefined.
///
/// If `ctor` unwinds, the process will crash.
///
/// This is a bit more fragile than, and a lot less common than, `operator=`,
/// but allows for taking advantage of copy/move elision more aggressively,
/// rather than requiring materialization into a temporary before triggering
/// assignment.
///
/// That means that e.g. instead of calling `x.assign(&*emplace!(foo))`, you can
/// directly call `x.reconstruct_unchecked(foo)` -- provided you are OK with the
/// differing constructor/destructor ordering, and satisfy safety criteria.
///
/// # Safety
///
/// Dividing sources of UB by language ruleset:
///
/// **C++**: The behavior is undefined if `self` is a base class subobject
/// or `[[no_unique_address]]` member.
///
/// See: http://eel.is/c++draft/basic.life#8
///
/// And see https://chat.google.com/room/AAAAl3r59xQ/auNOifgQa1c for discussion.
///
/// **Rust**: This is safe. Note that since this calls `drop()` on
/// the pinned pointer, it satisfies the pin guarantee, and is allowed to then
/// re-init it with something else. In effect, this is just the in-place Ctor
/// version of the existing method `Pin<T>::set(T)`.
pub trait ReconstructUnchecked: Sized {
/// # Safety
/// See trait documentation.
unsafe fn reconstruct_unchecked(self: Pin<&mut Self>, ctor: impl Ctor<Output = Self>) {
let self_ptr = Pin::into_inner_unchecked(self) as *mut _;
core::ptr::drop_in_place(self_ptr);
abort_on_unwind(move || {
let maybe_uninit_self = &mut *(self_ptr as *mut MaybeUninit<Self>);
ctor.ctor(Pin::new_unchecked(maybe_uninit_self));
});
}
}
impl<T> ReconstructUnchecked for T {}
/// Destroy-then-reconstruct. Sidesteps `operator=`, instead reconstructing
/// in-place.
///
/// If `ctor` unwinds, the process will crash.
///
/// This is a bit more fragile than, and a lot less common than, `operator=`,
/// but allows for taking advantage of copy/move elision more aggressively,
/// rather than requiring materialization into a temporary before triggering
/// assignment.
///
/// That means that e.g. instead of calling `x.assign(&*emplace!(foo))`, you can
/// directly call `x.reconstruct(foo)` -- provided you are OK with the differing
/// constructor/destructor ordering.
///
/// # Implementation safety
///
/// Implementors must ensure that it is safe to destroy and reconstruct the
/// object. Most notably, if `Self` is a C++ class, it must not be a base class.
/// See http://eel.is/c++draft/basic.life#8
///
/// # Safety
///
/// Note that this is not safe to call on `[[no_unique_address]]` member
/// variables, but the interface is marked safe, because those can only be
/// produced via unsafe means.
pub unsafe trait Reconstruct: ReconstructUnchecked {
fn reconstruct(self: Pin<&mut Self>, ctor: impl Ctor<Output = Self>) {
unsafe { self.reconstruct_unchecked(ctor) };
}
}
/// Safety: anything implementing `Unpin` is Rust-assignable, and
/// Rust-assignment is inherently destroy+reconstruct.
unsafe impl<T: Unpin> Reconstruct for T {}
/// Run f, aborting if it unwinds.
///
/// Because this aborts on unwind, f is not required to be unwind-safe.
#[inline]
fn abort_on_unwind<T, F: FnOnce() -> T>(f: F) -> T {
// Here is another way we COULD implement abort_on_panic:
// let f = std::panic::AssertUnwindSafe(f);
// let result = std::panic::catch_unwind(move || f.0());
// if result.is_err() {
// std::process::abort();
// }
// It turns out this is a bad idea, because FFI unwinding, if/when added to
// Rust, will sidestep catch_unwind, but NOT sidestep drop. So a C++
// exception thrown in a Ctor might not get converted to a panic, but
// instead to raw foreign unwinding, and drop impls in parent scopes
// would be run. Since abort_on_unwind is intended to prevent *any* code from
// executing and seeing temporarily-invalid state, this is incorrect.
//
// See e.g.: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
//
// Instead, the only correct way to protect code from unwinding, even from
// foreign unwinding, is via drop, as so:
/// A safety guard which panics if dropped, converting unwinds into aborts.
///
/// In general, you can't, as the *author* of drop(), assume that it will be
/// called: callers can just call std::mem::forget(), among other
/// things. However, as the *user*, you can know that drop is called for
/// unmoved values at the end of a drop scope. Drop is a defined behavior.
/// So while there is ordinarily a prohibition on relying on drop for
/// safety, that only occurs for API owners that are allowing values to
/// be used in arbitrary ways (including forget). Here, we are API
/// *users* as well, and specifically using the Bomb in a way that
/// guarantees its drop will be called in a particular circumstance.
///
/// See https://doc.rust-lang.org/reference/destructors.html, specifically this: "When control
/// flow leaves a drop scope all variables associated to that scope are
/// dropped in reverse order of declaration (for variables) or creation
/// (for temporaries)."
struct Bomb;
impl Drop for Bomb {
fn drop(&mut self) {
panic!("Unwinding occurred when no safe recovery is possible.");
}
}
let bomb = Bomb;
let rv = f();
core::mem::forget(bomb);
rv
}
/// Overloaded assignment operator.
///
/// Conventionally, C++ copy-assignment is Assign<&T>, and C++ move-assignment
/// is Assign<RvalueReference<'_, T>>.
pub trait Assign<From> {
fn assign(self: Pin<&mut Self>, src: From);
}
/// Assignment from a Copy desugars to an assignment from a &T.
impl<T: for<'a> Assign<&'a T>, P: Deref<Target = T>> Assign<Copy<P>> for T {
fn assign(self: Pin<&mut Self>, src: Copy<P>) {
self.assign(&*src.0);
}
}
// TODO(jeanpierreda): Make these less repetitive.
impl<'a, T: Unpin + CtorNew<&'a T>> Assign<&'a T> for T {
fn assign(mut self: Pin<&mut Self>, src: &'a Self) {
if core::mem::needs_drop::<Self>() {
let mut constructed = MaybeUninit::uninit();
unsafe {
T::ctor_new(src).ctor(Pin::new(&mut constructed));
*self = constructed.assume_init();
}
} else {
self.reconstruct(T::ctor_new(src));
}
}
}
impl<'a, T: Unpin + CtorNew<RvalueReference<'a, T>>> Assign<RvalueReference<'a, T>> for T {
fn assign(mut self: Pin<&mut Self>, src: RvalueReference<'a, Self>) {
if core::mem::needs_drop::<Self>() {
let mut constructed = MaybeUninit::uninit();
unsafe {
T::ctor_new(src).ctor(Pin::new(&mut constructed));
*self = constructed.assume_init();
}
} else {
self.reconstruct(T::ctor_new(src));
}
}
}
impl<'a, T: Unpin + CtorNew<ConstRvalueReference<'a, T>>> Assign<ConstRvalueReference<'a, T>>
for T
{
fn assign(mut self: Pin<&mut Self>, src: ConstRvalueReference<'a, Self>) {
if core::mem::needs_drop::<Self>() {
let mut constructed = MaybeUninit::uninit();
unsafe {
T::ctor_new(src).ctor(Pin::new(&mut constructed));
*self = constructed.assume_init();
}
} else {
self.reconstruct(T::ctor_new(src));
}
}
}
/// Overloaded assignment operator, but for Unpin types
/// TODO(b/219963671): use specialization instead of a distinct trait
pub trait UnpinAssign<From> {
fn unpin_assign(&mut self, src: From);
}
// =======================
// Constructor overloading
// =======================
//
// Constructors are unique among C++ functions in that overloading is common,
// *not avoidable*, and involves a nameless function best captured by a trait.
// In other words, it is the ideal choice for having a more generic trait as
// with From/Into.
//
// For exploration purposes, so that we can play with both approaches at the
// same time, this is built on *top* of the traits above.
/// Overloaded constructor trait.
///
/// `T : CtorNew<(A, B, C)>` is roughly equivalent to the C++ "T has a
/// constructor taking arguments of type A, B, and C". As an obvious special
/// case, for singleton tuples, you may use `CtorNew<A>`.
pub trait CtorNew<ConstructorArgs> {
type CtorType: Ctor<Output = Self>;
fn ctor_new(args: ConstructorArgs) -> Self::CtorType;
}
// ====
// Misc
// ====
/// A constructor for `PhantomPinned`.
///
/// ## Why doesn't `PhantomPinned` implement `Ctor<Output=Self>` ?
///
/// A self-impl, where `PhantomPinned : Ctor<Output=PhantomPinned>` would also
/// have been safe, even though `PhantomPinned` is `!Unpin`, because it's just a
/// marker. However, the trait coherence rules are not happy about this:
///
/// ```
/// // crate_1
/// #![feature(negative_impls)]
/// pub struct Foo;
/// impl !Unpin for Foo {}
/// ```
///
/// ```
/// // crate_2
/// trait MyTrait {}
/// impl<T: Unpin> MyTrait for T{}
/// impl MyTrait for crate_1::Foo {}
/// ```
///
/// (In our case, `Foo` is `std::marker::PhantomPinned`, and `MyTrait` is
/// `Ctor`)
///
/// Within `crate_1`, Rust understands that `Foo` is `!Unpin`. So `impl MyTrait
/// for Foo {}` is valid in `crate_1`. However, outside of `crate_1`, Rust
/// decides it doesn't know whether `Foo` implements `Unpin` or not, and so
/// incorrectly believes that the impls may conflict.
///
/// So while it is perfectly feasible to implement self-construction for any
/// type locally, we cannot give foreign impls.
pub struct PhantomPinnedCtor;
impl Ctor for PhantomPinnedCtor {
type Output = core::marker::PhantomPinned;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<Self::Output>>) {
RustMoveCtor(core::marker::PhantomPinned).ctor(dest)
}
}
impl !Unpin for PhantomPinnedCtor {}
/// A constructor for ManuallyDrop<T>, given a constructor for T.
///
/// ManuallyDrop is special as the only non-Copy type allowed in a union, so we
/// specifically support its use, even though it is not guaranteed to be
/// structurally pinned.
#[must_use = must_use_ctor!()]
pub struct ManuallyDropCtor<T: Ctor>(T);
impl<T: Ctor> ManuallyDropCtor<T> {
/// Safety: this structurally pins the contents of ManuallyDrop.
/// Therefore, it is not safe to use with anything that assumes that
/// ManuallyDrop is not structurally pinned.
pub unsafe fn new(x: T) -> Self {
ManuallyDropCtor(x)
}
}
impl<T: Ctor> Ctor for ManuallyDropCtor<T> {
type Output = ManuallyDrop<T::Output>;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<Self::Output>>) {
// Safety: ManuallyDrop<T> and T have the same layout.
let dest = Pin::into_inner_unchecked(dest);
let dest = &mut *(dest as *mut _ as *mut MaybeUninit<T::Output>);
let dest = Pin::new_unchecked(dest);
self.0.ctor(dest);
}
}
impl<T: Ctor> !Unpin for ManuallyDropCtor<T> {}
/// A utility trait to add lifetime bounds to an `impl Ctor`.
///
/// When returning trait impls, captures don't work as one would expect;
/// see https://github.com/rust-lang/rust/issues/66551
///
/// For example, this function does not compile:
///
/// ```compile_fail
/// fn adder<'a, 'b>(x: &'a i32, y: &'b i32) -> impl Fn() -> i32 + 'a + 'b {
/// move || *a + *b
/// }
/// ```
///
/// However, using Captures, we can write the following, which _does_ compile:
///
/// ```
/// fn adder<'a, 'b>(x: &'a i32, y: &'b i32) -> impl Fn() -> i32 + Captures<'a> + Captures<'b> {
/// move || *a + *b
/// }
/// ```
///
/// Since `Ctor` is a in essence glorified `impl Fn(...)`, and will often need
/// to retain function inputs, this helper trait is provided for such a use.
pub trait Captures<'a> {}
impl<'a, T> Captures<'a> for T {}
#[cfg(test)]
mod test {
use super::*;
use std::cell::RefCell;
use std::pin::Pin;
use std::sync::Mutex;
#[test]
fn test_default_rust_type() {
emplace! {let x = u32::ctor_new(());}
assert_eq!(*x, 0);
}
#[test]
fn test_copy_rust_type() {
let x: u32 = 42;
let mut y = Box::emplace(copy(&x));
assert_eq!(x, 42);
assert_eq!(*y, 42);
// Can also mutate:
let x = 50;
y.as_mut().assign(&x);
assert_eq!(x, 50);
assert_eq!(*y, 50);
// Can also mutate:
let x = 100;
y.as_mut().assign(copy(&x));
assert_eq!(x, 100);
assert_eq!(*y, 100);
}
#[test]
fn test_copy_smart_ptr() {
let x = Box::new(42_u32);
emplace! {
let y = copy(x);
}
// x is no longer in scope, as it was moved into the Copy.
assert_eq!(*y, 42);
}
/// Tests that the assigned variables have the correct type.
#[test]
fn test_emplace_type() {
let x: u32 = 42;
emplace! {
let foo = copy(&x);
}
let _foo: Pin<&mut u32> = foo; // type checks OK
}
#[test]
fn test_emplace() {
let x: u32 = 42;
emplace! {
let foo = copy(&x);
}
assert_eq!(*foo, 42);
}
#[test]
fn test_emplace_mut() {
let x: u32 = 42;
emplace! {
let mut foo = copy(&x);
}
assert_eq!(*foo, 42);
*foo = 0;
assert_eq!(*foo, 0);
}
#[test]
fn test_emplace_multi() {
let x: u32 = 42;
emplace! {
let foo = copy(&x);
let bar = copy(&*foo);
}
assert_eq!(*foo, 42);
assert_eq!(*bar, 42);
}
#[test]
fn test_emplace_type_syntax() {
let x: u32 = 42;
emplace! {
let mut foo: Pin<&mut u32> = copy(&x);
let bar: Pin<&mut u32> = copy(&x);
}
assert_eq!(*foo, 42);
*foo = 0;
assert_eq!(*foo, 0);
assert_eq!(*bar, 42);
}
#[test]
fn test_ctor_macro() {
struct MyStruct {
x: u32,
y: u32,
}
unsafe impl RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
emplace! { let my_struct = ctor!(MyStruct {
x: 4,
y: copy(&2)
});}
assert_eq!(my_struct.x, 4);
assert_eq!(my_struct.y, 2);
// test that trailing commas compile:
#[rustfmt::skip]
let _ = ctor!(MyStruct {
x: 0,
y: 0,
});
}
#[test]
fn test_ctor_macro_unit_struct() {
struct MyStruct;
unsafe impl RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
emplace! { let _my_struct = ctor!(MyStruct);}
emplace! { let _my_struct = ctor!(MyStruct {});}
}
#[test]
fn test_ctor_macro_named_tuple_struct() {
struct MyStruct(u32, u32);
unsafe impl RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
emplace! { let my_struct = ctor!(MyStruct {
0: 4,
1: copy(&2)
});}
assert_eq!(my_struct.0, 4);
assert_eq!(my_struct.1, 2);
}
#[test]
fn test_ctor_macro_tuple_struct() {
struct MyStruct(u32, u32);
unsafe impl RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
emplace! { let my_struct = ctor!(MyStruct (4, copy(&2)));}
assert_eq!(my_struct.0, 4);
assert_eq!(my_struct.1, 2);
}
#[test]
fn test_ctor_macro_manuallydrop_struct() {
struct MyStruct {
x: ManuallyDrop<Vec<u32>>,
y: u64,
}
unsafe impl RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
emplace! {let my_struct = ctor!(MyStruct {x: unsafe {ManuallyDropCtor::new(vec![42])}, y: 0 }); }
assert_eq!(&*my_struct.x, &vec![42]);
assert_eq!(my_struct.y, 0);
}
#[test]
fn test_ctor_macro_union() {
union MyUnion {
x: ManuallyDrop<Vec<u32>>,
y: u64,
}
unsafe impl RecursivelyPinned for MyUnion {
type CtorInitializedFields = Self;
}
emplace! {let mut my_union = ctor!(MyUnion {x: unsafe { ManuallyDropCtor::new(vec![42])} }); }
assert_eq!(unsafe { &*my_union.x }, &vec![42]);
// And now write to the union.
// First, should drop myunion.x.
unsafe { ManuallyDrop::drop(&mut Pin::into_inner_unchecked(my_union.as_mut()).x) }
// Second, we can overwrite.
my_union.as_mut().reconstruct(ctor!(MyUnion { y: 24 }));
assert_eq!(unsafe { my_union.y }, 24);
}
#[test]
fn test_ctor_macro_nested_struct() {
mod nested {
pub struct MyStruct {
pub x: u32,
pub y: u32,
}
unsafe impl crate::RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
}
emplace! { let my_struct = ctor!(nested::MyStruct {
x: 4,
y: copy(&2)
});}
assert_eq!(my_struct.x, 4);
assert_eq!(my_struct.y, 2);
}
#[test]
fn test_ctor_macro_nested_tuple_struct() {
mod nested {
pub struct MyStruct(pub u32, pub u32);
unsafe impl crate::RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
}
emplace! { let my_struct = ctor!(nested::MyStruct (4, copy(&2)));}
assert_eq!(my_struct.0, 4);
assert_eq!(my_struct.1, 2);
}
/// Test that the ctor macro safety check doesn't rely on the struct not
/// implementing Drop.
#[test]
fn test_ctor_macro_drop_struct() {
struct MyStruct {
x: String,
}
unsafe impl RecursivelyPinned for MyStruct {
type CtorInitializedFields = Self;
}
impl Drop for MyStruct {
fn drop(&mut self) {}
}
emplace! { let _my_struct = ctor!(MyStruct {
x: "".to_string()
});}
}
/// Struct which sets a bool to true when dropped.
///
/// We use Mutex so as to allow for mutating state in a way Rust believes is
/// unwind-safe.
struct DropNotify<'a>(&'a Mutex<bool>);
impl Drop for DropNotify<'_> {
fn drop(&mut self) {
*self.0.lock().unwrap() = true;
}
}
/// Ctor which constructs a DropNotify but panics before completing
/// construction.
///
/// This can be used to test both that a drop occurs (for e.g. previously
/// constructed DropNotify objects), as well as that one does not:
/// because it in fact does fully initialize the thing it is meant to
/// construct, it is not UB to then call drop on it -- even though it would
/// be UB for almost any other Ctor.
#[must_use = must_use_ctor!()]
struct PanicCtor<'a>(DropNotify<'a>);
impl<'a> Ctor for PanicCtor<'a> {
type Output = DropNotify<'a>;
unsafe fn ctor(self, dest: Pin<&mut MaybeUninit<Self::Output>>) {
self.0.ctor(dest);
panic!();
}
}
impl !Unpin for PanicCtor<'_> {}
/// Tests that drop() is called when the Ctor doesn't panic.
#[test]
fn test_emplace_drop() {
let is_dropped = Mutex::new(false);
{
emplace! { let _my_drop_notify = DropNotify(&is_dropped); }
}
assert!(*is_dropped.lock().unwrap());
}
/// Tests that when a panic occurs during emplace!{}, the uninitialized
/// value is not dropped.
#[test]
fn test_emplace_no_drop_on_panic() {
let is_dropped = Mutex::new(false);
let panic_result = std::panic::catch_unwind(|| {
emplace! { let _my_drop_notify = PanicCtor(DropNotify(&is_dropped)); }
});
assert!(panic_result.is_err());
assert!(!*is_dropped.lock().unwrap());
}
/// Tests that when a panic occurs during initialization of a struct with
/// ctor!, the initialized fields are dropped, and the uninitialized
/// fields are not.
#[test]
fn test_ctor_macro_drop() {
struct MyStruct<'a> {
x: DropNotify<'a>,
y: DropNotify<'a>,
}
unsafe impl RecursivelyPinned for MyStruct<'_> {
type CtorInitializedFields = Self;
}
let x_dropped = Mutex::new(false);
let y_dropped = Mutex::new(false);
let panic_result = std::panic::catch_unwind(|| {
emplace! { let _my_struct = ctor!(MyStruct {
x: DropNotify(&x_dropped),
y: PanicCtor(DropNotify(&y_dropped))
});}
});
assert!(panic_result.is_err());
assert!(*x_dropped.lock().unwrap());
assert!(!*y_dropped.lock().unwrap());
}
#[test]
fn test_ctor_initialized_fields_struct() {
pub struct CtorOnly {
pub field: i32,
_must_construct_using_ctor: [(); 0],
}
pub struct CtorOnlyPubFields {
pub field: i32,
}
unsafe impl RecursivelyPinned for CtorOnly {
type CtorInitializedFields = CtorOnlyPubFields;
}
// Fails to compile: did not specify _must_construct_using_ctor, and cannot
// (outside this crate) because it is private
// let x = CtorOnly {field: 3};
emplace! {let x = ctor!(CtorOnly {field: 3});}
assert_eq!(x.field, 3);
}
#[test]
fn ctor_initialized_fields_tuple_struct() {
pub struct CtorOnly(pub i32, [(); 0]);
pub struct CtorOnlyPubFields(i32);
unsafe impl RecursivelyPinned for CtorOnly {
type CtorInitializedFields = CtorOnlyPubFields;
}
// Fails to compile: did not specify field 1, and cannot (outside this crate)
// because it is private
// let x = CtorOnly(3);
emplace! {let x = ctor!(CtorOnly(3));}
assert_eq!(x.0, 3);
}
/// logs calls to the constructors, drop.
struct DropCtorLogger<'a> {
log: &'a RefCell<Vec<&'static str>>,
}
impl Drop for DropCtorLogger<'_> {
fn drop(&mut self) {
self.log.borrow_mut().push("drop");
}
}
struct LoggingCtor<'a> {
log: &'a RefCell<Vec<&'static str>>,
ctor_message: &'static str,
}
impl<'a> Ctor for LoggingCtor<'a> {
type Output = DropCtorLogger<'a>;
unsafe fn ctor(self, mut dest: Pin<&mut MaybeUninit<Self::Output>>) {
self.log.borrow_mut().push(self.ctor_message);
dest.as_mut_ptr().write(DropCtorLogger { log: self.log });
}
}
impl !Unpin for LoggingCtor<'_> {}
impl<'a> CtorNew<&DropCtorLogger<'a>> for DropCtorLogger<'a> {
type CtorType = LoggingCtor<'a>;
fn ctor_new(src: &DropCtorLogger<'a>) -> Self::CtorType {
LoggingCtor { log: &src.log, ctor_message: "copy ctor" }
}
}
impl<'a> CtorNew<RvalueReference<'_, DropCtorLogger<'a>>> for DropCtorLogger<'a> {
type CtorType = LoggingCtor<'a>;
fn ctor_new(src: RvalueReference<'_, DropCtorLogger<'a>>) -> Self::CtorType {
LoggingCtor { log: &src.0.log, ctor_message: "move ctor" }
}
}
/// Tests the ctor/drop order for copy-constructible Unpin types: ctor comes
/// before drop.
#[test]
fn test_copy_ctor_drop_order() {
let log = RefCell::new(vec![]);
let log = &log;
emplace! {
let notify_tester = DropCtorLogger {log};
let new_value = DropCtorLogger {log};
}
notify_tester.assign(&*new_value);
assert_eq!(*log.borrow(), vec!["copy ctor", "drop"]);
}
/// Tests the ctor/drop order for move-constructible Unpin types: ctor comes
/// before drop.
#[test]
fn test_move_ctor_drop_order() {
let log = RefCell::new(vec![]);
let log = &log;
emplace! {
let notify_tester = DropCtorLogger {log};
let new_value = DropCtorLogger {log};
}
notify_tester.assign(mov!(new_value));
assert_eq!(*log.borrow(), vec!["move ctor", "drop"]);
}
fn takes_rvalue_reference<T>(_: RvalueReference<T>) {}
/// Non-obvious fact: you can mov() an owned reference type! Moving anything
/// also performs a rust move, but the resulting rvalue reference is
/// still valid for a temporary's lifetime.
#[test]
fn test_mov_box() {
struct S;
let x: Pin<Box<S>> = Box::pin(S);
takes_rvalue_reference(mov!(x));
// let _x = x; // fails to compile: x is moved!
}
#[test]
fn test_mov_mut_ref_to_unpin() {
takes_rvalue_reference(mov!(&mut 1));
}
#[test]
fn test_mov_pinned_mut_ref() {
let x = &mut 2;
let pinned_mut_ref = Pin::new(x);
takes_rvalue_reference(mov!(pinned_mut_ref));
}
#[test]
fn test_ctor_then() {
emplace! {
let x = 40.ctor_then(|mut y| { *y += 2 });
}
assert_eq!(*x, 42);
}
/// Test that a slot can be created in a temporary.
#[test]
fn test_slot_temporary() {
assert_eq!(*emplace!(Slot::new(42)).as_opt().unwrap(), 42);
}
/// Test that a slot can be created in a local.
#[test]
fn test_slot_local() {
emplace! {let slot = Slot::new(42); }
assert_eq!(*slot.as_opt().unwrap(), 42);
}
/// Shows the use of Slot to implement a "slotted return value", similar to
/// moveit.
#[test]
fn test_slotted_return_value() {
// TODO(jeanpierreda): delete this, use doctests when doctests work.
fn foo(slot: Pin<&mut Slot<u32>>) -> Pin<&mut u32> {
slot.replace(42)
}
emplace! {let slot = Slot::uninit(); }
let rv = foo(slot);
assert_eq!(*rv, 42);
}
/// Shows the use of Slot to implement output parameters.
#[test]
fn test_slotted_output_parameter() {
// TODO(jeanpierreda): delete this, use doctests when doctests work.
fn foo(slot: Pin<&mut Slot<u32>>) {
slot.replace(42);
}
emplace! {let mut slot = Slot::uninit(); }
foo(slot.as_mut());
assert_eq!(*slot.as_opt().unwrap(), 42);
}
#[test]
fn test_ctor_trait_captures() {
fn adder<'a, 'b>(
x: &'a i32,
y: &'b i32,
) -> impl Ctor<Output = i32> + Captures<'a> + Captures<'b> {
FnCtor::new(|mut dest: Pin<&mut core::mem::MaybeUninit<i32>>| {
dest.write(*x + *y);
})
}
emplace! {
let sum = adder(&40, &2);
}
assert_eq!(*sum, 42);
}
// The following test is broken due to https://github.com/rust-lang/rust/issues/66551
// If that bug is fixed, this test should be uncommented, and `Captures`
// deprecated and removed.
//
// ```
// #[test]
// fn test_ctor_native_captures() {
// fn adder<'a, 'b>(
// x: &'a i32,
// y: &'b i32,
// ) -> impl Ctor<Output = i32> + 'a + 'b {
// FnCtor::new(|mut dest: Pin<&mut std::mem::MaybeUninit<i32>>| {
// dest.write(*x + *y);
// })
// }
// emplace! {
// let sum = adder(&40, &2);
// }
// assert_eq!(*sum, 42);
// }
// ```
}