mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Add support for generic parameters defaults in `#[pin_data]` by using
the newly introduced `decl_generics` instead of the `impl_generics`.
Before this would not compile:
    #[pin_data]
    struct Foo<const N: usize = 0> {
        // ...
    }
because it would be expanded to this:
    struct Foo<const N: usize = 0> {
        // ...
    }
    const _: () = {
        struct __ThePinData<const N: usize = 0> {
            __phantom: ::core::marker::PhantomData<fn(Foo<N>) -> Foo<N>>,
        }
        impl<const N: usize = 0> ::core::clone::Clone for __ThePinData<N> {
            fn clone(&self) -> Self {
                *self
            }
        }
        // [...] rest of expansion omitted
    };
The problem is with the `impl<const N: usize = 0>`, since that is
invalid Rust syntax. It should not mention the default value at all,
since default values only make sense on type definitions.
The new `impl_generics` do not contain the default values, thus
generating correct Rust code.
This is used by the next commit that puts `#[pin_data]` on
`kernel::workqueue::Work`.
Signed-off-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Tested-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20240309155243.482334-2-benno.lossin@proton.me
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
		
	
			
		
			
				
	
	
		
			129 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
// SPDX-License-Identifier: Apache-2.0 OR MIT
 | 
						|
 | 
						|
use crate::helpers::{parse_generics, Generics};
 | 
						|
use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree};
 | 
						|
 | 
						|
pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
 | 
						|
    // This proc-macro only does some pre-parsing and then delegates the actual parsing to
 | 
						|
    // `kernel::__pin_data!`.
 | 
						|
 | 
						|
    let (
 | 
						|
        Generics {
 | 
						|
            impl_generics,
 | 
						|
            decl_generics,
 | 
						|
            ty_generics,
 | 
						|
        },
 | 
						|
        rest,
 | 
						|
    ) = parse_generics(input);
 | 
						|
    // The struct definition might contain the `Self` type. Since `__pin_data!` will define a new
 | 
						|
    // type with the same generics and bounds, this poses a problem, since `Self` will refer to the
 | 
						|
    // new type as opposed to this struct definition. Therefore we have to replace `Self` with the
 | 
						|
    // concrete name.
 | 
						|
 | 
						|
    // Errors that occur when replacing `Self` with `struct_name`.
 | 
						|
    let mut errs = TokenStream::new();
 | 
						|
    // The name of the struct with ty_generics.
 | 
						|
    let struct_name = rest
 | 
						|
        .iter()
 | 
						|
        .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i.to_string() == "struct"))
 | 
						|
        .nth(1)
 | 
						|
        .and_then(|tt| match tt {
 | 
						|
            TokenTree::Ident(_) => {
 | 
						|
                let tt = tt.clone();
 | 
						|
                let mut res = vec![tt];
 | 
						|
                if !ty_generics.is_empty() {
 | 
						|
                    // We add this, so it is maximally compatible with e.g. `Self::CONST` which
 | 
						|
                    // will be replaced by `StructName::<$generics>::CONST`.
 | 
						|
                    res.push(TokenTree::Punct(Punct::new(':', Spacing::Joint)));
 | 
						|
                    res.push(TokenTree::Punct(Punct::new(':', Spacing::Alone)));
 | 
						|
                    res.push(TokenTree::Punct(Punct::new('<', Spacing::Alone)));
 | 
						|
                    res.extend(ty_generics.iter().cloned());
 | 
						|
                    res.push(TokenTree::Punct(Punct::new('>', Spacing::Alone)));
 | 
						|
                }
 | 
						|
                Some(res)
 | 
						|
            }
 | 
						|
            _ => None,
 | 
						|
        })
 | 
						|
        .unwrap_or_else(|| {
 | 
						|
            // If we did not find the name of the struct then we will use `Self` as the replacement
 | 
						|
            // and add a compile error to ensure it does not compile.
 | 
						|
            errs.extend(
 | 
						|
                "::core::compile_error!(\"Could not locate type name.\");"
 | 
						|
                    .parse::<TokenStream>()
 | 
						|
                    .unwrap(),
 | 
						|
            );
 | 
						|
            "Self".parse::<TokenStream>().unwrap().into_iter().collect()
 | 
						|
        });
 | 
						|
    let impl_generics = impl_generics
 | 
						|
        .into_iter()
 | 
						|
        .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &mut errs))
 | 
						|
        .collect::<Vec<_>>();
 | 
						|
    let mut rest = rest
 | 
						|
        .into_iter()
 | 
						|
        .flat_map(|tt| {
 | 
						|
            // We ignore top level `struct` tokens, since they would emit a compile error.
 | 
						|
            if matches!(&tt, TokenTree::Ident(i) if i.to_string() == "struct") {
 | 
						|
                vec![tt]
 | 
						|
            } else {
 | 
						|
                replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)
 | 
						|
            }
 | 
						|
        })
 | 
						|
        .collect::<Vec<_>>();
 | 
						|
    // This should be the body of the struct `{...}`.
 | 
						|
    let last = rest.pop();
 | 
						|
    let mut quoted = quote!(::kernel::__pin_data! {
 | 
						|
        parse_input:
 | 
						|
        @args(#args),
 | 
						|
        @sig(#(#rest)*),
 | 
						|
        @impl_generics(#(#impl_generics)*),
 | 
						|
        @ty_generics(#(#ty_generics)*),
 | 
						|
        @decl_generics(#(#decl_generics)*),
 | 
						|
        @body(#last),
 | 
						|
    });
 | 
						|
    quoted.extend(errs);
 | 
						|
    quoted
 | 
						|
}
 | 
						|
 | 
						|
/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `struct` `union` and `impl`
 | 
						|
/// keywords.
 | 
						|
///
 | 
						|
/// The error is appended to `errs` to allow normal parsing to continue.
 | 
						|
fn replace_self_and_deny_type_defs(
 | 
						|
    struct_name: &Vec<TokenTree>,
 | 
						|
    tt: TokenTree,
 | 
						|
    errs: &mut TokenStream,
 | 
						|
) -> Vec<TokenTree> {
 | 
						|
    match tt {
 | 
						|
        TokenTree::Ident(ref i)
 | 
						|
            if i.to_string() == "enum"
 | 
						|
                || i.to_string() == "trait"
 | 
						|
                || i.to_string() == "struct"
 | 
						|
                || i.to_string() == "union"
 | 
						|
                || i.to_string() == "impl" =>
 | 
						|
        {
 | 
						|
            errs.extend(
 | 
						|
                format!(
 | 
						|
                    "::core::compile_error!(\"Cannot use `{i}` inside of struct definition with \
 | 
						|
                        `#[pin_data]`.\");"
 | 
						|
                )
 | 
						|
                .parse::<TokenStream>()
 | 
						|
                .unwrap()
 | 
						|
                .into_iter()
 | 
						|
                .map(|mut tok| {
 | 
						|
                    tok.set_span(tt.span());
 | 
						|
                    tok
 | 
						|
                }),
 | 
						|
            );
 | 
						|
            vec![tt]
 | 
						|
        }
 | 
						|
        TokenTree::Ident(i) if i.to_string() == "Self" => struct_name.clone(),
 | 
						|
        TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt],
 | 
						|
        TokenTree::Group(g) => vec![TokenTree::Group(Group::new(
 | 
						|
            g.delimiter(),
 | 
						|
            g.stream()
 | 
						|
                .into_iter()
 | 
						|
                .flat_map(|tt| replace_self_and_deny_type_defs(struct_name, tt, errs))
 | 
						|
                .collect(),
 | 
						|
        ))],
 | 
						|
    }
 | 
						|
}
 |