Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pointer] Support Box and Arc #1970

Open
wants to merge 1 commit into
base: I25973ea67f59671329a920dd39183c1348942a1a
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -3480,10 +3480,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 @@ -3716,11 +3728,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).
#[inline(always)]
pub(crate) fn try_cast_into<U, R>(
self,
Expand Down
Loading