From 2cce50f5ab83d01cc3aafa3fb55cba97584aee51 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Wed, 16 Apr 2025 22:50:15 +0000 Subject: [PATCH 01/14] rust: pin-init: synchronize README.md The upstream version of the `README.md` differs by this change, so synchronize it. The reason that this wasn't in the original sync patch is that this was a late change that I didn't port back to the kernel repo, since it was generated by `cargo rdme`. Link: https://lore.kernel.org/all/20250416225002.25253-1-benno.lossin@proton.me Signed-off-by: Benno Lossin --- rust/pin-init/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md index 3d04796b212b..24d0f0a3f8fb 100644 --- a/rust/pin-init/README.md +++ b/rust/pin-init/README.md @@ -218,11 +218,13 @@ the `kernel` crate. The [`sync`] module is a good starting point. [pinning]: https://doc.rust-lang.org/std/pin/index.html [structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field [stack]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html -[`Arc`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html -[`Box`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html [`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html [`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html [`impl Init`]: https://docs.rs/pin-init/latest/pin_init/trait.Init.html [Rust-for-Linux]: https://rust-for-linux.com/ + + +[`Arc`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html +[`Box`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html From 31005209b88c83adc4b4ef63fbc02867cddb2689 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 14 Apr 2025 20:00:03 +0000 Subject: [PATCH 02/14] rust: pin-init: internal: skip rustfmt formatting of kernel-only module The `quote` module only is available in the kernel and thus running `cargo fmt` or `rustfmt internal/src/lib.rs` in the user-space repository [1] results in: error: couldn't read `~/pin-init/internal/src/../../../macros/quote.rs`: No such file or directory (os error 2) --> ~/pin-init/internal/src/lib.rs:25:1 | 25 | mod quote; | ^^^^^^^^^^ Error writing files: failed to resolve mod `quote`: ~/pin-init/internal/src/../../../macros/quote.rs does not exist Thus mark it with `rustfmt::skip` when compiling without kernel support. Link: https://github.com/Rust-for-Linux/pin-init [1] Link: https://github.com/Rust-for-Linux/pin-init/pull/33/commits/a6caf1945e51da38761aab4dffa56e63e2baa218 Link: https://lore.kernel.org/all/20250414195928.129040-2-benno.lossin@proton.me Reviewed-by: Christian Schrefl Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index babe5e878550..56aa9ecc1e1a 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -22,6 +22,7 @@ #[cfg(kernel)] #[path = "../../../macros/quote.rs"] #[macro_use] +#[cfg_attr(not(kernel), rustfmt::skip)] mod quote; #[cfg(not(kernel))] #[macro_use] From 5c4167b4056c9db951788541e23585dd4a1fa426 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 14 Apr 2025 20:00:20 +0000 Subject: [PATCH 03/14] rust: pin-init: examples: conditionally enable `feature(lint_reasons)` `lint_reasons` is unstable in Rust 1.80 and earlier, enable it conditionally in the examples to allow compiling them with older compilers. Link: https://github.com/Rust-for-Linux/pin-init/pull/33/commits/ec494fe686b0a97d5b59b5be5a42d3858038ea6a Link: https://lore.kernel.org/all/20250414195928.129040-3-benno.lossin@proton.me Signed-off-by: Benno Lossin --- rust/pin-init/examples/linked_list.rs | 1 + rust/pin-init/examples/mutex.rs | 1 + rust/pin-init/examples/pthread_mutex.rs | 2 ++ rust/pin-init/examples/static_init.rs | 1 + 4 files changed, 5 insertions(+) diff --git a/rust/pin-init/examples/linked_list.rs b/rust/pin-init/examples/linked_list.rs index 6d7eb0a0ec0d..0bbc7b8d83a1 100644 --- a/rust/pin-init/examples/linked_list.rs +++ b/rust/pin-init/examples/linked_list.rs @@ -2,6 +2,7 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] +#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] use core::{ cell::Cell, diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index 073bb79341d1..3e3630780c96 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -2,6 +2,7 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] +#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] #![allow(clippy::missing_safety_doc)] use core::{ diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs index 5ac22f1880d2..074ebf485a97 100644 --- a/rust/pin-init/examples/pthread_mutex.rs +++ b/rust/pin-init/examples/pthread_mutex.rs @@ -3,6 +3,8 @@ // inspired by #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] +#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] + #[cfg(not(windows))] mod pthread_mtx { #[cfg(feature = "alloc")] diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index 3487d761aa26..48531413ab94 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -2,6 +2,7 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] +#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] use core::{ cell::{Cell, UnsafeCell}, From 39051adb070432b283e6c11b2b24937281b9f97f Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 14 Apr 2025 20:00:31 +0000 Subject: [PATCH 04/14] rust: pin-init: examples: use `allow` instead of `expect` Rust 1.78 doesn't emit a `dead_code` error on the annotated element, resulting in the `unfulfilled_lint_expectations` error. Rust 1.85 does emit the `dead_code` error, so we still need an `allow`. Link: https://github.com/Rust-for-Linux/pin-init/pull/33/commits/0e28cbb895bd29f896a59b40e8ed506ea7bef13c Link: https://lore.kernel.org/all/20250414195928.129040-4-benno.lossin@proton.me Signed-off-by: Benno Lossin --- rust/pin-init/examples/pthread_mutex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs index 074ebf485a97..5acc5108b954 100644 --- a/rust/pin-init/examples/pthread_mutex.rs +++ b/rust/pin-init/examples/pthread_mutex.rs @@ -42,7 +42,7 @@ fn drop(self: Pin<&mut Self>) { #[derive(Debug)] pub enum Error { - #[expect(dead_code)] + #[allow(dead_code)] IO(std::io::Error), Alloc, } From 90348980a305cc24a067cc6e606e1c318e277930 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 21 Apr 2025 22:17:50 +0000 Subject: [PATCH 05/14] rust: pin-init: add `cast_[pin_]init` functions to change the initialized type These functions cast the given pointer from one type to another. They are particularly useful when initializing transparent wrapper types. Link: https://github.com/Rust-for-Linux/pin-init/pull/39/commits/80c03ddee41b154f1099fd8cc7c2bbd8c80af0ad Reviewed-by: Christian Schrefl Signed-off-by: Benno Lossin --- rust/pin-init/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 0806c689f693..a880c21d3f09 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1216,6 +1216,38 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { __internal::InitClosure(f, PhantomData) } +/// Changes the to be initialized type. +/// +/// # Safety +/// +/// - `*mut U` must be castable to `*mut T` and any value of type `T` written through such a +/// pointer must result in a valid `U`. +#[expect(clippy::let_and_return)] +pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl PinInit { + // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety + // requirements. + let res = unsafe { pin_init_from_closure(|ptr: *mut U| init.__pinned_init(ptr.cast::())) }; + // FIXME: remove the let statement once the nightly-MSRV allows it (1.78 otherwise encounters a + // cycle when computing the type returned by this function) + res +} + +/// Changes the to be initialized type. +/// +/// # Safety +/// +/// - `*mut U` must be castable to `*mut T` and any value of type `T` written through such a +/// pointer must result in a valid `U`. +#[expect(clippy::let_and_return)] +pub const unsafe fn cast_init(init: impl Init) -> impl Init { + // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety + // requirements. + let res = unsafe { init_from_closure(|ptr: *mut U| init.__init(ptr.cast::())) }; + // FIXME: remove the let statement once the nightly-MSRV allows it (1.78 otherwise encounters a + // cycle when computing the type returned by this function) + res +} + /// An initializer that leaves the memory uninitialized. /// /// The initializer is a no-op. The `slot` memory is not changed. From 2f7c73825f8f435ebdfb2cfa3b01cfa2b1c79041 Mon Sep 17 00:00:00 2001 From: Christian Schrefl Date: Mon, 21 Apr 2025 22:17:59 +0000 Subject: [PATCH 06/14] rust: pin-init: Add the `Wrapper` trait. This trait allows creating `PinInitializers` for wrapper or new-type structs with the inner value structurally pinned, when given the initializer for the inner value. Implement this trait for `UnsafeCell` and `MaybeUninit`. Signed-off-by: Christian Schrefl Link: https://github.com/Rust-for-Linux/pin-init/pull/37/commits/3ab4db083bd7b41a1bc23d937224f975d7400e50 [ Reworded commit message into imperative mode, fixed typo and fixed commit authorship. - Benno ] Signed-off-by: Benno Lossin --- rust/pin-init/src/lib.rs | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index a880c21d3f09..467ccc8bd616 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1513,3 +1513,47 @@ unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*) } impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J); + +/// This trait allows creating an instance of `Self` which contains exactly one +/// [structurally pinned value](https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning). +/// +/// This is useful when using wrapper `struct`s like [`UnsafeCell`] or with new-type `struct`s. +/// +/// # Examples +/// +/// ``` +/// # use core::cell::UnsafeCell; +/// # use pin_init::{pin_data, pin_init, Wrapper}; +/// +/// #[pin_data] +/// struct Foo {} +/// +/// #[pin_data] +/// struct Bar { +/// #[pin] +/// content: UnsafeCell +/// }; +/// +/// let foo_initializer = pin_init!(Foo{}); +/// let initializer = pin_init!(Bar { +/// content <- UnsafeCell::pin_init(foo_initializer) +/// }); +/// ``` +pub trait Wrapper { + /// Create an pin-initializer for a [`Self`] containing `T` form the `value_init` initializer. + fn pin_init(value_init: impl PinInit) -> impl PinInit; +} + +impl Wrapper for UnsafeCell { + fn pin_init(value_init: impl PinInit) -> impl PinInit { + // SAFETY: `UnsafeCell` has a compatible layout to `T`. + unsafe { cast_pin_init(value_init) } + } +} + +impl Wrapper for MaybeUninit { + fn pin_init(value_init: impl PinInit) -> impl PinInit { + // SAFETY: `MaybeUninit` has a compatible layout to `T`. + unsafe { cast_pin_init(value_init) } + } +} From b862aac8fd46601fa20226c9f5d6c6d308678b4d Mon Sep 17 00:00:00 2001 From: Christian Schrefl Date: Mon, 21 Apr 2025 22:18:06 +0000 Subject: [PATCH 07/14] rust: pin-init: Implement `Wrapper` for `UnsafePinned` behind feature flag. Add the `unsafe-pinned` feature which gates the `Wrapper` implementation of the `core::pin::UnsafePinned` struct. For now this is just a cargo feature, but once `core::pin::UnsafePinned` is stable a config flag can be added to allow the usage of this implementation in the linux kernel. Signed-off-by: Christian Schrefl Link: https://github.com/Rust-for-Linux/pin-init/pull/37/commits/99cb1934425357e780ea5b0628f66633123847b8 [ Fixed commit authorship. - Benno ] Signed-off-by: Benno Lossin --- rust/pin-init/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 467ccc8bd616..745cf534d239 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -269,6 +269,10 @@ #![forbid(missing_docs, unsafe_op_in_unsafe_fn)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "alloc", feature(allocator_api))] +#![cfg_attr( + all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED), + feature(unsafe_pinned) +)] use core::{ cell::UnsafeCell, @@ -1557,3 +1561,11 @@ fn pin_init(value_init: impl PinInit) -> impl PinInit { unsafe { cast_pin_init(value_init) } } } + +#[cfg(all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED))] +impl Wrapper for core::pin::UnsafePinned { + fn pin_init(init: impl PinInit) -> impl PinInit { + // SAFETY: `UnsafePinned` has a compatible layout to `T`. + unsafe { cast_pin_init(init) } + } +} From bc5f3e0e01a5f2d067ff4292d5a10093ae680f53 Mon Sep 17 00:00:00 2001 From: Christian Schrefl Date: Mon, 21 Apr 2025 22:18:13 +0000 Subject: [PATCH 08/14] rust: pin-init: Update Changelog and Readme Add Changelog entry for the `Wrapper` trait and document the `unsafe-pinned` feature in the Readme. Signed-off-by: Christian Schrefl Link: https://github.com/Rust-for-Linux/pin-init/pull/37/commits/986555f564645efb238e8092c6314388c859efe5 [ Fixed commit authorship. - Benno ] Signed-off-by: Benno Lossin --- rust/pin-init/README.md | 6 ++++++ rust/pin-init/src/lib.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md index 24d0f0a3f8fb..1a03b200d4ce 100644 --- a/rust/pin-init/README.md +++ b/rust/pin-init/README.md @@ -40,6 +40,12 @@ However, using the crate on stable compilers is possible by disabling `alloc`. I will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std mode. +### Nightly needed for `unsafe-pinned` feature + +This feature enables the `Wrapper` implementation on the unstable `core::pin::UnsafePinned` type. +This requires the [`unsafe_pinned` unstable feature](https://github.com/rust-lang/rust/issues/125735) +and therefore a nightly compiler. Note that this feature is not enabled by default. + ## Overview To initialize a `struct` with an in-place constructor you will need two things: diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 745cf534d239..1521500a46b1 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -32,6 +32,12 @@ //! will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std //! mode. //! +//! ## Nightly needed for `unsafe-pinned` feature +//! +//! This feature enables the `Wrapper` implementation on the unstable `core::pin::UnsafePinned` type. +//! This requires the [`unsafe_pinned` unstable feature](https://github.com/rust-lang/rust/issues/125735) +//! and therefore a nightly compiler. Note that this feature is not enabled by default. +//! //! # Overview //! //! To initialize a `struct` with an in-place constructor you will need two things: From c3815aa4bb5c6248e78785269357e87bfa4d0909 Mon Sep 17 00:00:00 2001 From: Christian Schrefl Date: Mon, 21 Apr 2025 22:18:23 +0000 Subject: [PATCH 09/14] rust: pin-init: Update the structural pinning link in readme. The previous link anchor was broken in rust 1.77, because the documentation was refactored in upstream rust. Change the link to refer to the new section in the rust documentation. Signed-off-by: Christian Schrefl Link: https://github.com/Rust-for-Linux/pin-init/pull/37/commits/a146142fe18cafa52f8c6da306ca2729d789cfbf [ Fixed commit authorship. - Benno ] Signed-off-by: Benno Lossin --- rust/pin-init/README.md | 2 +- rust/pin-init/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md index 1a03b200d4ce..2d0cda961d45 100644 --- a/rust/pin-init/README.md +++ b/rust/pin-init/README.md @@ -222,7 +222,7 @@ the `kernel` crate. The [`sync`] module is a good starting point. [`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html [pinning]: https://doc.rust-lang.org/std/pin/index.html -[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field +[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning [stack]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html [`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html [`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 1521500a46b1..774f8ca033bc 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -247,7 +247,7 @@ //! [`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html //! [pinning]: https://doc.rust-lang.org/std/pin/index.html //! [structurally pinned fields]: -//! https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field +//! https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning //! [stack]: crate::stack_pin_init #![cfg_attr( kernel, From 983d13fc2cf12f0a753700d48be7d04155a1272c Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 21 Apr 2025 22:18:33 +0000 Subject: [PATCH 10/14] rust: pin-init: allow `pub` fields in `derive(Zeroable)` Add support for parsing `pub`, `pub(crate)` and `pub(super)` to the derive macro `Zeroable`. Link: https://github.com/Rust-for-Linux/pin-init/pull/42/commits/e8311e52ca57273e7ed6d099144384971677a0ba Signed-off-by: Benno Lossin --- rust/pin-init/src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index 361623324d5c..e4054fe3ed3d 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -1393,7 +1393,7 @@ macro_rules! __derive_zeroable { @body({ $( $(#[$($field_attr:tt)*])* - $field:ident : $field_ty:ty + $field_vis:vis $field:ident : $field_ty:ty ),* $(,)? }), ) => { From a313d41a2b515bbb76d56df490b731ff6d64e571 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 21 Apr 2025 22:18:41 +0000 Subject: [PATCH 11/14] rust: pin-init: allow `Zeroable` derive macro to also be applied to unions Enabling the same behavior for unions as for structs is correct, but could be relaxed: the valid bit patterns for unions are the union of all valid bit patterns of their fields. So for a union to implement `Zeroable`, only a single field needs to implement `Zeroable`. This can be a future improvement, as it is currently only needed for unions where all fields implement `Zeroable`. There is no danger for mis-parsing with the two optional tokens (ie neither one or both tokens are parsed), as the compiler will already have rejected that before giving it as the input to the derive macro. Link: https://github.com/Rust-for-Linux/pin-init/pull/42/commits/5927b497ce522d82f6c082d5ba9235df57bfdb32 Signed-off-by: Benno Lossin --- rust/pin-init/src/macros.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index e4054fe3ed3d..332d7e08925b 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -1412,4 +1412,34 @@ fn ensure_zeroable<$($impl_generics)*>() } }; }; + (parse_input: + @sig( + $(#[$($struct_attr:tt)*])* + $vis:vis union $name:ident + $(where $($whr:tt)*)? + ), + @impl_generics($($impl_generics:tt)*), + @ty_generics($($ty_generics:tt)*), + @body({ + $( + $(#[$($field_attr:tt)*])* + $field_vis:vis $field:ident : $field_ty:ty + ),* $(,)? + }), + ) => { + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> + where + $($($whr)*)? + {} + const _: () = { + fn assert_zeroable() {} + fn ensure_zeroable<$($impl_generics)*>() + where $($($whr)*)? + { + $(assert_zeroable::<$field_ty>();)* + } + }; + }; } From 00fccd3ecc2129ee32fd181079eb643f497044c4 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 21 Apr 2025 22:18:52 +0000 Subject: [PATCH 12/14] 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: https://github.com/Rust-for-Linux/pin-init/pull/42/commits/1165cdad1a391b923efaf30cf76bc61e38da022e Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/lib.rs | 5 +++ rust/pin-init/internal/src/zeroable.rs | 27 +++++++++++- rust/pin-init/src/lib.rs | 30 +++++++++++++ rust/pin-init/src/macros.rs | 59 ++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index 56aa9ecc1e1a..297b0129a5bf 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -47,3 +47,8 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { pub fn derive_zeroable(input: TokenStream) -> TokenStream { zeroable::derive(input.into()).into() } + +#[proc_macro_derive(MaybeZeroable)] +pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream { + zeroable::maybe_derive(input.into()).into() +} diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs index acc94008c152..e0ed3998445c 100644 --- a/rust/pin-init/internal/src/zeroable.rs +++ b/rust/pin-init/internal/src/zeroable.rs @@ -6,7 +6,14 @@ use crate::helpers::{parse_generics, Generics}; use proc_macro::{TokenStream, TokenTree}; -pub(crate) fn derive(input: TokenStream) -> TokenStream { +pub(crate) fn parse_zeroable_derive_input( + input: TokenStream, +) -> ( + Vec, + Vec, + Vec, + Option, +) { let ( Generics { impl_generics, @@ -64,6 +71,11 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream { if in_generic && !inserted { new_impl_generics.extend(quote! { : ::pin_init::Zeroable }); } + (rest, new_impl_generics, ty_generics, last) +} + +pub(crate) fn derive(input: TokenStream) -> TokenStream { + let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); quote! { ::pin_init::__derive_zeroable!( parse_input: @@ -74,3 +86,16 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream { ); } } + +pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream { + let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); + quote! { + ::pin_init::__maybe_derive_zeroable!( + parse_input: + @sig(#(#rest)*), + @impl_generics(#(#new_impl_generics)*), + @ty_generics(#(#ty_generics)*), + @body(#last), + ); + } +} diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 774f8ca033bc..05a0cd6ad8f4 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -413,6 +413,36 @@ /// ``` pub use ::pin_init_internal::Zeroable; +/// Derives the [`Zeroable`] trait for the given struct if all fields implement [`Zeroable`]. +/// +/// Contrary to the derive macro named [`macro@Zeroable`], this one silently fails when a field +/// doesn't implement [`Zeroable`]. +/// +/// # Examples +/// +/// ``` +/// use pin_init::MaybeZeroable; +/// +/// // implmements `Zeroable` +/// #[derive(MaybeZeroable)] +/// pub struct DriverData { +/// id: i64, +/// buf_ptr: *mut u8, +/// len: usize, +/// } +/// +/// // does not implmement `Zeroable` +/// #[derive(MaybeZeroable)] +/// pub struct DriverData2 { +/// id: i64, +/// buf_ptr: *mut u8, +/// len: usize, +/// // this field doesn't implement `Zeroable` +/// other_data: &'static i32, +/// } +/// ``` +pub use ::pin_init_internal::MaybeZeroable; + /// Initialize and pin a type directly on the stack. /// /// # Examples diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index 332d7e08925b..935d77745d1d 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -1443,3 +1443,62 @@ fn ensure_zeroable<$($impl_generics)*>() }; }; } + +#[doc(hidden)] +#[macro_export] +macro_rules! __maybe_derive_zeroable { + (parse_input: + @sig( + $(#[$($struct_attr:tt)*])* + $vis:vis struct $name:ident + $(where $($whr:tt)*)? + ), + @impl_generics($($impl_generics:tt)*), + @ty_generics($($ty_generics:tt)*), + @body({ + $( + $(#[$($field_attr:tt)*])* + $field_vis:vis $field:ident : $field_ty:ty + ),* $(,)? + }), + ) => { + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> + where + $( + // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` + // feature . + $field_ty: for<'__dummy> $crate::Zeroable, + )* + $($($whr)*)? + {} + }; + (parse_input: + @sig( + $(#[$($struct_attr:tt)*])* + $vis:vis union $name:ident + $(where $($whr:tt)*)? + ), + @impl_generics($($impl_generics:tt)*), + @ty_generics($($ty_generics:tt)*), + @body({ + $( + $(#[$($field_attr:tt)*])* + $field_vis:vis $field:ident : $field_ty:ty + ),* $(,)? + }), + ) => { + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> + where + $( + // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` + // feature . + $field_ty: for<'__dummy> $crate::Zeroable, + )* + $($($whr)*)? + {} + }; +} From a919ba21594bfa1e67639785d409e1bdce332097 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 26 Apr 2025 08:39:22 +0000 Subject: [PATCH 13/14] rust: pin-init: fix typos Correct two typos in the `Wrapper::pin_init` documentation. Link: https://github.com/Rust-for-Linux/pin-init/pull/48/commits/fd0bf5e244b685188dc642fc4a0bd3f042468fdb Reviewed-by: Christian Schrefl Signed-off-by: Benno Lossin --- rust/pin-init/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 05a0cd6ad8f4..b5a295effd9c 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1580,7 +1580,7 @@ unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*) /// }); /// ``` pub trait Wrapper { - /// Create an pin-initializer for a [`Self`] containing `T` form the `value_init` initializer. + /// Creates an pin-initializer for a [`Self`] containing `T` from the `value_init` initializer. fn pin_init(value_init: impl PinInit) -> impl PinInit; } From 9de1a293c8ece00d226b21a35751ec178be2a9fa Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 26 Apr 2025 08:39:28 +0000 Subject: [PATCH 14/14] rust: pin-init: improve documentation for `Zeroable` derive macros Specify that both `MaybeZeroable` and `Zeroable` work on `union`s. Add a doc example for a union. Also include an example with visibility on the field. Link: https://github.com/Rust-for-Linux/pin-init/pull/48/commits/ab0985a0e08df06c60a32ca5888f74adcc2c1cf3 Reviewed-by: Christian Schrefl Signed-off-by: Benno Lossin --- rust/pin-init/src/lib.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index b5a295effd9c..9ab34036e6bc 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -395,9 +395,10 @@ /// ``` pub use ::pin_init_internal::pinned_drop; -/// Derives the [`Zeroable`] trait for the given struct. +/// Derives the [`Zeroable`] trait for the given `struct` or `union`. /// -/// This can only be used for structs where every field implements the [`Zeroable`] trait. +/// This can only be used for `struct`s/`union`s where every field implements the [`Zeroable`] +/// trait. /// /// # Examples /// @@ -406,14 +407,25 @@ /// /// #[derive(Zeroable)] /// pub struct DriverData { -/// id: i64, +/// pub(crate) id: i64, /// buf_ptr: *mut u8, /// len: usize, /// } /// ``` +/// +/// ``` +/// use pin_init::Zeroable; +/// +/// #[derive(Zeroable)] +/// pub union SignCast { +/// signed: i64, +/// unsigned: u64, +/// } +/// ``` pub use ::pin_init_internal::Zeroable; -/// Derives the [`Zeroable`] trait for the given struct if all fields implement [`Zeroable`]. +/// Derives the [`Zeroable`] trait for the given `struct` or `union` if all fields implement +/// [`Zeroable`]. /// /// Contrary to the derive macro named [`macro@Zeroable`], this one silently fails when a field /// doesn't implement [`Zeroable`]. @@ -426,7 +438,7 @@ /// // implmements `Zeroable` /// #[derive(MaybeZeroable)] /// pub struct DriverData { -/// id: i64, +/// pub(crate) id: i64, /// buf_ptr: *mut u8, /// len: usize, /// } @@ -434,7 +446,7 @@ /// // does not implmement `Zeroable` /// #[derive(MaybeZeroable)] /// pub struct DriverData2 { -/// id: i64, +/// pub(crate) id: i64, /// buf_ptr: *mut u8, /// len: usize, /// // this field doesn't implement `Zeroable`