mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-21 20:27:50 -04:00
Originally, when the Rust upstream `alloc` standard library crate was vendored, the SPDX License Identifiers were added to every file so that the license on those was clear. The same happened with the vendoring of `proc_macro2`, `quote` and `syn`. Please see: commit057b8d2571("rust: adapt `alloc` crate to the kernel") commit69942c0a89("rust: syn: add SPDX License Identifiers") commitddfa1b279d("rust: quote: add SPDX License Identifiers") commita9acfceb96("rust: proc-macro2: add SPDX License Identifiers") Thus do the same for the `zerocopy-derive` crate. This makes `scripts/spdxcheck.py` pass: use parentheses like commit06e9bfc1e5("ionic: make spdxcheck.py happy") did since we have two `OR` operators in the expression (three licenses). Finally, as requested, I filed an issue [1] with upstream about it. Cc: Joshua Liebow-Feeser <joshlf@google.com> Cc: Jack Wrenn <jswrenn@google.com> Link: https://github.com/google/zerocopy/issues/3428 [1] Link: https://patch.msgid.link/20260608141439.182634-15-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))
|
|
}
|