linux/rust/zerocopy-derive/derive/mod.rs
Miguel Ojeda 2808a39639 rust: zerocopy: update to v0.8.52
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>
2026-07-11 13:45:46 +02:00

137 lines
5.3 KiB
Rust

// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
//
pub mod from_bytes;
pub mod into_bytes;
pub mod known_layout;
pub mod try_from_bytes;
pub mod unaligned;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Data, Error};
use crate::{
repr::StructUnionRepr,
util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait},
};
pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
Ok(match &ctx.ast.data {
Data::Struct(strct) => {
ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build()
}
Data::Enum(enm) => {
ImplBlockBuilder::new(ctx, enm, Trait::Immutable, FieldBounds::ALL_SELF).build()
}
Data::Union(unn) => {
ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build()
}
})
}
pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
// This doesn't delegate to `impl_block` because `impl_block` assumes it is
// deriving a `zerocopy`-defined trait, and these trait impls share a common
// shape that `Hash` does not. In particular, `zerocopy` traits contain a
// method that only `zerocopy_derive` macros are supposed to implement, and
// `impl_block` generating this trait method is incompatible with `Hash`.
let type_ident = &ctx.ast.ident;
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let where_predicates = where_clause.map(|clause| &clause.predicates);
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
Ok(quote! {
impl #impl_generics #core::hash::Hash for #type_ident #ty_generics
where
Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
#where_predicates
{
fn hash<H: #core::hash::Hasher>(&self, state: &mut H) {
#core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(self))
}
fn hash_slice<H: #core::hash::Hasher>(data: &[Self], state: &mut H) {
#core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(data))
}
}
})
}
pub(crate) fn derive_eq(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
// This doesn't delegate to `impl_block` because `impl_block` assumes it is
// deriving a `zerocopy`-defined trait, and these trait impls share a common
// shape that `Eq` does not. In particular, `zerocopy` traits contain a
// method that only `zerocopy_derive` macros are supposed to implement, and
// `impl_block` generating this trait method is incompatible with `Eq`.
let type_ident = &ctx.ast.ident;
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let where_predicates = where_clause.map(|clause| &clause.predicates);
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
Ok(quote! {
impl #impl_generics #core::cmp::PartialEq for #type_ident #ty_generics
where
Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
#where_predicates
{
fn eq(&self, other: &Self) -> bool {
#core::cmp::PartialEq::eq(
#zerocopy_crate::IntoBytes::as_bytes(self),
#zerocopy_crate::IntoBytes::as_bytes(other),
)
}
}
impl #impl_generics #core::cmp::Eq for #type_ident #ty_generics
where
Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
#where_predicates
{
}
})
}
pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
match &ctx.ast.data {
Data::Struct(_) => {}
Data::Enum(_) | Data::Union(_) => {
return ctx
.error_or_skip(Error::new(Span::call_site(), "can only be applied to structs"));
}
};
if repr.get_packed().is_some() {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must not have #[repr(packed)] attribute",
));
}
if !(repr.is_c() || repr.is_transparent()) {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable",
));
}
let fields = ctx.ast.data.fields();
let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
trailing_field
} else {
return ctx.error_or_skip(Error::new(Span::call_site(), "must at least one field"));
};
let zerocopy_crate = &ctx.zerocopy_crate;
// SAFETY: `#ty`, per the above checks, is `repr(C)` or `repr(transparent)`
// and is not packed; its trailing field is guaranteed to be well-aligned
// for its type. By invariant on `FieldBounds::TRAILING_SELF`, the trailing
// slice of the trailing field is also well-aligned for its type.
Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF)
.inner_extras(quote! {
type Elem = <#trailing_field as #zerocopy_crate::SplitAt>::Elem;
})
.build())
}