Skip to content

Commit

Permalink
[pointer] Support Box and Arc
Browse files Browse the repository at this point in the history
gherrit-pr-id: I0676bf13e91d978a4c9c5961b8f8ed3613b7f83a
  • Loading branch information
joshlf committed Feb 20, 2025
1 parent 02f5982 commit 7af315e
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 11 deletions.
61 changes: 58 additions & 3 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -87,6 +90,11 @@ pub trait Validity: Sealed {

#[doc(hidden)]
type MappedTo<M: ValidityMapping>: 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`].
Expand All @@ -113,6 +121,11 @@ impl<T: ?Sized> Validity for Uninit<T> {
type WithInner<U: ?Sized> = Uninit<U>;

type MappedTo<M: ValidityMapping> = M::FromUninit<T>;

#[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`.
Expand Down Expand Up @@ -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<T>`, the referent's address is a
/// multiple of the `T`'s alignment.
pub enum Aligned {}
Expand Down Expand Up @@ -178,6 +205,11 @@ impl<T: ?Sized> Validity for AsInitialized<T> {
type Inner = T;
type WithInner<U: ?Sized> = AsInitialized<U>;
type MappedTo<M: ValidityMapping> = M::FromAsInitialized<T>;

#[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
Expand All @@ -187,6 +219,11 @@ impl<T: ?Sized> Validity for Initialized<T> {
type Inner = T;
type WithInner<U: ?Sized> = Initialized<U>;
type MappedTo<M: ValidityMapping> = M::FromInitialized<T>;

#[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`.
Expand All @@ -195,18 +232,29 @@ impl<T: ?Sized> Validity for Valid<T> {
type Inner = T;
type WithInner<U: ?Sized> = Valid<U>;
type MappedTo<M: ValidityMapping> = M::FromValid<T>;

#[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<A, R>` 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<A, R>` 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<A, R>`, then any `Ptr<T, (A, ...)>` is
Expand All @@ -223,6 +271,9 @@ define_because!(
);
// SAFETY: The aliasing parameter is `Exclusive`.
unsafe impl<T: ?Sized> Read<Exclusive, BecauseExclusive> for T {}
// SAFETY: The aliasing parameter is `Box`.
#[cfg(feature = "alloc")]
unsafe impl<T: ?Sized> Read<Box, BecauseExclusive> for T {}

define_because!(
/// Unsynchronized reads are permitted because no live [`Ptr`](crate::Ptr)s
Expand All @@ -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 {}

Expand Down
120 changes: 120 additions & 0 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,123 @@ where
{
ptr.as_bytes::<BecauseImmutable>().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<V: Validity<Inner = T>>(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<V: Validity<Inner = T>>(_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<V: Validity<Inner = T>>(_ptr: PtrInner<'t, T>) {}
}

#[cfg(feature = "alloc")]
unsafe impl<'t, T: ?Sized> Pointer<'t, T> for Box<T> {
type To<'u, U: 'u + ?Sized> = Box<U>;

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<T> {
unsafe { Box::from_raw(ptr.as_non_null().as_ptr()) }
}

#[inline(always)]
unsafe fn drop<V: Validity<Inner = T>>(ptr: PtrInner<'t, T>) {
unsafe { V::drop_box(ptr) }
}
}

#[cfg(feature = "std")]
unsafe impl<'t, T: ?Sized> Pointer<'t, T> for Arc<T> {
type To<'u, U: 'u + ?Sized> = Arc<U>;

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<T> {
unsafe { Arc::from_raw(ptr.as_non_null().as_ptr()) }
}

#[inline(always)]
unsafe fn drop<V: Validity<Inner = T>>(ptr: PtrInner<'t, T>) {
unsafe { V::drop_arc(ptr) }
}
}
}
16 changes: 11 additions & 5 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ mod _conversions {
// `Valid<T>`.
//
// 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
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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) }
Expand All @@ -947,7 +951,7 @@ mod _casts {
{
// SAFETY: Because `T::Inner` and `W` both implement
// `Read<I::Aliasing, _>`, 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
//
Expand Down Expand Up @@ -1075,8 +1079,10 @@ mod _casts {
// initialized, so `ptr` conforms to the validity invariant of
// `Initialized`.
// 1. Since `W: Read<I::Aliasing, _>`, 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
Expand Down
6 changes: 3 additions & 3 deletions src/pointer/transmute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ where
Dst: Validity,
Src::Inner: Read<A, R>,
Dst::Inner: Read<A, R> + CastFrom<Src::Inner>,
A: Aliasing,
A: Reference,
{
}

Expand All @@ -112,7 +112,7 @@ where
// UnsafeCellsAgree<Src>`.
unsafe impl<Src, Dst, A> TryTransmuteFromPtr<Src, A, BecauseBidirectional> for Dst
where
A: Aliasing,
A: Reference,
Src: Validity + TransmuteFrom<Dst>,
Dst: Validity + TransmuteFrom<Src>,
Src::Inner: UnsafeCellsAgree<Dst::Inner>,
Expand All @@ -130,7 +130,7 @@ where
// - `UnsafeCell` agreement guaranteed by `Src: Immutable + Dst: Immutable`.
unsafe impl<Src, Dst, A> TryTransmuteFromPtr<Src, A, BecauseFoo> for Dst
where
A: Aliasing,
A: Reference,
Src: Validity + TransmuteFrom<Dst>,
Dst: Validity,
Src::Inner: Immutable,
Expand Down

0 comments on commit 7af315e

Please sign in to comment.