forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a derive proc-macro for the `Zeroable` trait. The macro supports structs where every field implements the `Zeroable` trait. This way `unsafe` implementations can be avoided. The macro is split into two parts: - a proc-macro to parse generics into impl and ty generics, - a declarative macro that expands to the impl block. Suggested-by: Asahi Lina <[email protected]> Signed-off-by: Benno Lossin <[email protected]> Reviewed-by: Gary Guo <[email protected]> Reviewed-by: Martin Rodriguez Reboredo <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ Added `ignore` to the `lib.rs` example and cleaned trivial nit. ] Signed-off-by: Miguel Ojeda <[email protected]>
- Loading branch information
Showing
5 changed files
with
140 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
use crate::helpers::{parse_generics, Generics}; | ||
use proc_macro::{TokenStream, TokenTree}; | ||
|
||
pub(crate) fn derive(input: TokenStream) -> TokenStream { | ||
let ( | ||
Generics { | ||
impl_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! { : ::kernel::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! { ::kernel::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), | ||
} | ||
} | ||
assert_eq!(nested, 0); | ||
if in_generic && !inserted { | ||
new_impl_generics.extend(quote! { : ::kernel::init::Zeroable }); | ||
} | ||
quote! { | ||
::kernel::__derive_zeroable!( | ||
parse_input: | ||
@sig(#(#rest)*), | ||
@impl_generics(#(#new_impl_generics)*), | ||
@ty_generics(#(#ty_generics)*), | ||
@body(#last), | ||
); | ||
} | ||
} |