Skip to content
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
26 changes: 16 additions & 10 deletions zerocopy/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ const _: () = {
pub enum value {}

// SAFETY: See safety comment on `ProjectToTag`.
unsafe impl<T: ?Sized> HasTag for ManuallyDrop<T> {
unsafe impl<T: ?Sized, Client> HasTag<Client> for ManuallyDrop<T> {
#[inline]
fn only_derive_is_allowed_to_implement_this_trait()
where
Expand Down Expand Up @@ -796,8 +796,8 @@ const _: () = {
// private field, and because it is the name it is referred to in the public
// documentation of `ManuallyDrop::new`, `ManuallyDrop::into_inner`,
// `ManuallyDrop::take` and `ManuallyDrop::drop`.
unsafe impl<T: ?Sized>
HasField<value, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!(value) }>
unsafe impl<T: ?Sized, Client>
HasField<Client, value, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!(value) }>
for ManuallyDrop<T>
{
#[inline]
Expand Down Expand Up @@ -1015,8 +1015,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(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) }>()))
$(TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<crate::TryFromBytesDerive, _, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($head_I) }>())) &&)*
TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<crate::TryFromBytesDerive, _, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($next_I) }>()))
});

// SAFETY: If all fields in `Self` are `FromZeros`, so too is `Self`.
Expand All @@ -1026,7 +1026,9 @@ mod tuples {
unsafe_impl!($($head_T: FromBytes,)* $next_T: FromBytes => FromBytes for ($($head_T,)* $next_T,));

// SAFETY: See safety comment on `ProjectToTag`.
unsafe impl<$($head_T,)* $next_T> crate::HasTag for ($($head_T,)* $next_T,) {
unsafe impl<Client, $($head_T,)* $next_T> crate::HasTag<Client>
for ($($head_T,)* $next_T,)
{
#[inline]
fn only_derive_is_allowed_to_implement_this_trait()
where
Expand Down Expand Up @@ -1077,7 +1079,8 @@ mod tuples {
// - `()` has the same visibility as the `.$CurrI` field (ie, `.0`,
// `.1`, etc)
// - `Type` has the same type as `$CurrI`; i.e., `$CurrT`.
unsafe impl<$($AllT),+> crate::HasField<
unsafe impl<Client, $($AllT),+> crate::HasField<
Client,
(),
{ crate::STRUCT_VARIANT_ID },
{ crate::ident_id!($CurrI)}
Expand All @@ -1103,7 +1106,8 @@ mod tuples {
}

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
unsafe impl<Client, Aliasing, Alignment, $($AllT),+> crate::ProjectField<
Client,
(),
(Aliasing, Alignment, crate::invariant::Uninit),
{ crate::STRUCT_VARIANT_ID },
Expand All @@ -1129,7 +1133,8 @@ mod tuples {
}

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
unsafe impl<Client, Aliasing, Alignment, $($AllT),+> crate::ProjectField<
Client,
(),
(Aliasing, Alignment, crate::invariant::Initialized),
{ crate::STRUCT_VARIANT_ID },
Expand All @@ -1155,7 +1160,8 @@ mod tuples {
}

// SAFETY: See comments on items.
unsafe impl<Aliasing, Alignment, $($AllT),+> crate::ProjectField<
unsafe impl<Client, Aliasing, Alignment, $($AllT),+> crate::ProjectField<
Client,
(),
(Aliasing, Alignment, crate::invariant::Valid),
{ crate::STRUCT_VARIANT_ID },
Expand Down
49 changes: 32 additions & 17 deletions zerocopy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,12 +1181,18 @@ pub const STRUCT_VARIANT_ID: i128 = -1;
pub const UNION_VARIANT_ID: i128 = -2;
#[doc(hidden)]
pub const REPR_C_UNION_VARIANT_ID: i128 = -3;
#[doc(hidden)]
#[derive(Copy, Clone, Debug)]
pub enum TryFromBytesDerive {}

/// # Safety
///
/// `Self::ProjectToTag` must satisfy its safety invariant.
/// `<Self as HasTag<Client>>::ProjectToTag` must satisfy its safety invariant.
///
/// The `Client` parameter exists solely to disambiguate between implementations
/// of `HasTag` that would otherwise conflict.
#[doc(hidden)]
pub unsafe trait HasTag {
pub unsafe trait HasTag<Client = TryFromBytesDerive> {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized;
Expand All @@ -1199,7 +1205,8 @@ pub unsafe trait HasTag {
/// # Safety
///
/// It must be the case that, for all `slf: Ptr<'_, Self, I>`, it is sound
/// to project from `slf` to `Ptr<'_, Self::Tag, I>` using this projection.
/// to project from `slf` to
/// `Ptr<'_, <Self as HasTag<Client>>::Tag, I>` using this projection.
type ProjectToTag: pointer::cast::Project<Self, Self::Tag>;
}

Expand All @@ -1209,6 +1216,9 @@ pub unsafe trait HasTag {
/// should use the same `Field` type; this ensures that `Field` is inferable
/// given an explicit `VARIANT_ID` and `FIELD_ID`.
///
/// The `Client` parameter exists solely to disambiguate between implementations
/// of `HasField` that would otherwise conflict.
///
/// # Safety
///
/// A field `f` is `HasField` for `Self` if and only if:
Expand All @@ -1232,8 +1242,8 @@ pub unsafe trait HasTag {
///
/// The implementation of `project` must satisfy its safety post-condition.
#[doc(hidden)]
pub unsafe trait HasField<Field, const VARIANT_ID: i128, const FIELD_ID: i128>:
HasTag
pub unsafe trait HasField<Client, Field, const VARIANT_ID: i128, const FIELD_ID: i128>:
HasTag<Client>
{
fn only_derive_is_allowed_to_implement_this_trait()
where
Expand Down Expand Up @@ -1263,15 +1273,18 @@ pub unsafe trait HasField<Field, const VARIANT_ID: i128, const FIELD_ID: i128>:
/// other words, it is a type-level function over invariants; `I` goes in,
/// `Self::Invariants` comes out.
///
/// The `Client` parameter exists solely to disambiguate between implementations
/// of `HasField` that would otherwise conflict.
///
/// # Safety
///
/// `T: ProjectField<Field, I, VARIANT_ID, FIELD_ID>` if, for a
/// `T: ProjectField<Client, Field, I, VARIANT_ID, FIELD_ID>` if, for a
/// `ptr: Ptr<'_, T, I>` such that `T::is_projectable(ptr).is_ok()`,
/// `<T as HasField<Field, VARIANT_ID, FIELD_ID>>::project(ptr.as_inner())`
/// `<T as HasField<Client, Field, VARIANT_ID, FIELD_ID>>::project(ptr.as_inner())`
/// conforms to `T::Invariants`.
#[doc(hidden)]
pub unsafe trait ProjectField<Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>:
HasField<Field, VARIANT_ID, FIELD_ID>
pub unsafe trait ProjectField<Client, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>:
HasField<Client, Field, VARIANT_ID, FIELD_ID>
where
I: invariant::Invariants,
{
Expand All @@ -1297,22 +1310,24 @@ where
/// This method must be overriden if the field's projectability depends on
/// the value of the bytes in `ptr`.
#[inline(always)]
fn is_projectable<'a>(_ptr: Ptr<'a, Self::Tag, I>) -> Result<(), Self::Error> {
fn is_projectable<'a>(
_ptr: Ptr<'a, <Self as HasTag<Client>>::Tag, I>,
) -> Result<(), Self::Error> {
trait IsInfallible {
const IS_INFALLIBLE: bool;
}

struct Projection<T, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>(
PhantomData<(Field, I, T)>,
struct Projection<T, Client, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>(
PhantomData<(Client, Field, I, T)>,
)
where
T: ?Sized + HasField<Field, VARIANT_ID, FIELD_ID>,
T: ?Sized + HasField<Client, 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>
impl<T, Client, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128> IsInfallible
for Projection<T, Client, Field, I, VARIANT_ID, FIELD_ID>
where
T: ?Sized + HasField<Field, VARIANT_ID, FIELD_ID>,
T: ?Sized + HasField<Client, Field, VARIANT_ID, FIELD_ID>,
I: invariant::Invariants,
{
const IS_INFALLIBLE: bool = {
Expand Down Expand Up @@ -1348,7 +1363,7 @@ where
}

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

Ok(())
Expand Down
18 changes: 11 additions & 7 deletions zerocopy/src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,18 +240,22 @@ pub mod cast {
///
/// A `Projection` is a [`Project`] which implements projection by
/// delegating to an implementation of [`HasField::project`].
///
/// The `Client` parameter exists to select between implementations of
/// [`HasField`] that would otherwise conflict.
#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct Projection<F: ?Sized, const VARIANT_ID: i128, const FIELD_ID: i128> {
pub struct Projection<Client, F: ?Sized, const VARIANT_ID: i128, const FIELD_ID: i128> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment explaining the purpose of this type param?

_never: core::convert::Infallible,
_client: PhantomData<Client>,
_phantom: PhantomData<F>,
}

// SAFETY: `HasField::project` has the same safety post-conditions as
// `Project::project`.
unsafe impl<T: ?Sized, F, const VARIANT_ID: i128, const FIELD_ID: i128> Project<T, T::Type>
for Projection<F, VARIANT_ID, FIELD_ID>
unsafe impl<T: ?Sized, Client, F, const VARIANT_ID: i128, const FIELD_ID: i128>
Project<T, T::Type> for Projection<Client, F, VARIANT_ID, FIELD_ID>
where
T: HasField<F, VARIANT_ID, FIELD_ID>,
T: HasField<Client, F, VARIANT_ID, FIELD_ID>,
{
#[inline(always)]
fn project(src: PtrInner<'_, T>) -> *mut T::Type {
Expand Down Expand Up @@ -292,10 +296,10 @@ pub mod cast {
//
// FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/595):
// Cite the documentation once it's updated.
unsafe impl<T: ?Sized, F, const FIELD_ID: i128> Cast<T, T::Type>
for Projection<F, { crate::REPR_C_UNION_VARIANT_ID }, FIELD_ID>
unsafe impl<T: ?Sized, Client, F, const FIELD_ID: i128> Cast<T, T::Type>
for Projection<Client, F, { crate::REPR_C_UNION_VARIANT_ID }, FIELD_ID>
where
T: HasField<F, { crate::REPR_C_UNION_VARIANT_ID }, FIELD_ID>,
T: HasField<Client, F, { crate::REPR_C_UNION_VARIANT_ID }, FIELD_ID>,
{
}

Expand Down
29 changes: 18 additions & 11 deletions zerocopy/src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,19 +875,22 @@ mod _casts {
}

#[inline(always)]
pub fn project<F, const VARIANT_ID: i128, const FIELD_ID: i128>(
pub fn project<Client, F, const VARIANT_ID: i128, const FIELD_ID: i128>(
mut self,
) -> Result<Ptr<'a, T::Type, T::Invariants>, T::Error>
where
T: ProjectField<F, I, VARIANT_ID, FIELD_ID>,
T: ProjectField<Client, F, I, VARIANT_ID, FIELD_ID>,
I::Aliasing: Reference,
{
use crate::pointer::cast::Projection;
match T::is_projectable(self.reborrow().project_tag()) {
match <T as ProjectField<Client, F, I, VARIANT_ID, FIELD_ID>>::is_projectable(
self.reborrow().project_tag::<Client>(),
) {
Ok(()) => {
let inner = self.as_inner();
let projected = inner.project::<_, Projection<F, VARIANT_ID, FIELD_ID>>();
// SAFETY: By `T: ProjectField<F, I, VARIANT_ID, FIELD_ID>`,
let projected =
inner.project::<_, Projection<Client, F, VARIANT_ID, FIELD_ID>>();
// SAFETY: By `T: ProjectField<Client, 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
Expand All @@ -908,15 +911,19 @@ mod _casts {

#[must_use]
#[inline(always)]
pub(crate) fn project_tag(self) -> Ptr<'a, T::Tag, I>
pub(crate) fn project_tag<Client>(self) -> Ptr<'a, <T as HasTag<Client>>::Tag, I>
where
T: HasTag,
T: HasTag<Client>,
{
// SAFETY: By invariant on `Self::ProjectToTag`, this is a sound
// SAFETY: By invariant on
// `<T as HasTag<Client>>::ProjectToTag`, this is a sound
// projection.
let tag = unsafe { self.project_transmute_unchecked::<_, _, T::ProjectToTag>() };
// SAFETY: By invariant on `Self::ProjectToTag`, the projected
// pointer has the same alignment as `ptr`.
let tag = unsafe {
self.project_transmute_unchecked::<_, _, <T as HasTag<Client>>::ProjectToTag>()
};
// SAFETY: By invariant on
// `<T as HasTag<Client>>::ProjectToTag`, the projected pointer has
// the same alignment as `self`.
let tag = unsafe { tag.assume_alignment() };
tag.unify_invariants()
}
Expand Down
44 changes: 24 additions & 20 deletions zerocopy/src/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,38 +751,40 @@ impl<T: ?Sized + Immutable + Debug> Debug for ReadOnly<T> {
}

// SAFETY: See safety comment on `ProjectToTag`.
unsafe impl<T: HasTag + ?Sized> HasTag for ReadOnly<T> {
unsafe impl<T: HasTag<Client> + ?Sized, Client> HasTag<Client> for ReadOnly<T> {
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized,
{
}

type Tag = T::Tag;
type Tag = <T as HasTag<Client>>::Tag;

// SAFETY: `<T as SizeEq<ReadOnly<T>>>::CastFrom` is a no-op projection that
// produces a pointer with the same referent. By invariant, for any `Ptr<'_,
// T, I>` it is sound to use `T::ProjectToTag` to project to a `Ptr<'_,
// T::Tag, I>`. Since `ReadOnly<T>` has the same layout and validity as `T`,
// the same is true of projecting from a `Ptr<'_, ReadOnly<T>, I>`.
// produces a pointer with the same referent. By invariant, for any
// `Ptr<'_, T, I>` it is sound to use
// `<T as HasTag<Client>>::ProjectToTag` to project to a
// `Ptr<'_, <T as HasTag<Client>>::Tag, I>`. Since `ReadOnly<T>` has the
// same layout and validity as `T`, the same is true of projecting from a
// `Ptr<'_, ReadOnly<T>, I>`.
type ProjectToTag = crate::pointer::cast::TransitiveProject<
T,
<T as SizeEq<ReadOnly<T>>>::CastFrom,
T::ProjectToTag,
<T as HasTag<Client>>::ProjectToTag,
>;
}

// SAFETY: `ReadOnly<T>` is a `#[repr(transparent)]` wrapper around `T`, and so
// has the same fields at the same offsets. Thus, it satisfies the safety
// invariants of `HasField<Field, VARIANT_ID, FIELD_ID>` for field `f` exactly
// invariants of `HasField<Client, Field, VARIANT_ID, FIELD_ID>` for field `f` exactly
// when `T` does, as guaranteed by the `T: HasField` bound:
// - If `VARIANT_ID` is `STRUCT_VARIANT_ID` or `UNION_VARIANT_ID`, then `T` has
// the layout of a struct or union type. Since `ReadOnly<T>` is a transparent
// wrapper around `T`, it does too. Otherwise, if `VARIANT_ID` is an enum
// variant index, then `T` has the layout of an enum type, and `ReadOnly<T>`
// does too.
// - By `T: HasField<_, _, FIELD_ID>`:
// - By `T: HasField<_, _, _, FIELD_ID>`:
// - `T` has a field `f` with name `n` such that
// `FIELD_ID = zerocopy::ident_id!(n)` or at index `i` such that
// `FIELD_ID = zerocopy::ident_id!(i)`.
Expand All @@ -794,10 +796,10 @@ unsafe impl<T: HasTag + ?Sized> HasTag for ReadOnly<T> {
// refers to a non-strict subset of the bytes of `slf`'s referent, and has the
// same provenance as `slf` – because all intermediate operations satisfy those
// same conditions.
unsafe impl<T, Field, const VARIANT_ID: i128, const FIELD_ID: i128>
HasField<Field, VARIANT_ID, FIELD_ID> for ReadOnly<T>
unsafe impl<T, Client, Field, const VARIANT_ID: i128, const FIELD_ID: i128>
HasField<Client, Field, VARIANT_ID, FIELD_ID> for ReadOnly<T>
where
T: HasField<Field, VARIANT_ID, FIELD_ID> + ?Sized,
T: HasField<Client, Field, VARIANT_ID, FIELD_ID> + ?Sized,
{
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait()
Expand All @@ -811,7 +813,7 @@ where
#[inline(always)]
fn project(slf: PtrInner<'_, Self>) -> *mut ReadOnly<T::Type> {
slf.project::<_, <T as SizeEq<ReadOnly<T>>>::CastFrom>()
.project::<_, crate::pointer::cast::Projection<Field, VARIANT_ID, FIELD_ID>>()
.project::<_, crate::pointer::cast::Projection<Client, Field, VARIANT_ID, FIELD_ID>>()
.project::<_, <ReadOnly<T::Type> as SizeEq<T::Type>>::CastFrom>()
.as_non_null()
.as_ptr()
Expand All @@ -822,10 +824,10 @@ where
// has the same fields at the same offsets. `is_projectable` simply delegates to
// `T::is_projectable`, which is sound because a `Ptr<'_, ReadOnly<T>, I>` will
// be projectable exactly when a `Ptr<'_, T, I>` referent is.
unsafe impl<T, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>
ProjectField<Field, I, VARIANT_ID, FIELD_ID> for ReadOnly<T>
unsafe impl<T, Client, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>
ProjectField<Client, Field, I, VARIANT_ID, FIELD_ID> for ReadOnly<T>
where
T: ProjectField<Field, I, VARIANT_ID, FIELD_ID> + ?Sized,
T: ProjectField<Client, Field, I, VARIANT_ID, FIELD_ID> + ?Sized,
I: invariant::Invariants,
{
#[allow(clippy::missing_inline_in_public_items)]
Expand All @@ -835,13 +837,15 @@ where
{
}

type Invariants = T::Invariants;
type Invariants = <T as ProjectField<Client, Field, I, VARIANT_ID, FIELD_ID>>::Invariants;

type Error = T::Error;
type Error = <T as ProjectField<Client, Field, I, VARIANT_ID, FIELD_ID>>::Error;

#[inline(always)]
fn is_projectable<'a>(ptr: Ptr<'a, Self::Tag, I>) -> Result<(), Self::Error> {
T::is_projectable(ptr)
fn is_projectable<'a>(
ptr: Ptr<'a, <Self as HasTag<Client>>::Tag, I>,
) -> Result<(), Self::Error> {
<T as ProjectField<Client, Field, I, VARIANT_ID, FIELD_ID>>::is_projectable(ptr)
}
}

Expand Down
Loading
Loading