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 #2355

Open
wants to merge 1 commit into
base: I8d5d162c1b6fe43e3dcb90a6dc5bf58a7a203bf8
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
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
Loading