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
20 changes: 12 additions & 8 deletions zerocopy/src/impls.rs
Original file line number Diff line number Diff line change
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 Down Expand Up @@ -1077,7 +1077,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 +1104,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 +1131,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 +1158,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
33 changes: 21 additions & 12 deletions zerocopy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,9 @@ 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
///
Expand Down Expand Up @@ -1209,6 +1212,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,7 +1238,7 @@ 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>:
pub unsafe trait HasField<Client, Field, const VARIANT_ID: i128, const FIELD_ID: i128>:
HasTag
{
fn only_derive_is_allowed_to_implement_this_trait()
Expand Down Expand Up @@ -1263,15 +1269,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 Down Expand Up @@ -1302,17 +1311,17 @@ where
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 +1357,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
9 changes: 5 additions & 4 deletions zerocopy/src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,19 +875,20 @@ 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()) {
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 Down
18 changes: 9 additions & 9 deletions zerocopy/src/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,14 +775,14 @@ unsafe impl<T: HasTag + ?Sized> HasTag for ReadOnly<T> {

// 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 +794,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 +811,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 +822,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 Down
18 changes: 13 additions & 5 deletions zerocopy/zerocopy-derive/src/derive/try_from_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use syn::{
use crate::{
repr::{EnumRepr, StructUnionRepr},
util::{
const_block, enum_size_from_repr, generate_tag_enum, Ctx, DataExt, FieldBounds,
const_block, enum_size_from_repr, generate_tag_enum, Client, Ctx, DataExt, FieldBounds,
ImplBlockBuilder, Trait, TraitBound,
},
};
Expand Down Expand Up @@ -238,6 +238,7 @@ pub(crate) fn derive_is_bit_valid(
let variant_struct_field_index = Index::from(idx + 1);
let (_, ty_generics, _) = ctx.ast.generics.split_for_impl();
let has_field_trait = Trait::HasField {
client: Client::TryFromBytesDerive,
variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }),
// Since Rust does not presently support explicit visibility
// modifiers on enum fields, any public type is suitable here;
Expand All @@ -260,10 +261,10 @@ pub(crate) fn derive_is_bit_valid(
use #zerocopy_crate::pointer::cast::{CastSized, Projection};

slf.project::<___ZerocopyRawEnum #ty_generics, CastSized>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>>()
.project::<_, Projection<_, { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field_ident) }>>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(value) }>>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>>()
.project::<_, Projection<#zerocopy_crate::TryFromBytesDerive, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>>()
.project::<_, Projection<#zerocopy_crate::TryFromBytesDerive, _, { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field_ident) }>>()
.project::<_, Projection<#zerocopy_crate::TryFromBytesDerive, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(value) }>>()
.project::<_, Projection<#zerocopy_crate::TryFromBytesDerive, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>>()
.as_ptr()
}
})
Expand All @@ -273,6 +274,7 @@ pub(crate) fn derive_is_bit_valid(
ctx,
data,
Trait::ProjectField {
client: Client::TryFromBytesDerive,
variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }),
// Since Rust does not presently support explicit visibility
// modifiers on enum fields, any public type is suitable
Expand Down Expand Up @@ -321,6 +323,7 @@ pub(crate) fn derive_is_bit_valid(
let variant_md = variants.cast::<
_,
#zerocopy_crate::pointer::cast::Projection<
#zerocopy_crate::TryFromBytesDerive,
// #zerocopy_crate::ReadOnly<_>,
_,
{ #zerocopy_crate::REPR_C_UNION_VARIANT_ID },
Expand Down Expand Up @@ -431,6 +434,7 @@ pub(crate) fn derive_is_bit_valid(
>();

let variants = #zerocopy_crate::into_inner!(raw_enum.project::<
#zerocopy_crate::TryFromBytesDerive,
_,
{ #zerocopy_crate::STRUCT_VARIANT_ID },
{ #zerocopy_crate::ident_id!(variants) }
Expand Down Expand Up @@ -491,6 +495,7 @@ fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream {
let field: Box<Type> = parse_quote!(#field_token);
let field_id: Box<Expr> = parse_quote!({ #zerocopy_crate::ident_id!(#ident) });
let has_field_trait = Trait::HasField {
client: Client::TryFromBytesDerive,
variant_id: variant_id.clone(),
field: field.clone(),
field_id: field_id.clone(),
Expand Down Expand Up @@ -530,6 +535,7 @@ fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream {
ctx,
data,
Trait::ProjectField {
client: Client::TryFromBytesDerive,
variant_id: variant_id.clone(),
field,
field_id,
Expand Down Expand Up @@ -585,6 +591,7 @@ fn derive_try_from_bytes_struct(
{
true #(&& {
let field_candidate = #zerocopy_crate::into_inner!(candidate.reborrow().project::<
#zerocopy_crate::TryFromBytesDerive,
_,
{ #zerocopy_crate::STRUCT_VARIANT_ID },
{ #zerocopy_crate::ident_id!(#field_names) }
Expand Down Expand Up @@ -644,6 +651,7 @@ fn derive_try_from_bytes_union(ctx: &Ctx, unn: &DataUnion, top_level: Trait) ->
_,
_,
#zerocopy_crate::pointer::cast::Projection<
#zerocopy_crate::TryFromBytesDerive,
_,
#variant_id,
{ #zerocopy_crate::ident_id!(#field_names) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const _: () = {
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::HasField<
::zerocopy::TryFromBytesDerive,
αΊ•a,
{ ::zerocopy::UNION_VARIANT_ID },
{ ::zerocopy::ident_id!(a) },
Expand All @@ -91,6 +92,7 @@ const _: () = {
fn project(
slf: ::zerocopy::pointer::PtrInner<'_, Self>,
) -> *mut <Self as ::zerocopy::HasField<
::zerocopy::TryFromBytesDerive,
αΊ•a,
{ ::zerocopy::UNION_VARIANT_ID },
{ ::zerocopy::ident_id!(a) },
Expand Down
Loading
Loading