From e8df8c094bc7e9d93a3abb0a3dff5dd72e0402e9 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Mon, 14 Oct 2024 21:14:56 -0700 Subject: [PATCH] [pointer] Support Box and Arc gherrit-pr-id: I70880ddd683a1edd8cfdf553f9238db2ae0f7149 --- src/impls.rs | 8 ++-- src/lib.rs | 26 ++++++++---- src/pointer/inner.rs | 3 +- src/pointer/invariant.rs | 56 ++++++++++++++++++++++++- src/pointer/mod.rs | 89 ++++++++++++++++++++++++++++++++++++++++ src/pointer/ptr.rs | 26 ++++++++++++ 6 files changed, 192 insertions(+), 16 deletions(-) diff --git a/src/impls.rs b/src/impls.rs index 4cf7360852..71b6374738 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -648,7 +648,7 @@ unsafe impl TryFromBytes for UnsafeCell { } #[inline] - fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { + fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { // The only way to implement this function is using an exclusive-aliased // pointer. `UnsafeCell`s cannot be read via shared-aliased pointers // (other than by using `unsafe` code, which we can't use since we can't @@ -1124,7 +1124,7 @@ mod tests { pub(super) trait TestIsBitValidShared { #[allow(clippy::needless_lifetimes)] - fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>( + fn test_is_bit_valid_shared<'ptr, A: invariant::Aliasing>( &self, candidate: Maybe<'ptr, T, A>, ) -> Option; @@ -1132,7 +1132,7 @@ mod tests { impl TestIsBitValidShared for AutorefWrapper { #[allow(clippy::needless_lifetimes)] - fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>( + fn test_is_bit_valid_shared<'ptr, A: invariant::Aliasing>( &self, candidate: Maybe<'ptr, T, A>, ) -> Option { @@ -1222,7 +1222,7 @@ mod tests { #[allow(unused, non_local_definitions)] impl AutorefWrapper<$ty> { #[allow(clippy::needless_lifetimes)] - fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>( + fn test_is_bit_valid_shared<'ptr, A: invariant::Aliasing>( &mut self, candidate: Maybe<'ptr, $ty, A>, ) -> Option { diff --git a/src/lib.rs b/src/lib.rs index 943e9c6add..2a132491e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3458,10 +3458,22 @@ pub unsafe trait FromBytes: FromZeros { where Self: KnownLayout + Immutable, { - static_assert_dst_is_not_zst!(Self); - match Ptr::from_ref(source).try_cast_into_no_leftover::<_, BecauseImmutable>(None) { - Ok(ptr) => Ok(ptr.bikeshed_recall_valid().as_ref()), - Err(err) => Err(err.map_src(|src| src.as_ref())), + Self::from_bytes(source) + } + + #[must_use = "has no side effects"] + #[inline] + fn from_bytes<'a, P: pointer::Pointer<'a, Self>, R>( + source: P::To<'a, [u8]>, + ) -> Result, Self>> + where + Self: 'a + KnownLayout + invariant::Read, + { + match Ptr::<'_, _, (P::Aliasing, _, _)>::from_pointer(source) + .try_cast_into_no_leftover::<_, R>(None) + { + Ok(ptr) => Ok(ptr.bikeshed_recall_valid().into_pointer()), + Err(err) => Err(err.map_src(|src| src.into_pointer())), } } @@ -3694,11 +3706,7 @@ pub unsafe trait FromBytes: FromZeros { where Self: IntoBytes + KnownLayout, { - static_assert_dst_is_not_zst!(Self); - match Ptr::from_mut(source).try_cast_into_no_leftover::<_, BecauseExclusive>(None) { - Ok(ptr) => Ok(ptr.bikeshed_recall_valid().as_mut()), - Err(err) => Err(err.map_src(|src| src.as_mut())), - } + Self::from_bytes(source) } /// Interprets the prefix of the given `source` as a `&mut Self` without diff --git a/src/pointer/inner.rs b/src/pointer/inner.rs index 48d4e8f113..0ddc189534 100644 --- a/src/pointer/inner.rs +++ b/src/pointer/inner.rs @@ -25,7 +25,8 @@ mod _def { /// `Ptr<'a, T>` is [covariant] in `'a` and `T`. /// /// [covariant]: https://doc.rust-lang.org/reference/subtyping.html - pub(crate) struct PtrInner<'a, T> + #[allow(missing_debug_implementations)] + pub struct PtrInner<'a, T> where T: ?Sized, { diff --git a/src/pointer/invariant.rs b/src/pointer/invariant.rs index 2bb82b89f3..08d860853c 100644 --- a/src/pointer/invariant.rs +++ b/src/pointer/invariant.rs @@ -89,6 +89,15 @@ pub trait Aliasing: Sealed { /// Aliasing>::Variance<'a, T>` to inherit this variance. #[doc(hidden)] type Variance<'a, T: 'a + ?Sized>; + + // #[doc(hidden)] + // type Applied<'a, T: 'a + ?Sized>; + + // #[doc(hidden)] + // fn into_ptr<'a, T: 'a + ?Sized>(ptr: Self::Applied<'a, T>) -> PtrInner<'a, T>; + + // #[doc(hidden)] + // unsafe fn from_ptr<'a, T: 'a + ?Sized>(ptr: PtrInner<'a, T>) -> Self::Applied<'a, T>; } // NOTE: The `AlignmentInner`/`Alignment` distinction is required so that we can @@ -186,6 +195,34 @@ impl Aliasing for Exclusive { } impl Reference for Exclusive {} +#[cfg(feature = "alloc")] +mod _alloc { + use alloc::boxed::Box as Bx; + + use super::*; + + pub enum Box {} + impl Aliasing for Box { + const IS_EXCLUSIVE: bool = false; + type Variance<'a, T: 'a + ?Sized> = Bx; + } +} + +#[cfg(feature = "std")] +pub use _std::*; +#[cfg(feature = "std")] +mod _std { + use std::sync::Arc as Ac; + + use super::*; + + pub enum Arc {} + impl Aliasing for Arc { + const IS_EXCLUSIVE: bool = false; + type Variance<'a, T: 'a + ?Sized> = Ac; + } +} + /// The referent is aligned: for `Ptr`, the referent's address is a multiple /// of the `T`'s alignment. pub enum Aligned {} @@ -237,6 +274,18 @@ impl ValidityInner for Valid { type MappedTo = M::FromValid; } +// Shared, Arc, etc +pub trait SharedFoo: Aliasing {} +impl SharedFoo for Shared {} +#[cfg(feature = "std")] +impl SharedFoo for Arc {} + +// Exclusive, Box, etc +pub trait ExclusiveFoo: Aliasing {} +impl ExclusiveFoo for Exclusive {} +#[cfg(feature = "alloc")] +impl ExclusiveFoo for Box {} + /// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations. /// /// `T: Read` implies that a pointer to `T` with aliasing `A` permits @@ -261,8 +310,7 @@ define_because!( #[doc(hidden)] pub BecauseExclusive ); -// SAFETY: The aliasing parameter is `Exclusive`. -unsafe impl Read for T {} +unsafe impl Read for T {} define_because!( /// Unsynchronized reads are permitted because no live [`Ptr`](crate::Ptr)s @@ -283,6 +331,10 @@ mod sealed { impl Sealed for Shared {} impl Sealed for Exclusive {} + #[cfg(feature = "alloc")] + impl Sealed for Box {} + #[cfg(feature = "std")] + impl Sealed for Arc {} impl Sealed for Aligned {} diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index 944a959da7..35f9b80a15 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -86,3 +86,92 @@ where { ptr.as_bytes::().as_ref().iter().all(|&byte| byte == 0) } + +pub use _pointer::*; +mod _pointer { + #[cfg(feature = "alloc")] + use alloc::boxed::Box; + #[cfg(feature = "std")] + use std::sync::Arc; + + use super::{inner::PtrInner, invariant::*}; + + pub unsafe trait Pointer<'t, T: ?Sized> { + type To<'u, U: 'u + ?Sized>: Pointer<'u, U, Aliasing = Self::Aliasing>; + + #[doc(hidden)] + type Aliasing: Aliasing; + + #[doc(hidden)] + fn into_ptr(self) -> PtrInner<'t, T>; + + #[doc(hidden)] + unsafe fn from_ptr(ptr: PtrInner<'t, T>) -> Self; + } + + unsafe impl<'t, T: ?Sized> Pointer<'t, T> for &'t T { + type To<'u, U: 'u + ?Sized> = &'u U; + + type Aliasing = Shared; + + #[inline(always)] + fn into_ptr(self) -> PtrInner<'t, T> { + PtrInner::from_ref(self) + } + + #[inline(always)] + unsafe fn from_ptr(ptr: PtrInner<'t, T>) -> Self { + unsafe { ptr.as_non_null().as_ref() } + } + } + + unsafe impl<'t, T: ?Sized> Pointer<'t, T> for &'t mut T { + type To<'u, U: 'u + ?Sized> = &'u mut U; + + type Aliasing = Exclusive; + + #[inline(always)] + fn into_ptr(self) -> PtrInner<'t, T> { + PtrInner::from_ref(self) + } + + #[inline(always)] + unsafe fn from_ptr(ptr: PtrInner<'t, T>) -> Self { + unsafe { ptr.as_non_null().as_mut() } + } + } + + #[cfg(feature = "alloc")] + unsafe impl<'t, T: ?Sized> Pointer<'t, T> for Box { + type To<'u, U: 'u + ?Sized> = Box; + + type Aliasing = super::invariant::Box; + + #[inline(always)] + fn into_ptr(self) -> PtrInner<'t, T> { + PtrInner::from_box(self) + } + + #[inline(always)] + unsafe fn from_ptr(ptr: PtrInner<'t, T>) -> Box { + unsafe { Box::from_raw(ptr.as_non_null().as_ptr()) } + } + } + + #[cfg(feature = "std")] + unsafe impl<'t, T: ?Sized> Pointer<'t, T> for Arc { + type To<'u, U: 'u + ?Sized> = Arc; + + type Aliasing = super::invariant::Arc; + + #[inline(always)] + fn into_ptr(self) -> PtrInner<'t, T> { + PtrInner::from_arc(self) + } + + #[inline(always)] + unsafe fn from_ptr(ptr: PtrInner<'t, T>) -> Arc { + unsafe { Arc::from_raw(ptr.as_non_null().as_ptr()) } + } + } +} diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 9c64d4121e..e989325150 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -176,6 +176,29 @@ mod _conversions { TransmuteFromAlignment, TransmuteFromPtr, TransmuteFromPtrOld, }; + // TODO: How to make this a `From` impl without blanket impl conflicts? + impl<'a, T, A> Ptr<'a, T, (A, Aligned, Valid)> + where + T: 'a + ?Sized, + A: Aliasing, + { + /// Constructs a `Ptr` from an existing smart pointer or reference type. + #[doc(hidden)] + #[inline] + pub fn from_pointer>(ptr: P) -> Self { + let ptr = P::into_ptr(ptr); + unsafe { Self::from_inner(ptr) } + } + + /// Constructs `self` to a smart pointer or reference type. + #[doc(hidden)] + #[inline] + #[must_use] + pub fn into_pointer>(self) -> P { + unsafe { P::from_ptr(self.as_inner()) } + } + } + /// `&'a T` → `Ptr<'a, T>` impl<'a, T> Ptr<'a, T, (Shared, Aligned, Valid)> where @@ -927,6 +950,9 @@ mod _casts { /// - If this is a prefix cast, `ptr` has the same address as `self`. /// - If this is a suffix cast, `remainder` has the same address as /// `self`. + // TODO: This is currently unsound - need to bound with `I::Aliasing: + // Reference` in order to prove that splitting is okay (it isn't for + // e.g. Box and Arc). pub(crate) fn try_cast_into( self, cast_type: CastType,