mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Update our vendored copy of `zerocopy` (and `zerocopy-derive`) to v0.8.52.
Most SPDX identifiers have been added upstream at our request [1]
(without parentheses -- supporting them is an issue on the kernel side,
but it does already reduce our differences). The CSS one for `rustdoc`
was added too [2], but will be picked up in a later version.
For `zerocopy`, enable `--cfg no_fp_fmt_parse`, which was added at our
request to avoid our local workaround [3]. This means one less difference,
thus indicate so in our `README.md`.
For `zerocopy-derive`, enable `--cfg zerocopy_unstable_linux`. This
allows us to use `#[derive(zerocopy_derive::most_traits)]`, a new feature
upstream added for us [4]. We noticed a minor doc render bug [5], which
will be fixed for a future version too.
The following script may be used to check for the remaining differences:
for path in $(cd rust/zerocopy-derive/ && find . -type f ! -name README.md); do
curl --silent --show-error --location \
https://github.com/google/zerocopy/raw/v0.8.52/zerocopy/zerocopy-derive/src/$path |
git diff --no-index - rust/zerocopy-derive/$path &&
echo $path: OK
done
for path in $(cd rust/zerocopy/ && find . -type f ! -name README.md); do
curl --silent --show-error --location \
https://github.com/google/zerocopy/raw/v0.8.52/zerocopy/$path |
git diff --no-index - rust/zerocopy/$path &&
echo $path: OK
done
Cc: Joshua Liebow-Feeser <joshlf@google.com>
Cc: Jack Wrenn <jswrenn@google.com>
Link: https://github.com/google/zerocopy/issues/3428 [1]
Link: https://github.com/google/zerocopy/issues/3457 [2]
Link: https://github.com/google/zerocopy/issues/3426 [3]
Link: https://github.com/google/zerocopy/pull/3416 [4]
Link: https://github.com/google/zerocopy/issues/3466 [5]
Acked-by: Nicolas Schier <n.schier@fritz.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/20260625231919.692444-1-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
766 lines
32 KiB
Rust
766 lines
32 KiB
Rust
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
|
|
//
|
|
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use syn::{
|
|
parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error,
|
|
Expr, Fields, Ident, Index, Type,
|
|
};
|
|
|
|
use crate::{
|
|
repr::{EnumRepr, StructUnionRepr},
|
|
util::{
|
|
const_block, enum_size_from_repr, generate_tag_enum, Ctx, DataExt, FieldBounds,
|
|
ImplBlockBuilder, Trait, TraitBound,
|
|
},
|
|
};
|
|
fn tag_ident(variant_ident: &Ident) -> Ident {
|
|
ident!(("___ZEROCOPY_TAG_{}", variant_ident), variant_ident.span())
|
|
}
|
|
|
|
/// Generates a constant for the tag associated with each variant of the enum.
|
|
/// When we match on the enum's tag, each arm matches one of these constants. We
|
|
/// have to use constants here because:
|
|
///
|
|
/// - The type that we're matching on is not the type of the tag, it's an
|
|
/// integer of the same size as the tag type and with the same bit patterns.
|
|
/// - We can't read the enum tag as an enum because the bytes may not represent
|
|
/// a valid variant.
|
|
/// - Patterns do not currently support const expressions, so we have to assign
|
|
/// these constants to names rather than use them inline in the `match`
|
|
/// statement.
|
|
fn generate_tag_consts(data: &DataEnum) -> TokenStream {
|
|
let tags = data.variants.iter().map(|v| {
|
|
let variant_ident = &v.ident;
|
|
let tag_ident = tag_ident(variant_ident);
|
|
|
|
quote! {
|
|
// This casts the enum variant to its discriminant, and then
|
|
// converts the discriminant to the target integral type via a
|
|
// numeric cast [1].
|
|
//
|
|
// Because these are the same size, this is defined to be a no-op
|
|
// and therefore is a lossless conversion [2].
|
|
//
|
|
// [1] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#enum-cast:
|
|
//
|
|
// Casts an enum to its discriminant.
|
|
//
|
|
// [2] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#numeric-cast:
|
|
//
|
|
// Casting between two integers of the same size (e.g. i32 -> u32)
|
|
// is a no-op.
|
|
const #tag_ident: ___ZerocopyTagPrimitive =
|
|
___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive;
|
|
}
|
|
});
|
|
|
|
quote! {
|
|
#(#tags)*
|
|
}
|
|
}
|
|
|
|
fn variant_struct_ident(variant_ident: &Ident) -> Ident {
|
|
ident!(("___ZerocopyVariantStruct_{}", variant_ident), variant_ident.span())
|
|
}
|
|
|
|
/// Generates variant structs for the given enum variant.
|
|
///
|
|
/// These are structs associated with each variant of an enum. They are
|
|
/// `repr(C)` tuple structs with the same fields as the variant after a
|
|
/// `MaybeUninit<___ZerocopyInnerTag>`.
|
|
///
|
|
/// In order to unify the generated types for `repr(C)` and `repr(int)` enums,
|
|
/// we use a "fused" representation with fields for both an inner tag and an
|
|
/// outer tag. Depending on the repr, we will set one of these tags to the tag
|
|
/// type and the other to `()`. This lets us generate the same code but put the
|
|
/// tags in different locations.
|
|
fn generate_variant_structs(ctx: &Ctx, data: &DataEnum) -> TokenStream {
|
|
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
|
|
|
|
let enum_name = &ctx.ast.ident;
|
|
|
|
// All variant structs have a `PhantomData<MyEnum<...>>` field because we
|
|
// don't know which generic parameters each variant will use, and unused
|
|
// generic parameters are a compile error.
|
|
let core = ctx.core_path();
|
|
let phantom_ty = quote! {
|
|
#core::marker::PhantomData<#enum_name #ty_generics>
|
|
};
|
|
|
|
let variant_structs = data.variants.iter().filter_map(|variant| {
|
|
// We don't generate variant structs for unit variants because we only
|
|
// need to check the tag. This helps cut down our generated code a bit.
|
|
if matches!(variant.fields, Fields::Unit) {
|
|
return None;
|
|
}
|
|
|
|
let variant_struct_ident = variant_struct_ident(&variant.ident);
|
|
let field_types = variant.fields.iter().map(|f| &f.ty);
|
|
|
|
let variant_struct = parse_quote! {
|
|
#[repr(C)]
|
|
struct #variant_struct_ident #impl_generics (
|
|
#core::mem::MaybeUninit<___ZerocopyInnerTag>,
|
|
#(#field_types,)*
|
|
#phantom_ty,
|
|
) #where_clause;
|
|
};
|
|
|
|
// We do this rather than emitting `#[derive(::zerocopy::TryFromBytes)]`
|
|
// because that is not hygienic, and this is also more performant.
|
|
let try_from_bytes_impl =
|
|
derive_try_from_bytes(&ctx.with_input(&variant_struct), Trait::TryFromBytes)
|
|
.expect("derive_try_from_bytes should not fail on synthesized type");
|
|
|
|
Some(quote! {
|
|
#variant_struct
|
|
#try_from_bytes_impl
|
|
})
|
|
});
|
|
|
|
quote! {
|
|
#(#variant_structs)*
|
|
}
|
|
}
|
|
|
|
fn variants_union_field_ident(ident: &Ident) -> Ident {
|
|
// Field names are prefixed with `__field_` to prevent name collision
|
|
// with the `__nonempty` field.
|
|
ident!(("__field_{}", ident), ident.span())
|
|
}
|
|
|
|
fn generate_variants_union(ctx: &Ctx, data: &DataEnum) -> TokenStream {
|
|
let generics = &ctx.ast.generics;
|
|
let (_, ty_generics, _) = generics.split_for_impl();
|
|
|
|
let fields = data.variants.iter().filter_map(|variant| {
|
|
// We don't generate variant structs for unit variants because we only
|
|
// need to check the tag. This helps cut down our generated code a bit.
|
|
if matches!(variant.fields, Fields::Unit) {
|
|
return None;
|
|
}
|
|
|
|
let field_name = variants_union_field_ident(&variant.ident);
|
|
let variant_struct_ident = variant_struct_ident(&variant.ident);
|
|
|
|
let core = ctx.core_path();
|
|
Some(quote! {
|
|
#field_name: #core::mem::ManuallyDrop<#variant_struct_ident #ty_generics>,
|
|
})
|
|
});
|
|
|
|
let variants_union = parse_quote! {
|
|
#[repr(C)]
|
|
union ___ZerocopyVariants #generics {
|
|
#(#fields)*
|
|
// Enums can have variants with no fields, but unions must
|
|
// have at least one field. So we just add a trailing unit
|
|
// to ensure that this union always has at least one field.
|
|
// Because this union is `repr(C)`, this unit type does not
|
|
// affect the layout.
|
|
__nonempty: (),
|
|
}
|
|
};
|
|
|
|
let has_field =
|
|
derive_has_field_struct_union(&ctx.with_input(&variants_union), &variants_union.data);
|
|
|
|
quote! {
|
|
#variants_union
|
|
#has_field
|
|
}
|
|
}
|
|
|
|
/// Generates an implementation of `is_bit_valid` for an arbitrary enum.
|
|
///
|
|
/// The general process is:
|
|
///
|
|
/// 1. Generate a tag enum. This is an enum with the same repr, variants, and
|
|
/// corresponding discriminants as the original enum, but without any fields
|
|
/// on the variants. This gives us access to an enum where the variants have
|
|
/// the same discriminants as the one we're writing `is_bit_valid` for.
|
|
/// 2. Make constants from the variants of the tag enum. We need these because
|
|
/// we can't put const exprs in match arms.
|
|
/// 3. Generate variant structs. These are structs which have the same fields as
|
|
/// each variant of the enum, and are `#[repr(C)]` with an optional "inner
|
|
/// tag".
|
|
/// 4. Generate a variants union, with one field for each variant struct type.
|
|
/// 5. And finally, our raw enum is a `#[repr(C)]` struct of an "outer tag" and
|
|
/// the variants union.
|
|
///
|
|
/// See these reference links for fully-worked example decompositions.
|
|
///
|
|
/// - `repr(C)`: <https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields>
|
|
/// - `repr(int)`: <https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields>
|
|
/// - `repr(C, int)`: <https://doc.rust-lang.org/reference/type-layout.html#combining-primitive-representations-of-enums-with-fields-and-reprc>
|
|
pub(crate) fn derive_is_bit_valid(
|
|
ctx: &Ctx,
|
|
data: &DataEnum,
|
|
repr: &EnumRepr,
|
|
) -> Result<TokenStream, Error> {
|
|
let trait_path = Trait::TryFromBytes.crate_path(ctx);
|
|
let tag_enum = generate_tag_enum(ctx, repr, data);
|
|
let tag_consts = generate_tag_consts(data);
|
|
|
|
let (outer_tag_type, inner_tag_type) = if repr.is_c() {
|
|
(quote! { ___ZerocopyTag }, quote! { () })
|
|
} else if repr.is_primitive() {
|
|
(quote! { () }, quote! { ___ZerocopyTag })
|
|
} else {
|
|
return Err(Error::new(
|
|
ctx.ast.span(),
|
|
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
|
|
));
|
|
};
|
|
|
|
let variant_structs = generate_variant_structs(ctx, data);
|
|
let variants_union = generate_variants_union(ctx, data);
|
|
|
|
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
|
|
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None)
|
|
.inner_extras(quote! {
|
|
type Tag = ___ZerocopyTag;
|
|
type ProjectToTag = #zerocopy_crate::pointer::cast::CastSized;
|
|
})
|
|
.build();
|
|
let has_fields = data.variants().into_iter().flat_map(|(variant, fields)| {
|
|
let variant_ident = &variant.unwrap().ident;
|
|
let variants_union_field_ident = variants_union_field_ident(variant_ident);
|
|
let field: Box<syn::Type> = parse_quote!(());
|
|
fields.into_iter().enumerate().map(move |(idx, (vis, ident, ty))| {
|
|
// Rust does not presently support explicit visibility modifiers on
|
|
// enum fields, but we guard against the possibility to ensure this
|
|
// derive remains sound.
|
|
assert!(matches!(vis, syn::Visibility::Inherited));
|
|
let variant_struct_field_index = Index::from(idx + 1);
|
|
let (_, ty_generics, _) = ctx.ast.generics.split_for_impl();
|
|
let has_field_trait = Trait::HasField {
|
|
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;
|
|
// we use `()`.
|
|
field: field.clone(),
|
|
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
|
|
};
|
|
let has_field_path = has_field_trait.crate_path(ctx);
|
|
let has_field = ImplBlockBuilder::new(
|
|
ctx,
|
|
data,
|
|
has_field_trait,
|
|
FieldBounds::None,
|
|
)
|
|
.inner_extras(quote! {
|
|
type Type = #ty;
|
|
|
|
#[inline(always)]
|
|
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
|
|
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) }>>()
|
|
.as_ptr()
|
|
}
|
|
})
|
|
.build();
|
|
|
|
let project = ImplBlockBuilder::new(
|
|
ctx,
|
|
data,
|
|
Trait::ProjectField {
|
|
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; we use `()`.
|
|
field: field.clone(),
|
|
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
|
|
invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)),
|
|
},
|
|
FieldBounds::None,
|
|
)
|
|
.param_extras(vec![
|
|
parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing),
|
|
parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment),
|
|
])
|
|
.inner_extras(quote! {
|
|
type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible;
|
|
type Invariants = (Aliasing, Alignment, #zerocopy_crate::invariant::Initialized);
|
|
})
|
|
.build();
|
|
|
|
quote! {
|
|
#has_field
|
|
#project
|
|
}
|
|
})
|
|
});
|
|
|
|
let core = ctx.core_path();
|
|
let match_arms = data.variants.iter().map(|variant| {
|
|
let tag_ident = tag_ident(&variant.ident);
|
|
let variant_struct_ident = variant_struct_ident(&variant.ident);
|
|
let variants_union_field_ident = variants_union_field_ident(&variant.ident);
|
|
|
|
if matches!(variant.fields, Fields::Unit) {
|
|
// Unit variants don't need any further validation beyond checking
|
|
// the tag.
|
|
quote! {
|
|
#tag_ident => true
|
|
}
|
|
} else {
|
|
quote! {
|
|
#tag_ident => {
|
|
// SAFETY: Since we know that the tag is `#tag_ident`, we
|
|
// know that no other `&`s exist which refer to this enum
|
|
// as any other variant.
|
|
let variant_md = variants.cast::<
|
|
_,
|
|
#zerocopy_crate::pointer::cast::Projection<
|
|
// #zerocopy_crate::ReadOnly<_>,
|
|
_,
|
|
{ #zerocopy_crate::REPR_C_UNION_VARIANT_ID },
|
|
{ #zerocopy_crate::ident_id!(#variants_union_field_ident) }
|
|
>,
|
|
_
|
|
>();
|
|
let variant = variant_md.cast::<
|
|
#zerocopy_crate::ReadOnly<#variant_struct_ident #ty_generics>,
|
|
#zerocopy_crate::pointer::cast::CastSized,
|
|
(#zerocopy_crate::pointer::BecauseRead, _)
|
|
>();
|
|
<
|
|
#variant_struct_ident #ty_generics as #trait_path
|
|
>::is_bit_valid(variant)
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
let generics = &ctx.ast.generics;
|
|
let raw_enum: DeriveInput = parse_quote! {
|
|
#[repr(C)]
|
|
struct ___ZerocopyRawEnum #generics {
|
|
tag: ___ZerocopyOuterTag,
|
|
variants: ___ZerocopyVariants #ty_generics,
|
|
}
|
|
};
|
|
|
|
let self_ident = &ctx.ast.ident;
|
|
let invariants_eq_impl = quote! {
|
|
// SAFETY: `___ZerocopyRawEnum` is designed to have the same layout,
|
|
// validity, and invariants as `Self`.
|
|
unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<___ZerocopyRawEnum #ty_generics> for #self_ident #ty_generics #where_clause {}
|
|
};
|
|
|
|
let raw_enum_projections =
|
|
derive_has_field_struct_union(&ctx.with_input(&raw_enum), &raw_enum.data);
|
|
|
|
let raw_enum = quote! {
|
|
#raw_enum
|
|
#invariants_eq_impl
|
|
#raw_enum_projections
|
|
};
|
|
|
|
Ok(quote! {
|
|
// SAFETY: We use `is_bit_valid` to validate that the bit pattern of the
|
|
// enum's tag corresponds to one of the enum's discriminants. Then, we
|
|
// check the bit validity of each field of the corresponding variant.
|
|
// Thus, this is a sound implementation of `is_bit_valid`.
|
|
#[inline]
|
|
fn is_bit_valid<___ZcAlignment>(
|
|
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
|
|
) -> #core::primitive::bool
|
|
where
|
|
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
|
|
{
|
|
#tag_enum
|
|
|
|
type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag<
|
|
{ #core::mem::size_of::<___ZerocopyTag>() },
|
|
>;
|
|
|
|
#tag_consts
|
|
|
|
type ___ZerocopyOuterTag = #outer_tag_type;
|
|
type ___ZerocopyInnerTag = #inner_tag_type;
|
|
|
|
#variant_structs
|
|
|
|
#variants_union
|
|
|
|
#raw_enum
|
|
|
|
#has_tag
|
|
|
|
#(#has_fields)*
|
|
|
|
let tag = {
|
|
// SAFETY:
|
|
// - The provided cast addresses a subset of the bytes addressed
|
|
// by `candidate` because it addresses the starting tag of the
|
|
// enum.
|
|
// - Because the pointer is cast from `candidate`, it has the
|
|
// same provenance as it.
|
|
// - There are no `UnsafeCell`s in the tag because it is a
|
|
// primitive integer.
|
|
// - `tag_ptr` is casted from `candidate`, whose referent is
|
|
// `Initialized`. Since we have not written uninitialized
|
|
// bytes into the referent, `tag_ptr` is also `Initialized`.
|
|
//
|
|
// FIXME(#2874): Revise this to a `cast` once `candidate`
|
|
// references a `ReadOnly<Self>`.
|
|
let tag_ptr = unsafe {
|
|
candidate.reborrow().project_transmute_unchecked::<
|
|
_,
|
|
#zerocopy_crate::invariant::Initialized,
|
|
#zerocopy_crate::pointer::cast::CastSized
|
|
>()
|
|
};
|
|
tag_ptr.recall_validity::<_, (_, (_, _))>().read::<#zerocopy_crate::BecauseImmutable>()
|
|
};
|
|
|
|
let mut raw_enum = candidate.cast::<
|
|
#zerocopy_crate::ReadOnly<___ZerocopyRawEnum #ty_generics>,
|
|
#zerocopy_crate::pointer::cast::CastSized,
|
|
(#zerocopy_crate::pointer::BecauseRead, _)
|
|
>();
|
|
|
|
let variants = #zerocopy_crate::into_inner!(raw_enum.project::<
|
|
_,
|
|
{ #zerocopy_crate::STRUCT_VARIANT_ID },
|
|
{ #zerocopy_crate::ident_id!(variants) }
|
|
>());
|
|
|
|
match tag {
|
|
#(#match_arms,)*
|
|
_ => false,
|
|
}
|
|
}
|
|
})
|
|
}
|
|
pub(crate) fn derive_try_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
|
|
match &ctx.ast.data {
|
|
Data::Struct(strct) => derive_try_from_bytes_struct(ctx, strct, top_level),
|
|
Data::Enum(enm) => derive_try_from_bytes_enum(ctx, enm, top_level),
|
|
Data::Union(unn) => Ok(derive_try_from_bytes_union(ctx, unn, top_level)),
|
|
}
|
|
}
|
|
fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream {
|
|
let fields = ctx.ast.data.fields();
|
|
if fields.is_empty() {
|
|
return quote! {};
|
|
}
|
|
|
|
let field_tokens = fields.iter().map(|(vis, ident, _)| {
|
|
let ident = ident!(("__z{}", ident), ident.span());
|
|
quote!(
|
|
#vis enum #ident {}
|
|
)
|
|
});
|
|
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let variant_id: Box<Expr> = match &ctx.ast.data {
|
|
Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }),
|
|
Data::Union(_) => {
|
|
let is_repr_c = StructUnionRepr::from_attrs(&ctx.ast.attrs)
|
|
.map(|repr| repr.is_c())
|
|
.unwrap_or(false);
|
|
if is_repr_c {
|
|
parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID })
|
|
} else {
|
|
parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID })
|
|
}
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let core = ctx.core_path();
|
|
let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None)
|
|
.inner_extras(quote! {
|
|
type Tag = ();
|
|
type ProjectToTag = #zerocopy_crate::pointer::cast::CastToUnit;
|
|
})
|
|
.build();
|
|
let has_fields = fields.iter().map(move |(_, ident, ty)| {
|
|
let field_token = ident!(("__z{}", ident), ident.span());
|
|
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 {
|
|
variant_id: variant_id.clone(),
|
|
field: field.clone(),
|
|
field_id: field_id.clone(),
|
|
};
|
|
let has_field_path = has_field_trait.crate_path(ctx);
|
|
ImplBlockBuilder::new(
|
|
ctx,
|
|
data,
|
|
has_field_trait,
|
|
FieldBounds::None,
|
|
)
|
|
.inner_extras(quote! {
|
|
type Type = #ty;
|
|
|
|
#[inline(always)]
|
|
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
|
|
let slf = slf.as_ptr();
|
|
// SAFETY: By invariant on `PtrInner`, `slf` is a non-null
|
|
// pointer whose referent is zero-sized or lives in a valid
|
|
// allocation. Since `#ident` is a struct or union field of
|
|
// `Self`, this projection preserves or shrinks the referent
|
|
// size, and so the resulting referent also fits in the same
|
|
// allocation.
|
|
unsafe { #core::ptr::addr_of_mut!((*slf).#ident) }
|
|
}
|
|
}).outer_extras(if matches!(&ctx.ast.data, Data::Struct(..)) {
|
|
let fields_preserve_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs)
|
|
.map(|repr| repr.get_packed().is_none())
|
|
.unwrap();
|
|
let alignment = if fields_preserve_alignment {
|
|
quote! { Alignment }
|
|
} else {
|
|
quote! { #zerocopy_crate::invariant::Unaligned }
|
|
};
|
|
// SAFETY: See comments on items.
|
|
ImplBlockBuilder::new(
|
|
ctx,
|
|
data,
|
|
Trait::ProjectField {
|
|
variant_id: variant_id.clone(),
|
|
field,
|
|
field_id,
|
|
invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)),
|
|
},
|
|
FieldBounds::None,
|
|
)
|
|
.param_extras(vec![
|
|
parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing),
|
|
parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment),
|
|
])
|
|
.inner_extras(quote! {
|
|
// SAFETY: Projection into structs is always infallible.
|
|
type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible;
|
|
// SAFETY: The alignment of the projected `Ptr` is `Unaligned`
|
|
// if the structure is packed; otherwise inherited from the
|
|
// outer `Ptr`. If the validity of the outer pointer is
|
|
// `Initialized`, so too is the validity of its fields.
|
|
type Invariants = (Aliasing, #alignment, #zerocopy_crate::invariant::Initialized);
|
|
})
|
|
.build()
|
|
} else {
|
|
quote! {}
|
|
})
|
|
.build()
|
|
});
|
|
|
|
const_block(field_tokens.into_iter().chain(Some(has_tag)).chain(has_fields).map(Some))
|
|
}
|
|
fn derive_try_from_bytes_struct(
|
|
ctx: &Ctx,
|
|
strct: &DataStruct,
|
|
top_level: Trait,
|
|
) -> Result<TokenStream, Error> {
|
|
let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| {
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let fields = strct.fields();
|
|
let field_names = fields.iter().map(|(_vis, name, _ty)| name);
|
|
let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
|
|
let core = ctx.core_path();
|
|
quote!(
|
|
// SAFETY: We use `is_bit_valid` to validate that each field is
|
|
// bit-valid, and only return `true` if all of them are. The bit
|
|
// validity of a struct is just the composition of the bit
|
|
// validities of its fields, so this is a sound implementation
|
|
// of `is_bit_valid`.
|
|
#[inline]
|
|
fn is_bit_valid<___ZcAlignment>(
|
|
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
|
|
) -> #core::primitive::bool
|
|
where
|
|
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
|
|
{
|
|
true #(&& {
|
|
let field_candidate = #zerocopy_crate::into_inner!(candidate.reborrow().project::<
|
|
_,
|
|
{ #zerocopy_crate::STRUCT_VARIANT_ID },
|
|
{ #zerocopy_crate::ident_id!(#field_names) }
|
|
>());
|
|
<#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
|
|
})*
|
|
}
|
|
)
|
|
});
|
|
Ok(ImplBlockBuilder::new(ctx, strct, Trait::TryFromBytes, FieldBounds::ALL_SELF)
|
|
.inner_extras(extras)
|
|
.outer_extras(derive_has_field_struct_union(ctx, strct))
|
|
.build())
|
|
}
|
|
fn derive_try_from_bytes_union(ctx: &Ctx, unn: &DataUnion, top_level: Trait) -> TokenStream {
|
|
let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
|
|
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let variant_id: Box<Expr> = {
|
|
let is_repr_c =
|
|
StructUnionRepr::from_attrs(&ctx.ast.attrs).map(|repr| repr.is_c()).unwrap_or(false);
|
|
if is_repr_c {
|
|
parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID })
|
|
} else {
|
|
parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID })
|
|
}
|
|
};
|
|
|
|
let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| {
|
|
let fields = unn.fields();
|
|
let field_names = fields.iter().map(|(_vis, name, _ty)| name);
|
|
let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
|
|
let core = ctx.core_path();
|
|
quote!(
|
|
// SAFETY: We use `is_bit_valid` to validate that any field is
|
|
// bit-valid; we only return `true` if at least one of them is.
|
|
// The bit validity of a union is not yet well defined in Rust,
|
|
// but it is guaranteed to be no more strict than this
|
|
// definition. See #696 for a more in-depth discussion.
|
|
#[inline]
|
|
fn is_bit_valid<___ZcAlignment>(
|
|
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
|
|
) -> #core::primitive::bool
|
|
where
|
|
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
|
|
{
|
|
false #(|| {
|
|
// SAFETY:
|
|
// - Since `ReadOnly<Self>: Immutable` unconditionally,
|
|
// neither `*slf` nor the returned pointer's referent
|
|
// permit interior mutation.
|
|
// - Both source and destination validity are
|
|
// `Initialized`, which is always a sound
|
|
// transmutation.
|
|
let field_candidate = unsafe {
|
|
candidate.reborrow().project_transmute_unchecked::<
|
|
_,
|
|
_,
|
|
#zerocopy_crate::pointer::cast::Projection<
|
|
_,
|
|
#variant_id,
|
|
{ #zerocopy_crate::ident_id!(#field_names) }
|
|
>
|
|
>()
|
|
};
|
|
|
|
<#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
|
|
})*
|
|
}
|
|
)
|
|
});
|
|
ImplBlockBuilder::new(ctx, unn, Trait::TryFromBytes, field_type_trait_bounds)
|
|
.inner_extras(extras)
|
|
.outer_extras(derive_has_field_struct_union(ctx, unn))
|
|
.build()
|
|
}
|
|
fn derive_try_from_bytes_enum(
|
|
ctx: &Ctx,
|
|
enm: &DataEnum,
|
|
top_level: Trait,
|
|
) -> Result<TokenStream, Error> {
|
|
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
|
|
|
|
// If an enum has no fields, it has a well-defined integer representation,
|
|
// and every possible bit pattern corresponds to a valid discriminant tag,
|
|
// then it *could* be `FromBytes` (even if the user hasn't derived
|
|
// `FromBytes`). This holds if, for `repr(uN)` or `repr(iN)`, there are 2^N
|
|
// variants.
|
|
let could_be_from_bytes = enum_size_from_repr(&repr)
|
|
.map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
|
|
.unwrap_or(false);
|
|
|
|
let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ctx, top_level);
|
|
let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
|
|
(Some(is_bit_valid), _) => is_bit_valid,
|
|
// SAFETY: It would be sound for the enum to implement `FromBytes`, as
|
|
// required by `gen_trivial_is_bit_valid_unchecked`.
|
|
(None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(ctx) },
|
|
(None, false) => match derive_is_bit_valid(ctx, enm, &repr) {
|
|
Ok(extra) => extra,
|
|
Err(_) if ctx.skip_on_error => return Ok(TokenStream::new()),
|
|
Err(e) => return Err(e),
|
|
},
|
|
};
|
|
|
|
Ok(ImplBlockBuilder::new(ctx, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF)
|
|
.inner_extras(extra)
|
|
.build())
|
|
}
|
|
fn try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream> {
|
|
// If the top-level trait is `FromBytes` and `Self` has no type parameters,
|
|
// then the `FromBytes` derive will fail compilation if `Self` is not
|
|
// actually soundly `FromBytes`, and so we can rely on that for our
|
|
// `is_bit_valid` impl. It's plausible that we could make changes - or Rust
|
|
// could make changes (such as the "trivial bounds" language feature) - that
|
|
// make this no longer true. To hedge against these, we include an explicit
|
|
// `Self: FromBytes` check in the generated `is_bit_valid`, which is
|
|
// bulletproof.
|
|
//
|
|
// If `ctx.skip_on_error` is true, we can't rely on the `FromBytes` derive
|
|
// to fail compilation if `Self` is not actually soundly `FromBytes`.
|
|
if matches!(top_level, Trait::FromBytes)
|
|
&& ctx.ast.generics.params.is_empty()
|
|
&& !ctx.skip_on_error
|
|
{
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let core = ctx.core_path();
|
|
Some(quote!(
|
|
// SAFETY: See inline.
|
|
#[inline(always)]
|
|
fn is_bit_valid<___ZcAlignment>(
|
|
_candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
|
|
) -> #core::primitive::bool
|
|
where
|
|
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
|
|
{
|
|
if false {
|
|
fn assert_is_from_bytes<T>()
|
|
where
|
|
T: #zerocopy_crate::FromBytes,
|
|
T: ?#core::marker::Sized,
|
|
{
|
|
}
|
|
|
|
assert_is_from_bytes::<Self>();
|
|
}
|
|
|
|
// SAFETY: The preceding code only compiles if `Self:
|
|
// FromBytes`. Thus, this code only compiles if all initialized
|
|
// byte sequences represent valid instances of `Self`.
|
|
true
|
|
}
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// All initialized bit patterns must be valid for `Self`.
|
|
unsafe fn gen_trivial_is_bit_valid_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream {
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let core = ctx.core_path();
|
|
quote!(
|
|
// SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has
|
|
// promised that all initialized bit patterns are valid for `Self`.
|
|
#[inline(always)]
|
|
fn is_bit_valid<___ZcAlignment>(
|
|
_candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
|
|
) -> #core::primitive::bool
|
|
where
|
|
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
|
|
{
|
|
true
|
|
}
|
|
)
|
|
}
|