Skip to content

Commit 00fccd3

Browse files
committed
rust: pin-init: add MaybeZeroable derive macro
This derive macro implements `Zeroable` for structs & unions precisely if all fields also implement `Zeroable` and does nothing otherwise. The plain `Zeroable` derive macro instead errors when it cannot derive `Zeroable` safely. The `MaybeZeroable` derive macro is useful in cases where manual checking is infeasible such as with the bindings crate. Move the zeroable generics parsing into a standalone function in order to avoid code duplication between the two derive macros. Link: Rust-for-Linux/pin-init@1165cda Signed-off-by: Benno Lossin <[email protected]>
1 parent a313d41 commit 00fccd3

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

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

+5
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
4747
pub fn derive_zeroable(input: TokenStream) -> TokenStream {
4848
zeroable::derive(input.into()).into()
4949
}
50+
51+
#[proc_macro_derive(MaybeZeroable)]
52+
pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
53+
zeroable::maybe_derive(input.into()).into()
54+
}

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

+26-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ use proc_macro2 as proc_macro;
66
use crate::helpers::{parse_generics, Generics};
77
use proc_macro::{TokenStream, TokenTree};
88

9-
pub(crate) fn derive(input: TokenStream) -> TokenStream {
9+
pub(crate) fn parse_zeroable_derive_input(
10+
input: TokenStream,
11+
) -> (
12+
Vec<TokenTree>,
13+
Vec<TokenTree>,
14+
Vec<TokenTree>,
15+
Option<TokenTree>,
16+
) {
1017
let (
1118
Generics {
1219
impl_generics,
@@ -64,6 +71,11 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {
6471
if in_generic && !inserted {
6572
new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
6673
}
74+
(rest, new_impl_generics, ty_generics, last)
75+
}
76+
77+
pub(crate) fn derive(input: TokenStream) -> TokenStream {
78+
let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
6779
quote! {
6880
::pin_init::__derive_zeroable!(
6981
parse_input:
@@ -74,3 +86,16 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {
7486
);
7587
}
7688
}
89+
90+
pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream {
91+
let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
92+
quote! {
93+
::pin_init::__maybe_derive_zeroable!(
94+
parse_input:
95+
@sig(#(#rest)*),
96+
@impl_generics(#(#new_impl_generics)*),
97+
@ty_generics(#(#ty_generics)*),
98+
@body(#last),
99+
);
100+
}
101+
}

rust/pin-init/src/lib.rs

+30
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,36 @@ pub use ::pin_init_internal::pinned_drop;
413413
/// ```
414414
pub use ::pin_init_internal::Zeroable;
415415

416+
/// Derives the [`Zeroable`] trait for the given struct if all fields implement [`Zeroable`].
417+
///
418+
/// Contrary to the derive macro named [`macro@Zeroable`], this one silently fails when a field
419+
/// doesn't implement [`Zeroable`].
420+
///
421+
/// # Examples
422+
///
423+
/// ```
424+
/// use pin_init::MaybeZeroable;
425+
///
426+
/// // implmements `Zeroable`
427+
/// #[derive(MaybeZeroable)]
428+
/// pub struct DriverData {
429+
/// id: i64,
430+
/// buf_ptr: *mut u8,
431+
/// len: usize,
432+
/// }
433+
///
434+
/// // does not implmement `Zeroable`
435+
/// #[derive(MaybeZeroable)]
436+
/// pub struct DriverData2 {
437+
/// id: i64,
438+
/// buf_ptr: *mut u8,
439+
/// len: usize,
440+
/// // this field doesn't implement `Zeroable`
441+
/// other_data: &'static i32,
442+
/// }
443+
/// ```
444+
pub use ::pin_init_internal::MaybeZeroable;
445+
416446
/// Initialize and pin a type directly on the stack.
417447
///
418448
/// # Examples

rust/pin-init/src/macros.rs

+59
Original file line numberDiff line numberDiff line change
@@ -1443,3 +1443,62 @@ macro_rules! __derive_zeroable {
14431443
};
14441444
};
14451445
}
1446+
1447+
#[doc(hidden)]
1448+
#[macro_export]
1449+
macro_rules! __maybe_derive_zeroable {
1450+
(parse_input:
1451+
@sig(
1452+
$(#[$($struct_attr:tt)*])*
1453+
$vis:vis struct $name:ident
1454+
$(where $($whr:tt)*)?
1455+
),
1456+
@impl_generics($($impl_generics:tt)*),
1457+
@ty_generics($($ty_generics:tt)*),
1458+
@body({
1459+
$(
1460+
$(#[$($field_attr:tt)*])*
1461+
$field_vis:vis $field:ident : $field_ty:ty
1462+
),* $(,)?
1463+
}),
1464+
) => {
1465+
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
1466+
#[automatically_derived]
1467+
unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
1468+
where
1469+
$(
1470+
// the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
1471+
// feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
1472+
$field_ty: for<'__dummy> $crate::Zeroable,
1473+
)*
1474+
$($($whr)*)?
1475+
{}
1476+
};
1477+
(parse_input:
1478+
@sig(
1479+
$(#[$($struct_attr:tt)*])*
1480+
$vis:vis union $name:ident
1481+
$(where $($whr:tt)*)?
1482+
),
1483+
@impl_generics($($impl_generics:tt)*),
1484+
@ty_generics($($ty_generics:tt)*),
1485+
@body({
1486+
$(
1487+
$(#[$($field_attr:tt)*])*
1488+
$field_vis:vis $field:ident : $field_ty:ty
1489+
),* $(,)?
1490+
}),
1491+
) => {
1492+
// SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
1493+
#[automatically_derived]
1494+
unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
1495+
where
1496+
$(
1497+
// the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
1498+
// feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
1499+
$field_ty: for<'__dummy> $crate::Zeroable,
1500+
)*
1501+
$($($whr)*)?
1502+
{}
1503+
};
1504+
}

0 commit comments

Comments
 (0)