Skip to content

Commit

Permalink
[pointer] Make invariants opaque, more ergonomic
Browse files Browse the repository at this point in the history
Closes #1876
  • Loading branch information
joshlf committed Oct 14, 2024
1 parent 25ddf05 commit 9e69c15
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 43 deletions.
68 changes: 48 additions & 20 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,40 @@ pub trait Invariants: Sealed {
type Aliasing: Aliasing;
type Alignment: Alignment;
type Validity: Validity;

/// Invariants identical to `Self` except with a different aliasing
/// invariant.
type WithAliasing<A: Aliasing>: Invariants<
Aliasing = A,
Alignment = Self::Alignment,
Validity = Self::Validity,
>;

/// Invariants identical to `Self` except with a different alignment
/// invariant.
type WithAlignment<A: Alignment>: Invariants<
Aliasing = Self::Aliasing,
Alignment = A,
Validity = Self::Validity,
>;

/// Invariants identical to `Self` except with a different validity
/// invariant.
type WithValidity<V: Validity>: Invariants<
Aliasing = Self::Aliasing,
Alignment = Self::Alignment,
Validity = V,
>;
}

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

type WithAliasing<AB: Aliasing> = (AB, AA, V);
type WithAlignment<AB: Alignment> = (A, AB, V);
type WithValidity<VB: Validity> = (A, AA, VB);
}

/// The aliasing invariant of a [`Ptr`][super::Ptr].
Expand Down Expand Up @@ -107,29 +135,29 @@ impl Alignment for Aligned {}
/// The byte ranges initialized in `T` are also initialized in the referent.
///
/// Formally: uninitialized bytes may only be present in `Ptr<T>`'s referent
/// where they are guaranteed to be present in `T`. This is a dynamic property:
/// if, at a particular byte offset, a valid enum discriminant is set, the
/// subsequent bytes may only have uninitialized bytes as specificed by the
/// corresponding enum.
/// where they are guaranteed to be present in `T`. This is a dynamic
/// property: if, at a particular byte offset, a valid enum discriminant is
/// set, the subsequent bytes may only have uninitialized bytes as
/// specificed by the corresponding enum.
///
/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`, in
/// the range `[0, len)`:
/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in `t`
/// is initialized, then the byte at offset `b` within `*ptr` must be
/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`,
/// in the range `[0, len)`:
/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in
/// `t` is initialized, then the byte at offset `b` within `*ptr` must be
/// initialized.
/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S` be
/// the subset of valid instances of `T` of length `len` which contain `c` in
/// the offset range `[0, b)`. If, in any instance of `t: T` in `S`, the byte
/// at offset `b` in `t` is initialized, then the byte at offset `b` in `*ptr`
/// must be initialized.
/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S`
/// be the subset of valid instances of `T` of length `len` which contain
/// `c` in the offset range `[0, b)`. If, in any instance of `t: T` in
/// `S`, the byte at offset `b` in `t` is initialized, then the byte at
/// offset `b` in `*ptr` must be initialized.
///
/// Pragmatically, this means that if `*ptr` is guaranteed to contain an enum
/// type at a particular offset, and the enum discriminant stored in `*ptr`
/// corresponds to a valid variant of that enum type, then it is guaranteed
/// that the appropriate bytes of `*ptr` are initialized as defined by that
/// 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).
/// Pragmatically, this means that if `*ptr` is guaranteed to contain an
/// enum type at a particular offset, and the enum discriminant stored in
/// `*ptr` corresponds to a valid variant of that enum type, then it is
/// guaranteed that the appropriate bytes of `*ptr` are initialized as
/// defined by that 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 {}
impl Validity for AsInitialized {}

Expand Down
38 changes: 15 additions & 23 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,9 +401,7 @@ mod _conversions {
{
/// Converts a `Ptr` an unaligned `T` into a `Ptr` to an aligned
/// `Unalign<T>`.
pub(crate) fn into_unalign(
self,
) -> Ptr<'a, crate::Unalign<T>, (I::Aliasing, Aligned, I::Validity)> {
pub(crate) fn into_unalign(self) -> Ptr<'a, crate::Unalign<T>, I::WithAlignment<Aligned>> {
// SAFETY:
// - This cast preserves provenance.
// - This cast preserves address. `Unalign<T>` promises to have the
Expand All @@ -421,7 +419,7 @@ mod _conversions {
// SAFETY: `Unalign<T>` promises to have alignment 1, and so it is
// trivially aligned.
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
ptr
ptr.unify_invariants()
}
}
}
Expand All @@ -446,7 +444,7 @@ mod _transitions {
#[inline]
pub(crate) fn into_exclusive_or_post_monomorphization_error(
self,
) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> {
) -> Ptr<'a, T, I::WithAliasing<Exclusive>> {
// NOTE(https://github.com/rust-lang/rust/issues/131625): We do this
// rather than just having `Aliasing::IS_EXCLUSIVE` have the panic
// behavior because doing it that way causes rustdoc to fail while
Expand Down Expand Up @@ -506,7 +504,7 @@ mod _transitions {
#[inline]
pub(crate) const unsafe fn assume_aliasing<A: Aliasing>(
self,
) -> Ptr<'a, T, (A, I::Alignment, I::Validity)> {
) -> Ptr<'a, T, I::WithAliasing<A>> {
// SAFETY: The caller promises that `self` satisfies the aliasing
// requirements of `A`.
unsafe { self.assume_invariants() }
Expand All @@ -523,7 +521,7 @@ mod _transitions {
#[inline]
pub(crate) const unsafe fn assume_exclusive(
self,
) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> {
) -> Ptr<'a, T, I::WithAliasing<Exclusive>> {
// SAFETY: The caller promises that `self` satisfies the aliasing
// requirements of `Exclusive`.
unsafe { self.assume_aliasing::<Exclusive>() }
Expand All @@ -539,7 +537,7 @@ mod _transitions {
#[inline]
pub(crate) const unsafe fn assume_alignment<A: Alignment>(
self,
) -> Ptr<'a, T, (I::Aliasing, A, I::Validity)> {
) -> Ptr<'a, T, I::WithAlignment<A>> {
// SAFETY: The caller promises that `self`'s referent is
// well-aligned for `T` if required by `A` .
unsafe { self.assume_invariants() }
Expand All @@ -549,7 +547,7 @@ mod _transitions {
/// on success.
pub(crate) fn bikeshed_try_into_aligned(
self,
) -> Result<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>, AlignmentError<Self, T>>
) -> Result<Ptr<'a, T, I::WithAlignment<Aligned>>, AlignmentError<Self, T>>
where
T: Sized,
{
Expand All @@ -567,9 +565,7 @@ mod _transitions {
#[inline]
// TODO(#859): Reconsider the name of this method before making it
// public.
pub(crate) const fn bikeshed_recall_aligned(
self,
) -> Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>
pub(crate) const fn bikeshed_recall_aligned(self) -> Ptr<'a, T, I::WithAlignment<Aligned>>
where
T: crate::Unaligned,
{
Expand All @@ -588,9 +584,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const unsafe fn assume_validity<V: Validity>(
self,
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, V)> {
pub const unsafe fn assume_validity<V: Validity>(self) -> Ptr<'a, T, I::WithValidity<V>> {
// SAFETY: The caller promises that `self`'s referent conforms to
// the validity requirement of `V`.
unsafe { self.assume_invariants() }
Expand All @@ -605,9 +599,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const unsafe fn assume_initialized(
self,
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Initialized)> {
pub const unsafe fn assume_initialized(self) -> Ptr<'a, T, I::WithValidity<Initialized>> {
// SAFETY: The caller has promised to uphold the safety
// preconditions.
unsafe { self.assume_validity::<Initialized>() }
Expand All @@ -622,7 +614,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const unsafe fn assume_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)> {
pub const unsafe fn assume_valid(self) -> Ptr<'a, T, I::WithValidity<Valid>> {
// SAFETY: The caller has promised to uphold the safety
// preconditions.
unsafe { self.assume_validity::<Valid>() }
Expand All @@ -634,7 +626,7 @@ mod _transitions {
#[inline]
// TODO(#859): Reconsider the name of this method before making it
// public.
pub const fn bikeshed_recall_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>
pub const fn bikeshed_recall_valid(self) -> Ptr<'a, T, I::WithValidity<Valid>>
where
T: crate::FromBytes,
I: Invariants<Validity = Initialized>,
Expand All @@ -661,7 +653,7 @@ mod _transitions {
#[inline]
pub(crate) fn try_into_valid(
mut self,
) -> Result<Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>, ValidityError<Self, T>>
) -> Result<Ptr<'a, T, I::WithValidity<Valid>>, ValidityError<Self, T>>
where
T: TryFromBytes,
I::Aliasing: Reference,
Expand All @@ -670,7 +662,7 @@ mod _transitions {
// This call may panic. If that happens, it doesn't cause any soundness
// issues, as we have not generated any invalid state which we need to
// fix before returning.
if T::is_bit_valid(self.reborrow().forget_aligned()) {
if T::is_bit_valid(self.reborrow().forget_aligned().unify_invariants()) {
// SAFETY: If `T::is_bit_valid`, code may assume that `self`
// contains a bit-valid instance of `Self`.
Ok(unsafe { self.assume_valid() })
Expand All @@ -683,7 +675,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const fn forget_aligned(self) -> Ptr<'a, T, (I::Aliasing, Any, I::Validity)> {
pub const fn forget_aligned(self) -> Ptr<'a, T, I::WithAlignment<Any>> {
// SAFETY: `Any` is less restrictive than `Aligned`.
unsafe { self.assume_invariants() }
}
Expand Down

0 comments on commit 9e69c15

Please sign in to comment.