mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -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>
351 lines
14 KiB
Rust
351 lines
14 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, Data, Error, Type};
|
|
|
|
use crate::{
|
|
repr::StructUnionRepr,
|
|
util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, SelfBounds, Trait},
|
|
};
|
|
|
|
fn derive_known_layout_for_repr_c_struct<'a>(
|
|
ctx: &'a Ctx,
|
|
repr: &StructUnionRepr,
|
|
fields: &[(&'a syn::Visibility, TokenStream, &'a Type)],
|
|
) -> Option<(SelfBounds<'a>, TokenStream, Option<TokenStream>)> {
|
|
let (trailing_field, leading_fields) = fields.split_last()?;
|
|
|
|
let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
|
|
let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
|
|
|
|
let core = ctx.core_path();
|
|
let repr_align = repr
|
|
.get_align()
|
|
.map(|align| {
|
|
let align = align.t.get();
|
|
quote!(#core::num::NonZeroUsize::new(#align as usize))
|
|
})
|
|
.unwrap_or_else(|| quote!(#core::option::Option::None));
|
|
let repr_packed = repr
|
|
.get_packed()
|
|
.map(|packed| {
|
|
let packed = packed.get();
|
|
quote!(#core::num::NonZeroUsize::new(#packed as usize))
|
|
})
|
|
.unwrap_or_else(|| quote!(#core::option::Option::None));
|
|
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let make_methods = |trailing_field_ty| {
|
|
quote! {
|
|
// SAFETY:
|
|
// - The returned pointer has the same address and provenance as
|
|
// `bytes`:
|
|
// - The recursive call to `raw_from_ptr_len` preserves both
|
|
// address and provenance.
|
|
// - The `as` cast preserves both address and provenance.
|
|
// - `NonNull::new_unchecked` preserves both address and
|
|
// provenance.
|
|
// - If `Self` is a slice DST, the returned pointer encodes
|
|
// `elems` elements in the trailing slice:
|
|
// - This is true of the recursive call to `raw_from_ptr_len`.
|
|
// - `trailing.as_ptr() as *mut Self` preserves trailing slice
|
|
// element count [1].
|
|
// - `NonNull::new_unchecked` preserves trailing slice element
|
|
// count.
|
|
//
|
|
// [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
|
|
//
|
|
// `*const T`` / `*mut T` can be cast to `*const U` / `*mut U`
|
|
// with the following behavior:
|
|
// ...
|
|
// - If `T` and `U` are both unsized, the pointer is also
|
|
// returned unchanged. In particular, the metadata is
|
|
// preserved exactly.
|
|
//
|
|
// For instance, a cast from `*const [T]` to `*const [U]`
|
|
// preserves the number of elements. ... The same holds
|
|
// for str and any compound type whose unsized tail is a
|
|
// slice type, such as struct `Foo(i32, [u8])` or
|
|
// `(u64, Foo)`.
|
|
#[inline(always)]
|
|
fn raw_from_ptr_len(
|
|
bytes: #core::ptr::NonNull<u8>,
|
|
meta: <Self as #zerocopy_crate::KnownLayout>::PointerMetadata,
|
|
) -> #core::ptr::NonNull<Self> {
|
|
let trailing = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::raw_from_ptr_len(bytes, meta);
|
|
let slf = trailing.as_ptr() as *mut Self;
|
|
// SAFETY: Constructed from `trailing`, which is non-null.
|
|
unsafe { #core::ptr::NonNull::new_unchecked(slf) }
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn pointer_to_metadata(ptr: *mut Self) -> <Self as #zerocopy_crate::KnownLayout>::PointerMetadata {
|
|
<#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
|
|
}
|
|
}
|
|
};
|
|
|
|
let inner_extras = {
|
|
let leading_fields_tys = leading_fields_tys.clone();
|
|
let methods = make_methods(*trailing_field_ty);
|
|
let (_, ty_generics, _) = ctx.ast.generics.split_for_impl();
|
|
|
|
quote!(
|
|
type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
|
|
|
|
type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
|
|
|
|
// SAFETY: `LAYOUT` accurately describes the layout of `Self`.
|
|
// The documentation of `DstLayout::for_repr_c_struct` vows that
|
|
// invocations in this manner will accurately describe a type,
|
|
// so long as:
|
|
//
|
|
// - that type is `repr(C)`,
|
|
// - its fields are enumerated in the order they appear,
|
|
// - the presence of `repr_align` and `repr_packed` are
|
|
// correctly accounted for.
|
|
//
|
|
// We respect all three of these preconditions here. This
|
|
// expansion is only used if `is_repr_c_struct`, we enumerate
|
|
// the fields in order, and we extract the values of `align(N)`
|
|
// and `packed(N)`.
|
|
const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_repr_c_struct(
|
|
#repr_align,
|
|
#repr_packed,
|
|
&[
|
|
#(#zerocopy_crate::DstLayout::for_type::<#leading_fields_tys>(),)*
|
|
<#trailing_field_ty as #zerocopy_crate::KnownLayout>::LAYOUT
|
|
],
|
|
);
|
|
|
|
#methods
|
|
)
|
|
};
|
|
|
|
let outer_extras = {
|
|
let ident = &ctx.ast.ident;
|
|
let vis = &ctx.ast.vis;
|
|
let params = &ctx.ast.generics.params;
|
|
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
|
|
|
|
let predicates = if let Some(where_clause) = where_clause {
|
|
where_clause.predicates.clone()
|
|
} else {
|
|
Default::default()
|
|
};
|
|
|
|
// Generate a valid ident for a type-level handle to a field of a
|
|
// given `name`.
|
|
let field_index = |name: &TokenStream| ident!(("__Zerocopy_Field_{}", name), ident.span());
|
|
|
|
let field_indices: Vec<_> =
|
|
fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
|
|
|
|
// Define the collection of type-level field handles.
|
|
let field_defs = field_indices.iter().zip(fields).map(|(idx, (vis, _, _))| {
|
|
quote! {
|
|
#vis struct #idx;
|
|
}
|
|
});
|
|
|
|
let field_impls = field_indices.iter().zip(fields).map(|(idx, (_, _, ty))| quote! {
|
|
// SAFETY: `#ty` is the type of `#ident`'s field at `#idx`.
|
|
//
|
|
// We implement `Field` for each field of the struct to create a
|
|
// projection from the field index to its type. This allows us
|
|
// to refer to the field's type in a way that respects `Self`
|
|
// hygiene. If we just copy-pasted the tokens of `#ty`, we
|
|
// would not respect `Self` hygiene, as `Self` would refer to
|
|
// the helper struct we are generating, not the derive target
|
|
// type.
|
|
unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
|
|
where
|
|
#predicates
|
|
{
|
|
type Type = #ty;
|
|
}
|
|
});
|
|
|
|
let trailing_field_index = field_index(trailing_field_name);
|
|
let leading_field_indices =
|
|
leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
|
|
|
|
// We use `Field` to project the type of the trailing field. This is
|
|
// required to ensure that if the field type uses `Self`, it
|
|
// resolves to the derive target type, not the helper struct we are
|
|
// generating.
|
|
let trailing_field_ty = quote! {
|
|
<#ident #ty_generics as
|
|
#zerocopy_crate::util::macro_util::Field<#trailing_field_index>
|
|
>::Type
|
|
};
|
|
|
|
let methods = make_methods(&parse_quote! {
|
|
<#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
|
|
});
|
|
|
|
let core = ctx.core_path();
|
|
|
|
quote! {
|
|
#(#field_defs)*
|
|
|
|
#(#field_impls)*
|
|
|
|
// SAFETY: This has the same layout as the derive target type,
|
|
// except that it admits uninit bytes. This is ensured by using
|
|
// the same repr as the target type, and by using field types
|
|
// which have the same layout as the target type's fields,
|
|
// except that they admit uninit bytes. We indirect through
|
|
// `Field` to ensure that occurrences of `Self` resolve to
|
|
// `#ty`, not `__ZerocopyKnownLayoutMaybeUninit` (see #2116).
|
|
#repr
|
|
#[doc(hidden)]
|
|
#vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
|
|
#(#core::mem::MaybeUninit<
|
|
<#ident #ty_generics as
|
|
#zerocopy_crate::util::macro_util::Field<#leading_field_indices>
|
|
>::Type
|
|
>,)*
|
|
// NOTE(#2302): We wrap in `ManuallyDrop` here in case the
|
|
// type we're operating on is both generic and
|
|
// `repr(packed)`. In that case, Rust needs to know that the
|
|
// type is *either* `Sized` or has a trivial `Drop`.
|
|
// `ManuallyDrop` has a trivial `Drop`, and so satisfies
|
|
// this requirement.
|
|
#core::mem::ManuallyDrop<
|
|
<#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
|
|
>
|
|
)
|
|
where
|
|
#trailing_field_ty: #zerocopy_crate::KnownLayout,
|
|
#predicates;
|
|
|
|
// SAFETY: We largely defer to the `KnownLayout` implementation
|
|
// on the derive target type (both by using the same tokens, and
|
|
// by deferring to impl via type-level indirection). This is
|
|
// sound, since `__ZerocopyKnownLayoutMaybeUninit` is guaranteed
|
|
// to have the same layout as the derive target type, except
|
|
// that `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes.
|
|
unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
|
|
where
|
|
#trailing_field_ty: #zerocopy_crate::KnownLayout,
|
|
#predicates
|
|
{
|
|
fn only_derive_is_allowed_to_implement_this_trait() {}
|
|
|
|
type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
|
|
|
|
type MaybeUninit = Self;
|
|
|
|
const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
|
|
|
|
#methods
|
|
}
|
|
}
|
|
};
|
|
|
|
Some((SelfBounds::None, inner_extras, Some(outer_extras)))
|
|
}
|
|
|
|
pub(crate) fn derive(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
|
|
// If this is a `repr(C)` struct, then `c_struct_repr` contains the entire
|
|
// `repr` attribute.
|
|
let c_struct_repr = match &ctx.ast.data {
|
|
Data::Struct(..) => {
|
|
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
|
|
if repr.is_c() {
|
|
Some(repr)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
Data::Enum(..) | Data::Union(..) => None,
|
|
};
|
|
|
|
let fields = ctx.ast.data.fields();
|
|
|
|
let (self_bounds, inner_extras, outer_extras) = c_struct_repr
|
|
.as_ref()
|
|
.and_then(|repr| {
|
|
derive_known_layout_for_repr_c_struct(ctx, repr, &fields)
|
|
})
|
|
.unwrap_or_else(|| {
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let core = ctx.core_path();
|
|
|
|
// For enums, unions, and non-`repr(C)` structs, we require that
|
|
// `Self` is sized, and as a result don't need to reason about the
|
|
// internals of the type.
|
|
(
|
|
SelfBounds::SIZED,
|
|
quote!(
|
|
type PointerMetadata = ();
|
|
type MaybeUninit =
|
|
#core::mem::MaybeUninit<Self>;
|
|
|
|
// SAFETY: `LAYOUT` is guaranteed to accurately describe the
|
|
// layout of `Self`, because that is the documented safety
|
|
// contract of `DstLayout::for_type`.
|
|
const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
|
|
|
|
// SAFETY: `.cast` preserves address and provenance.
|
|
//
|
|
// FIXME(#429): Add documentation to `.cast` that promises that
|
|
// it preserves provenance.
|
|
#[inline(always)]
|
|
fn raw_from_ptr_len(bytes: #core::ptr::NonNull<u8>, _meta: ()) -> #core::ptr::NonNull<Self> {
|
|
bytes.cast::<Self>()
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn pointer_to_metadata(_ptr: *mut Self) -> () {}
|
|
),
|
|
None,
|
|
)
|
|
});
|
|
Ok(match &ctx.ast.data {
|
|
Data::Struct(strct) => {
|
|
let require_trait_bound_on_field_types =
|
|
if matches!(self_bounds, SelfBounds::All(&[Trait::Sized])) {
|
|
FieldBounds::None
|
|
} else {
|
|
FieldBounds::TRAILING_SELF
|
|
};
|
|
|
|
// A bound on the trailing field is required, since structs are
|
|
// unsized if their trailing field is unsized. Reflecting the layout
|
|
// of an usized trailing field requires that the field is
|
|
// `KnownLayout`.
|
|
ImplBlockBuilder::new(
|
|
ctx,
|
|
strct,
|
|
Trait::KnownLayout,
|
|
require_trait_bound_on_field_types,
|
|
)
|
|
.self_type_trait_bounds(self_bounds)
|
|
.inner_extras(inner_extras)
|
|
.outer_extras(outer_extras)
|
|
.build()
|
|
}
|
|
Data::Enum(enm) => {
|
|
// A bound on the trailing field is not required, since enums cannot
|
|
// currently be unsized.
|
|
ImplBlockBuilder::new(ctx, enm, Trait::KnownLayout, FieldBounds::None)
|
|
.self_type_trait_bounds(SelfBounds::SIZED)
|
|
.inner_extras(inner_extras)
|
|
.outer_extras(outer_extras)
|
|
.build()
|
|
}
|
|
Data::Union(unn) => {
|
|
// A bound on the trailing field is not required, since unions
|
|
// cannot currently be unsized.
|
|
ImplBlockBuilder::new(ctx, unn, Trait::KnownLayout, FieldBounds::None)
|
|
.self_type_trait_bounds(SelfBounds::SIZED)
|
|
.inner_extras(inner_extras)
|
|
.outer_extras(outer_extras)
|
|
.build()
|
|
}
|
|
})
|
|
}
|