Skip to content

Commit

Permalink
[pointer] Support Box and Arc
Browse files Browse the repository at this point in the history
gherrit-pr-id: I70880ddd683a1edd8cfdf553f9238db2ae0f7149
  • Loading branch information
joshlf committed Oct 23, 2024
1 parent 4887e03 commit e8df8c0
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 16 deletions.
8 changes: 4 additions & 4 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
}

#[inline]
fn is_bit_valid<A: invariant::Reference>(candidate: Maybe<'_, Self, A>) -> bool {
fn is_bit_valid<A: invariant::Aliasing>(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
Expand Down Expand Up @@ -1124,15 +1124,15 @@ mod tests {

pub(super) trait TestIsBitValidShared<T: ?Sized> {
#[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<bool>;
}

impl<T: TryFromBytes + Immutable + ?Sized> TestIsBitValidShared<T> for AutorefWrapper<T> {
#[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<bool> {
Expand Down Expand Up @@ -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<bool> {
Expand Down
26 changes: 17 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P, CastError<P::To<'a, [u8]>, Self>>
where
Self: 'a + KnownLayout + invariant::Read<P::Aliasing, R>,
{
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())),
}
}

Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/pointer/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
{
Expand Down
56 changes: 54 additions & 2 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<T>;
}
}

#[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<T>;
}
}

/// 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 @@ -237,6 +274,18 @@ impl ValidityInner for Valid {
type MappedTo<M: ValidityMapping> = 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<A, R>` implies that a pointer to `T` with aliasing `A` permits
Expand All @@ -261,8 +310,7 @@ define_because!(
#[doc(hidden)]
pub BecauseExclusive
);
// SAFETY: The aliasing parameter is `Exclusive`.
unsafe impl<T: ?Sized> Read<Exclusive, BecauseExclusive> for T {}
unsafe impl<A: ExclusiveFoo, T: ?Sized> Read<A, BecauseExclusive> for T {}

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

Expand Down
89 changes: 89 additions & 0 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,92 @@ 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 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<T> {
type To<'u, U: 'u + ?Sized> = Box<U>;

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

#[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()) }
}
}
}
26 changes: 26 additions & 0 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P: Pointer<'a, T, Aliasing = A>>(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<P: Pointer<'a, T, Aliasing = A>>(self) -> P {
unsafe { P::from_ptr(self.as_inner()) }
}
}

/// `&'a T` → `Ptr<'a, T>`
impl<'a, T> Ptr<'a, T, (Shared, Aligned, Valid)>
where
Expand Down Expand Up @@ -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<U, R>(
self,
cast_type: CastType,
Expand Down

0 comments on commit e8df8c0

Please sign in to comment.