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>
165 lines
7.1 KiB
Rust
165 lines
7.1 KiB
Rust
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
|
|
//
|
|
use proc_macro2::{Span, TokenStream};
|
|
use quote::quote;
|
|
use syn::{Data, DataEnum, DataStruct, DataUnion, Error, Type};
|
|
|
|
use crate::{
|
|
repr::{EnumRepr, StructUnionRepr},
|
|
util::{
|
|
generate_tag_enum, Ctx, DataExt, FieldBounds, ImplBlockBuilder, PaddingCheck, Trait,
|
|
TraitBound,
|
|
},
|
|
};
|
|
pub(crate) fn derive_into_bytes(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
|
|
match &ctx.ast.data {
|
|
Data::Struct(strct) => derive_into_bytes_struct(ctx, strct),
|
|
Data::Enum(enm) => derive_into_bytes_enum(ctx, enm),
|
|
Data::Union(unn) => derive_into_bytes_union(ctx, unn),
|
|
}
|
|
}
|
|
fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream, Error> {
|
|
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
|
|
|
|
let is_transparent = repr.is_transparent();
|
|
let is_c = repr.is_c();
|
|
let is_packed_1 = repr.is_packed_1();
|
|
let num_fields = strct.fields().len();
|
|
|
|
let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
|
|
// No padding check needed.
|
|
// - repr(transparent): The layout and ABI of the whole struct is the
|
|
// same as its only non-ZST field (meaning there's no padding outside
|
|
// of that field) and we require that field to be `IntoBytes` (meaning
|
|
// there's no padding in that field).
|
|
// - repr(packed): Any inter-field padding bytes are removed, meaning
|
|
// that any padding bytes would need to come from the fields, all of
|
|
// which we require to be `IntoBytes` (meaning they don't have any
|
|
// padding). Note that this holds regardless of other `repr`
|
|
// attributes, including `repr(Rust)`. [1]
|
|
//
|
|
// [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-alignment-modifiers:
|
|
//
|
|
// An important consequence of these rules is that a type with
|
|
// `#[repr(packed(1))]`` (or `#[repr(packed)]``) will have no
|
|
// inter-field padding.
|
|
(None, false)
|
|
} else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
|
|
// No padding check needed. A repr(C) struct with zero or one field has
|
|
// no padding unless #[repr(align)] explicitly adds padding, which we
|
|
// check for in this branch's condition.
|
|
(None, false)
|
|
} else if ctx.ast.generics.params.is_empty() {
|
|
// Is the last field a syntactic slice, i.e., `[SomeType]`.
|
|
let is_syntactic_dst =
|
|
strct.fields().last().map(|(_, _, ty)| matches!(ty, Type::Slice(_))).unwrap_or(false);
|
|
// Since there are no generics, we can emit a padding check. All reprs
|
|
// guarantee that fields won't overlap [1], so the padding check is
|
|
// sound. This is more permissive than the next case, which requires
|
|
// that all field types implement `Unaligned`.
|
|
//
|
|
// [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-rust-representation:
|
|
//
|
|
// The only data layout guarantees made by [`repr(Rust)`] are those
|
|
// required for soundness. They are:
|
|
// ...
|
|
// 2. The fields do not overlap.
|
|
// ...
|
|
if is_c && is_syntactic_dst {
|
|
(Some(PaddingCheck::ReprCStruct), false)
|
|
} else {
|
|
(Some(PaddingCheck::Struct), false)
|
|
}
|
|
} else if is_c && !repr.is_align_gt_1() {
|
|
// We can't use a padding check since there are generic type arguments.
|
|
// Instead, we require all field types to implement `Unaligned`. This
|
|
// ensures that the `repr(C)` layout algorithm will not insert any
|
|
// padding unless #[repr(align)] explicitly adds padding, which we check
|
|
// for in this branch's condition.
|
|
//
|
|
// FIXME(#10): Support type parameters for non-transparent, non-packed
|
|
// structs without requiring `Unaligned`.
|
|
(None, true)
|
|
} else {
|
|
return ctx.error_or_skip(Error::new(
|
|
Span::call_site(),
|
|
"must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout",
|
|
));
|
|
};
|
|
|
|
let field_bounds = if require_unaligned_fields {
|
|
FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
|
|
} else {
|
|
FieldBounds::ALL_SELF
|
|
};
|
|
|
|
Ok(ImplBlockBuilder::new(ctx, strct, Trait::IntoBytes, field_bounds)
|
|
.padding_check(padding_check)
|
|
.build())
|
|
}
|
|
|
|
fn derive_into_bytes_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
|
|
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
|
|
if !repr.is_c() && !repr.is_primitive() {
|
|
return ctx.error_or_skip(Error::new(
|
|
Span::call_site(),
|
|
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
|
|
));
|
|
}
|
|
|
|
let tag_type_definition = generate_tag_enum(ctx, &repr, enm);
|
|
Ok(ImplBlockBuilder::new(ctx, enm, Trait::IntoBytes, FieldBounds::ALL_SELF)
|
|
.padding_check(PaddingCheck::Enum { tag_type_definition })
|
|
.build())
|
|
}
|
|
|
|
fn derive_into_bytes_union(ctx: &Ctx, unn: &DataUnion) -> Result<TokenStream, Error> {
|
|
// See #1792 for more context.
|
|
//
|
|
// By checking for `zerocopy_derive_union_into_bytes` both here and in the
|
|
// generated code, we ensure that `--cfg zerocopy_derive_union_into_bytes`
|
|
// need only be passed *either* when compiling this crate *or* when
|
|
// compiling the user's crate. The former is preferable, but in some
|
|
// situations (such as when cross-compiling using `cargo build --target`),
|
|
// it doesn't get propagated to this crate's build by default.
|
|
let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
|
|
quote!()
|
|
} else {
|
|
let core = ctx.core_path();
|
|
let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
|
|
please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
|
|
quote!(
|
|
#[allow(unused_attributes, unexpected_cfgs)]
|
|
const _: () = {
|
|
#[cfg(not(zerocopy_derive_union_into_bytes))]
|
|
#core::compile_error!(#error_message);
|
|
};
|
|
)
|
|
};
|
|
|
|
// FIXME(#10): Support type parameters.
|
|
if !ctx.ast.generics.params.is_empty() {
|
|
return ctx.error_or_skip(Error::new(
|
|
Span::call_site(),
|
|
"unsupported on types with type parameters",
|
|
));
|
|
}
|
|
|
|
// Because we don't support generics, we don't need to worry about
|
|
// special-casing different reprs. So long as there is *some* repr which
|
|
// guarantees the layout, our `PaddingCheck::Union` guarantees that there is
|
|
// no padding.
|
|
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
|
|
if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
|
|
return ctx.error_or_skip(Error::new(
|
|
Span::call_site(),
|
|
"must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
|
|
));
|
|
}
|
|
|
|
let impl_block = ImplBlockBuilder::new(ctx, unn, Trait::IntoBytes, FieldBounds::ALL_SELF)
|
|
.padding_check(PaddingCheck::Union)
|
|
.build();
|
|
Ok(quote!(#cfg_compile_error #impl_block))
|
|
}
|