diff options
| author | Miguel Ojeda <ojeda@kernel.org> | 2026-01-27 14:33:55 +0100 |
|---|---|---|
| committer | Miguel Ojeda <ojeda@kernel.org> | 2026-01-28 00:19:32 +0100 |
| commit | 99ba0fa10de0cc0386ea61e6e5068a78a8394060 (patch) | |
| tree | e3151a4c7ef9ca55b30b0b071295794eca6e8fb8 /rust/pin-init/internal/src/zeroable.rs | |
| parent | 74a862d251ad5f57b58738cfe0192d807b1a0313 (diff) | |
| parent | aeb5ecad5316f6af160993915163367290825b6b (diff) | |
Merge tag 'pin-init-v7.0' of https://github.com/Rust-for-Linux/linux into rust-next
Pull pin-init updates from Benno Lossin:
"Added:
- Implement 'InPlaceWrite' for '&'static mut MaybeUninit<T>'. This
enables users to use external allocation mechanisms such as
'static_cell'.
- Add Gary Guo as a Maintainer.
Changed:
- Rewrote all proc-macros ('[pin_]init!', '#[pin_data]',
'#[pinned_drop]', 'derive([Maybe]Zeroable)'), using 'syn' with
better diagnostics.
- Support tuple structs in 'derive([Maybe]Zeroable)'.
- Support attributes on fields in '[pin_]init!' (such as
'#[cfg(...)]').
- Add a '#[default_error(<type>)]' attribute to '[pin_]init!' to
override the default error (when no '? Error' is specified).
- Support packed structs in '[pin_]init!' with
'#[disable_initialized_field_access]'.
Removed:
- Remove 'try_[pin_]init!' in favor of merging their feature
with '[pin_]init!'. Update the kernel's own 'try_[pin_]init!'
macros to use the 'default_error' attribute.
Fixed:
- Correct 'T: Sized' bounds to 'T: ?Sized' in the generated
'PinnedDrop' check by '#[pin_data]'."
* tag 'pin-init-v7.0' of https://github.com/Rust-for-Linux/linux:
rust: pin-init: Implement `InPlaceWrite<T>` for `&'static mut MaybeUninit<T>`
MAINTAINERS: add Gary Guo to pin-init
rust: pin-init: internal: init: simplify Zeroable safety check
rust: pin-init: internal: init: add escape hatch for referencing initialized fields
rust: pin-init: internal: init: add support for attributes on initializer fields
rust: init: use `#[default_error(err)]` for the initializer macros
rust: pin-init: add `#[default_error(<type>)]` attribute to initializer macros
rust: pin-init: rewrite the initializer macros using `syn`
rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro
rust: pin-init: rewrite `#[pin_data]` using `syn`
rust: pin-init: rewrite the `#[pinned_drop]` attribute macro using `syn`
rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn`
rust: pin-init: internal: add utility API for syn error handling
rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds
rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests
rust: pin-init: remove `try_` versions of the initializer macros
Diffstat (limited to 'rust/pin-init/internal/src/zeroable.rs')
| -rw-r--r-- | rust/pin-init/internal/src/zeroable.rs | 157 |
1 files changed, 67 insertions, 90 deletions
diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs index e0ed3998445c..05683319b0f7 100644 --- a/rust/pin-init/internal/src/zeroable.rs +++ b/rust/pin-init/internal/src/zeroable.rs @@ -1,101 +1,78 @@ // SPDX-License-Identifier: GPL-2.0 -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_quote, Data, DeriveInput, Field, Fields}; -use crate::helpers::{parse_generics, Generics}; -use proc_macro::{TokenStream, TokenTree}; +use crate::{diagnostics::ErrorGuaranteed, DiagCtxt}; -pub(crate) fn parse_zeroable_derive_input( - input: TokenStream, -) -> ( - Vec<TokenTree>, - Vec<TokenTree>, - Vec<TokenTree>, - Option<TokenTree>, -) { - let ( - Generics { - impl_generics, - decl_generics: _, - ty_generics, - }, - mut rest, - ) = parse_generics(input); - // This should be the body of the struct `{...}`. - let last = rest.pop(); - // Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`. - let mut new_impl_generics = Vec::with_capacity(impl_generics.len()); - // Are we inside of a generic where we want to add `Zeroable`? - let mut in_generic = !impl_generics.is_empty(); - // Have we already inserted `Zeroable`? - let mut inserted = false; - // Level of `<>` nestings. - let mut nested = 0; - for tt in impl_generics { - match &tt { - // If we find a `,`, then we have finished a generic/constant/lifetime parameter. - TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => { - if in_generic && !inserted { - new_impl_generics.extend(quote! { : ::pin_init::Zeroable }); - } - in_generic = true; - inserted = false; - new_impl_generics.push(tt); - } - // If we find `'`, then we are entering a lifetime. - TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => { - in_generic = false; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => { - new_impl_generics.push(tt); - if in_generic { - new_impl_generics.extend(quote! { ::pin_init::Zeroable + }); - inserted = true; - } - } - TokenTree::Punct(p) if p.as_char() == '<' => { - nested += 1; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if p.as_char() == '>' => { - assert!(nested > 0); - nested -= 1; - new_impl_generics.push(tt); - } - _ => new_impl_generics.push(tt), +pub(crate) fn derive( + input: DeriveInput, + dcx: &mut DiagCtxt, +) -> Result<TokenStream, ErrorGuaranteed> { + let fields = match input.data { + Data::Struct(data_struct) => data_struct.fields, + Data::Union(data_union) => Fields::Named(data_union.fields), + Data::Enum(data_enum) => { + return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum")); } + }; + let name = input.ident; + let mut generics = input.generics; + for param in generics.type_params_mut() { + param.bounds.insert(0, parse_quote!(::pin_init::Zeroable)); } - assert_eq!(nested, 0); - if in_generic && !inserted { - new_impl_generics.extend(quote! { : ::pin_init::Zeroable }); - } - (rest, new_impl_generics, ty_generics, last) + let (impl_gen, ty_gen, whr) = generics.split_for_impl(); + let field_type = fields.iter().map(|field| &field.ty); + Ok(quote! { + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen + #whr + {} + const _: () = { + fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {} + fn ensure_zeroable #impl_gen () + #whr + { + #( + assert_zeroable::<#field_type>(); + )* + } + }; + }) } -pub(crate) fn derive(input: TokenStream) -> TokenStream { - let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); - quote! { - ::pin_init::__derive_zeroable!( - parse_input: - @sig(#(#rest)*), - @impl_generics(#(#new_impl_generics)*), - @ty_generics(#(#ty_generics)*), - @body(#last), - ); +pub(crate) fn maybe_derive( + input: DeriveInput, + dcx: &mut DiagCtxt, +) -> Result<TokenStream, ErrorGuaranteed> { + let fields = match input.data { + Data::Struct(data_struct) => data_struct.fields, + Data::Union(data_union) => Fields::Named(data_union.fields), + Data::Enum(data_enum) => { + return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum")); + } + }; + let name = input.ident; + let mut generics = input.generics; + for param in generics.type_params_mut() { + param.bounds.insert(0, parse_quote!(::pin_init::Zeroable)); } -} - -pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream { - let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); - quote! { - ::pin_init::__maybe_derive_zeroable!( - parse_input: - @sig(#(#rest)*), - @impl_generics(#(#new_impl_generics)*), - @ty_generics(#(#ty_generics)*), - @body(#last), - ); + for Field { ty, .. } in fields { + generics + .make_where_clause() + .predicates + // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` + // feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>. + .push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable)); } + let (impl_gen, ty_gen, whr) = generics.split_for_impl(); + Ok(quote! { + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen + #whr + {} + }) } |
