From 82355bfb4528933becb3ba08232d0c22e80416a5 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 18 Feb 2025 13:24:07 -0800 Subject: [PATCH] [pointer] Support Box and Arc gherrit-pr-id: I0676bf13e91d978a4c9c5961b8f8ed3613b7f83a --- src/pointer/invariant.rs | 61 +++++++++++++++++++- src/pointer/mod.rs | 120 +++++++++++++++++++++++++++++++++++++++ src/pointer/ptr.rs | 16 ++++-- src/pointer/transmute.rs | 6 +- 4 files changed, 192 insertions(+), 11 deletions(-) diff --git a/src/pointer/invariant.rs b/src/pointer/invariant.rs index 06c2733ce2..7a9cb704cf 100644 --- a/src/pointer/invariant.rs +++ b/src/pointer/invariant.rs @@ -26,6 +26,9 @@ use core::marker::PhantomData; +#[cfg(feature = "alloc")] +use crate::pointer::inner::PtrInner; + /// The aliasing and alignment invariants of a [`Ptr`][super::Ptr]. pub trait Invariants: Sealed { type Aliasing: Aliasing; @@ -87,6 +90,11 @@ pub trait Validity: Sealed { #[doc(hidden)] type MappedTo: Validity; + + #[cfg(feature = "alloc")] + unsafe fn drop_box<'a>(ptr: PtrInner<'a, Self::Inner>); + #[cfg(feature = "std")] + unsafe fn drop_arc<'a>(ptr: PtrInner<'a, Self::Inner>); } /// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`]. @@ -113,6 +121,11 @@ impl Validity for Uninit { type WithInner = Uninit; type MappedTo = M::FromUninit; + + #[cfg(feature = "alloc")] + unsafe fn drop_box<'a>(_ptr: PtrInner<'a, T>) {} + #[cfg(feature = "std")] + unsafe fn drop_arc<'a>(_ptr: PtrInner<'a, T>) {} } /// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`. @@ -140,6 +153,20 @@ impl Aliasing for Exclusive { } impl Reference for Exclusive {} +#[cfg(feature = "alloc")] +pub enum Box {} +#[cfg(feature = "alloc")] +impl Aliasing for Box { + const IS_EXCLUSIVE: bool = false; +} + +#[cfg(feature = "std")] +pub enum Arc {} +#[cfg(feature = "std")] +impl Aliasing for Arc { + const IS_EXCLUSIVE: bool = false; +} + /// The referent is aligned: for `Ptr`, the referent's address is a /// multiple of the `T`'s alignment. pub enum Aligned {} @@ -178,6 +205,11 @@ impl Validity for AsInitialized { type Inner = T; type WithInner = AsInitialized; type MappedTo = M::FromAsInitialized; + + #[cfg(feature = "alloc")] + unsafe fn drop_box<'a>(_ptr: PtrInner<'a, T>) {} + #[cfg(feature = "std")] + unsafe fn drop_arc<'a>(_ptr: PtrInner<'a, T>) {} } /// The byte ranges in the referent are fully initialized. In other words, if @@ -187,6 +219,11 @@ impl Validity for Initialized { type Inner = T; type WithInner = Initialized; type MappedTo = M::FromInitialized; + + #[cfg(feature = "alloc")] + unsafe fn drop_box<'a>(_ptr: PtrInner<'a, T>) {} + #[cfg(feature = "std")] + unsafe fn drop_arc<'a>(_ptr: PtrInner<'a, T>) {} } /// The referent is bit-valid for `T`. @@ -195,18 +232,29 @@ impl Validity for Valid { type Inner = T; type WithInner = Valid; type MappedTo = M::FromValid; + + #[cfg(feature = "alloc")] + unsafe fn drop_box<'a>(ptr: PtrInner<'a, T>) { + drop(unsafe { alloc::boxed::Box::from_raw(ptr.as_non_null().as_ptr()) }); + } + + #[cfg(feature = "std")] + unsafe fn drop_arc<'a>(ptr: PtrInner<'a, T>) { + drop(unsafe { std::sync::Arc::from_raw(ptr.as_non_null().as_ptr()) }); + } } /// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations. /// /// `T: Read` implies that a pointer to `T` with aliasing `A` permits -/// unsynchronized read oeprations. This can be because `A` is [`Exclusive`] or -/// because `T` does not permit interior mutation. +/// unsynchronized read oeprations. This can be because `A` is an exclusive +/// aliasing mode (i.e., [`Exclusive`] or [`Box`]) or because `T` does not +/// permit interior mutation. /// /// # Safety /// /// `T: Read` if either of the following conditions holds: -/// - `A` is [`Exclusive`] +/// - `A` is [`Exclusive`] or [`Box`] /// - `T` implements [`Immutable`](crate::Immutable) /// /// As a consequence, if `T: Read`, then any `Ptr` is @@ -223,6 +271,9 @@ define_because!( ); // SAFETY: The aliasing parameter is `Exclusive`. unsafe impl Read for T {} +// SAFETY: The aliasing parameter is `Box`. +#[cfg(feature = "alloc")] +unsafe impl Read for T {} define_because!( /// Unsynchronized reads are permitted because no live [`Ptr`](crate::Ptr)s @@ -245,6 +296,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 ddaf1600f6..7c78fa647c 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -86,3 +86,123 @@ 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 FromBytes { + fn from_bytes<'a, P: Pointer<'a, [u8]>>(bytes: P) -> P::To<'a, Self>; + } + + pub unsafe trait Pointer<'t, T: ?Sized> { + type To<'u, U: 'u + ?Sized>: Pointer<'u, U, Aliasing = Self::Aliasing>; + + #[doc(hidden)] + type Aliasing: Aliasing; + + // Used to call is_bit_valid + #[doc(hidden)] + type ReborrowAliasing: Reference; + + #[doc(hidden)] + fn into_ptr(self) -> PtrInner<'t, T>; + + #[doc(hidden)] + unsafe fn from_ptr(ptr: PtrInner<'t, T>) -> Self; + + #[doc(hidden)] + unsafe fn drop>(ptr: PtrInner<'t, T>); + } + + unsafe impl<'t, T: ?Sized> Pointer<'t, T> for &'t T { + type To<'u, U: 'u + ?Sized> = &'u U; + + type Aliasing = Shared; + type ReborrowAliasing = 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() } + } + + #[inline(always)] + unsafe fn drop>(_ptr: PtrInner<'t, T>) {} + } + + unsafe impl<'t, T: ?Sized> Pointer<'t, T> for &'t mut T { + type To<'u, U: 'u + ?Sized> = &'u mut U; + + type Aliasing = Exclusive; + type ReborrowAliasing = 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() } + } + + #[inline(always)] + unsafe fn drop>(_ptr: PtrInner<'t, T>) {} + } + + #[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; + type ReborrowAliasing = Exclusive; + + #[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()) } + } + + #[inline(always)] + unsafe fn drop>(ptr: PtrInner<'t, T>) { + unsafe { V::drop_box(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; + type ReborrowAliasing = Shared; + + #[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()) } + } + + #[inline(always)] + unsafe fn drop>(ptr: PtrInner<'t, T>) { + unsafe { V::drop_arc(ptr) } + } + } +} diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index b676b2b6b9..ae887b52f9 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -331,7 +331,7 @@ mod _conversions { // `Valid`. // // 4. You must enforce Rust’s aliasing rules. This is ensured by - // contract on `Ptr`, because the `ALIASING_INVARIANT` is + // contract on `Ptr`, because the aliasing invariant is // `Exclusive`. // // [1]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.as_mut @@ -838,6 +838,9 @@ mod _casts { V: Validity, I: Invariants, { + // TODO: This (and callers) needs more safety preconditions related to + // preserving size and alignment in Box and Arc. + /// Casts to a different (unsized) target type without checking interior /// mutability. /// @@ -920,7 +923,8 @@ mod _casts { // pointer will permit mutation of this byte during `'a`, by // invariant on `self`, no other code assumes that this will // not happen. - // - `Inaccessible`: There are no restrictions we need to uphold. + // - `Box`: TODO + // - `Arc`: TODO // 8. `ptr`, trivially, conforms to the alignment invariant of // `Unknown`. unsafe { Ptr::new(ptr) } @@ -947,7 +951,7 @@ mod _casts { { // SAFETY: Because `T::Inner` and `W` both implement // `Read`, either: - // - `I::Aliasing` is `Exclusive` + // - `I::Aliasing` is `Exclusive` or `Box` // - `V::Inner` and `W` are both `Immutable`, in which case they // trivially contain `UnsafeCell`s at identical locations // @@ -1075,8 +1079,10 @@ mod _casts { // initialized, so `ptr` conforms to the validity invariant of // `Initialized`. // 1. Since `W: Read`, either: - // - `I::Aliasing` is `Exclusive`, in which case both `src` and - // `ptr` conform to `Exclusive` + // - `I::Aliasing` is `Exclusive` or `Box`. `I::Aliasing: + // Reference` only permits `Exclusive`, so `I::Aliasing` is + // `Exclusive`. In this case, both `src` and `ptr` conform to + // `Exclusive` // - `I::Aliasing` is `Shared` or `Inaccessible` and `W` is // `Immutable` (we already know that `[u8]: Immutable`). In // this case, neither `W` nor `[u8]` permit mutation, and so diff --git a/src/pointer/transmute.rs b/src/pointer/transmute.rs index c6d266ca44..2cb4278aad 100644 --- a/src/pointer/transmute.rs +++ b/src/pointer/transmute.rs @@ -89,7 +89,7 @@ where Dst: Validity, Src::Inner: Read, Dst::Inner: Read + CastFrom, - A: Aliasing, + A: Reference, { } @@ -112,7 +112,7 @@ where // UnsafeCellsAgree`. unsafe impl TryTransmuteFromPtr for Dst where - A: Aliasing, + A: Reference, Src: Validity + TransmuteFrom, Dst: Validity + TransmuteFrom, Src::Inner: UnsafeCellsAgree, @@ -130,7 +130,7 @@ where // - `UnsafeCell` agreement guaranteed by `Src: Immutable + Dst: Immutable`. unsafe impl TryTransmuteFromPtr for Dst where - A: Aliasing, + A: Reference, Src: Validity + TransmuteFrom, Dst: Validity, Src::Inner: Immutable,