Skip to content

Commit 7304864

Browse files
committed
rust: pin-init: rewrite derive(Zeroable) and derive(MaybeZeroable) using syn
Rewrite the two derive macros for `Zeroable` using `syn`. One positive side effect of this change is that tuple structs are now supported by them. Additionally, syntax errors and the error emitted when trying to use one of the derive macros on an `enum` are improved. Otherwise no functional changes intended. For example: #[derive(Zeroable)] enum Num { A(u32), B(i32), } Produced this error before this commit: error: no rules expected keyword `enum` --> tests/ui/compile-fail/zeroable/enum.rs:5:1 | 5 | enum Num { | ^^^^ no rules expected this token in macro call | note: while trying to match keyword `struct` --> src/macros.rs | | $vis:vis struct $name:ident | ^^^^^^ Now the error is: error: cannot derive `Zeroable` for an enum --> tests/ui/compile-fail/zeroable/enum.rs:5:1 | 5 | enum Num { | ^^^^ error: cannot derive `Zeroable` for an enum Signed-off-by: Benno Lossin <lossin@kernel.org>
1 parent 3f51000 commit 7304864

3 files changed

Lines changed: 69 additions & 216 deletions

File tree

rust/pin-init/internal/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#![allow(missing_docs)]
1212

1313
use proc_macro::TokenStream;
14+
use syn::parse_macro_input;
1415

1516
mod helpers;
1617
mod pin_data;
@@ -29,19 +30,18 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
2930

3031
#[proc_macro_derive(Zeroable)]
3132
pub fn derive_zeroable(input: TokenStream) -> TokenStream {
32-
zeroable::derive(input.into()).into()
33+
ok_or_compile_error(zeroable::derive(parse_macro_input!(input)))
3334
}
3435

3536
#[proc_macro_derive(MaybeZeroable)]
3637
pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
37-
zeroable::maybe_derive(input.into()).into()
38+
ok_or_compile_error(zeroable::maybe_derive(parse_macro_input!(input)))
3839
}
3940

40-
#[expect(dead_code)]
4141
fn ok_or_compile_error(res: syn::Result<proc_macro2::TokenStream>) -> TokenStream {
4242
match res {
4343
Ok(stream) => stream,
44-
Err(error) => error.into_compile_error(),
44+
Err(err) => err.into_compile_error(),
4545
}
4646
.into()
4747
}
Lines changed: 65 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,76 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3-
use crate::helpers::{parse_generics, Generics};
4-
use proc_macro2::{TokenStream, TokenTree};
3+
use proc_macro2::TokenStream;
54
use quote::quote;
5+
use syn::{parse_quote, Data, DeriveInput, Error, Field, Fields, Result};
66

7-
pub(crate) fn parse_zeroable_derive_input(
8-
input: TokenStream,
9-
) -> (
10-
Vec<TokenTree>,
11-
Vec<TokenTree>,
12-
Vec<TokenTree>,
13-
Option<TokenTree>,
14-
) {
15-
let (
16-
Generics {
17-
impl_generics,
18-
decl_generics: _,
19-
ty_generics,
20-
},
21-
mut rest,
22-
) = parse_generics(input);
23-
// This should be the body of the struct `{...}`.
24-
let last = rest.pop();
25-
// Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`.
26-
let mut new_impl_generics = Vec::with_capacity(impl_generics.len());
27-
// Are we inside of a generic where we want to add `Zeroable`?
28-
let mut in_generic = !impl_generics.is_empty();
29-
// Have we already inserted `Zeroable`?
30-
let mut inserted = false;
31-
// Level of `<>` nestings.
32-
let mut nested = 0;
33-
for tt in impl_generics {
34-
match &tt {
35-
// If we find a `,`, then we have finished a generic/constant/lifetime parameter.
36-
TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => {
37-
if in_generic && !inserted {
38-
new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
39-
}
40-
in_generic = true;
41-
inserted = false;
42-
new_impl_generics.push(tt);
43-
}
44-
// If we find `'`, then we are entering a lifetime.
45-
TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => {
46-
in_generic = false;
47-
new_impl_generics.push(tt);
48-
}
49-
TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => {
50-
new_impl_generics.push(tt);
51-
if in_generic {
52-
new_impl_generics.extend(quote! { ::pin_init::Zeroable + });
53-
inserted = true;
54-
}
55-
}
56-
TokenTree::Punct(p) if p.as_char() == '<' => {
57-
nested += 1;
58-
new_impl_generics.push(tt);
59-
}
60-
TokenTree::Punct(p) if p.as_char() == '>' => {
61-
assert!(nested > 0);
62-
nested -= 1;
63-
new_impl_generics.push(tt);
64-
}
65-
_ => new_impl_generics.push(tt),
7+
pub(crate) fn derive(input: DeriveInput) -> Result<TokenStream> {
8+
let fields = match input.data {
9+
Data::Struct(data_struct) => data_struct.fields,
10+
Data::Union(data_union) => Fields::Named(data_union.fields),
11+
Data::Enum(data_enum) => {
12+
return Err(Error::new_spanned(
13+
data_enum.enum_token,
14+
"cannot derive `Zeroable` for an enum",
15+
));
6616
}
17+
};
18+
let name = input.ident;
19+
let mut generics = input.generics;
20+
for param in generics.type_params_mut() {
21+
param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
6722
}
68-
assert_eq!(nested, 0);
69-
if in_generic && !inserted {
70-
new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
71-
}
72-
(rest, new_impl_generics, ty_generics, last)
23+
let (impl_gen, ty_gen, whr) = generics.split_for_impl();
24+
let field_type = fields.iter().map(|field| &field.ty);
25+
Ok(quote! {
26+
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
27+
#[automatically_derived]
28+
unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
29+
#whr
30+
{}
31+
const _: () = {
32+
fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {}
33+
fn ensure_zeroable #impl_gen ()
34+
#whr
35+
{
36+
#(
37+
assert_zeroable::<#field_type>();
38+
)*
39+
}
40+
};
41+
})
7342
}
7443

75-
pub(crate) fn derive(input: TokenStream) -> TokenStream {
76-
let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
77-
quote! {
78-
::pin_init::__derive_zeroable!(
79-
parse_input:
80-
@sig(#(#rest)*),
81-
@impl_generics(#(#new_impl_generics)*),
82-
@ty_generics(#(#ty_generics)*),
83-
@body(#last),
84-
);
44+
pub(crate) fn maybe_derive(input: DeriveInput) -> Result<TokenStream> {
45+
let fields = match input.data {
46+
Data::Struct(data_struct) => data_struct.fields,
47+
Data::Union(data_union) => Fields::Named(data_union.fields),
48+
Data::Enum(data_enum) => {
49+
return Err(Error::new_spanned(
50+
data_enum.enum_token,
51+
"cannot derive `Zeroable` for an enum",
52+
));
53+
}
54+
};
55+
let name = input.ident;
56+
let mut generics = input.generics;
57+
for param in generics.type_params_mut() {
58+
param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
8559
}
86-
}
87-
88-
pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream {
89-
let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
90-
quote! {
91-
::pin_init::__maybe_derive_zeroable!(
92-
parse_input:
93-
@sig(#(#rest)*),
94-
@impl_generics(#(#new_impl_generics)*),
95-
@ty_generics(#(#ty_generics)*),
96-
@body(#last),
97-
);
60+
for Field { ty, .. } in fields {
61+
generics
62+
.make_where_clause()
63+
.predicates
64+
// the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
65+
// feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
66+
.push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable));
9867
}
68+
let (impl_gen, ty_gen, whr) = generics.split_for_impl();
69+
Ok(quote! {
70+
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
71+
#[automatically_derived]
72+
unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
73+
#whr
74+
{}
75+
})
9976
}

rust/pin-init/src/macros.rs

Lines changed: 0 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,127 +1551,3 @@ macro_rules! __init_internal {
15511551
);
15521552
};
15531553
}
1554-
1555-
#[doc(hidden)]
1556-
#[macro_export]
1557-
macro_rules! __derive_zeroable {
1558-
(parse_input:
1559-
@sig(
1560-
$(#[$($struct_attr:tt)*])*
1561-
$vis:vis struct $name:ident
1562-
$(where $($whr:tt)*)?
1563-
),
1564-
@impl_generics($($impl_generics:tt)*),
1565-
@ty_generics($($ty_generics:tt)*),
1566-
@body({
1567-
$(
1568-
$(#[$($field_attr:tt)*])*
1569-
$field_vis:vis $field:ident : $field_ty:ty
1570-
),* $(,)?
1571-
}),
1572-
) => {
1573-
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
1574-
#[automatically_derived]
1575-
unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
1576-
where
1577-
$($($whr)*)?
1578-
{}
1579-
const _: () = {
1580-
fn assert_zeroable<T: ?::core::marker::Sized + $crate::Zeroable>() {}
1581-
fn ensure_zeroable<$($impl_generics)*>()
1582-
where $($($whr)*)?
1583-
{
1584-
$(assert_zeroable::<$field_ty>();)*
1585-
}
1586-
};
1587-
};
1588-
(parse_input:
1589-
@sig(
1590-
$(#[$($struct_attr:tt)*])*
1591-
$vis:vis union $name:ident
1592-
$(where $($whr:tt)*)?
1593-
),
1594-
@impl_generics($($impl_generics:tt)*),
1595-
@ty_generics($($ty_generics:tt)*),
1596-
@body({
1597-
$(
1598-
$(#[$($field_attr:tt)*])*
1599-
$field_vis:vis $field:ident : $field_ty:ty
1600-
),* $(,)?
1601-
}),
1602-
) => {
1603-
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
1604-
#[automatically_derived]
1605-
unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
1606-
where
1607-
$($($whr)*)?
1608-
{}
1609-
const _: () = {
1610-
fn assert_zeroable<T: ?::core::marker::Sized + $crate::Zeroable>() {}
1611-
fn ensure_zeroable<$($impl_generics)*>()
1612-
where $($($whr)*)?
1613-
{
1614-
$(assert_zeroable::<$field_ty>();)*
1615-
}
1616-
};
1617-
};
1618-
}
1619-
1620-
#[doc(hidden)]
1621-
#[macro_export]
1622-
macro_rules! __maybe_derive_zeroable {
1623-
(parse_input:
1624-
@sig(
1625-
$(#[$($struct_attr:tt)*])*
1626-
$vis:vis struct $name:ident
1627-
$(where $($whr:tt)*)?
1628-
),
1629-
@impl_generics($($impl_generics:tt)*),
1630-
@ty_generics($($ty_generics:tt)*),
1631-
@body({
1632-
$(
1633-
$(#[$($field_attr:tt)*])*
1634-
$field_vis:vis $field:ident : $field_ty:ty
1635-
),* $(,)?
1636-
}),
1637-
) => {
1638-
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
1639-
#[automatically_derived]
1640-
unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
1641-
where
1642-
$(
1643-
// the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
1644-
// feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
1645-
$field_ty: for<'__dummy> $crate::Zeroable,
1646-
)*
1647-
$($($whr)*)?
1648-
{}
1649-
};
1650-
(parse_input:
1651-
@sig(
1652-
$(#[$($struct_attr:tt)*])*
1653-
$vis:vis union $name:ident
1654-
$(where $($whr:tt)*)?
1655-
),
1656-
@impl_generics($($impl_generics:tt)*),
1657-
@ty_generics($($ty_generics:tt)*),
1658-
@body({
1659-
$(
1660-
$(#[$($field_attr:tt)*])*
1661-
$field_vis:vis $field:ident : $field_ty:ty
1662-
),* $(,)?
1663-
}),
1664-
) => {
1665-
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
1666-
#[automatically_derived]
1667-
unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
1668-
where
1669-
$(
1670-
// the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
1671-
// feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
1672-
$field_ty: for<'__dummy> $crate::Zeroable,
1673-
)*
1674-
$($($whr)*)?
1675-
{}
1676-
};
1677-
}

0 commit comments

Comments
 (0)