Skip to content

Commit 5871bd9

Browse files
committed
internal: add syn version of the Zeroable derive macro
Implement the `Zeroable` derive macro using syn to simplify parsing by not going through an additional declarative macro. The syn version is only enabled in the user-space version and disabled in the kernel until syn becomes available there. Signed-off-by: Benno Lossin <benno.lossin@proton.me>
1 parent 4740111 commit 5871bd9

5 files changed

Lines changed: 132 additions & 7 deletions

File tree

internal/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ extern crate quote;
3131
mod helpers;
3232
mod pin_data;
3333
mod pinned_drop;
34+
#[cfg(kernel)]
35+
mod zeroable;
36+
37+
#[cfg(not(kernel))]
38+
#[path = "syn_zeroable.rs"]
3439
mod zeroable;
3540

3641
#[proc_macro_attribute]

internal/src/syn_zeroable.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
3+
use proc_macro2::TokenStream;
4+
use quote::quote;
5+
use syn::{
6+
parse_macro_input, parse_quote, Data, DataStruct, DataUnion, DeriveInput, Error, GenericParam,
7+
Generics, Ident, Result, Type, TypeParam, WherePredicate,
8+
};
9+
10+
pub(crate) fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
11+
let raw = input.clone().into();
12+
match do_derive(&mut parse_macro_input!(input as DeriveInput), raw) {
13+
Ok((generics, ident, field_ty)) => expand(generics, ident, field_ty),
14+
Err(e) => e.into_compile_error(),
15+
}
16+
.into()
17+
}
18+
19+
pub(crate) fn maybe_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
20+
let raw = input.clone().into();
21+
match do_derive(&mut parse_macro_input!(input as DeriveInput), raw) {
22+
Ok((generics, ident, field_ty)) => expand_maybe(generics, ident, field_ty),
23+
Err(e) => e.into_compile_error(),
24+
}
25+
.into()
26+
}
27+
28+
fn do_derive(
29+
DeriveInput {
30+
ident,
31+
ref mut generics,
32+
data,
33+
..
34+
}: &mut DeriveInput,
35+
raw_input: TokenStream,
36+
) -> Result<(&mut Generics, &Ident, impl Iterator<Item = Type>)> {
37+
let field_ty = match data {
38+
Data::Struct(DataStruct { fields, .. }) => {
39+
fields.iter().map(|f| f.ty.clone()).collect::<Vec<_>>()
40+
}
41+
Data::Union(DataUnion { fields, .. }) => fields
42+
.named
43+
.iter()
44+
.map(|f| f.ty.clone())
45+
.collect::<Vec<_>>(),
46+
_ => {
47+
return Err(Error::new_spanned(
48+
raw_input,
49+
"`Zeroable` can only be derived for structs and unions.",
50+
))
51+
}
52+
};
53+
let zeroable_bounds = generics
54+
.params
55+
.iter()
56+
.filter_map(|p| match p {
57+
GenericParam::Type(TypeParam { ident, .. }) => {
58+
Some(parse_quote!(#ident: ::pin_init::Zeroable))
59+
}
60+
_ => None,
61+
})
62+
.collect::<Vec<WherePredicate>>();
63+
generics
64+
.make_where_clause()
65+
.predicates
66+
.extend(zeroable_bounds);
67+
Ok((generics, ident, field_ty.into_iter()))
68+
}
69+
70+
fn expand(
71+
generics: &mut Generics,
72+
ident: &Ident,
73+
field_ty: impl Iterator<Item = Type>,
74+
) -> TokenStream {
75+
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
76+
quote! {
77+
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
78+
#[automatically_derived]
79+
unsafe impl #impl_generics ::pin_init::Zeroable for #ident #ty_generics
80+
#whr
81+
{}
82+
const _: () = {
83+
fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {}
84+
fn ensure_zeroable #impl_generics ()
85+
#whr
86+
{
87+
#(assert_zeroable::<#field_ty>();)*
88+
}
89+
};
90+
}
91+
}
92+
93+
fn expand_maybe(
94+
generics: &mut Generics,
95+
ident: &Ident,
96+
field_ty: impl Iterator<Item = Type>,
97+
) -> TokenStream {
98+
generics
99+
.make_where_clause()
100+
.predicates
101+
.extend(field_ty.map(|bounded_ty| -> WherePredicate {
102+
parse_quote!(
103+
// the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
104+
// feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
105+
#bounded_ty: for<'__dummy> ::pin_init::Zeroable
106+
)
107+
}));
108+
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
109+
quote! {
110+
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
111+
#[automatically_derived]
112+
unsafe impl #impl_generics ::pin_init::Zeroable for #ident #ty_generics
113+
#whr
114+
{}
115+
}
116+
}

tests/ui/compile-fail/zeroable/not_all_fields_zeroable.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ note: required by a bound in `assert_zeroable`
99
|
1010
4 | #[derive(Zeroable)]
1111
| ^^^^^^^^ required by this bound in `assert_zeroable`
12-
= note: this error originates in the macro `::pin_init::__derive_zeroable` which comes from the expansion of the derive macro `Zeroable` (in Nightly builds, run with -Z macro-backtrace for more info)
12+
= note: this error originates in the derive macro `Zeroable` (in Nightly builds, run with -Z macro-backtrace for more info)
1313
help: consider removing the leading `&`-reference
1414
|
1515
7 - b: &'static Foo,

tests/ui/expand/zeroable.expanded.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@ struct WithGenerics<'a, T, U: Trait> {
2828
u: &'a U,
2929
}
3030
#[automatically_derived]
31-
unsafe impl<
32-
'a,
31+
unsafe impl<'a, T, U: Trait> ::pin_init::Zeroable for WithGenerics<'a, T, U>
32+
where
3333
T: ::pin_init::Zeroable,
34-
U: ::pin_init::Zeroable + Trait,
35-
> ::pin_init::Zeroable for WithGenerics<'a, T, U> {}
34+
U: ::pin_init::Zeroable,
35+
{}
3636
const _: () = {
3737
fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {}
38-
fn ensure_zeroable<'a, T: ::pin_init::Zeroable, U: ::pin_init::Zeroable + Trait>() {
38+
fn ensure_zeroable<'a, T, U: Trait>()
39+
where
40+
T: ::pin_init::Zeroable,
41+
U: ::pin_init::Zeroable,
42+
{
3943
assert_zeroable::<T>();
4044
assert_zeroable::<&'a U>();
4145
}

tests/zeroing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const MARKS: usize = 64;
1111
pub struct Foo {
1212
buf: [u8; 1024 * 1024],
1313
marks: [*mut u8; MARKS],
14-
pos: usize,
14+
_pos: usize,
1515
#[pin]
1616
_pin: PhantomPinned,
1717
}

0 commit comments

Comments
 (0)