|
1 |
| -// SPDX-License-Identifier: GPL-2.0 |
| 1 | +// SPDX-License-Identifier: Apache-2.0 OR MIT |
2 | 2 |
|
3 |
| -use crate::helpers::{parse_generics, Generics}; |
4 |
| -use proc_macro2::{TokenStream, TokenTree}; |
| 3 | +use proc_macro2::TokenStream; |
5 | 4 | use quote::quote;
|
| 5 | +use syn::{ |
| 6 | + parse_quote, Data, DataStruct, DeriveInput, Error, GenericParam, Result, TypeParam, |
| 7 | + WherePredicate, |
| 8 | +}; |
6 | 9 |
|
7 |
| -pub(crate) fn derive(input: TokenStream) -> TokenStream { |
8 |
| - let ( |
9 |
| - Generics { |
10 |
| - impl_generics, |
11 |
| - decl_generics: _, |
12 |
| - ty_generics, |
13 |
| - }, |
14 |
| - mut rest, |
15 |
| - ) = parse_generics(input); |
16 |
| - // This should be the body of the struct `{...}`. |
17 |
| - let last = rest.pop(); |
18 |
| - // Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`. |
19 |
| - let mut new_impl_generics = Vec::with_capacity(impl_generics.len()); |
20 |
| - // Are we inside of a generic where we want to add `Zeroable`? |
21 |
| - let mut in_generic = !impl_generics.is_empty(); |
22 |
| - // Have we already inserted `Zeroable`? |
23 |
| - let mut inserted = false; |
24 |
| - // Level of `<>` nestings. |
25 |
| - let mut nested = 0; |
26 |
| - for tt in impl_generics { |
27 |
| - match &tt { |
28 |
| - // If we find a `,`, then we have finished a generic/constant/lifetime parameter. |
29 |
| - TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => { |
30 |
| - if in_generic && !inserted { |
31 |
| - new_impl_generics.extend(quote! { : ::kernel::init::Zeroable }); |
32 |
| - } |
33 |
| - in_generic = true; |
34 |
| - inserted = false; |
35 |
| - new_impl_generics.push(tt); |
| 10 | +pub(crate) fn derive( |
| 11 | + DeriveInput { |
| 12 | + ident, |
| 13 | + mut generics, |
| 14 | + data, |
| 15 | + .. |
| 16 | + }: DeriveInput, |
| 17 | + raw_input: TokenStream, |
| 18 | +) -> Result<TokenStream> { |
| 19 | + let zeroable_bounds = generics |
| 20 | + .params |
| 21 | + .iter() |
| 22 | + .filter_map(|p| match p { |
| 23 | + GenericParam::Type(t) => Some(t), |
| 24 | + _ => None, |
| 25 | + }) |
| 26 | + .map(|TypeParam { ident, .. }| { |
| 27 | + parse_quote! { #ident: ::kernel::init::Zeroable, } |
| 28 | + }) |
| 29 | + .collect::<Vec<WherePredicate>>(); |
| 30 | + generics |
| 31 | + .make_where_clause() |
| 32 | + .predicates |
| 33 | + .extend(zeroable_bounds); |
| 34 | + let (impl_g, type_g, whr) = generics.split_for_impl(); |
| 35 | + let Data::Struct(DataStruct { fields, .. }) = data else { |
| 36 | + return Err(Error::new_spanned( |
| 37 | + raw_input, |
| 38 | + "`Zeroable` can only be derived for structs.", |
| 39 | + )); |
| 40 | + }; |
| 41 | + let field_ty = fields.iter().map(|f| &f.ty); |
| 42 | + Ok(quote! { |
| 43 | + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. |
| 44 | + #[automatically_derived] |
| 45 | + unsafe impl #impl_g ::kernel::init::Zeroable for #ident #type_g |
| 46 | + #whr |
| 47 | + {} |
| 48 | + const _: () = { |
| 49 | + fn assert_zeroable<T: ?::core::marker::Sized + ::kernel::init::Zeroable>() {} |
| 50 | + fn ensure_zeroable #impl_g () |
| 51 | + #whr |
| 52 | + { |
| 53 | + #(assert_zeroable::<#field_ty>();)* |
36 | 54 | }
|
37 |
| - // If we find `'`, then we are entering a lifetime. |
38 |
| - TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => { |
39 |
| - in_generic = false; |
40 |
| - new_impl_generics.push(tt); |
41 |
| - } |
42 |
| - TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => { |
43 |
| - new_impl_generics.push(tt); |
44 |
| - if in_generic { |
45 |
| - new_impl_generics.extend(quote! { ::kernel::init::Zeroable + }); |
46 |
| - inserted = true; |
47 |
| - } |
48 |
| - } |
49 |
| - TokenTree::Punct(p) if p.as_char() == '<' => { |
50 |
| - nested += 1; |
51 |
| - new_impl_generics.push(tt); |
52 |
| - } |
53 |
| - TokenTree::Punct(p) if p.as_char() == '>' => { |
54 |
| - assert!(nested > 0); |
55 |
| - nested -= 1; |
56 |
| - new_impl_generics.push(tt); |
57 |
| - } |
58 |
| - _ => new_impl_generics.push(tt), |
59 |
| - } |
60 |
| - } |
61 |
| - assert_eq!(nested, 0); |
62 |
| - if in_generic && !inserted { |
63 |
| - new_impl_generics.extend(quote! { : ::kernel::init::Zeroable }); |
64 |
| - } |
65 |
| - quote! { |
66 |
| - ::kernel::__derive_zeroable!( |
67 |
| - parse_input: |
68 |
| - @sig(#(#rest)*), |
69 |
| - @impl_generics(#(#new_impl_generics)*), |
70 |
| - @ty_generics(#(#ty_generics)*), |
71 |
| - @body(#last), |
72 |
| - ); |
73 |
| - } |
| 55 | + }; |
| 56 | + }) |
74 | 57 | }
|
0 commit comments