From fc6c6baa1f40ded13e539d0c1a17bcefc00abad9 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Apr 2023 12:25:51 +0000 Subject: rust: init: add initialization macros Add the following initializer macros: - `#[pin_data]` to annotate structurally pinned fields of structs, needed for `pin_init!` and `try_pin_init!` to select the correct initializer of fields. - `pin_init!` create a pin-initializer for a struct with the `Infallible` error type. - `try_pin_init!` create a pin-initializer for a struct with a custom error type (`kernel::error::Error` is the default). - `init!` create an in-place-initializer for a struct with the `Infallible` error type. - `try_init!` create an in-place-initializer for a struct with a custom error type (`kernel::error::Error` is the default). Also add their needed internal helper traits and structs. Co-developed-by: Gary Guo Signed-off-by: Gary Guo Signed-off-by: Benno Lossin Reviewed-by: Alice Ryhl Reviewed-by: Andreas Hindborg Link: https://lore.kernel.org/r/20230408122429.1103522-8-y86-dev@protonmail.com [ Fixed three typos. ] Signed-off-by: Miguel Ojeda --- rust/macros/pin_data.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 rust/macros/pin_data.rs (limited to 'rust/macros/pin_data.rs') diff --git a/rust/macros/pin_data.rs b/rust/macros/pin_data.rs new file mode 100644 index 000000000000..954149d77181 --- /dev/null +++ b/rust/macros/pin_data.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use proc_macro::{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!`. + // + // In here we only collect the generics, since parsing them in declarative macros is very + // elaborate. We also do not need to analyse their structure, we only need to collect them. + + // `impl_generics`, the declared generics with their bounds. + let mut impl_generics = vec![]; + // Only the names of the generics, without any bounds. + let mut ty_generics = vec![]; + // Tokens not related to the generics e.g. the `impl` token. + let mut rest = vec![]; + // The current level of `<`. + let mut nesting = 0; + let mut toks = input.into_iter(); + // If we are at the beginning of a generic parameter. + let mut at_start = true; + for tt in &mut toks { + match tt.clone() { + TokenTree::Punct(p) if p.as_char() == '<' => { + if nesting >= 1 { + impl_generics.push(tt); + } + nesting += 1; + } + TokenTree::Punct(p) if p.as_char() == '>' => { + if nesting == 0 { + break; + } else { + nesting -= 1; + if nesting >= 1 { + impl_generics.push(tt); + } + if nesting == 0 { + break; + } + } + } + tt => { + if nesting == 1 { + match &tt { + TokenTree::Ident(i) if i.to_string() == "const" => {} + TokenTree::Ident(_) if at_start => { + ty_generics.push(tt.clone()); + ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + at_start = false; + } + TokenTree::Punct(p) if p.as_char() == ',' => at_start = true, + TokenTree::Punct(p) if p.as_char() == '\'' && at_start => { + ty_generics.push(tt.clone()); + } + _ => {} + } + } + if nesting >= 1 { + impl_generics.push(tt); + } else if nesting == 0 { + rest.push(tt); + } + } + } + } + rest.extend(toks); + // This should be the body of the struct `{...}`. + let last = rest.pop(); + quote!(::kernel::__pin_data! { + parse_input: + @args(#args), + @sig(#(#rest)*), + @impl_generics(#(#impl_generics)*), + @ty_generics(#(#ty_generics)*), + @body(#last), + }) +} -- cgit v1.2.3