Diffstat (limited to 'src/any/indirect.rs')
| -rw-r--r-- | src/any/indirect.rs | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/any/indirect.rs b/src/any/indirect.rs new file mode 100644 index 0000000..eef24f0 --- /dev/null +++ b/src/any/indirect.rs @@ -0,0 +1,237 @@ +//! Generic over borrow mutability. + +use core::mem::{ManuallyDrop, MaybeUninit}; + +/// A higher kinded type. +pub trait HigherKinded<'a> { + /// The type with `T` applied. + type ForT<T: ?Sized + 'a>: Unpin + 'a; +} + +/// A pointer like type. +/// +/// Allows being generic over `&T` and `&mut T`. +/// +/// Implemented by marker types [`Ref`] and [`Mut`]. +pub trait Indirect<'a>: HigherKinded<'a> + sealed::Sealed<'a> {} + +pub(super) mod sealed { + use core::marker::PhantomData; + + use super::*; + + /// Sealed trait to prevent users from implementing Indirect and provides conversion + /// to and from a raw indirect. + pub trait Sealed<'a>: HigherKinded<'a> + Sized { + /// Convert the pointer into a raw indirection. + fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]>; + + /// Convert a raw indirection back into the pointer. + /// + /// # Safety + /// `value` must have been created by `Self::into_raw`. + /// This function must not be called twice for the same `value`. + /// The type `T` must be the one given to `Self::into_raw`. + unsafe fn from_raw<T: ?Sized + 'a>(value: MaybeUninit<[u8; INDIRECT_SIZE]>) -> Self::ForT<T>; + } + + /// An opaque set of bytes the size of a fat pointer. + /// + /// Repr wise this is exactly `MaybeUninit<[u8; { size of a fat pointer }]>`. + #[repr(transparent)] + pub struct RawIndirect<'a, I> { + indirect: MaybeUninit<[u8; INDIRECT_SIZE]>, + _marker: PhantomData<fn(&'a ()) -> (&'a (), I)>, + } + + impl<'a, I: Indirect<'a>> RawIndirect<'a, I> { + pub fn new<T: ?Sized>(indirect: I::ForT<T>) -> Self { + Self { + indirect: I::into_raw(indirect), + _marker: PhantomData, + } + } + + /// # Safety + /// The type `T` must be the same one used in `Self::new`. + pub unsafe fn into_inner<T: ?Sized>(self) -> I::ForT<T> { + // SAFETY: indirect was created with this I's into_raw in Self::new. + // This function cannot be called twice because we take ownership of self and + // this type is not clone or copy. + // The caller makes sure that the T is the same. + // Also the lifetime 'a must be the same because Self is invariant over it. + unsafe { + I::from_raw(self.indirect) + } + } + } + + /// Size of a fat pointer. + const INDIRECT_SIZE: usize = core::mem::size_of::<usize>() * 2; + + /// Helper trait for double checking the sizes of fat pointers. + trait Helper { + fn run(&self); + } + + const _: () = + assert!(core::mem::size_of::<&dyn Helper>() <= core::mem::size_of::<RawIndirect<'_, Ref>>()); + + // Borrow doesn't need to be dropped. + const _: () = + assert!(!core::mem::needs_drop::<&i32>()); + + const _: () = assert!( + core::mem::size_of::<&mut dyn Helper>() <= core::mem::size_of::<sealed::RawIndirect<'_, Mut>>() + ); + + // Mutable borrow doesn't need to be dropped. + const _: () = + assert!(!core::mem::needs_drop::<&mut i32>()); + + impl<'a> Sealed<'a> for Ref { + fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]> { + // SAFETY: A possibly fat borrow can be stores in a 2 usize wide maybe uninit array. + unsafe { transmute::<&'a T, MaybeUninit<[u8; INDIRECT_SIZE]>>(value) } + } + + unsafe fn from_raw<T: ?Sized + 'a>(any: MaybeUninit<[u8; INDIRECT_SIZE]>) -> Self::ForT<T> { + // SAFETY: We know the value is from Self::into_raw because of the caller invariants. + unsafe { transmute::<MaybeUninit<[u8; INDIRECT_SIZE]>, &'a T>(any) } + } + } + + impl<'a> Sealed<'a> for Mut { + fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]> { + // SAFETY: A possibly fat borrow can be stores in a 2 usize wide maybe uninit array. + unsafe { transmute::<&'a mut T, MaybeUninit<[u8; INDIRECT_SIZE]>>(value) } + } + + unsafe fn from_raw<T: ?Sized + 'a>(any: MaybeUninit<[u8; INDIRECT_SIZE]>) -> Self::ForT<T> { + // SAFETY: We know the value is from Self::into_raw because of the caller invariants. + unsafe { transmute::<MaybeUninit<[u8; INDIRECT_SIZE]>, &'a mut T>(any) } + } + } +} + +/// Marker type for [`AnyTraitObject`](super::AnyTraitObject) for a borrow `&T`. +pub enum Ref {} + +impl<'a> HigherKinded<'a> for Ref { + type ForT<T: ?Sized + 'a> = &'a T; +} + +impl<'a> Indirect<'a> for Ref {} + +/// Marker type for [`AnyTraitObject`](super::AnyTraitObject) for a mutable borrow `&mut T`. +pub enum Mut {} + +impl<'a> HigherKinded<'a> for Mut { + type ForT<T: ?Sized + 'a> = &'a mut T; +} + +impl<'a> Indirect<'a> for Mut {} + +/// # Safety +/// Same rules as [`core::mem::transmute()`]. +unsafe fn transmute<T, U>(value: T) -> U { + // Create union type that can store a `T` or a `U`. + // We can then use this to convert between them. + // + // The repr(C) layout forces no offset between `t` and `u` as talked about here + // https://rust-lang.github.io/unsafe-code-guidelines/layout/unions.html#c-compatible-layout-repr-c + #[repr(C)] + union Transmute<T, U> { + t: ManuallyDrop<T>, + u: ManuallyDrop<U>, + } + + // Create the union in the `T` state. + let value = Transmute { + t: ManuallyDrop::new(value), + }; + + // Read from the union in the `U` state. + // SAFETY: This is safe because the caller has promised that `T` can be transmuted to `U`. + // The following reference link talks about repr(C) unions being used this way. + // https://doc.rust-lang.org/reference/items/unions.html#reading-and-writing-union-fields + ManuallyDrop::into_inner(unsafe { value.u }) +} + +#[cfg(test)] +mod test { + use super::*; + + use sealed::RawIndirect; + + #[test] + fn can_type_erase_borrow() { + let x = 42; + let y: &i32 = &x; + let z = RawIndirect::<Ref>::new(y); + + // SAFETY: Same type as y which we made it from. + let w: &i32 = unsafe { z.into_inner() }; + + assert_eq!(w, y); + } + + trait AsInt { + fn as_int(&self) -> i32; + } + + impl AsInt for i32 { + fn as_int(&self) -> i32 { + *self + } + } + + #[test] + fn can_type_erase_unsized_borrow() { + let x = 42; + let y: &dyn AsInt = &x; + let z = RawIndirect::<Ref>::new(y); + + // SAFETY: Same type as y which we made it from. + let w: &i32 = unsafe { z.into_inner() }; + + assert_eq!(w.as_int(), x); + } + + #[test] + fn can_type_erase_mut_borrow() { + let mut x = 42; + let y: &mut i32 = &mut x; + let z = RawIndirect::<Mut>::new(y); + + // SAFETY: Same type as y which we made it from. + let w: &mut i32 = unsafe { z.into_inner() }; + + *w += 1; + assert_eq!(x, 43); + } + + trait AddOne { + fn add_one(&mut self); + } + + impl AddOne for i32 { + fn add_one(&mut self) { + *self += 1; + } + } + + #[test] + fn can_type_erase_unsized_mut_borrow() { + let mut x = 42; + let y: &mut dyn AddOne = &mut x; + let z = RawIndirect::<Mut>::new(y); + + // SAFETY: Same type as y which we made it from. + let w: &mut dyn AddOne = unsafe { z.into_inner() }; + + w.add_one(); + + assert_eq!(x, 43); + } +} |