diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index cce9b80f29660..864116673982d 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -96,6 +96,7 @@ multiple instances to collectively own the data it points to, and as a result, f |---------------------|-----------|-------|--------------|-------|--------|----------------|------------------| |`ConstNonNull` |No |No |Yes |No |Yes |No |Yes | |`ThinSlicePtr<'a, T>`|Yes |No |Yes |Yes |Yes |Yes |Yes | +|`MovingPtr<'a, T>` |Yes |Yes |Yes |Maybe |Yes |Yes |Yes | |`OwningPtr<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | |`Ptr<'a>` |Yes |No |No |Maybe |Yes |No |No | |`PtrMut<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | @@ -109,3 +110,8 @@ accessing elements in the slice is `unsafe`. In debug builds, the length is incl `OwningPtr<'a>`, `Ptr<'a>`, and `PtrMut<'a>` act like `NonNull<()>`, but attempts to restore much of the safety guarantees of `Unique`, `&T`, and `&mut T`. They allow working with heterogenous type erased storage (i.e. ECS tables, typemaps) without the overhead of dynamic dispatch in a manner that progressively translates back to safe borrows. These types also support optional alignment requirements at a type level, and will verify it on dereference in debug builds. + +`MovingPtr<'a, T>` is like a lifetimed-`Box` or a typed `OwningPtr<'a>` made for cheaply moving potentially large values around in memory. +It's a pointer that owns the value it points to but does not own the allocation. If dropped, it will drop the value it points to, just as +if you dropped a value of the inner type but won't deallocate the allocation where the value lived in. It provides a number of methods for moving the value +into another location in memory, including options for partial or deconstructive moves. diff --git a/crates/bevy_ptr/src/lib.rs b/crates/bevy_ptr/src/lib.rs index 15a193d737530..ea5f741e4391f 100644 --- a/crates/bevy_ptr/src/lib.rs +++ b/crates/bevy_ptr/src/lib.rs @@ -11,26 +11,142 @@ use core::{ cell::UnsafeCell, fmt::{self, Debug, Formatter, Pointer}, marker::PhantomData, - mem::ManuallyDrop, + mem::{self, ManuallyDrop, MaybeUninit}, num::NonZeroUsize, ptr::{self, NonNull}, }; -/// Used as a type argument to [`Ptr`], [`PtrMut`] and [`OwningPtr`] to specify that the pointer is aligned. +/// Used as a type argument to [`Ptr`], [`PtrMut`], [`OwningPtr`], and [`MovingPtr`] to specify that the pointer is guaranteed +/// to be [aligned]. +/// +/// [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[derive(Debug, Copy, Clone)] pub struct Aligned; -/// Used as a type argument to [`Ptr`], [`PtrMut`] and [`OwningPtr`] to specify that the pointer is not aligned. +/// Used as a type argument to [`Ptr`], [`PtrMut`], [`OwningPtr`], and [`MovingPtr`] to specify that the pointer may not [aligned]. +/// +/// [aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[derive(Debug, Copy, Clone)] pub struct Unaligned; /// Trait that is only implemented for [`Aligned`] and [`Unaligned`] to work around the lack of ability /// to have const generics of an enum. -pub trait IsAligned: sealed::Sealed {} +pub trait IsAligned: sealed::Sealed { + /// Reads the value pointed to by `ptr`. + /// + /// # Safety + /// - `ptr` must be valid for reads. + /// - `ptr` must point to a valid instance of type `T` + /// - If this type is [`Aligned`], then `ptr` must be [properly aligned] for type `T`. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment + #[doc(hidden)] + unsafe fn read_ptr(ptr: *const T) -> T; + + /// Copies `count * size_of::()` bytes from `src` to `dst`. The source + /// and destination must *not* overlap. + /// + /// # Safety + /// - `src` must be valid for reads of `count * size_of::()` bytes. + /// - `dst` must be valid for writes of `count * size_of::()` bytes. + /// - The region of memory beginning at `src` with a size of `count * + /// size_of::()` bytes must *not* overlap with the region of memory + /// beginning at `dst` with the same size. + /// - If this type is [`Aligned`], then both `src` and `dst` must properly + /// be aligned for values of type `T`. + #[doc(hidden)] + unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); + + /// Reads the value pointed to by `ptr`. + /// + /// # Safety + /// - `ptr` must be valid for reads and writes. + /// - `ptr` must point to a valid instance of type `T` + /// - If this type is [`Aligned`], then `ptr` must be [properly aligned] for type `T`. + /// - The value pointed to by `ptr` must be valid for dropping. + /// - While `drop_in_place` is executing, the only way to access parts of `ptr` is through + /// the `&mut Self` supplied to it's `Drop::drop` impl. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment + #[doc(hidden)] + unsafe fn drop_in_place(ptr: *mut T); +} + +impl IsAligned for Aligned { + #[inline] + unsafe fn read_ptr(ptr: *const T) -> T { + // SAFETY: + // - The caller is required to ensure that `src` must be valid for reads. + // - The caller is required to ensure that `src` points to a valid instance of type `T`. + // - This type is `Aligned` so the caller must ensure that `src` is properly aligned for type `T`. + unsafe { ptr.read() } + } -impl IsAligned for Aligned {} + #[inline] + unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { + // SAFETY: + // - The caller is required to ensure that `src` must be valid for reads. + // - The caller is required to ensure that `dst` must be valid for writes. + // - The caller is required to ensure that `src` and `dst` are aligned. + // - The caller is required to ensure that the memory region covered by `src` + // and `dst`, fitting up to `count` elements do not overlap. + unsafe { + ptr::copy_nonoverlapping(src, dst, count); + } + } -impl IsAligned for Unaligned {} + #[inline] + unsafe fn drop_in_place(ptr: *mut T) { + // SAFETY: + // - The caller is required to ensure that `ptr` must be valid for reads and writes. + // - The caller is required to ensure that `ptr` points to a valid instance of type `T`. + // - This type is `Aligned` so the caller must ensure that `ptr` is properly aligned for type `T`. + // - The caller is required to ensure that `ptr` points must be valid for dropping. + // - The caller is required to ensure that the value `ptr` points must not be used after this function + // call. + unsafe { ptr::drop_in_place(ptr) }; + } +} + +impl IsAligned for Unaligned { + #[inline] + unsafe fn read_ptr(ptr: *const T) -> T { + // SAFETY: + // - The caller is required to ensure that `src` must be valid for reads. + // - The caller is required to ensure that `src` points to a valid instance of type `T`. + unsafe { ptr.read_unaligned() } + } + + #[inline] + unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { + // SAFETY: + // - The caller is required to ensure that `src` must be valid for reads. + // - The caller is required to ensure that `dst` must be valid for writes. + // - This is doing a byte-wise copy. `src` and `dst` are always guaranteed to be + // aligned. + // - The caller is required to ensure that the memory region covered by `src` + // and `dst`, fitting up to `count` elements do not overlap. + unsafe { + ptr::copy_nonoverlapping::( + src.cast::(), + dst.cast::(), + count * size_of::(), + ); + } + } + + #[inline] + unsafe fn drop_in_place(ptr: *mut T) { + // SAFETY: + // - The caller is required to ensure that `ptr` must be valid for reads and writes. + // - The caller is required to ensure that `ptr` points to a valid instance of type `T`. + // - This type is not `Aligned` so the caller does not need to ensure that `ptr` is properly aligned for type `T`. + // - The caller is required to ensure that `ptr` points must be valid for dropping. + // - The caller is required to ensure that the value `ptr` points must not be used after this function + // call. + unsafe { drop(ptr.read_unaligned()) } + } +} mod sealed { pub trait Sealed {} @@ -98,7 +214,7 @@ impl ConstNonNull { /// /// When calling this method, you have to ensure that all of the following is true: /// - /// * The pointer must be properly aligned. + /// * The pointer must be [properly aligned]. /// /// * It must be "dereferenceable" in the sense defined in [the module documentation]. /// @@ -126,6 +242,7 @@ impl ConstNonNull { /// ``` /// /// [the module documentation]: core::ptr#safety + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn as_ref<'a>(&self) -> &'a T { // SAFETY: This function's safety invariants are identical to `NonNull::as_ref` @@ -151,16 +268,19 @@ impl<'a, T: ?Sized> From<&'a mut T> for ConstNonNull { ConstNonNull(NonNull::from(value)) } } + /// Type-erased borrow of some unknown type chosen when constructing this type. /// /// This type tries to act "borrow-like" which means that: /// - It should be considered immutable: its target must not be changed while this pointer is alive. -/// - It must always points to a valid value of whatever the pointee type is. +/// - It must always point to a valid value of whatever the pointee type is. /// - The lifetime `'a` accurately represents how long the pointer is valid for. -/// - Must be sufficiently aligned for the unknown pointee type. +/// - If `A` is [`Aligned`], the pointer must always be [properly aligned] for the unknown pointee type. /// /// It may be helpful to think of this type as similar to `&'a dyn Any` but without /// the metadata and able to point to data that does not correspond to a Rust type. +/// +/// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[derive(Copy, Clone)] #[repr(transparent)] pub struct Ptr<'a, A: IsAligned = Aligned>(NonNull, PhantomData<(&'a u8, A)>); @@ -170,34 +290,63 @@ pub struct Ptr<'a, A: IsAligned = Aligned>(NonNull, PhantomData<(&'a u8, A)> /// This type tries to act "borrow-like" which means that: /// - Pointer is considered exclusive and mutable. It cannot be cloned as this would lead to /// aliased mutability. -/// - It must always points to a valid value of whatever the pointee type is. +/// - It must always point to a valid value of whatever the pointee type is. /// - The lifetime `'a` accurately represents how long the pointer is valid for. -/// - Must be sufficiently aligned for the unknown pointee type. +/// - If `A` is [`Aligned`], the pointer must always be [properly aligned] for the unknown pointee type. /// /// It may be helpful to think of this type as similar to `&'a mut dyn Any` but without /// the metadata and able to point to data that does not correspond to a Rust type. +/// +/// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[repr(transparent)] pub struct PtrMut<'a, A: IsAligned = Aligned>(NonNull, PhantomData<(&'a mut u8, A)>); -/// Type-erased Box-like pointer to some unknown type chosen when constructing this type. +/// Type-erased [`Box`]-like pointer to some unknown type chosen when constructing this type. /// /// Conceptually represents ownership of whatever data is being pointed to and so is /// responsible for calling its `Drop` impl. This pointer is _not_ responsible for freeing /// the memory pointed to by this pointer as it may be pointing to an element in a `Vec` or /// to a local in a function etc. /// -/// This type tries to act "borrow-like" like which means that: +/// This type tries to act "borrow-like" which means that: /// - Pointer should be considered exclusive and mutable. It cannot be cloned as this would lead /// to aliased mutability and potentially use after free bugs. -/// - It must always points to a valid value of whatever the pointee type is. +/// - It must always point to a valid value of whatever the pointee type is. /// - The lifetime `'a` accurately represents how long the pointer is valid for. -/// - Must be sufficiently aligned for the unknown pointee type. +/// - If `A` is [`Aligned`], the pointer must always be [properly aligned] for the unknown pointee type. /// /// It may be helpful to think of this type as similar to `&'a mut ManuallyDrop` but /// without the metadata and able to point to data that does not correspond to a Rust type. +/// +/// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment +/// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html #[repr(transparent)] pub struct OwningPtr<'a, A: IsAligned = Aligned>(NonNull, PhantomData<(&'a mut u8, A)>); +/// A [`Box`]-like pointer for moving a value to a new memory location without needing to pass by +/// value. +/// +/// Conceptually represents ownership of whatever data is being pointed to and will call its +/// [`Drop`] impl upon being dropped. This pointer is _not_ responsible for freeing +/// the memory pointed to by this pointer as it may be pointing to an element in a `Vec` or +/// to a local in a function etc. +/// +/// This type tries to act "borrow-like" which means that: +/// - Pointer should be considered exclusive and mutable. It cannot be cloned as this would lead +/// to aliased mutability and potentially use after free bugs. +/// - It must always point to a valid value of whatever the pointee type is. +/// - The lifetime `'a` accurately represents how long the pointer is valid for. +/// - It does not support pointer arithmetic in any way. +/// - If `A` is [`Aligned`], the pointer must always be [properly aligned] for the type `T`. +/// +/// A value can be deconstructed into its fields via [`deconstruct_moving_ptr`], see it's documentation +/// for an example on how to use it. +/// +/// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment +/// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html +#[repr(transparent)] +pub struct MovingPtr<'a, T, A: IsAligned = Aligned>(NonNull, PhantomData<(&'a mut T, A)>); + macro_rules! impl_ptr { ($ptr:ident) => { impl<'a> $ptr<'a, Aligned> { @@ -272,6 +421,7 @@ macro_rules! impl_ptr { write!(f, "{}({:?})", stringify!($ptr), self.0) } } + impl Debug for $ptr<'_, Unaligned> { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -285,15 +435,330 @@ impl_ptr!(Ptr); impl_ptr!(PtrMut); impl_ptr!(OwningPtr); +impl<'a, T> MovingPtr<'a, T, Aligned> { + /// Removes the alignment requirement of this pointer + #[inline] + pub fn to_unaligned(self) -> MovingPtr<'a, T, Unaligned> { + let value = MovingPtr(self.0, PhantomData); + mem::forget(self); + value + } + + /// Consumes a value and creates an [`MovingPtr`] to it while ensuring a double drop does not happen. + #[inline] + pub fn make) -> R, R>(val: T, f: F) -> R { + let mut val = MaybeUninit::new(val); + // SAFETY: The value behind the pointer will not get dropped or observed later. + f(unsafe { MovingPtr::from_value(&mut val) }) + } + + /// Creates a [`MovingPtr`] from a provided value of type `T`. + /// + /// # Safety + /// - `value` must store a properly initialized value of type `T`. + /// - Once the returned [`MovingPtr`] has been used, `value` must be treated as + /// it were uninitialized unless it was explicitly leaked via [`core::mem::forget`]. + #[inline] + pub unsafe fn from_value(value: &'a mut MaybeUninit) -> Self { + // SAFETY: + // - MaybeUninit has the same memory layout as T + // - The caller guarantees that `value` must point to a valid instance of type `T`. + MovingPtr(NonNull::from(value).cast::(), PhantomData) + } +} + +impl<'a, T, A: IsAligned> MovingPtr<'_, T, A> { + /// Creates a new instance from a raw pointer. + /// + /// # Safety + /// - `inner` must point to valid value of `T`. + /// - If the `A` type parameter is [`Aligned`] then `inner` must be be [properly aligned] for `T`. + /// - `inner` must have correct provenance to allow read and writes of the pointee type. + /// - The lifetime `'a` must be constrained such that this [`MovingPtr`] will stay valid and nothing + /// else can read or mutate the pointee while this [`MovingPtr`] is live. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment + #[inline] + pub unsafe fn new(inner: NonNull) -> Self { + Self(inner, PhantomData) + } + + /// Partially moves out some fields inside of `self`. + /// + /// The partially returned value is returned back pointing to `MaybeUninit`. + /// + /// # Safety + /// - The call into `f` must not complete having dropped the provided pointer. + /// - The fields moved out of in `f` must not be accessed or dropped after this function returns. + /// + /// As a result, it is strongly recommended to call [`forget`] on the provided pointer once the + /// partial deconstruction has completed. + /// + /// # Example + /// + /// ``` + /// use core::mem::{offset_of, MaybeUninit}; + /// use bevy_ptr::MovingPtr; + /// # use bevy_ptr::Unaligned; + /// # struct FieldAType(usize); + /// # struct FieldBType(usize); + /// # struct FieldCType(usize); + /// # fn insert(_ptr: MovingPtr<'_, T, Unaligned>) {} + /// + /// struct Parent { + /// field_a: FieldAType, + /// field_b: FieldBType, + /// field_c: FieldCType, + /// } + /// + /// # let parent = Parent { + /// # field_a: FieldAType(0), + /// # field_b: FieldBType(0), + /// # field_c: FieldCType(0), + /// # }; + /// + /// MovingPtr::make(parent, |parent_ptr| unsafe { + /// // SAFETY: + /// // - It is impossible for the provided closure to drop the provided pointer as `move_field` cannot panic. + /// // - `field_a` and `field_b` are moved out of but never accessed after this. + /// let partial_parent = MovingPtr::partial_move(parent_ptr, |parent_ptr| { + /// bevy_ptr::deconstruct_moving_ptr!(parent_ptr, Parent { + /// field_a: FieldAType => { insert(field_a) }, + /// field_b: FieldBType => { insert(field_b) }, + /// }); + /// }); + /// + /// // Move the rest of fields out of the parent. + /// bevy_ptr::deconstruct_moving_ptr!(partial_parent, Parent { + /// field_c: FieldBType => { insert(field_c) }, + /// }); + /// }); + /// ``` + /// + /// [`forget`]: core::mem::forget + #[inline] + pub unsafe fn partial_move( + ptr: MovingPtr<'a, T, A>, + f: impl FnOnce(MovingPtr<'a, T, A>), + ) -> MovingPtr<'a, MaybeUninit, A> { + let partial_ptr = ptr.0; + f(ptr); + MovingPtr(partial_ptr.cast::>(), PhantomData) + } + + /// Reads the value pointed to by this pointer. + #[inline] + pub fn read(self) -> T { + // SAFETY: + // - `self.0` must be valid for reads as this type owns the value it points to. + // - `self.0` must always point to a valid instance of type `T` + // - If `A` is [`Aligned`], then `ptr` must be properly aligned for type `T`. + let value = unsafe { A::read_ptr(self.0.as_ptr()) }; + mem::forget(self); + value + } + + /// Writes the value pointed to by this pointer to a provided location. + /// + /// This does *not* drop the value stored at `dst` and it's the caller's responsibility + /// to ensure that it's properly dropped. + /// + /// # Safety + /// - `dst` must be valid for writes. + /// - If the `A` type parameter is [`Aligned`] then `dst` must be [properly aligned] for `T`. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment + #[inline] + pub unsafe fn write_to(self, dst: *mut T) { + let src = self.0.as_ptr(); + mem::forget(self); + // SAFETY: + // - `src` must be valid for reads as this pointer is considered to own the value it points to. + // - The caller is required to ensure that `dst` must be valid for writes. + // - As `A` is `Aligned`, the caller is required to ensure that `dst` is aligned and `src` must + // be aligned by the type's invariants. + unsafe { A::copy_nonoverlapping(src, dst, 1) }; + } + + /// Writes the value pointed to by this pointer into `dst`. + /// + /// The value previously stored at `dst` will be dropped. + #[inline] + pub fn assign_to(self, dst: &mut T) { + // SAFETY: + // - `dst` is a mutable borrow, it must point to a valid instance of `T`. + // - `dst` is a mutable borrow, it must point to value that is valid for dropping. + // - `dst` is a mutable borrow, it must not alias any other access. + unsafe { + ptr::drop_in_place(dst); + } + // SAFETY: + // - `dst` is a mutable borrow, it must be valid for writes. + // - `dst` is a mutable borrow, it must always be aligned. + unsafe { + self.write_to(dst); + } + } + + /// Creates a [`MovingPtr`] for a specific field within `self`. This is a building block for + /// [`deconstruct_moving_ptr`] and should generally not be accessed directly. + /// + /// This function is explicitly made for deconstructive moves. + /// + /// The correct `byte_offset` for a field can be obtained via [`core::mem::offset_of`]. + /// + /// The returned value will always be considered unaligned as `repr(packed)` types may result in + /// unaligned fields. The pointer is convertible back into an aligned one using the [`TryFrom`] impl. + /// + /// # Safety + /// - `U` must be the correct type for the field at `byte_offset` within `self`. + /// - `self` should not be accessed or dropped as if it were a complete value. + /// Other fields that have not been moved out of may still be accessed or dropped separately. + /// - This function cannot alias the field with any other access, including other calls to [`move_field`] + /// for the same field, without first calling [`forget`] on it first. + /// + /// A result of the above invariants means that any operation that could cause `self` to be dropped while + /// the pointers to the fields are held will result in undefined behavior. This requires exctra caution + /// around code that may panic. See the example below for an example of how to safely use this function. + /// + /// # Example + /// + /// ``` + /// use core::mem::offset_of; + /// use bevy_ptr::MovingPtr; + /// # use bevy_ptr::Unaligned; + /// # struct FieldAType(usize); + /// # struct FieldBType(usize); + /// # struct FieldCType(usize); + /// # fn insert(_ptr: MovingPtr<'_, T, Unaligned>) {} + /// + /// struct Parent { + /// field_a: FieldAType, + /// field_b: FieldBType, + /// field_c: FieldCType, + /// } + /// + /// # let parent = Parent { + /// # field_a: FieldAType(0), + /// # field_b: FieldBType(0), + /// # field_c: FieldCType(0), + /// # }; + /// + /// MovingPtr::make(parent, |parent_ptr| unsafe { + /// let field_a = parent_ptr.move_field::(offset_of!(Parent, field_a)); + /// let field_b = parent_ptr.move_field::(offset_of!(Parent, field_b)); + /// let field_c = parent_ptr.move_field::(offset_of!(Parent, field_c)); + /// // Each call to insert may panic! Ensure that `parent_ptr` cannot be dropped before + /// // calling them! + /// core::mem::forget(parent_ptr); + /// insert(field_a); + /// insert(field_b); + /// insert(field_c); + /// }); + /// ``` + /// + /// [`forget`]: core::mem::forget + /// [`move_field`]: Self::move_field + #[inline] + #[doc(hidden)] + pub unsafe fn move_field(&self, byte_offset: usize) -> MovingPtr<'a, U, Unaligned> { + MovingPtr( + // SAFETY: The caller must ensure that `U` is the correct type for the field at `byte_offset`. + unsafe { self.0.byte_add(byte_offset) }.cast::(), + PhantomData, + ) + } +} + +impl<'a, T, A: IsAligned> MovingPtr<'a, MaybeUninit, A> { + /// Creates a [`MovingPtr`] pointing to a valid instance of `T`. + /// + /// See also: [`MaybeUninit::assume_init`]. + /// + /// # Safety + /// It's up to the caller to ensure that the value pointed to by `self` + /// is really in an initialized state. Calling this when the content is not yet + /// fully initialized causes immediate undefined behavior. + #[inline] + pub unsafe fn assume_init(self) -> MovingPtr<'a, T, A> { + let value = MovingPtr(self.0.cast::(), PhantomData); + mem::forget(self); + value + } +} + +impl Pointer for MovingPtr<'_, T, A> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Pointer::fmt(&self.0, f) + } +} + +impl Debug for MovingPtr<'_, T, Aligned> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}({:?})", stringify!($ptr), self.0) + } +} + +impl Debug for MovingPtr<'_, T, Unaligned> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}({:?})", stringify!($ptr), self.0) + } +} + +impl<'a, T, A: IsAligned> From> for OwningPtr<'a, A> { + #[inline] + fn from(value: MovingPtr<'a, T, A>) -> Self { + // SAFETY: + // - `value.0` must always point to valid value of type `T`. + // - The type parameter `A` is mirrored from input to output, keeping the same alignment guarantees. + // - `value.0` by construction must have correct provenance to allow read and writes of type `T`. + // - The lifetime `'a` is mirrored from input to output, keeping the same lifetime guarantees. + // - `OwningPtr` maintains the same aliasing invariants as `MovingPtr`. + let ptr = unsafe { OwningPtr::new(value.0.cast::()) }; + mem::forget(value); + ptr + } +} + +impl<'a, T> TryFrom> for MovingPtr<'a, T, Aligned> { + type Error = MovingPtr<'a, T, Unaligned>; + #[inline] + fn try_from(value: MovingPtr<'a, T, Unaligned>) -> Result { + let ptr = value.0; + if ptr.as_ptr().is_aligned() { + Ok(MovingPtr(ptr, PhantomData)) + } else { + Err(value) + } + } +} + +impl Drop for MovingPtr<'_, T, A> { + fn drop(&mut self) { + // SAFETY: + // - `self.0` must be valid for reads and writes as this pointer type owns the value it points to. + // - `self.0` must always point to a valid instance of type `T` + // - If `A` is `Aligned`, then `ptr` must be properly aligned for type `T` by construction. + // - `self.0` owns the value it points to so it must always be valid for dropping until this pointer is dropped. + // - This type owns the value it points to, so it's required to not mutably alias value that it points to. + unsafe { A::drop_in_place(self.0.as_ptr()) }; + } +} + impl<'a, A: IsAligned> Ptr<'a, A> { /// Creates a new instance from a raw pointer. /// /// # Safety /// - `inner` must point to valid value of whatever the pointee type is. - /// - If the `A` type parameter is [`Aligned`] then `inner` must be sufficiently aligned for the pointee type. + /// - If the `A` type parameter is [`Aligned`] then `inner` must be be [properly aligned]for the pointee type. /// - `inner` must have correct provenance to allow reads of the pointee type. /// - The lifetime `'a` must be constrained such that this [`Ptr`] will stay valid and nothing /// can mutate the pointee while this [`Ptr`] is live except through an [`UnsafeCell`]. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn new(inner: NonNull) -> Self { Self(inner, PhantomData) @@ -314,8 +779,10 @@ impl<'a, A: IsAligned> Ptr<'a, A> { /// /// # Safety /// - `T` must be the erased pointee type for this [`Ptr`]. - /// - If the type parameter `A` is [`Unaligned`] then this pointer must be sufficiently aligned + /// - If the type parameter `A` is [`Unaligned`] then this pointer must be be [properly aligned] /// for the pointee type `T`. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn deref(self) -> &'a T { let ptr = self.as_ptr().cast::().debug_ensure_aligned(); @@ -347,10 +814,12 @@ impl<'a, A: IsAligned> PtrMut<'a, A> { /// /// # Safety /// - `inner` must point to valid value of whatever the pointee type is. - /// - If the `A` type parameter is [`Aligned`] then `inner` must be sufficiently aligned for the pointee type. + /// - If the `A` type parameter is [`Aligned`] then `inner` must be be [properly aligned] for the pointee type. /// - `inner` must have correct provenance to allow read and writes of the pointee type. /// - The lifetime `'a` must be constrained such that this [`PtrMut`] will stay valid and nothing /// else can read or mutate the pointee while this [`PtrMut`] is live. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn new(inner: NonNull) -> Self { Self(inner, PhantomData) @@ -369,8 +838,10 @@ impl<'a, A: IsAligned> PtrMut<'a, A> { /// /// # Safety /// - `T` must be the erased pointee type for this [`PtrMut`]. - /// - If the type parameter `A` is [`Unaligned`] then this pointer must be sufficiently aligned + /// - If the type parameter `A` is [`Unaligned`] then this pointer must be be [properly aligned] /// for the pointee type `T`. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn deref_mut(self) -> &'a mut T { let ptr = self.as_ptr().cast::().debug_ensure_aligned(); @@ -438,10 +909,12 @@ impl<'a, A: IsAligned> OwningPtr<'a, A> { /// /// # Safety /// - `inner` must point to valid value of whatever the pointee type is. - /// - If the `A` type parameter is [`Aligned`] then `inner` must be sufficiently aligned for the pointee type. + /// - If the `A` type parameter is [`Aligned`] then `inner` must be [properly aligned] for the pointee type. /// - `inner` must have correct provenance to allow read and writes of the pointee type. /// - The lifetime `'a` must be constrained such that this [`OwningPtr`] will stay valid and nothing /// else can read or mutate the pointee while this [`OwningPtr`] is live. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn new(inner: NonNull) -> Self { Self(inner, PhantomData) @@ -451,8 +924,10 @@ impl<'a, A: IsAligned> OwningPtr<'a, A> { /// /// # Safety /// - `T` must be the erased pointee type for this [`OwningPtr`]. - /// - If the type parameter `A` is [`Unaligned`] then this pointer must be sufficiently aligned + /// - If the type parameter `A` is [`Unaligned`] then this pointer must be be [properly aligned] /// for the pointee type `T`. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn read(self) -> T { let ptr = self.as_ptr().cast::().debug_ensure_aligned(); @@ -460,12 +935,23 @@ impl<'a, A: IsAligned> OwningPtr<'a, A> { unsafe { ptr.read() } } + /// Casts to a concrete type as a [`MovingPtr`]. + /// + /// # Safety + /// - `T` must be the erased pointee type for this [`OwningPtr`]. + #[inline] + pub unsafe fn cast(self) -> MovingPtr<'a, T, A> { + MovingPtr(self.0.cast::(), PhantomData) + } + /// Consumes the [`OwningPtr`] to drop the underlying data of type `T`. /// /// # Safety /// - `T` must be the erased pointee type for this [`OwningPtr`]. - /// - If the type parameter `A` is [`Unaligned`] then this pointer must be sufficiently aligned + /// - If the type parameter `A` is [`Unaligned`] then this pointer must be be [properly aligned] /// for the pointee type `T`. + /// + /// [properly aligned]: https://doc.rust-lang.org/std/ptr/index.html#alignment #[inline] pub unsafe fn drop_as(self) { let ptr = self.as_ptr().cast::().debug_ensure_aligned(); @@ -652,3 +1138,71 @@ impl DebugEnsureAligned for *mut T { self } } + +/// Deconstructs a [`MovingPtr`] into its individual fields. +/// +/// This consumes the [`MovingPtr`] and hands out [`MovingPtr`] wrappers around +/// pointers to each of its fields. The value will *not* be dropped. +/// +/// The field move expressions will be executed in the order they're provided to the macro. +/// In the example below, the call to [`assign_to`] for `field_a` will always run before the +/// calls for `field_b` and `field_c`. +/// +/// # Safety +/// This macro generates unsafe code and must be set up correctly to avoid undefined behavior. +/// - The provided type must match the type of the value pointed to by the [`MovingPtr`]. +/// - The type and name of the fields must match the type's definition. For tuples and tuple structs, +/// this would be the tuple indices. +/// +/// # Example +/// +/// ``` +/// use core::mem::{offset_of, MaybeUninit}; +/// use bevy_ptr::MovingPtr; +/// # use bevy_ptr::Unaligned; +/// # struct FieldAType(usize); +/// # struct FieldBType(usize); +/// # struct FieldCType(usize); +/// +/// pub struct Parent { +/// pub field_a: FieldAType, +/// pub field_b: FieldBType, +/// pub field_c: FieldCType, +/// } +/// +/// let parent = Parent { +/// field_a: FieldAType(11), +/// field_b: FieldBType(22), +/// field_c: FieldCType(33), +/// }; +/// +/// let mut target_a = FieldAType(101); +/// let mut target_b = FieldBType(102); +/// let mut target_c = FieldCType(103); +/// +/// MovingPtr::make(parent, |parent_ptr| unsafe { +/// bevy_ptr::deconstruct_moving_ptr!(parent_ptr, Parent { +/// // The field name and type must match the name used in the type definition. +/// // Each one will be a `MovingPtr` of the supplied type +/// field_a: FieldAType => { field_a.assign_to(&mut target_a) }, +/// field_b: FieldBType => { field_b.assign_to(&mut target_b) }, +/// field_c: FieldCType => { field_c.assign_to(&mut target_c) }, +/// }); +/// }); +/// +/// assert_eq!(target_a.0, 11); +/// assert_eq!(target_b.0, 22); +/// assert_eq!(target_c.0, 33); +/// ``` +/// +/// [`assign_to`]: MovingPtr::assign_to +#[macro_export] +macro_rules! deconstruct_moving_ptr { + ($ptr:ident, $self_type:tt {$($field_name:tt: $field_type:tt => $field_block:block,)*}) => { + $(let $field_name = $ptr.move_field::<$field_type>(core::mem::offset_of!($self_type, $field_name));)* + // Each field block may panic! Ensure that `parent_ptr` cannot be dropped before + // calling them! + core::mem::forget($ptr); + $($field_block)* + }; +}