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][WIP] Validity in referent #2406

Open
wants to merge 1 commit into
base: Iad14813bc6d933312bc8d7a1ddcf1aafc7126938
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
80 changes: 56 additions & 24 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
pub trait Invariants: Sealed {
type Aliasing: Aliasing;
type Alignment: Alignment;
type Validity: Validity;
// type Validity: Validity;
}

impl<A: Aliasing, AA: Alignment, V: Validity> Invariants for (A, AA, V) {
impl<A: Aliasing, AA: Alignment> Invariants for (A, AA) {
type Aliasing = A;
type Alignment = AA;
type Validity = V;
// type Validity = V;
}

/// The aliasing invariant of a [`Ptr`][super::Ptr].
Expand Down Expand Up @@ -83,7 +83,17 @@ pub trait Alignment: Sealed {}
/// mechanism (e.g. a `&` reference used to derive `src`) to write `x` where
/// `x ∈ S(T, V)` but `x ∉ S(U, W)`, which would violate the guarantee that
/// `dst`'s referent may only contain values in `S(U, W)`.
pub unsafe trait Validity: Sealed {}
pub unsafe trait Validity: Sealed {
type Inner: ?Sized;
}

/// Does `V` have the same validity invariant as `Self`?
///
/// # Safety
///
/// Unsafe code may assume that `W: SameValidity<V>` guarantees that `V` and `W`
/// have the same validity invariant.
pub unsafe trait SameValidity<V>: Sealed {}

/// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`].
///
Expand Down Expand Up @@ -128,14 +138,24 @@ impl Alignment for Unaligned {}
pub enum Aligned {}
impl Alignment for Aligned {}

struct NeverPhantomData<T: ?Sized> {
_marker: core::marker::PhantomData<T>,
_never: core::convert::Infallible,
}

/// Any bit pattern is allowed in the `Ptr`'s referent, including uninitialized
/// bytes.
pub enum Uninit {}
pub struct Uninit<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `Uninit`'s validity is well-defined for all `T: ?Sized`, and is not a
// function of any property of `T` other than its bit validity (in fact, it's
// not even a property of `T`'s bit validity, but this is more than we are
// required to uphold).
unsafe impl Validity for Uninit {}
unsafe impl<T: ?Sized> Validity for Uninit<T> {
type Inner = T;
}

// SAFETY: The same validity (`Uninit`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<Uninit<T>> for Uninit<U> {}

/// The byte ranges initialized in `T` are also initialized in the referent of a
/// `Ptr<T>`.
Expand Down Expand Up @@ -164,36 +184,48 @@ unsafe impl Validity for Uninit {}
/// variant's bit validity (although note that the variant may contain another
/// enum type, in which case the same rules apply depending on the state of
/// its discriminant, and so on recursively).
pub enum AsInitialized {}
pub struct AsInitialized<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `AsInitialized`'s validity is well-defined for all `T: ?Sized`, and
// is not a function of any property of `T` other than its bit validity.
unsafe impl Validity for AsInitialized {}
unsafe impl<T: ?Sized> Validity for AsInitialized<T> {
type Inner = T;
}
// SAFETY: The same validity (`AsInitialized`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<AsInitialized<T>> for AsInitialized<U> {}

/// The byte ranges in the referent are fully initialized. In other words, if
/// the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`.
pub enum Initialized {}
pub struct Initialized<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `Initialized`'s validity is well-defined for all `T: ?Sized`, and is
// not a function of any property of `T` other than its bit validity (in fact,
// it's not even a property of `T`'s bit validity, but this is more than we are
// required to uphold).
unsafe impl Validity for Initialized {}
unsafe impl<T: ?Sized> Validity for Initialized<T> {
type Inner = T;
}
// SAFETY: The same validity (`Initialized`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<Initialized<T>> for Initialized<U> {}

/// The referent of a `Ptr<T>` is bit-valid for `T`.
pub enum Valid {}
pub struct Valid<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `Valid`'s validity is well-defined for all `T: ?Sized`, and is not a
// function of any property of `T` other than its bit validity.
unsafe impl Validity for Valid {}
unsafe impl<T: ?Sized> Validity for Valid<T> {
type Inner = T;
}
// SAFETY: The same validity (`Valid`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<Valid<T>> for Valid<U> {}

/// # Safety
///
/// `DT: CastableFrom<ST, SV, DV>` is sound if `SV = DV = Uninit` or `SV = DV =
/// Initialized`.
pub unsafe trait CastableFrom<ST: ?Sized, SV, DV> {}
/// `U: CastableFrom<T>` is sound if `T` and `U` have the same validity, and
/// that validity is either [`Uninit`] or [`Initialized`].
pub unsafe trait CastableFrom<T: ?Sized> {}

// SAFETY: `SV = DV = Uninit`.
unsafe impl<ST: ?Sized, DT: ?Sized> CastableFrom<ST, Uninit, Uninit> for DT {}
// SAFETY: `SV = DV = Initialized`.
unsafe impl<ST: ?Sized, DT: ?Sized> CastableFrom<ST, Initialized, Initialized> for DT {}
// SAFETY: Both types have validity `Uninit`.
unsafe impl<T: ?Sized, U: ?Sized> CastableFrom<Uninit<T>> for Uninit<U> {}
// SAFETY: Both types have validity `Initialized`.
unsafe impl<T: ?Sized, U: ?Sized> CastableFrom<Initialized<T>> for Initialized<U> {}

/// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations.
///
Expand Down Expand Up @@ -238,12 +270,12 @@ mod sealed {
impl Sealed for Unaligned {}
impl Sealed for Aligned {}

impl Sealed for Uninit {}
impl Sealed for AsInitialized {}
impl Sealed for Initialized {}
impl Sealed for Valid {}
impl<T: ?Sized> Sealed for Uninit<T> {}
impl<T: ?Sized> Sealed for AsInitialized<T> {}
impl<T: ?Sized> Sealed for Initialized<T> {}
impl<T: ?Sized> Sealed for Valid<T> {}

impl<A: Sealed, AA: Sealed, V: Sealed> Sealed for (A, AA, V) {}
impl<A: Sealed, AA: Sealed> Sealed for (A, AA) {}

impl Sealed for BecauseImmutable {}
impl Sealed for BecauseExclusive {}
Expand Down
42 changes: 19 additions & 23 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,55 +17,51 @@ mod transmute;
#[doc(hidden)]
pub(crate) use transmute::*;
#[doc(hidden)]
pub use {
invariant::{BecauseExclusive, BecauseImmutable, Read},
ptr::Ptr,
};
pub use {invariant::*, ptr::Ptr};

use crate::Unaligned;

/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
/// to [`TryFromBytes::is_bit_valid`].
///
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;
pub type Maybe<'a, T, Aliasing = Shared, Alignment = Unaligned> =
Ptr<'a, Initialized<T>, (Aliasing, Alignment)>;

/// A semi-user-facing wrapper type representing a maybe-aligned reference, for
/// use in [`TryFromBytes::is_bit_valid`].
///
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
pub type MaybeAligned<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Valid)>;
pub type MaybeAligned<'a, T, Aliasing = Shared, Alignment = Unaligned> =
Ptr<'a, Valid<T>, (Aliasing, Alignment)>;

// These methods are defined on the type alias, `MaybeAligned`, so as to bring
// them to the forefront of the rendered rustdoc for that type alias.
impl<'a, T, Aliasing, Alignment> MaybeAligned<'a, T, Aliasing, Alignment>
impl<'a, T, A, AA> MaybeAligned<'a, T, A, AA>
where
T: 'a + ?Sized,
Aliasing: invariant::Aliasing,
Alignment: invariant::Alignment,
A: Aliasing,
AA: Alignment,
{
/// Reads the value from `MaybeAligned`.
#[must_use]
#[inline]
pub fn read_unaligned<R>(self) -> T
where
T: Copy,
T: invariant::Read<Aliasing, R>,
T: Read<A, R> + Copy,
{
// SAFETY: By invariant on `MaybeAligned`, `self` contains
// validly-initialized data for `T`. By `T: Read<Aliasing>`, we are
// permitted to perform a read of `self`'s referent.
// validly-initialized data for `T`. By `T: Read<A>`, we are permitted
// to perform a read of `self`'s referent.
unsafe { self.as_inner().read_unaligned() }
}
}

impl<'a, T, Aliasing, Alignment> MaybeAligned<'a, T, Aliasing, Alignment>
impl<'a, T, A, AA> MaybeAligned<'a, T, A, AA>
where
T: 'a + ?Sized,
Aliasing: invariant::Reference,
Alignment: invariant::Alignment,
A: Reference,
AA: Alignment,
{
/// Views the value as an aligned reference.
///
Expand All @@ -74,18 +70,18 @@ where
#[inline]
pub fn unaligned_as_ref(self) -> &'a T
where
T: Unaligned,
T: crate::Unaligned,
{
self.bikeshed_recall_aligned().as_ref()
}
}

/// Checks if the referent is zeroed.
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, Initialized<T>, I>) -> bool
where
T: crate::Immutable + crate::KnownLayout,
I: invariant::Invariants<Validity = invariant::Initialized>,
I::Aliasing: invariant::Reference,
T: crate::Immutable + crate::KnownLayout + ?Sized,
I: Invariants,
I::Aliasing: Reference,
{
ptr.as_bytes::<BecauseImmutable>().as_ref().iter().all(|&byte| byte == 0)
}
Loading
Loading