Skip to content
Merged
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
82 changes: 80 additions & 2 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,8 +1019,8 @@ mod tuples {
// SAFETY: If all fields in `c` are `is_bit_valid`, so too is `c`.
unsafe_impl!($($head_T: TryFromBytes,)* $next_T: TryFromBytes => TryFromBytes for ($($head_T,)* $next_T,); |c| {
let mut c = c;
$(TryFromBytes::is_bit_valid(c.reborrow().project::<_, { crate::ident_id!($head_I) }>()) &&)*
TryFromBytes::is_bit_valid(c.reborrow().project::<_, { crate::ident_id!($next_I) }>())
$(TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($head_I) }>())) &&)*
TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($next_I) }>()))
});

// SAFETY: If all fields in `Self` are `FromZeros`, so too is `Self`.
Expand Down Expand Up @@ -1083,6 +1083,84 @@ mod tuples {
}
}

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
(),
(Aliasing, Alignment, crate::invariant::Uninit),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
> for ($($AllT,)+)
where
Aliasing: crate::invariant::Aliasing,
Alignment: crate::invariant::Alignment,
{
#[inline]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized
{}

// SAFETY: Tuples are product types whose fields are
// well-aligned, so projection preserves both the alignment and
// validity invariants of the outer pointer.
Comment thread
joshlf marked this conversation as resolved.
type Invariants = (Aliasing, Alignment, crate::invariant::Uninit);

// SAFETY: Tuples are product types and so projection is infallible;
type Error = core::convert::Infallible;
}

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
(),
(Aliasing, Alignment, crate::invariant::Initialized),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
> for ($($AllT,)+)
where
Aliasing: crate::invariant::Aliasing,
Alignment: crate::invariant::Alignment,
{
#[inline]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized
{}

// SAFETY: Tuples are product types whose fields are
// well-aligned, so projection preserves both the alignment and
// validity invariants of the outer pointer.
type Invariants = (Aliasing, Alignment, crate::invariant::Initialized);

// SAFETY: Tuples are product types and so projection is infallible;
Comment thread
joshlf marked this conversation as resolved.
type Error = core::convert::Infallible;
}

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
(),
(Aliasing, Alignment, crate::invariant::Valid),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
> for ($($AllT,)+)
where
Aliasing: crate::invariant::Aliasing,
Alignment: crate::invariant::Alignment,
{
#[inline]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized
{}

// SAFETY: Tuples are product types whose fields are
// well-aligned, so projection preserves both the alignment and
// validity invariants of the outer pointer.
Comment thread
joshlf marked this conversation as resolved.
type Invariants = (Aliasing, Alignment, crate::invariant::Valid);

// SAFETY: Tuples are product types and so projection is infallible;
type Error = core::convert::Infallible;
}

// Recurse to the next index.
impl_tuple!(@variants [$($AllT $AllI)+] [$($BeforeT)* $CurrT] [$($AfterT $AfterI)*]);
};
Expand Down
101 changes: 100 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ use core::{
#[cfg(feature = "std")]
use std::io;

use crate::pointer::invariant::{self, BecauseExclusive};
#[doc(hidden)]
pub use crate::pointer::invariant::{self, BecauseExclusive};
Comment thread
joshlf marked this conversation as resolved.
#[doc(hidden)]
pub use crate::pointer::PtrInner;
pub use crate::{
Expand Down Expand Up @@ -1151,6 +1152,104 @@ pub unsafe trait HasField<Field, const VARIANT_ID: i128, const FIELD_ID: i128> {
fn project(slf: PtrInner<'_, Self>) -> *mut Self::Type;
}

/// Projects a given field from `Self`.
///
/// Implementations of this trait encode the conditions under which a field can
/// be projected from a `Ptr<'_, Self, I>`, and how the invariants of that
/// [`Ptr`] (`I`) determine the invariants of pointers projected from it. In
/// other words, it is a type-level function over invariants; `I` goes in,
/// `Self::Invariants` comes out.
///
/// # Safety
Comment thread
joshlf marked this conversation as resolved.
///
/// `T: ProjectField<Field, I, VARIANT_ID, FIELD_ID>` if, for a
/// `slf:Β Ptr<'_,Β T,Β I>` such that `if let Ok(ptr) = T::is_projectable(slf)`,
/// `<TΒ asΒ HasField<Field, VARIANT_ID, FIELD_ID>>::project(ptr.as_inner())`
/// conforms to `T::Invariants`.
Comment thread
joshlf marked this conversation as resolved.
#[doc(hidden)]
pub unsafe trait ProjectField<Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>:
HasField<Field, VARIANT_ID, FIELD_ID>
where
I: invariant::Invariants,
{
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized;

/// The invariants of the projected field pointer, with respect to the
/// invariants, `I`, of the containing pointer. The aliasing dimension of
/// the invariants is guaranteed to remain unchanged.
type Invariants: invariant::Invariants<Aliasing = I::Aliasing>;
Comment thread
joshlf marked this conversation as resolved.

/// The failure mode of projection. `()` if the projection is fallible,
/// otherwise [`core::convert::Infallible`].
type Error;

/// Is the given field projectable from `ptr`?
///
/// If a field with [`Self::Invariants`] is projectable from the referent,
/// this function produces an `Ok(ptr)` from which the projection can be
/// made; otherwise `Err`.
///
/// This method must be overriden if the field's projectability depends on
/// the value of the bytes in `ptr`.
Comment thread
joshlf marked this conversation as resolved.
#[inline(always)]
fn is_projectable<'a>(ptr: Ptr<'a, Self, I>) -> Result<Ptr<'a, Self, I>, Self::Error> {
Comment thread
jswrenn marked this conversation as resolved.
trait IsInfallible {
const IS_INFALLIBLE: bool;
}

struct Projection<T, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>(
PhantomData<(Field, I, T)>,
)
where
T: ?Sized + HasField<Field, VARIANT_ID, FIELD_ID>,
I: invariant::Invariants;

impl<T, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128> IsInfallible
for Projection<T, Field, I, VARIANT_ID, FIELD_ID>
where
T: ?Sized + HasField<Field, VARIANT_ID, FIELD_ID>,
I: invariant::Invariants,
{
const IS_INFALLIBLE: bool = {
let is_infallible = match VARIANT_ID {
// For nondestructive projections of struct and union
// fields, the projected field's satisfaction of
// `Invariants` does not depend on the value of the
// referent. This default implementation of `is_projectable`
// is non-destructive, as it does not overwrite any part of
// the referent.
crate::STRUCT_VARIANT_ID | crate::UNION_VARIANT_ID => true,
_enum_variant => {
use crate::invariant::{Validity, ValidityKind};
match I::Validity::KIND {
// The projectability of a field from an `Uninit` or
// `Initialized` union cannot generally depend on
// the referent's value, because the union's
// discriminant is not in a valid state, and thus
// cannot be used to discriminate between variants.
ValidityKind::Uninit | ValidityKind::Initialized => true,
// The projectability of an enum field from an
// `AsInitialized` or `Valid` state is a dynamic
// property of its tag.
ValidityKind::AsInitialized | ValidityKind::Valid => false,
}
}
};
const_assert!(is_infallible);
Comment thread
joshlf marked this conversation as resolved.
is_infallible
};
}

const_assert!(
<Projection<Self, Field, I, VARIANT_ID, FIELD_ID> as IsInfallible>::IS_INFALLIBLE
);

Ok(ptr)
}
Comment thread
joshlf marked this conversation as resolved.
}

/// Analyzes whether a type is [`FromZeros`].
///
/// This derive analyzes, at compile time, whether the annotated type satisfies
Expand Down
4 changes: 2 additions & 2 deletions src/pointer/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {

/// # Safety
///
/// The caller may assume that the resulting `PtrInner` addresses a subset
/// of the bytes of `self`'s referent.
/// The caller may assume that the resulting `PtrInner` addresses the subset
/// of the bytes of `self`'s referent addressed by `C::project(self)`.
#[must_use]
#[inline(always)]
pub fn project<U: ?Sized, C: cast::Project<T, U>>(self) -> PtrInner<'a, U> {
Expand Down
27 changes: 22 additions & 5 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,16 @@ 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 {
const KIND: ValidityKind;
}

pub enum ValidityKind {
Uninit,
AsInitialized,
Initialized,
Valid,
}

/// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`].
///
Expand Down Expand Up @@ -137,7 +146,9 @@ pub enum Uninit {}
// 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 Validity for Uninit {
const KIND: ValidityKind = ValidityKind::Uninit;
}

/// The byte ranges initialized in `T` are also initialized in the referent of a
/// `Ptr<T>`.
Expand Down Expand Up @@ -169,7 +180,9 @@ unsafe impl Validity for Uninit {}
pub enum AsInitialized {}
// 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 Validity for AsInitialized {
const KIND: ValidityKind = ValidityKind::AsInitialized;
}

/// 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]`.
Expand All @@ -178,14 +191,18 @@ pub enum Initialized {}
// 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 Validity for Initialized {
const KIND: ValidityKind = ValidityKind::Initialized;
}

/// The referent of a `Ptr<T>` is valid for `T`, upholding bit validity and any
/// library safety invariants.
pub enum Valid {}
// 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 Validity for Valid {
const KIND: ValidityKind = ValidityKind::Valid;
}

/// # Safety
///
Expand Down
52 changes: 26 additions & 26 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ mod _casts {
use super::*;
use crate::{
pointer::cast::{AsBytesCast, Cast},
HasField,
ProjectField,
};

impl<'a, T, I> Ptr<'a, T, I>
Expand Down Expand Up @@ -929,35 +929,35 @@ mod _casts {
unsafe { self.cast_unchecked::<_, C>() }
}

// FIXME(#196): Support all validity invariants (not just those that are
// `CastableFrom`).
#[must_use]
#[inline(always)]
pub fn project<F, const FIELD_ID: i128>(
pub fn project<F, const VARIANT_ID: i128, const FIELD_ID: i128>(
self,
) -> Ptr<'a, T::Type, (I::Aliasing, Unaligned, I::Validity)>
) -> Result<Ptr<'a, T::Type, T::Invariants>, T::Error>
where
T: HasField<F, { crate::STRUCT_VARIANT_ID }, FIELD_ID>,
T::Type: 'a + CastableFrom<T, I::Validity, I::Validity>,
T: ProjectField<F, I, VARIANT_ID, FIELD_ID>,
{
let ptr = self.as_inner().project::<_, crate::pointer::cast::Projection<
F,
{ crate::STRUCT_VARIANT_ID },
FIELD_ID,
>>();

// SAFETY:
// 0. `PtrInner::project` promises that it produces a pointer which
// references a subset of its argument's referent. Since, by
// invariant on `Ptr`, its argument (`self.as_inner()`) satisfies
// the aliasing invariant `I::Aliasing`, so does `ptr`.
// 1. The `Ptr` has alignment `Unaligned`, which is trivially
// satisfied.
// 2. By `CastableFrom<T, I::Validity, I::Validity>`, `I::Validity`
// is `Uninit` or `Initialized`. In either case, if `I::Validity`
// holds of `self`'s referent, then it holds any subset of its
// referent.
unsafe { Ptr::from_inner(ptr) }
use crate::pointer::cast::Projection;
match T::is_projectable(self) {
Ok(ptr) => {
let inner = ptr.as_inner();
let projected = inner.project::<_, Projection<F, VARIANT_ID, FIELD_ID>>();
// SAFETY: By `T: ProjectField<F, I, VARIANT_ID, FIELD_ID>`,
// for `self:Β Ptr<'_,Β T,Β I>` such that `T::is_projectable`
// (which we've verified in this match arm),
// `T::project(self.as_inner())` conforms to
// `T::Invariants`. The `projected` pointer satisfies these
// invariants because it is produced by way of an
// abstraction that is equivalent to
// `T::project(ptr.as_inner())`: by invariant on
// `PtrInner::project`, `projected` is guaranteed to address
// the subset of the bytes of `inner`'s referent addressed
// by `Projection::project(inner)`, and by invariant on
// `Projection`, `Projection::project` is implemented by
// delegating to an implementation of `HasField::project`.
Ok(unsafe { Ptr::from_inner(projected) })
}
Err(err) => Err(err),
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/util/macro_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,18 @@ macro_rules! assert_size_eq {
}};
}

/// Unwraps an infallible `Result`.
#[doc(hidden)]
#[macro_export]
macro_rules! into_inner {
($e:expr) => {
match $e {
$crate::util::macro_util::core_reexport::result::Result::Ok(e) => e,
$crate::util::macro_util::core_reexport::result::Result::Err(i) => match i {},
}
};
}

/// Translates an identifier or tuple index into a numeric identifier.
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
#[macro_export]
Expand Down
4 changes: 2 additions & 2 deletions tests/ui-nightly/ptr-is-invariant-over-v.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ error: lifetime may not live long enough
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, &u32, (invariant::Exclusive, Aligned, Valid)>`, which makes the generic argument `&u32` invariant
= note: requirement occurs because of the type `Ptr<'_, &u32, (zerocopy::invariant::Exclusive, Aligned, zerocopy::invariant::Valid)>`, which makes the generic argument `&u32` invariant
= note: the struct `Ptr<'a, T, I>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

Expand All @@ -26,6 +26,6 @@ error: lifetime may not live long enough
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, &u32, (Shared, Aligned, Valid)>`, which makes the generic argument `&u32` invariant
= note: requirement occurs because of the type `Ptr<'_, &u32, (Shared, Aligned, zerocopy::invariant::Valid)>`, which makes the generic argument `&u32` invariant
= note: the struct `Ptr<'a, T, I>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
Loading
Loading