public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/15] `syn` rewrite of pin-init
@ 2026-01-11 12:24 ` Benno Lossin
  2026-01-11 12:24   ` [PATCH v2 01/15] rust: pin-init: remove `try_` versions of the initializer macros Benno Lossin
                     ` (15 more replies)
  0 siblings, 16 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:24 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: linux-kernel, rust-for-linux

Rewrite the proc-macros of pin-init by using the `syn` crate for Rust
syntax parsing. This series has been a long way coming. At the very
start of pin-init, I initially implemented everything using syn, since
parsing is so much easier with it. Now after several years it is finally
time to remove the dreaded 1600 lines of declarative macros required to
parse and expand the initializer syntax.

The move to syn is not only a blessing for the maintenance work, but
also improves the implementation of new features. This series includes
many such improvements.

* Patch 1, 2, 3 and 4 prepare for the rewrite. The first removes the
  superfluous `try_` variants of the initializer macros from pin-init.
  Note that the kernel defines its own, so no code changes in the kernel
  are required. The second patch allows using `::pin_init` in the
  pin-init crate. The third adds the syn dependency and cleans up some
  old workarounds and new clippy warnings. The fourth adds better error
  handling on top of `syn`.
* Patch 5, 6, 7, and 9 rewrite the derive macros for Zeroable,
  `#[pinned_drop]` attribute macro, `#[pin_data]` attribute macro, and
  the initializer macros respectively using `syn`.
* Patch 8 ensures soundness in the future by fixing generic bounds in
  generated code by `#[pin_data]`.
* Patch 10 adds the `#[default_error(type)]` attribute to initializer
  macros allowing them to specify a default error that is used when no
  error is manually specified.
* Patch 11 uses `#[default_error(type)]` in the definition of the
  kernel's `try_` variants of the initializer macros (which defaults to
  the kernel's `Error` type).
* Patch 12 allows putting attributes on fields in initializer macros
  (for example `cfg` attributes).
* Patch 13 adds `#[disable_initialized_field_access]` to support packed
  structs.
* Patch 14 simplifies the code generated by the initializer macros.
* Patch 15 adds Gary as a maintainer.

In addition to the new features, using syn results in much cleaner error
messages and more accurate span information. The code is much easier to
read as well and hopefully easier to understand as well.

As always, tests that ensure the correctness of the macro output are
included and updated in the upstream pin-init repository. Take a look at
the pull request on GitHub for the diff in the test output:

    https://github.com/Rust-for-Linux/pin-init/pull/89

I would greatly appreciate Tested-by's from people actively using
pin-init weather in the kernel or outside.

One thing I forgot to mention last time: the commit adding syn support
to pin-init is also used by Gary's series to rewrite the `macros` crate
using `syn`:

    https://lore.kernel.org/all/20260107161729.3855851-1-gary@kernel.org

My series can be found at:

    https://github.com/BennoLossin/linux/commits/pin-init/sync

The rust-dev branch in the RfL repository will be updated to contain
both patch series for ease of use & testing.

Link to v1:

    https://lore.kernel.org/all/20260108135127.3153925-1-lossin@kernel.org

Cheers,
Benno



Benno Lossin (15):
  rust: pin-init: remove `try_` versions of the initializer macros
  rust: pin-init: allow the crate to refer to itself as `pin-init` in
    doc tests
  rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and
    `quote` workarounds
  rust: pin-init: internal: add utility API for syn error handling
  rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)`
    using `syn`
  rust: pin-init: rewrite the `#[pinned_drop]` attribute macro using
    `syn`
  rust: pin-init: rewrite `#[pin_data]` using `syn`
  rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro
  rust: pin-init: rewrite the initializer macros using `syn`
  rust: pin-init: add `#[default_error(<type>)]` attribute to
    initializer macros
  rust: init: use `#[default_error(err)]` for the initializer macros
  rust: pin-init: internal: init: add support for attributes on
    initializer fields
  rust: pin-init: internal: init: add escape hatch for referencing
    initialized fields
  rust: pin-init: internal: init: simplify Zeroable safety check
  MAINTAINERS: add Gary Guo to pin-init

 MAINTAINERS                               |    1 +
 rust/Makefile                             |   16 +-
 rust/kernel/init.rs                       |   40 +-
 rust/pin-init/README.md                   |    2 +-
 rust/pin-init/examples/linked_list.rs     |   19 +-
 rust/pin-init/examples/pthread_mutex.rs   |   10 +-
 rust/pin-init/internal/src/helpers.rs     |  152 --
 rust/pin-init/internal/src/init.rs        |  549 +++++++
 rust/pin-init/internal/src/lib.rs         |   93 +-
 rust/pin-init/internal/src/pin_data.rs    |  634 ++++++--
 rust/pin-init/internal/src/pinned_drop.rs |   93 +-
 rust/pin-init/internal/src/zeroable.rs    |  157 +-
 rust/pin-init/src/lib.rs                  |  173 +--
 rust/pin-init/src/macros.rs               | 1677 ---------------------
 scripts/generate_rust_analyzer.py         |    2 +-
 15 files changed, 1314 insertions(+), 2304 deletions(-)
 delete mode 100644 rust/pin-init/internal/src/helpers.rs
 create mode 100644 rust/pin-init/internal/src/init.rs
 delete mode 100644 rust/pin-init/src/macros.rs


base-commit: 9448598b22c50c8a5bb77a9103e2d49f134c9578
-- 
2.52.0


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH v2 01/15] rust: pin-init: remove `try_` versions of the initializer macros
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
@ 2026-01-11 12:24   ` Benno Lossin
  2026-01-12 12:57     ` Gary Guo
  2026-01-11 12:25   ` [PATCH v2 02/15] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests Benno Lossin
                     ` (14 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:24 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl
  Cc: rust-for-linux, linux-kernel

The `try_[pin_]init!` versions of the initializer macros are
superfluous. Instead of forcing the user to always write an error in
`try_[pin_]init!` and not allowing one in `[pin_]init!`, combine them
into `[pin_]init!` that defaults the error to
`core::convert::Infallible`, but also allows to specify a custom one.

Projects using pin-init still can provide their own defaulting
initializers using the `try_` prefix by using the `#[default_error]`
attribute added in a future patch.

[ Adjust the definition of the kernel's version of the `try_`
  initializer macros - Benno]

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2: none
---
 rust/kernel/init.rs                     |   8 +-
 rust/pin-init/README.md                 |   2 +-
 rust/pin-init/examples/linked_list.rs   |  19 ++--
 rust/pin-init/examples/pthread_mutex.rs |  10 +-
 rust/pin-init/src/lib.rs                | 118 ++++--------------------
 5 files changed, 35 insertions(+), 122 deletions(-)

diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index 899b9a962762..917f7ef001fd 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -222,14 +222,14 @@ macro_rules! try_init {
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }) => {
-        ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
+        ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? {
             $($fields)*
         }? $crate::error::Error)
     };
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }? $err:ty) => {
-        ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
+        ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? {
             $($fields)*
         }? $err)
     };
@@ -282,14 +282,14 @@ macro_rules! try_pin_init {
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }) => {
-        ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
+        ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
             $($fields)*
         }? $crate::error::Error)
     };
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }? $err:ty) => {
-        ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
+        ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
             $($fields)*
         }? $err)
     };
diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md
index 74bbb4e0a2f7..6cee6ab1eb57 100644
--- a/rust/pin-init/README.md
+++ b/rust/pin-init/README.md
@@ -135,7 +135,7 @@ struct DriverData {
 
 impl DriverData {
     fn new() -> impl PinInit<Self, Error> {
-        try_pin_init!(Self {
+        pin_init!(Self {
             status <- CMutex::new(0),
             buffer: Box::init(pin_init::init_zeroed())?,
         }? Error)
diff --git a/rust/pin-init/examples/linked_list.rs b/rust/pin-init/examples/linked_list.rs
index f9e117c7dfe0..8445a5890cb7 100644
--- a/rust/pin-init/examples/linked_list.rs
+++ b/rust/pin-init/examples/linked_list.rs
@@ -6,7 +6,6 @@
 
 use core::{
     cell::Cell,
-    convert::Infallible,
     marker::PhantomPinned,
     pin::Pin,
     ptr::{self, NonNull},
@@ -31,31 +30,31 @@ pub struct ListHead {
 
 impl ListHead {
     #[inline]
-    pub fn new() -> impl PinInit<Self, Infallible> {
-        try_pin_init!(&this in Self {
+    pub fn new() -> impl PinInit<Self> {
+        pin_init!(&this in Self {
             next: unsafe { Link::new_unchecked(this) },
             prev: unsafe { Link::new_unchecked(this) },
             pin: PhantomPinned,
-        }? Infallible)
+        })
     }
 
     #[inline]
     #[allow(dead_code)]
-    pub fn insert_next(list: &ListHead) -> impl PinInit<Self, Infallible> + '_ {
-        try_pin_init!(&this in Self {
+    pub fn insert_next(list: &ListHead) -> impl PinInit<Self> + '_ {
+        pin_init!(&this in Self {
             prev: list.next.prev().replace(unsafe { Link::new_unchecked(this)}),
             next: list.next.replace(unsafe { Link::new_unchecked(this)}),
             pin: PhantomPinned,
-        }? Infallible)
+        })
     }
 
     #[inline]
-    pub fn insert_prev(list: &ListHead) -> impl PinInit<Self, Infallible> + '_ {
-        try_pin_init!(&this in Self {
+    pub fn insert_prev(list: &ListHead) -> impl PinInit<Self> + '_ {
+        pin_init!(&this in Self {
             next: list.prev.next().replace(unsafe { Link::new_unchecked(this)}),
             prev: list.prev.replace(unsafe { Link::new_unchecked(this)}),
             pin: PhantomPinned,
-        }? Infallible)
+        })
     }
 
     #[inline]
diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs
index 49b004c8c137..4e082ec7d5de 100644
--- a/rust/pin-init/examples/pthread_mutex.rs
+++ b/rust/pin-init/examples/pthread_mutex.rs
@@ -98,11 +98,11 @@ fn init_raw() -> impl PinInit<UnsafeCell<libc::pthread_mutex_t>, Error> {
                 // SAFETY: mutex has been initialized
                 unsafe { pin_init_from_closure(init) }
             }
-            try_pin_init!(Self {
-            data: UnsafeCell::new(data),
-            raw <- init_raw(),
-            pin: PhantomPinned,
-        }? Error)
+            pin_init!(Self {
+                data: UnsafeCell::new(data),
+                raw <- init_raw(),
+                pin: PhantomPinned,
+            }? Error)
         }
 
         #[allow(dead_code)]
diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index 8dc9dd5ac6fd..8673008f45d2 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -146,7 +146,7 @@
 //!
 //! impl DriverData {
 //!     fn new() -> impl PinInit<Self, Error> {
-//!         try_pin_init!(Self {
+//!         pin_init!(Self {
 //!             status <- CMutex::new(0),
 //!             buffer: Box::init(pin_init::init_zeroed())?,
 //!         }? Error)
@@ -528,7 +528,7 @@ macro_rules! stack_pin_init {
 ///     x: u32,
 /// }
 ///
-/// stack_try_pin_init!(let foo: Foo = try_pin_init!(Foo {
+/// stack_try_pin_init!(let foo: Foo = pin_init!(Foo {
 ///     a <- CMutex::new(42),
 ///     b: Box::try_new(Bar {
 ///         x: 64,
@@ -555,7 +555,7 @@ macro_rules! stack_pin_init {
 ///     x: u32,
 /// }
 ///
-/// stack_try_pin_init!(let foo: Foo =? try_pin_init!(Foo {
+/// stack_try_pin_init!(let foo: Foo =? pin_init!(Foo {
 ///     a <- CMutex::new(42),
 ///     b: Box::try_new(Bar {
 ///         x: 64,
@@ -584,10 +584,10 @@ macro_rules! stack_try_pin_init {
     };
 }
 
-/// Construct an in-place, pinned initializer for `struct`s.
+/// Construct an in-place, fallible pinned initializer for `struct`s.
 ///
-/// This macro defaults the error to [`Infallible`]. If you need a different error, then use
-/// [`try_pin_init!`].
+/// The error type defaults to [`Infallible`]; if you need a different one, write `? Error` at the
+/// end, after the struct initializer.
 ///
 /// The syntax is almost identical to that of a normal `struct` initializer:
 ///
@@ -783,54 +783,10 @@ macro_rules! pin_init {
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }) => {
-        $crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
+        $crate::pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
             $($fields)*
         }? ::core::convert::Infallible)
     };
-}
-
-/// Construct an in-place, fallible pinned initializer for `struct`s.
-///
-/// If the initialization can complete without error (or [`Infallible`]), then use [`pin_init!`].
-///
-/// You can use the `?` operator or use `return Err(err)` inside the initializer to stop
-/// initialization and return the error.
-///
-/// IMPORTANT: if you have `unsafe` code inside of the initializer you have to ensure that when
-/// initialization fails, the memory can be safely deallocated without any further modifications.
-///
-/// The syntax is identical to [`pin_init!`] with the following exception: you must append `? $type`
-/// after the `struct` initializer to specify the error type you want to use.
-///
-/// # Examples
-///
-/// ```rust
-/// # #![feature(allocator_api)]
-/// # #[path = "../examples/error.rs"] mod error; use error::Error;
-/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, init_zeroed};
-///
-/// #[pin_data]
-/// struct BigBuf {
-///     big: Box<[u8; 1024 * 1024 * 1024]>,
-///     small: [u8; 1024 * 1024],
-///     ptr: *mut u8,
-/// }
-///
-/// impl BigBuf {
-///     fn new() -> impl PinInit<Self, Error> {
-///         try_pin_init!(Self {
-///             big: Box::init(init_zeroed())?,
-///             small: [0; 1024 * 1024],
-///             ptr: core::ptr::null_mut(),
-///         }? Error)
-///     }
-/// }
-/// # let _ = Box::pin_init(BigBuf::new());
-/// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! try_pin_init {
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }? $err:ty) => {
@@ -847,10 +803,10 @@ macro_rules! try_pin_init {
     }
 }
 
-/// Construct an in-place initializer for `struct`s.
+/// Construct an in-place, fallible initializer for `struct`s.
 ///
-/// This macro defaults the error to [`Infallible`]. If you need a different error, then use
-/// [`try_init!`].
+/// This macro defaults the error to [`Infallible`]; if you need a different one, write `? Error`
+/// at the end, after the struct initializer.
 ///
 /// The syntax is identical to [`pin_init!`] and its safety caveats also apply:
 /// - `unsafe` code must guarantee either full initialization or return an error and allow
@@ -890,52 +846,10 @@ macro_rules! init {
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }) => {
-        $crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
+        $crate::init!($(&$this in)? $t $(::<$($generics),*>)? {
             $($fields)*
         }? ::core::convert::Infallible)
-    }
-}
-
-/// Construct an in-place fallible initializer for `struct`s.
-///
-/// If the initialization can complete without error (or [`Infallible`]), then use
-/// [`init!`].
-///
-/// The syntax is identical to [`try_pin_init!`]. You need to specify a custom error
-/// via `? $type` after the `struct` initializer.
-/// The safety caveats from [`try_pin_init!`] also apply:
-/// - `unsafe` code must guarantee either full initialization or return an error and allow
-///   deallocation of the memory.
-/// - the fields are initialized in the order given in the initializer.
-/// - no references to fields are allowed to be created inside of the initializer.
-///
-/// # Examples
-///
-/// ```rust
-/// # #![feature(allocator_api)]
-/// # use core::alloc::AllocError;
-/// # use pin_init::InPlaceInit;
-/// use pin_init::{try_init, Init, init_zeroed};
-///
-/// struct BigBuf {
-///     big: Box<[u8; 1024 * 1024 * 1024]>,
-///     small: [u8; 1024 * 1024],
-/// }
-///
-/// impl BigBuf {
-///     fn new() -> impl Init<Self, AllocError> {
-///         try_init!(Self {
-///             big: Box::init(init_zeroed())?,
-///             small: [0; 1024 * 1024],
-///         }? AllocError)
-///     }
-/// }
-/// # let _ = Box::init(BigBuf::new());
-/// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! try_init {
+    };
     ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
         $($fields:tt)*
     }? $err:ty) => {
@@ -1410,14 +1324,14 @@ pub fn pin_init_array_from_fn<I, const N: usize, T, E>(
 /// fn init_foo() -> impl PinInit<Foo, Error> {
 ///     pin_init_scope(|| {
 ///         let bar = lookup_bar()?;
-///         Ok(try_pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
+///         Ok(pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
 ///     })
 /// }
 /// ```
 ///
 /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
 /// initializer itself will fail with that error. If it returned `Ok`, then it will run the
-/// initializer returned by the [`try_pin_init!`] invocation.
+/// initializer returned by the [`pin_init!`] invocation.
 pub fn pin_init_scope<T, E, F, I>(make_init: F) -> impl PinInit<T, E>
 where
     F: FnOnce() -> Result<I, E>,
@@ -1453,14 +1367,14 @@ pub fn pin_init_scope<T, E, F, I>(make_init: F) -> impl PinInit<T, E>
 /// fn init_foo() -> impl Init<Foo, Error> {
 ///     init_scope(|| {
 ///         let bar = lookup_bar()?;
-///         Ok(try_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
+///         Ok(init!(Foo { a: bar.a.into(), b: bar.b }? Error))
 ///     })
 /// }
 /// ```
 ///
 /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
 /// initializer itself will fail with that error. If it returned `Ok`, then it will run the
-/// initializer returned by the [`try_init!`] invocation.
+/// initializer returned by the [`init!`] invocation.
 pub fn init_scope<T, E, F, I>(make_init: F) -> impl Init<T, E>
 where
     F: FnOnce() -> Result<I, E>,
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 02/15] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
  2026-01-11 12:24   ` [PATCH v2 01/15] rust: pin-init: remove `try_` versions of the initializer macros Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-12 15:55     ` Gary Guo
  2026-01-11 12:25   ` [PATCH v2 03/15] rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds Benno Lossin
                     ` (13 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl
  Cc: rust-for-linux, linux-kernel

The `syn` approach requires use of `::pin_init::...` instead of the
`$crate::...` construct available to declarative macros. To be able to
use the `pin_init` crate from itself (which includes doc tests), we have
to declare it as such.

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2: none
---
 rust/pin-init/src/lib.rs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index 8673008f45d2..0e707f00061f 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -290,6 +290,11 @@
     ptr::{self, NonNull},
 };
 
+// This is used by doc-tests -- the proc-macros expand to `::pin_init::...` and without this the
+// doc-tests wouldn't have an extern crate named `pin_init`.
+#[allow(unused_extern_crates)]
+extern crate self as pin_init;
+
 #[doc(hidden)]
 pub mod __internal;
 #[doc(hidden)]
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 03/15] rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
  2026-01-11 12:24   ` [PATCH v2 01/15] rust: pin-init: remove `try_` versions of the initializer macros Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 02/15] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 04/15] rust: pin-init: internal: add utility API for syn error handling Benno Lossin
                     ` (12 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Tamir Duberstein,
	Christian Schrefl
  Cc: rust-for-linux, linux-kernel

`syn` makes parsing Rust from proc-macros a lot simpler. `pin-init` has
not used `syn` up until now, because the we did not support it. That
changed in commit 54e3eae85562 ("Merge patch series "`syn` support""),
so we can finally utilize the added ergonomics of parsing proc-macro
input with `syn`.

Previously we only had the `proc-macro` library available, whereas the
user-space version also used `proc-macro2` and `quote`. Now both are
available, so remove the workarounds.

Due to these changes, clippy emits warnings about unnecessary
`.to_string()` as `proc-macro2` provides an additional `PartialEq` impl
on `Ident`, so the warnings are fixed.

[ Adjusted wording from upstream version and added build system changes
  for the kernel - Benno ]

Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Tamir Duberstein <tamird@gmail.com>
Signed-off-by: Benno Lossin <lossin@kernel.org>
---
This commit is also used in Gary's series to rewrite the `macros` crate
using syn: 

    https://lore.kernel.org/all/20260107161729.3855851-1-gary@kernel.org

Changes in v2: none
---
 rust/Makefile                             | 16 ++++++++++------
 rust/pin-init/internal/src/helpers.rs     |  7 ++-----
 rust/pin-init/internal/src/lib.rs         | 16 ----------------
 rust/pin-init/internal/src/pin_data.rs    | 18 ++++++------------
 rust/pin-init/internal/src/pinned_drop.rs | 10 ++++------
 rust/pin-init/internal/src/zeroable.rs    |  6 ++----
 scripts/generate_rust_analyzer.py         |  2 +-
 7 files changed, 25 insertions(+), 50 deletions(-)

diff --git a/rust/Makefile b/rust/Makefile
index 5d357dce1704..82bda6ccfe39 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -212,9 +212,10 @@ rustdoc-ffi: $(src)/ffi.rs rustdoc-core FORCE
 
 rustdoc-pin_init_internal: private rustdoc_host = yes
 rustdoc-pin_init_internal: private rustc_target_flags = --cfg kernel \
-    --extern proc_macro --crate-type proc-macro
+    --extern proc_macro --extern proc_macro2 --extern quote --extern syn \
+    --crate-type proc-macro
 rustdoc-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \
-    rustdoc-clean FORCE
+    rustdoc-clean rustdoc-proc_macro2 rustdoc-quote rustdoc-syn FORCE
 	+$(call if_changed,rustdoc)
 
 rustdoc-pin_init: private rustdoc_host = yes
@@ -273,9 +274,10 @@ rusttestlib-macros: $(src)/macros/lib.rs \
 	+$(call if_changed,rustc_test_library)
 
 rusttestlib-pin_init_internal: private rustc_target_flags = --cfg kernel \
-    --extern proc_macro
+    --extern proc_macro --extern proc_macro2 --extern quote --extern syn
 rusttestlib-pin_init_internal: private rustc_test_library_proc = yes
-rusttestlib-pin_init_internal: $(src)/pin-init/internal/src/lib.rs FORCE
+rusttestlib-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \
+    rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE
 	+$(call if_changed,rustc_test_library)
 
 rusttestlib-pin_init: private rustc_target_flags = --extern pin_init_internal \
@@ -547,8 +549,10 @@ $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \
     $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
 	+$(call if_changed_dep,rustc_procmacro)
 
-$(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel
-$(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs FORCE
+$(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel \
+    --extern proc_macro2 --extern quote --extern syn
+$(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs \
+    $(obj)/libproc_macro2.rlib $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
 	+$(call if_changed_dep,rustc_procmacro)
 
 quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@
diff --git a/rust/pin-init/internal/src/helpers.rs b/rust/pin-init/internal/src/helpers.rs
index 236f989a50f2..90f85eaa4123 100644
--- a/rust/pin-init/internal/src/helpers.rs
+++ b/rust/pin-init/internal/src/helpers.rs
@@ -1,9 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
-#[cfg(not(kernel))]
-use proc_macro2 as proc_macro;
-
-use proc_macro::{TokenStream, TokenTree};
+use proc_macro2::{TokenStream, TokenTree};
 
 /// Parsed generics.
 ///
@@ -101,7 +98,7 @@ pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
                     1 => {
                         // Here depending on the token, it might be a generic variable name.
                         match tt.clone() {
-                            TokenTree::Ident(i) if at_start && i.to_string() == "const" => {
+                            TokenTree::Ident(i) if at_start && i == "const" => {
                                 let Some(name) = toks.next() else {
                                     // Parsing error.
                                     break;
diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 297b0129a5bf..4c4dc639ce82 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -7,27 +7,11 @@
 //! `pin-init` proc macros.
 
 #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
-// Allow `.into()` to convert
-// - `proc_macro2::TokenStream` into `proc_macro::TokenStream` in the user-space version.
-// - `proc_macro::TokenStream` into `proc_macro::TokenStream` in the kernel version.
-//   Clippy warns on this conversion, but it's required by the user-space version.
-//
-// Remove once we have `proc_macro2` in the kernel.
-#![allow(clippy::useless_conversion)]
 // Documentation is done in the pin-init crate instead.
 #![allow(missing_docs)]
 
 use proc_macro::TokenStream;
 
-#[cfg(kernel)]
-#[path = "../../../macros/quote.rs"]
-#[macro_use]
-#[cfg_attr(not(kernel), rustfmt::skip)]
-mod quote;
-#[cfg(not(kernel))]
-#[macro_use]
-extern crate quote;
-
 mod helpers;
 mod pin_data;
 mod pinned_drop;
diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 87d4a7eb1d35..86a53b37cc66 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -1,10 +1,8 @@
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
-#[cfg(not(kernel))]
-use proc_macro2 as proc_macro;
-
 use crate::helpers::{parse_generics, Generics};
-use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree};
+use proc_macro2::{Group, Punct, Spacing, TokenStream, TokenTree};
+use quote::quote;
 
 pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
     // This proc-macro only does some pre-parsing and then delegates the actual parsing to
@@ -28,7 +26,7 @@ pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
     // The name of the struct with ty_generics.
     let struct_name = rest
         .iter()
-        .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i.to_string() == "struct"))
+        .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i == "struct"))
         .nth(1)
         .and_then(|tt| match tt {
             TokenTree::Ident(_) => {
@@ -65,7 +63,7 @@ pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
         .into_iter()
         .flat_map(|tt| {
             // We ignore top level `struct` tokens, since they would emit a compile error.
-            if matches!(&tt, TokenTree::Ident(i) if i.to_string() == "struct") {
+            if matches!(&tt, TokenTree::Ident(i) if i == "struct") {
                 vec![tt]
             } else {
                 replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)
@@ -98,11 +96,7 @@ fn replace_self_and_deny_type_defs(
 ) -> Vec<TokenTree> {
     match tt {
         TokenTree::Ident(ref i)
-            if i.to_string() == "enum"
-                || i.to_string() == "trait"
-                || i.to_string() == "struct"
-                || i.to_string() == "union"
-                || i.to_string() == "impl" =>
+            if i == "enum" || i == "trait" || i == "struct" || i == "union" || i == "impl" =>
         {
             errs.extend(
                 format!(
@@ -119,7 +113,7 @@ fn replace_self_and_deny_type_defs(
             );
             vec![tt]
         }
-        TokenTree::Ident(i) if i.to_string() == "Self" => struct_name.clone(),
+        TokenTree::Ident(i) if i == "Self" => struct_name.clone(),
         TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt],
         TokenTree::Group(g) => vec![TokenTree::Group(Group::new(
             g.delimiter(),
diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/internal/src/pinned_drop.rs
index c4ca7a70b726..cf8cd1c42984 100644
--- a/rust/pin-init/internal/src/pinned_drop.rs
+++ b/rust/pin-init/internal/src/pinned_drop.rs
@@ -1,15 +1,13 @@
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
-#[cfg(not(kernel))]
-use proc_macro2 as proc_macro;
-
-use proc_macro::{TokenStream, TokenTree};
+use proc_macro2::{TokenStream, TokenTree};
+use quote::quote;
 
 pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream {
     let mut toks = input.into_iter().collect::<Vec<_>>();
     assert!(!toks.is_empty());
     // Ensure that we have an `impl` item.
-    assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() == "impl"));
+    assert!(matches!(&toks[0], TokenTree::Ident(i) if i == "impl"));
     // Ensure that we are implementing `PinnedDrop`.
     let mut nesting: usize = 0;
     let mut pinned_drop_idx = None;
@@ -27,7 +25,7 @@ pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream
         if i >= 1 && nesting == 0 {
             // Found the end of the generics, this should be `PinnedDrop`.
             assert!(
-                matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"),
+                matches!(tt, TokenTree::Ident(i) if i == "PinnedDrop"),
                 "expected 'PinnedDrop', found: '{tt:?}'"
             );
             pinned_drop_idx = Some(i);
diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs
index e0ed3998445c..d8a5ef3883f4 100644
--- a/rust/pin-init/internal/src/zeroable.rs
+++ b/rust/pin-init/internal/src/zeroable.rs
@@ -1,10 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 
-#[cfg(not(kernel))]
-use proc_macro2 as proc_macro;
-
 use crate::helpers::{parse_generics, Generics};
-use proc_macro::{TokenStream, TokenTree};
+use proc_macro2::{TokenStream, TokenTree};
+use quote::quote;
 
 pub(crate) fn parse_zeroable_derive_input(
     input: TokenStream,
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index 147d0cc94068..d31d93888658 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -123,7 +123,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
     append_crate(
         "pin_init_internal",
         srctree / "rust" / "pin-init" / "internal" / "src" / "lib.rs",
-        [],
+        ["std", "proc_macro", "proc_macro2", "quote", "syn"],
         cfg=["kernel"],
         is_proc_macro=True,
     )
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 04/15] rust: pin-init: internal: add utility API for syn error handling
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (2 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 03/15] rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-12 16:14     ` Gary Guo
  2026-01-11 12:25   ` [PATCH v2 05/15] rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn` Benno Lossin
                     ` (11 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens
  Cc: rust-for-linux, linux-kernel

Add a function to turn a `syn::Result<TokenStream>` into a
`TokenStream`, either containing the error or the normal stream.

Add a nullable custom error type wrapping `syn::Error`.

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2: added this patch
---
 rust/pin-init/internal/src/lib.rs | 44 +++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 4c4dc639ce82..21f5e33486ce 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -36,3 +36,47 @@ pub fn derive_zeroable(input: TokenStream) -> TokenStream {
 pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
     zeroable::maybe_derive(input.into()).into()
 }
+
+#[expect(dead_code)]
+fn ok_or_compile_error(res: syn::Result<proc_macro2::TokenStream>) -> TokenStream {
+    match res {
+        Ok(stream) => stream,
+        Err(error) => error.into_compile_error(),
+    }
+    .into()
+}
+
+pub(crate) struct Error(Option<syn::Error>);
+
+impl From<syn::Error> for Error {
+    fn from(value: syn::Error) -> Self {
+        Self(Some(value))
+    }
+}
+
+impl Error {
+    #[expect(dead_code)]
+    pub(crate) fn none() -> Self {
+        Self(None)
+    }
+
+    #[expect(dead_code)]
+    pub(crate) fn combine(&mut self, error: impl Into<Self>) {
+        let error = error.into();
+        if let Some(this) = self.0.as_mut() {
+            if let Some(error) = error.0 {
+                this.combine(error);
+            }
+        } else {
+            self.0 = error.0;
+        }
+    }
+}
+
+impl quote::ToTokens for Error {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        if let Some(error) = self.0.as_ref() {
+            quote::ToTokens::to_tokens(&error.to_compile_error(), tokens);
+        }
+    }
+}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 05/15] rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn`
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (3 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 04/15] rust: pin-init: internal: add utility API for syn error handling Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 06/15] rust: pin-init: rewrite the `#[pinned_drop]` attribute macro " Benno Lossin
                     ` (10 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl, Alban Kurti
  Cc: rust-for-linux, linux-kernel

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>
---
Changes in v2:
* improved error handling
---
 rust/pin-init/internal/src/lib.rs      |   8 +-
 rust/pin-init/internal/src/zeroable.rs | 153 +++++++++++--------------
 rust/pin-init/src/macros.rs            | 124 --------------------
 3 files changed, 69 insertions(+), 216 deletions(-)

diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 21f5e33486ce..d3878cbccd68 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -11,6 +11,7 @@
 #![allow(missing_docs)]
 
 use proc_macro::TokenStream;
+use syn::parse_macro_input;
 
 mod helpers;
 mod pin_data;
@@ -29,19 +30,18 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
 
 #[proc_macro_derive(Zeroable)]
 pub fn derive_zeroable(input: TokenStream) -> TokenStream {
-    zeroable::derive(input.into()).into()
+    ok_or_compile_error(zeroable::derive(parse_macro_input!(input)))
 }
 
 #[proc_macro_derive(MaybeZeroable)]
 pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
-    zeroable::maybe_derive(input.into()).into()
+    ok_or_compile_error(zeroable::maybe_derive(parse_macro_input!(input)))
 }
 
-#[expect(dead_code)]
 fn ok_or_compile_error(res: syn::Result<proc_macro2::TokenStream>) -> TokenStream {
     match res {
         Ok(stream) => stream,
-        Err(error) => error.into_compile_error(),
+        Err(err) => err.into_compile_error(),
     }
     .into()
 }
diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs
index d8a5ef3883f4..f899815f8d15 100644
--- a/rust/pin-init/internal/src/zeroable.rs
+++ b/rust/pin-init/internal/src/zeroable.rs
@@ -1,99 +1,76 @@
 // SPDX-License-Identifier: GPL-2.0
 
-use crate::helpers::{parse_generics, Generics};
-use proc_macro2::{TokenStream, TokenTree};
+use proc_macro2::TokenStream;
 use quote::quote;
+use syn::{parse_quote, Data, DeriveInput, Error, Field, Fields, Result};
 
-pub(crate) fn parse_zeroable_derive_input(
-    input: TokenStream,
-) -> (
-    Vec<TokenTree>,
-    Vec<TokenTree>,
-    Vec<TokenTree>,
-    Option<TokenTree>,
-) {
-    let (
-        Generics {
-            impl_generics,
-            decl_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! { : ::pin_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! { ::pin_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),
+pub(crate) fn derive(input: DeriveInput) -> Result<TokenStream> {
+    let fields = match input.data {
+        Data::Struct(data_struct) => data_struct.fields,
+        Data::Union(data_union) => Fields::Named(data_union.fields),
+        Data::Enum(data_enum) => {
+            return Err(Error::new_spanned(
+                data_enum.enum_token,
+                "cannot derive `Zeroable` for an enum",
+            ));
         }
+    };
+    let name = input.ident;
+    let mut generics = input.generics;
+    for param in generics.type_params_mut() {
+        param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
     }
-    assert_eq!(nested, 0);
-    if in_generic && !inserted {
-        new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
-    }
-    (rest, new_impl_generics, ty_generics, last)
+    let (impl_gen, ty_gen, whr) = generics.split_for_impl();
+    let field_type = fields.iter().map(|field| &field.ty);
+    Ok(quote! {
+        // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
+        #[automatically_derived]
+        unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
+            #whr
+        {}
+        const _: () = {
+            fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {}
+            fn ensure_zeroable #impl_gen ()
+                #whr
+            {
+                #(
+                    assert_zeroable::<#field_type>();
+                )*
+            }
+        };
+    })
 }
 
-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:
-                @sig(#(#rest)*),
-                @impl_generics(#(#new_impl_generics)*),
-                @ty_generics(#(#ty_generics)*),
-                @body(#last),
-        );
+pub(crate) fn maybe_derive(input: DeriveInput) -> Result<TokenStream> {
+    let fields = match input.data {
+        Data::Struct(data_struct) => data_struct.fields,
+        Data::Union(data_union) => Fields::Named(data_union.fields),
+        Data::Enum(data_enum) => {
+            return Err(Error::new_spanned(
+                data_enum.enum_token,
+                "cannot derive `Zeroable` for an enum",
+            ));
+        }
+    };
+    let name = input.ident;
+    let mut generics = input.generics;
+    for param in generics.type_params_mut() {
+        param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
     }
-}
-
-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),
-        );
+    for Field { ty, .. } in fields {
+        generics
+            .make_where_clause()
+            .predicates
+            // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
+            // feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
+            .push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable));
     }
+    let (impl_gen, ty_gen, whr) = generics.split_for_impl();
+    Ok(quote! {
+        // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
+        #[automatically_derived]
+        unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
+            #whr
+        {}
+    })
 }
diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs
index 682c61a587a0..53ed5ce860fc 100644
--- a/rust/pin-init/src/macros.rs
+++ b/rust/pin-init/src/macros.rs
@@ -1551,127 +1551,3 @@ fn assert_zeroable<T: $crate::Zeroable>(_: *mut T) {}
         );
     };
 }
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! __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
-            $($($whr)*)?
-        {}
-        const _: () = {
-            fn assert_zeroable<T: ?::core::marker::Sized + $crate::Zeroable>() {}
-            fn ensure_zeroable<$($impl_generics)*>()
-                where $($($whr)*)?
-            {
-                $(assert_zeroable::<$field_ty>();)*
-            }
-        };
-    };
-    (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<T: ?::core::marker::Sized + $crate::Zeroable>() {}
-            fn ensure_zeroable<$($impl_generics)*>()
-                where $($($whr)*)?
-            {
-                $(assert_zeroable::<$field_ty>();)*
-            }
-        };
-    };
-}
-
-#[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 <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
-                $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 <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
-                $field_ty: for<'__dummy> $crate::Zeroable,
-            )*
-            $($($whr)*)?
-        {}
-    };
-}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 06/15] rust: pin-init: rewrite the `#[pinned_drop]` attribute macro using `syn`
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (4 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 05/15] rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn` Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 07/15] rust: pin-init: rewrite `#[pin_data]` " Benno Lossin
                     ` (9 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl,
	Tamir Duberstein, Alban Kurti
  Cc: rust-for-linux, linux-kernel

Rewrite the attribute macro for implementing `PinnedDrop` using `syn`.
Otherwise no functional changes intended aside from improved error
messages on syntactic and semantical errors. For example:

When missing the `drop` function in the implementation, the old error
was:

    error: no rules expected `)`
     --> tests/ui/compile-fail/pinned_drop/no_fn.rs:6:1
      |
    6 | #[pinned_drop]
      | ^^^^^^^^^^^^^^ no rules expected this token in macro call
      |
    note: while trying to match keyword `fn`
     --> src/macros.rs
      |
      |             fn drop($($sig:tt)*) {
      |             ^^
      = note: this error originates in the attribute macro `pinned_drop` (in Nightly builds, run with -Z macro-backtrace for more info)

And the new one is:

    error[E0046]: not all trait items implemented, missing: `drop`
     --> tests/ui/compile-fail/pinned_drop/no_fn.rs:7:1
      |
    7 | impl PinnedDrop for Foo {}
      | ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation
      |
      = help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }`

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2:
* improved error handling
* improved error span
---
 rust/pin-init/internal/src/lib.rs         |  4 +-
 rust/pin-init/internal/src/pinned_drop.rs | 89 +++++++++++++----------
 rust/pin-init/src/macros.rs               | 28 -------
 3 files changed, 52 insertions(+), 69 deletions(-)

diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index d3878cbccd68..04794c864c78 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -25,7 +25,7 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
 
 #[proc_macro_attribute]
 pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
-    pinned_drop::pinned_drop(args.into(), input.into()).into()
+    pinned_drop::pinned_drop(parse_macro_input!(args), parse_macro_input!(input)).into()
 }
 
 #[proc_macro_derive(Zeroable)]
@@ -55,12 +55,10 @@ fn from(value: syn::Error) -> Self {
 }
 
 impl Error {
-    #[expect(dead_code)]
     pub(crate) fn none() -> Self {
         Self(None)
     }
 
-    #[expect(dead_code)]
     pub(crate) fn combine(&mut self, error: impl Into<Self>) {
         let error = error.into();
         if let Some(this) = self.0.as_mut() {
diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/internal/src/pinned_drop.rs
index cf8cd1c42984..e1df034844bb 100644
--- a/rust/pin-init/internal/src/pinned_drop.rs
+++ b/rust/pin-init/internal/src/pinned_drop.rs
@@ -1,49 +1,62 @@
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
-use proc_macro2::{TokenStream, TokenTree};
+use proc_macro2::TokenStream;
 use quote::quote;
+use syn::{parse::Nothing, parse_quote, spanned::Spanned, Error, ImplItem, ItemImpl, Token};
 
-pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream {
-    let mut toks = input.into_iter().collect::<Vec<_>>();
-    assert!(!toks.is_empty());
-    // Ensure that we have an `impl` item.
-    assert!(matches!(&toks[0], TokenTree::Ident(i) if i == "impl"));
-    // Ensure that we are implementing `PinnedDrop`.
-    let mut nesting: usize = 0;
-    let mut pinned_drop_idx = None;
-    for (i, tt) in toks.iter().enumerate() {
-        match tt {
-            TokenTree::Punct(p) if p.as_char() == '<' => {
-                nesting += 1;
+pub(crate) fn pinned_drop(_args: Nothing, mut input: ItemImpl) -> TokenStream {
+    let mut error = crate::Error::none();
+    if let Some(unsafety) = input.unsafety {
+        error.combine(Error::new_spanned(
+            unsafety,
+            "implementing `PinnedDrop` is safe",
+        ));
+    }
+    input.unsafety = Some(Token![unsafe](input.impl_token.span));
+    match &mut input.trait_ {
+        Some((not, path, _for)) => {
+            if let Some(not) = not {
+                error.combine(Error::new_spanned(not, "cannot implement `!PinnedDrop`"));
             }
-            TokenTree::Punct(p) if p.as_char() == '>' => {
-                nesting = nesting.checked_sub(1).unwrap();
-                continue;
+            for (seg, expected) in path
+                .segments
+                .iter()
+                .rev()
+                .zip(["PinnedDrop", "pin_init", ""])
+            {
+                if expected.is_empty() || seg.ident != expected {
+                    error.combine(Error::new_spanned(seg, "bad import path for `PinnedDrop`"));
+                }
+                if !seg.arguments.is_none() {
+                    error.combine(Error::new_spanned(
+                        &seg.arguments,
+                        "unexpected arguments for `PinnedDrop` path",
+                    ));
+                }
             }
-            _ => {}
+            *path = parse_quote!(::pin_init::PinnedDrop);
         }
-        if i >= 1 && nesting == 0 {
-            // Found the end of the generics, this should be `PinnedDrop`.
-            assert!(
-                matches!(tt, TokenTree::Ident(i) if i == "PinnedDrop"),
-                "expected 'PinnedDrop', found: '{tt:?}'"
-            );
-            pinned_drop_idx = Some(i);
-            break;
+        None => {
+            let span = input
+                .impl_token
+                .span
+                .join(input.self_ty.span())
+                .unwrap_or(input.impl_token.span);
+            error.combine(Error::new(
+                span,
+                "expected `impl ... PinnedDrop for ...`, got inherent impl",
+            ));
         }
     }
-    let idx = pinned_drop_idx
-        .unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`."));
-    // Fully qualify the `PinnedDrop`, as to avoid any tampering.
-    toks.splice(idx..idx, quote!(::pin_init::));
-    // Take the `{}` body and call the declarative macro.
-    if let Some(TokenTree::Group(last)) = toks.pop() {
-        let last = last.stream();
-        quote!(::pin_init::__pinned_drop! {
-            @impl_sig(#(#toks)*),
-            @impl_body(#last),
-        })
-    } else {
-        TokenStream::from_iter(toks)
+    for item in &mut input.items {
+        if let ImplItem::Fn(fn_item) = item {
+            if fn_item.sig.ident == "drop" {
+                fn_item
+                    .sig
+                    .inputs
+                    .push(parse_quote!(_: ::pin_init::__internal::OnlyCallFromDrop));
+            }
+        }
     }
+    quote!(#error #input)
 }
diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs
index 53ed5ce860fc..b80c95612fd6 100644
--- a/rust/pin-init/src/macros.rs
+++ b/rust/pin-init/src/macros.rs
@@ -503,34 +503,6 @@
 #[cfg(not(kernel))]
 pub use ::paste::paste;
 
-/// Creates a `unsafe impl<...> PinnedDrop for $type` block.
-///
-/// See [`PinnedDrop`] for more information.
-///
-/// [`PinnedDrop`]: crate::PinnedDrop
-#[doc(hidden)]
-#[macro_export]
-macro_rules! __pinned_drop {
-    (
-        @impl_sig($($impl_sig:tt)*),
-        @impl_body(
-            $(#[$($attr:tt)*])*
-            fn drop($($sig:tt)*) {
-                $($inner:tt)*
-            }
-        ),
-    ) => {
-        // SAFETY: TODO.
-        unsafe $($impl_sig)* {
-            // Inherit all attributes and the type/ident tokens for the signature.
-            $(#[$($attr)*])*
-            fn drop($($sig)*, _: $crate::__internal::OnlyCallFromDrop) {
-                $($inner)*
-            }
-        }
-    }
-}
-
 /// This macro first parses the struct definition such that it separates pinned and not pinned
 /// fields. Afterwards it declares the struct and implement the `PinData` trait safely.
 #[doc(hidden)]
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 07/15] rust: pin-init: rewrite `#[pin_data]` using `syn`
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (5 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 06/15] rust: pin-init: rewrite the `#[pinned_drop]` attribute macro " Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 08/15] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Benno Lossin
                     ` (8 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Tamir Duberstein, Alban Kurti
  Cc: linux-kernel, rust-for-linux

Rewrite the attribute macro `#[pin_data]` using `syn`. No functional
changes intended aside from improved error messages on syntactic and
semantical errors. For example if one forgets a comma at the end of a
field:

    #[pin_data]
    struct Foo {
        a: Box<Foo>
        b: Box<Foo>
    }

The declarative macro reports the following errors:

    error: expected `,`, or `}`, found `b`
     --> tests/ui/compile-fail/pin_data/missing_comma.rs:5:16
      |
    5 |     a: Box<Foo>
      |                ^ help: try adding a comma: `,`

    error: recursion limit reached while expanding `$crate::__pin_data!`
     --> tests/ui/compile-fail/pin_data/missing_comma.rs:3:1
      |
    3 | #[pin_data]
      | ^^^^^^^^^^^
      |
      = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`)
      = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)

The new `syn` version reports:

    error: expected `,`, or `}`, found `b`
     --> tests/ui/compile-fail/pin_data/missing_comma.rs:5:16
      |
    5 |     a: Box<Foo>
      |                ^ help: try adding a comma: `,`

    error: expected `,`
     --> tests/ui/compile-fail/pin_data/missing_comma.rs:6:5
      |
    6 |     b: Box<Foo>
      |     ^

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2:
* improved error handling
* fix clippy warnings
* fix typos and variable names
* collect the information about the pinned/not pinned fields only once
  at the beginning
---
 rust/pin-init/internal/src/helpers.rs  | 149 ------
 rust/pin-init/internal/src/lib.rs      |   8 +-
 rust/pin-init/internal/src/pin_data.rs | 630 ++++++++++++++++++++-----
 rust/pin-init/src/macros.rs            | 574 ----------------------
 4 files changed, 523 insertions(+), 838 deletions(-)
 delete mode 100644 rust/pin-init/internal/src/helpers.rs

diff --git a/rust/pin-init/internal/src/helpers.rs b/rust/pin-init/internal/src/helpers.rs
deleted file mode 100644
index 90f85eaa4123..000000000000
--- a/rust/pin-init/internal/src/helpers.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0 OR MIT
-
-use proc_macro2::{TokenStream, TokenTree};
-
-/// Parsed generics.
-///
-/// See the field documentation for an explanation what each of the fields represents.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// # let input = todo!();
-/// let (Generics { decl_generics, impl_generics, ty_generics }, rest) = parse_generics(input);
-/// quote! {
-///     struct Foo<$($decl_generics)*> {
-///         // ...
-///     }
-///
-///     impl<$impl_generics> Foo<$ty_generics> {
-///         fn foo() {
-///             // ...
-///         }
-///     }
-/// }
-/// ```
-pub(crate) struct Generics {
-    /// The generics with bounds and default values (e.g. `T: Clone, const N: usize = 0`).
-    ///
-    /// Use this on type definitions e.g. `struct Foo<$decl_generics> ...` (or `union`/`enum`).
-    pub(crate) decl_generics: Vec<TokenTree>,
-    /// The generics with bounds (e.g. `T: Clone, const N: usize`).
-    ///
-    /// Use this on `impl` blocks e.g. `impl<$impl_generics> Trait for ...`.
-    pub(crate) impl_generics: Vec<TokenTree>,
-    /// The generics without bounds and without default values (e.g. `T, N`).
-    ///
-    /// Use this when you use the type that is declared with these generics e.g.
-    /// `Foo<$ty_generics>`.
-    pub(crate) ty_generics: Vec<TokenTree>,
-}
-
-/// Parses the given `TokenStream` into `Generics` and the rest.
-///
-/// The generics are not present in the rest, but a where clause might remain.
-pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
-    // The generics with bounds and default values.
-    let mut decl_generics = vec![];
-    // `impl_generics`, the declared generics with their bounds.
-    let mut impl_generics = vec![];
-    // Only the names of the generics, without any bounds.
-    let mut ty_generics = vec![];
-    // Tokens not related to the generics e.g. the `where` token and definition.
-    let mut rest = vec![];
-    // The current level of `<`.
-    let mut nesting = 0;
-    let mut toks = input.into_iter();
-    // If we are at the beginning of a generic parameter.
-    let mut at_start = true;
-    let mut skip_until_comma = false;
-    while let Some(tt) = toks.next() {
-        if nesting == 1 && matches!(&tt, TokenTree::Punct(p) if p.as_char() == '>') {
-            // Found the end of the generics.
-            break;
-        } else if nesting >= 1 {
-            decl_generics.push(tt.clone());
-        }
-        match tt.clone() {
-            TokenTree::Punct(p) if p.as_char() == '<' => {
-                if nesting >= 1 && !skip_until_comma {
-                    // This is inside of the generics and part of some bound.
-                    impl_generics.push(tt);
-                }
-                nesting += 1;
-            }
-            TokenTree::Punct(p) if p.as_char() == '>' => {
-                // This is a parsing error, so we just end it here.
-                if nesting == 0 {
-                    break;
-                } else {
-                    nesting -= 1;
-                    if nesting >= 1 && !skip_until_comma {
-                        // We are still inside of the generics and part of some bound.
-                        impl_generics.push(tt);
-                    }
-                }
-            }
-            TokenTree::Punct(p) if skip_until_comma && p.as_char() == ',' => {
-                if nesting == 1 {
-                    impl_generics.push(tt.clone());
-                    impl_generics.push(tt);
-                    skip_until_comma = false;
-                }
-            }
-            _ if !skip_until_comma => {
-                match nesting {
-                    // If we haven't entered the generics yet, we still want to keep these tokens.
-                    0 => rest.push(tt),
-                    1 => {
-                        // Here depending on the token, it might be a generic variable name.
-                        match tt.clone() {
-                            TokenTree::Ident(i) if at_start && i == "const" => {
-                                let Some(name) = toks.next() else {
-                                    // Parsing error.
-                                    break;
-                                };
-                                impl_generics.push(tt);
-                                impl_generics.push(name.clone());
-                                ty_generics.push(name.clone());
-                                decl_generics.push(name);
-                                at_start = false;
-                            }
-                            TokenTree::Ident(_) if at_start => {
-                                impl_generics.push(tt.clone());
-                                ty_generics.push(tt);
-                                at_start = false;
-                            }
-                            TokenTree::Punct(p) if p.as_char() == ',' => {
-                                impl_generics.push(tt.clone());
-                                ty_generics.push(tt);
-                                at_start = true;
-                            }
-                            // Lifetimes begin with `'`.
-                            TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
-                                impl_generics.push(tt.clone());
-                                ty_generics.push(tt);
-                            }
-                            // Generics can have default values, we skip these.
-                            TokenTree::Punct(p) if p.as_char() == '=' => {
-                                skip_until_comma = true;
-                            }
-                            _ => impl_generics.push(tt),
-                        }
-                    }
-                    _ => impl_generics.push(tt),
-                }
-            }
-            _ => {}
-        }
-    }
-    rest.extend(toks);
-    (
-        Generics {
-            impl_generics,
-            decl_generics,
-            ty_generics,
-        },
-        rest,
-    )
-}
diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 04794c864c78..07ea6e0970a0 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -13,14 +13,16 @@
 use proc_macro::TokenStream;
 use syn::parse_macro_input;
 
-mod helpers;
 mod pin_data;
 mod pinned_drop;
 mod zeroable;
 
 #[proc_macro_attribute]
-pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
-    pin_data::pin_data(inner.into(), item.into()).into()
+pub fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
+    ok_or_compile_error(pin_data::pin_data(
+        parse_macro_input!(args),
+        parse_macro_input!(input),
+    ))
 }
 
 #[proc_macro_attribute]
diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 86a53b37cc66..3211fb522330 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -1,126 +1,532 @@
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
-use crate::helpers::{parse_generics, Generics};
-use proc_macro2::{Group, Punct, Spacing, TokenStream, TokenTree};
-use quote::quote;
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use syn::{
+    parse::{End, Nothing, Parse},
+    parse_quote, parse_quote_spanned,
+    spanned::Spanned,
+    visit_mut::VisitMut,
+    Error, Field, Generics, Ident, Item, PathSegment, Result, Type, TypePath, Visibility,
+    WhereClause,
+};
 
-pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
-    // This proc-macro only does some pre-parsing and then delegates the actual parsing to
-    // `pin_init::__pin_data!`.
+pub(crate) mod kw {
+    syn::custom_keyword!(PinnedDrop);
+}
 
-    let (
-        Generics {
-            impl_generics,
-            decl_generics,
-            ty_generics,
-        },
-        rest,
-    ) = parse_generics(input);
-    // The struct definition might contain the `Self` type. Since `__pin_data!` will define a new
-    // type with the same generics and bounds, this poses a problem, since `Self` will refer to the
-    // new type as opposed to this struct definition. Therefore we have to replace `Self` with the
-    // concrete name.
-
-    // Errors that occur when replacing `Self` with `struct_name`.
-    let mut errs = TokenStream::new();
-    // The name of the struct with ty_generics.
-    let struct_name = rest
+pub(crate) enum Args {
+    Nothing(Nothing),
+    #[allow(dead_code)]
+    PinnedDrop(kw::PinnedDrop),
+}
+
+impl Parse for Args {
+    fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
+        let lh = input.lookahead1();
+        if lh.peek(End) {
+            input.parse().map(Self::Nothing)
+        } else if lh.peek(kw::PinnedDrop) {
+            let res = input.parse().map(Self::PinnedDrop)?;
+            let lh = input.lookahead1();
+            if lh.peek(End) {
+                Ok(res)
+            } else {
+                Err(lh.error())
+            }
+        } else {
+            Err(lh.error())
+        }
+    }
+}
+
+pub(crate) fn pin_data(args: Args, input: Item) -> Result<TokenStream> {
+    let mut struct_ = match input {
+        Item::Struct(struct_) => struct_,
+        Item::Enum(enum_) => {
+            return Err(Error::new_spanned(
+                enum_.enum_token,
+                "`#[pin_data]` only supports structs for now",
+            ));
+        }
+        Item::Union(union) => {
+            return Err(Error::new_spanned(
+                union.union_token,
+                "`#[pin_data]` only supports structs for now",
+            ));
+        }
+        rest => {
+            return Err(Error::new_spanned(
+                rest,
+                "`#[pin_data]` can only be applied to struct, enum and union definitions",
+            ));
+        }
+    };
+
+    // The generics might contain the `Self` type. Since this macro will define a new type with the
+    // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed
+    // to this struct definition. Therefore we have to replace `Self` with the concrete name.
+    let mut replacer = {
+        let name = &struct_.ident;
+        let (_, ty_generics, _) = struct_.generics.split_for_impl();
+        SelfReplacer(parse_quote!(#name #ty_generics))
+    };
+    replacer.visit_generics_mut(&mut struct_.generics);
+    replacer.visit_fields_mut(&mut struct_.fields);
+
+    let fields: Vec<(bool, Field)> = struct_
+        .fields
         .iter()
-        .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i == "struct"))
-        .nth(1)
-        .and_then(|tt| match tt {
-            TokenTree::Ident(_) => {
-                let tt = tt.clone();
-                let mut res = vec![tt];
-                if !ty_generics.is_empty() {
-                    // We add this, so it is maximally compatible with e.g. `Self::CONST` which
-                    // will be replaced by `StructName::<$generics>::CONST`.
-                    res.push(TokenTree::Punct(Punct::new(':', Spacing::Joint)));
-                    res.push(TokenTree::Punct(Punct::new(':', Spacing::Alone)));
-                    res.push(TokenTree::Punct(Punct::new('<', Spacing::Alone)));
-                    res.extend(ty_generics.iter().cloned());
-                    res.push(TokenTree::Punct(Punct::new('>', Spacing::Alone)));
+        .cloned()
+        .map(|mut field| {
+            let pinned = is_field_structurally_pinned(&field);
+            field.attrs.retain(|a| !a.path().is_ident("pin"));
+            (pinned, field)
+        })
+        .collect();
+
+    let mut error: crate::Error = crate::Error::none();
+    for (pinned, field) in &fields {
+        if !pinned && is_phantom_pinned(&field.ty) {
+            error.combine(Error::new_spanned(
+                field,
+                format!(
+                    "The field `{}` of type `PhantomPinned` only has an effect \
+                    if it has the `#[pin]` attribute",
+                    field.ident.as_ref().unwrap(),
+                ),
+            ));
+        }
+    }
+
+    let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
+    let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
+    let projections =
+        generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
+    let the_pin_data =
+        generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
+
+    strip_pin_annotations(&mut struct_);
+
+    Ok(quote! {
+        #struct_
+        #projections
+        // We put the rest into this const item, because it then will not be accessible to anything
+        // outside.
+        const _: () = {
+            #the_pin_data
+            #unpin_impl
+            #drop_impl
+        };
+        #error
+    })
+}
+
+fn is_phantom_pinned(ty: &Type) -> bool {
+    match ty {
+        Type::Path(TypePath { qself: None, path }) => {
+            // Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user).
+            if path.segments.len() > 3 {
+                return false;
+            }
+            // If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or
+            // `::std::marker::PhantomPinned`.
+            if path.leading_colon.is_some() && path.segments.len() != 3 {
+                return false;
+            }
+            let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
+            for (actual, expected) in path.segments.iter().rev().zip(expected) {
+                if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
+                    return false;
                 }
-                Some(res)
             }
-            _ => None,
-        })
-        .unwrap_or_else(|| {
-            // If we did not find the name of the struct then we will use `Self` as the replacement
-            // and add a compile error to ensure it does not compile.
-            errs.extend(
-                "::core::compile_error!(\"Could not locate type name.\");"
-                    .parse::<TokenStream>()
-                    .unwrap(),
-            );
-            "Self".parse::<TokenStream>().unwrap().into_iter().collect()
-        });
-    let impl_generics = impl_generics
-        .into_iter()
-        .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &mut errs))
-        .collect::<Vec<_>>();
-    let mut rest = rest
-        .into_iter()
-        .flat_map(|tt| {
-            // We ignore top level `struct` tokens, since they would emit a compile error.
-            if matches!(&tt, TokenTree::Ident(i) if i == "struct") {
-                vec![tt]
+            true
+        }
+        _ => false,
+    }
+}
+
+fn is_field_structurally_pinned(field: &Field) -> bool {
+    field.attrs.iter().any(|a| a.path().is_ident("pin"))
+}
+
+fn generate_unpin_impl(
+    ident: &Ident,
+    generics: &Generics,
+    fields: &[(bool, Field)],
+) -> TokenStream {
+    let generics_with_pin_lt = {
+        let mut g = generics.clone();
+        g.params.insert(0, parse_quote!('__pin));
+        let _ = g.make_where_clause();
+        g
+    };
+    let (
+        impl_generics_with_pin_lt,
+        ty_generics_with_pin_lt,
+        Some(WhereClause {
+            where_token,
+            predicates,
+        }),
+    ) = generics_with_pin_lt.split_for_impl()
+    else {
+        unreachable!()
+    };
+    let (_, ty_generics, _) = generics.split_for_impl();
+    let pinned_fields = fields.iter().filter_map(|(b, f)| b.then_some(f));
+    quote! {
+        // This struct will be used for the unpin analysis. It is needed, because only structurally
+        // pinned fields are relevant whether the struct should implement `Unpin`.
+        #[allow(dead_code)] // The fields below are never used.
+        struct __Unpin #generics_with_pin_lt
+        #where_token
+            #predicates
+        {
+            __phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
+            __phantom: ::core::marker::PhantomData<
+                fn(#ident #ty_generics) -> #ident #ty_generics
+            >,
+            #(#pinned_fields),*
+        }
+
+        #[doc(hidden)]
+        impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics
+        #where_token
+            __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin,
+            #predicates
+        {}
+    }
+}
+
+fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {
+    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
+    let has_pinned_drop = matches!(args, Args::PinnedDrop(_));
+    // We need to disallow normal `Drop` implementation, the exact behavior depends on whether
+    // `PinnedDrop` was specified in `args`.
+    if has_pinned_drop {
+        // When `PinnedDrop` was specified we just implement `Drop` and delegate.
+        quote! {
+            impl #impl_generics ::core::ops::Drop for #ident #ty_generics
+                #whr
+            {
+                fn drop(&mut self) {
+                    // SAFETY: Since this is a destructor, `self` will not move after this function
+                    // terminates, since it is inaccessible.
+                    let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
+                    // SAFETY: Since this is a drop function, we can create this token to call the
+                    // pinned destructor of this type.
+                    let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
+                    ::pin_init::PinnedDrop::drop(pinned, token);
+                }
+            }
+        }
+    } else {
+        // When no `PinnedDrop` was specified, then we have to prevent implementing drop.
+        quote! {
+            // We prevent this by creating a trait that will be implemented for all types implementing
+            // `Drop`. Additionally we will implement this trait for the struct leading to a conflict,
+            // if it also implements `Drop`
+            trait MustNotImplDrop {}
+            #[expect(drop_bounds)]
+            impl<T: ::core::ops::Drop> MustNotImplDrop for T {}
+            impl #impl_generics MustNotImplDrop for #ident #ty_generics
+                #whr
+            {}
+            // We also take care to prevent users from writing a useless `PinnedDrop` implementation.
+            // They might implement `PinnedDrop` correctly for the struct, but forget to give
+            // `PinnedDrop` as the parameter to `#[pin_data]`.
+            #[expect(non_camel_case_types)]
+            trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
+            impl<T: ::pin_init::PinnedDrop>
+                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
+            impl #impl_generics
+                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
+                #whr
+            {}
+        }
+    }
+}
+
+fn generate_projections(
+    vis: &Visibility,
+    ident: &Ident,
+    generics: &Generics,
+    fields: &[(bool, Field)],
+) -> TokenStream {
+    let (impl_generics, ty_generics, _) = generics.split_for_impl();
+    let mut generics = generics.clone();
+    generics.params.insert(0, parse_quote!('__pin));
+    let (_, ty_generics_with_pin_lt, whr) = generics.split_for_impl();
+    let projection = format_ident!("{ident}Projection");
+    let this = format_ident!("this");
+
+    let (fields_decl, fields_proj) = collect_tuple(fields.iter().map(
+        |(
+            pinned,
+            Field {
+                vis,
+                ident,
+                ty,
+                attrs,
+                ..
+            },
+        )| {
+            let mut attrs = attrs.clone();
+            attrs.retain(|a| !a.path().is_ident("pin"));
+            let mut no_doc_attrs = attrs.clone();
+            no_doc_attrs.retain(|a| !a.path().is_ident("doc"));
+            let ident = ident
+                .as_ref()
+                .expect("only structs with named fields are supported");
+            if *pinned {
+                (
+                    quote!(
+                        #(#attrs)*
+                        #vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
+                    ),
+                    quote!(
+                        #(#no_doc_attrs)*
+                        // SAFETY: this field is structurally pinned.
+                        #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
+                    ),
+                )
             } else {
-                replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)
+                (
+                    quote!(
+                        #(#attrs)*
+                        #vis #ident: &'__pin mut #ty,
+                    ),
+                    quote!(
+                        #(#no_doc_attrs)*
+                        #ident: &mut #this.#ident,
+                    ),
+                )
             }
-        })
-        .collect::<Vec<_>>();
-    // This should be the body of the struct `{...}`.
-    let last = rest.pop();
-    let mut quoted = quote!(::pin_init::__pin_data! {
-        parse_input:
-        @args(#args),
-        @sig(#(#rest)*),
-        @impl_generics(#(#impl_generics)*),
-        @ty_generics(#(#ty_generics)*),
-        @decl_generics(#(#decl_generics)*),
-        @body(#last),
-    });
-    quoted.extend(errs);
-    quoted
+        },
+    ));
+    let structurally_pinned_fields_docs = fields
+        .iter()
+        .filter_map(|(pinned, field)| pinned.then_some(field))
+        .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
+    let not_structurally_pinned_fields_docs = fields
+        .iter()
+        .filter_map(|(pinned, field)| (!pinned).then_some(field))
+        .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
+    let docs = format!(" Pin-projections of [`{ident}`]");
+    quote! {
+        #[doc = #docs]
+        #[allow(dead_code)]
+        #[doc(hidden)]
+        #vis struct #projection #generics {
+            #(#fields_decl)*
+            ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
+        }
+
+        impl #impl_generics #ident #ty_generics
+            #whr
+        {
+            /// Pin-projects all fields of `Self`.
+            ///
+            /// These fields are structurally pinned:
+            #(#[doc = #structurally_pinned_fields_docs])*
+            ///
+            /// These fields are **not** structurally pinned:
+            #(#[doc = #not_structurally_pinned_fields_docs])*
+            #[inline]
+            #vis fn project<'__pin>(
+                self: ::core::pin::Pin<&'__pin mut Self>,
+            ) -> #projection #ty_generics_with_pin_lt {
+                // SAFETY: we only give access to `&mut` for fields not structurally pinned.
+                let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
+                #projection {
+                    #(#fields_proj)*
+                    ___pin_phantom_data: ::core::marker::PhantomData,
+                }
+            }
+        }
+    }
 }
 
-/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `struct` `union` and `impl`
-/// keywords.
-///
-/// The error is appended to `errs` to allow normal parsing to continue.
-fn replace_self_and_deny_type_defs(
-    struct_name: &Vec<TokenTree>,
-    tt: TokenTree,
-    errs: &mut TokenStream,
-) -> Vec<TokenTree> {
-    match tt {
-        TokenTree::Ident(ref i)
-            if i == "enum" || i == "trait" || i == "struct" || i == "union" || i == "impl" =>
+fn generate_the_pin_data(
+    vis: &Visibility,
+    ident: &Ident,
+    generics: &Generics,
+    fields: &[(bool, Field)],
+) -> TokenStream {
+    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
+
+    // For every field, we create an initializing projection function according to its projection
+    // type. If a field is structurally pinned, then it must be initialized via `PinInit`, if it is
+    // not structurally pinned, then it can be initialized via `Init`.
+    //
+    // The functions are `unsafe` to prevent accidentally calling them.
+    fn handle_field(
+        Field {
+            vis,
+            ident,
+            ty,
+            attrs,
+            ..
+        }: &Field,
+        struct_ident: &Ident,
+        pinned: bool,
+    ) -> TokenStream {
+        let mut attrs = attrs.clone();
+        attrs.retain(|a| !a.path().is_ident("pin"));
+        let ident = ident
+            .as_ref()
+            .expect("only structs with named fields are supported");
+        let project_ident = format_ident!("__project_{ident}");
+        let (init_ty, init_fn, project_ty, project_body, pin_safety) = if pinned {
+            (
+                quote!(PinInit),
+                quote!(__pinned_init),
+                quote!(::core::pin::Pin<&'__slot mut #ty>),
+                // SAFETY: this field is structurally pinned.
+                quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }),
+                quote!(
+                    /// - `slot` will not move until it is dropped, i.e. it will be pinned.
+                ),
+            )
+        } else {
+            (
+                quote!(Init),
+                quote!(__init),
+                quote!(&'__slot mut #ty),
+                quote!(slot),
+                quote!(),
+            )
+        };
+        let slot_safety = format!(
+            " `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.",
+        );
+        quote! {
+            /// # Safety
+            ///
+            /// - `slot` is a valid pointer to uninitialized memory.
+            /// - the caller does not touch `slot` when `Err` is returned, they are only permitted
+            ///   to deallocate.
+            #pin_safety
+            #(#attrs)*
+            #vis unsafe fn #ident<E>(
+                self,
+                slot: *mut #ty,
+                init: impl ::pin_init::#init_ty<#ty, E>,
+            ) -> ::core::result::Result<(), E> {
+                // SAFETY: this function has the same safety requirements as the __init function
+                // called below.
+                unsafe { ::pin_init::#init_ty::#init_fn(init, slot) }
+            }
+
+            /// # Safety
+            ///
+            #[doc = #slot_safety]
+            #(#attrs)*
+            #vis unsafe fn #project_ident<'__slot>(
+                self,
+                slot: &'__slot mut #ty,
+            ) -> #project_ty {
+                #project_body
+            }
+        }
+    }
+
+    let field_accessors = fields
+        .iter()
+        .map(|(pinned, field)| handle_field(field, ident, *pinned))
+        .collect::<TokenStream>();
+    quote! {
+        // We declare this struct which will host all of the projection function for our type. It
+        // will be invariant over all generic parameters which are inherited from the struct.
+        #[doc(hidden)]
+        #vis struct __ThePinData #generics
+            #whr
         {
-            errs.extend(
-                format!(
-                    "::core::compile_error!(\"Cannot use `{i}` inside of struct definition with \
-                        `#[pin_data]`.\");"
-                )
-                .parse::<TokenStream>()
-                .unwrap()
-                .into_iter()
-                .map(|mut tok| {
-                    tok.set_span(tt.span());
-                    tok
-                }),
-            );
-            vec![tt]
-        }
-        TokenTree::Ident(i) if i == "Self" => struct_name.clone(),
-        TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt],
-        TokenTree::Group(g) => vec![TokenTree::Group(Group::new(
-            g.delimiter(),
-            g.stream()
-                .into_iter()
-                .flat_map(|tt| replace_self_and_deny_type_defs(struct_name, tt, errs))
-                .collect(),
-        ))],
+            __phantom: ::core::marker::PhantomData<
+                fn(#ident #ty_generics) -> #ident #ty_generics
+            >,
+        }
+
+        impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
+            #whr
+        {
+            fn clone(&self) -> Self { *self }
+        }
+
+        impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
+            #whr
+        {}
+
+        #[allow(dead_code)] // Some functions might never be used and private.
+        #[expect(clippy::missing_safety_doc)]
+        impl #impl_generics __ThePinData #ty_generics
+            #whr
+        {
+            #field_accessors
+        }
+
+        // SAFETY: We have added the correct projection functions above to `__ThePinData` and
+        // we also use the least restrictive generics possible.
+        unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #ident #ty_generics
+            #whr
+        {
+            type PinData = __ThePinData #ty_generics;
+
+            unsafe fn __pin_data() -> Self::PinData {
+                __ThePinData { __phantom: ::core::marker::PhantomData }
+            }
+        }
+
+        // SAFETY: TODO
+        unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics
+            #whr
+        {
+            type Datee = #ident #ty_generics;
+        }
+    }
+}
+
+fn strip_pin_annotations(struct_: &mut syn::ItemStruct) {
+    for field in &mut struct_.fields {
+        field.attrs.retain(|a| !a.path().is_ident("pin"));
+    }
+}
+
+struct SelfReplacer(PathSegment);
+
+impl VisitMut for SelfReplacer {
+    fn visit_path_mut(&mut self, i: &mut syn::Path) {
+        if i.is_ident("Self") {
+            let span = i.span();
+            let seg = &self.0;
+            *i = parse_quote_spanned!(span=> #seg);
+        } else {
+            syn::visit_mut::visit_path_mut(self, i);
+        }
+    }
+
+    fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) {
+        if seg.ident == "Self" {
+            let span = seg.span();
+            let this = &self.0;
+            *seg = parse_quote_spanned!(span=> #this);
+        } else {
+            syn::visit_mut::visit_path_segment_mut(self, seg);
+        }
+    }
+
+    fn visit_item_mut(&mut self, _: &mut Item) {
+        // Do not descend into items, since items reset/change what `Self` refers to.
+    }
+}
+
+// replace with `.collect()` once MSRV is above 1.79
+fn collect_tuple<A, B>(iter: impl Iterator<Item = (A, B)>) -> (Vec<A>, Vec<B>) {
+    let mut res_a = vec![];
+    let mut res_b = vec![];
+    for (a, b) in iter {
+        res_a.push(a);
+        res_b.push(b);
     }
+    (res_a, res_b)
 }
diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs
index b80c95612fd6..eea8adc5c7ad 100644
--- a/rust/pin-init/src/macros.rs
+++ b/rust/pin-init/src/macros.rs
@@ -503,580 +503,6 @@
 #[cfg(not(kernel))]
 pub use ::paste::paste;
 
-/// This macro first parses the struct definition such that it separates pinned and not pinned
-/// fields. Afterwards it declares the struct and implement the `PinData` trait safely.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! __pin_data {
-    // Proc-macro entry point, this is supplied by the proc-macro pre-parsing.
-    (parse_input:
-        @args($($pinned_drop:ident)?),
-        @sig(
-            $(#[$($struct_attr:tt)*])*
-            $vis:vis struct $name:ident
-            $(where $($whr:tt)*)?
-        ),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @body({ $($fields:tt)* }),
-    ) => {
-        // We now use token munching to iterate through all of the fields. While doing this we
-        // identify fields marked with `#[pin]`, these fields are the 'pinned fields'. The user
-        // wants these to be structurally pinned. The rest of the fields are the
-        // 'not pinned fields'. Additionally we collect all fields, since we need them in the right
-        // order to declare the struct.
-        //
-        // In this call we also put some explaining comments for the parameters.
-        $crate::__pin_data!(find_pinned_fields:
-            // Attributes on the struct itself, these will just be propagated to be put onto the
-            // struct definition.
-            @struct_attrs($(#[$($struct_attr)*])*),
-            // The visibility of the struct.
-            @vis($vis),
-            // The name of the struct.
-            @name($name),
-            // The 'impl generics', the generics that will need to be specified on the struct inside
-            // of an `impl<$ty_generics>` block.
-            @impl_generics($($impl_generics)*),
-            // The 'ty generics', the generics that will need to be specified on the impl blocks.
-            @ty_generics($($ty_generics)*),
-            // The 'decl generics', the generics that need to be specified on the struct
-            // definition.
-            @decl_generics($($decl_generics)*),
-            // The where clause of any impl block and the declaration.
-            @where($($($whr)*)?),
-            // The remaining fields tokens that need to be processed.
-            // We add a `,` at the end to ensure correct parsing.
-            @fields_munch($($fields)* ,),
-            // The pinned fields.
-            @pinned(),
-            // The not pinned fields.
-            @not_pinned(),
-            // All fields.
-            @fields(),
-            // The accumulator containing all attributes already parsed.
-            @accum(),
-            // Contains `yes` or `` to indicate if `#[pin]` was found on the current field.
-            @is_pinned(),
-            // The proc-macro argument, this should be `PinnedDrop` or ``.
-            @pinned_drop($($pinned_drop)?),
-        );
-    };
-    (find_pinned_fields:
-        @struct_attrs($($struct_attrs:tt)*),
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        // We found a PhantomPinned field, this should generally be pinned!
-        @fields_munch($field:ident : $($($(::)?core::)?marker::)?PhantomPinned, $($rest:tt)*),
-        @pinned($($pinned:tt)*),
-        @not_pinned($($not_pinned:tt)*),
-        @fields($($fields:tt)*),
-        @accum($($accum:tt)*),
-        // This field is not pinned.
-        @is_pinned(),
-        @pinned_drop($($pinned_drop:ident)?),
-    ) => {
-        ::core::compile_error!(concat!(
-            "The field `",
-            stringify!($field),
-            "` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute.",
-        ));
-        $crate::__pin_data!(find_pinned_fields:
-            @struct_attrs($($struct_attrs)*),
-            @vis($vis),
-            @name($name),
-            @impl_generics($($impl_generics)*),
-            @ty_generics($($ty_generics)*),
-            @decl_generics($($decl_generics)*),
-            @where($($whr)*),
-            @fields_munch($($rest)*),
-            @pinned($($pinned)* $($accum)* $field: ::core::marker::PhantomPinned,),
-            @not_pinned($($not_pinned)*),
-            @fields($($fields)* $($accum)* $field: ::core::marker::PhantomPinned,),
-            @accum(),
-            @is_pinned(),
-            @pinned_drop($($pinned_drop)?),
-        );
-    };
-    (find_pinned_fields:
-        @struct_attrs($($struct_attrs:tt)*),
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        // We reached the field declaration.
-        @fields_munch($field:ident : $type:ty, $($rest:tt)*),
-        @pinned($($pinned:tt)*),
-        @not_pinned($($not_pinned:tt)*),
-        @fields($($fields:tt)*),
-        @accum($($accum:tt)*),
-        // This field is pinned.
-        @is_pinned(yes),
-        @pinned_drop($($pinned_drop:ident)?),
-    ) => {
-        $crate::__pin_data!(find_pinned_fields:
-            @struct_attrs($($struct_attrs)*),
-            @vis($vis),
-            @name($name),
-            @impl_generics($($impl_generics)*),
-            @ty_generics($($ty_generics)*),
-            @decl_generics($($decl_generics)*),
-            @where($($whr)*),
-            @fields_munch($($rest)*),
-            @pinned($($pinned)* $($accum)* $field: $type,),
-            @not_pinned($($not_pinned)*),
-            @fields($($fields)* $($accum)* $field: $type,),
-            @accum(),
-            @is_pinned(),
-            @pinned_drop($($pinned_drop)?),
-        );
-    };
-    (find_pinned_fields:
-        @struct_attrs($($struct_attrs:tt)*),
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        // We reached the field declaration.
-        @fields_munch($field:ident : $type:ty, $($rest:tt)*),
-        @pinned($($pinned:tt)*),
-        @not_pinned($($not_pinned:tt)*),
-        @fields($($fields:tt)*),
-        @accum($($accum:tt)*),
-        // This field is not pinned.
-        @is_pinned(),
-        @pinned_drop($($pinned_drop:ident)?),
-    ) => {
-        $crate::__pin_data!(find_pinned_fields:
-            @struct_attrs($($struct_attrs)*),
-            @vis($vis),
-            @name($name),
-            @impl_generics($($impl_generics)*),
-            @ty_generics($($ty_generics)*),
-            @decl_generics($($decl_generics)*),
-            @where($($whr)*),
-            @fields_munch($($rest)*),
-            @pinned($($pinned)*),
-            @not_pinned($($not_pinned)* $($accum)* $field: $type,),
-            @fields($($fields)* $($accum)* $field: $type,),
-            @accum(),
-            @is_pinned(),
-            @pinned_drop($($pinned_drop)?),
-        );
-    };
-    (find_pinned_fields:
-        @struct_attrs($($struct_attrs:tt)*),
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        // We found the `#[pin]` attr.
-        @fields_munch(#[pin] $($rest:tt)*),
-        @pinned($($pinned:tt)*),
-        @not_pinned($($not_pinned:tt)*),
-        @fields($($fields:tt)*),
-        @accum($($accum:tt)*),
-        @is_pinned($($is_pinned:ident)?),
-        @pinned_drop($($pinned_drop:ident)?),
-    ) => {
-        $crate::__pin_data!(find_pinned_fields:
-            @struct_attrs($($struct_attrs)*),
-            @vis($vis),
-            @name($name),
-            @impl_generics($($impl_generics)*),
-            @ty_generics($($ty_generics)*),
-            @decl_generics($($decl_generics)*),
-            @where($($whr)*),
-            @fields_munch($($rest)*),
-            // We do not include `#[pin]` in the list of attributes, since it is not actually an
-            // attribute that is defined somewhere.
-            @pinned($($pinned)*),
-            @not_pinned($($not_pinned)*),
-            @fields($($fields)*),
-            @accum($($accum)*),
-            // Set this to `yes`.
-            @is_pinned(yes),
-            @pinned_drop($($pinned_drop)?),
-        );
-    };
-    (find_pinned_fields:
-        @struct_attrs($($struct_attrs:tt)*),
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        // We reached the field declaration with visibility, for simplicity we only munch the
-        // visibility and put it into `$accum`.
-        @fields_munch($fvis:vis $field:ident $($rest:tt)*),
-        @pinned($($pinned:tt)*),
-        @not_pinned($($not_pinned:tt)*),
-        @fields($($fields:tt)*),
-        @accum($($accum:tt)*),
-        @is_pinned($($is_pinned:ident)?),
-        @pinned_drop($($pinned_drop:ident)?),
-    ) => {
-        $crate::__pin_data!(find_pinned_fields:
-            @struct_attrs($($struct_attrs)*),
-            @vis($vis),
-            @name($name),
-            @impl_generics($($impl_generics)*),
-            @ty_generics($($ty_generics)*),
-            @decl_generics($($decl_generics)*),
-            @where($($whr)*),
-            @fields_munch($field $($rest)*),
-            @pinned($($pinned)*),
-            @not_pinned($($not_pinned)*),
-            @fields($($fields)*),
-            @accum($($accum)* $fvis),
-            @is_pinned($($is_pinned)?),
-            @pinned_drop($($pinned_drop)?),
-        );
-    };
-    (find_pinned_fields:
-        @struct_attrs($($struct_attrs:tt)*),
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        // Some other attribute, just put it into `$accum`.
-        @fields_munch(#[$($attr:tt)*] $($rest:tt)*),
-        @pinned($($pinned:tt)*),
-        @not_pinned($($not_pinned:tt)*),
-        @fields($($fields:tt)*),
-        @accum($($accum:tt)*),
-        @is_pinned($($is_pinned:ident)?),
-        @pinned_drop($($pinned_drop:ident)?),
-    ) => {
-        $crate::__pin_data!(find_pinned_fields:
-            @struct_attrs($($struct_attrs)*),
-            @vis($vis),
-            @name($name),
-            @impl_generics($($impl_generics)*),
-            @ty_generics($($ty_generics)*),
-            @decl_generics($($decl_generics)*),
-            @where($($whr)*),
-            @fields_munch($($rest)*),
-            @pinned($($pinned)*),
-            @not_pinned($($not_pinned)*),
-            @fields($($fields)*),
-            @accum($($accum)* #[$($attr)*]),
-            @is_pinned($($is_pinned)?),
-            @pinned_drop($($pinned_drop)?),
-        );
-    };
-    (find_pinned_fields:
-        @struct_attrs($($struct_attrs:tt)*),
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        // We reached the end of the fields, plus an optional additional comma, since we added one
-        // before and the user is also allowed to put a trailing comma.
-        @fields_munch($(,)?),
-        @pinned($($pinned:tt)*),
-        @not_pinned($($not_pinned:tt)*),
-        @fields($($fields:tt)*),
-        @accum(),
-        @is_pinned(),
-        @pinned_drop($($pinned_drop:ident)?),
-    ) => {
-        // Declare the struct with all fields in the correct order.
-        $($struct_attrs)*
-        $vis struct $name <$($decl_generics)*>
-        where $($whr)*
-        {
-            $($fields)*
-        }
-
-        $crate::__pin_data!(make_pin_projections:
-            @vis($vis),
-            @name($name),
-            @impl_generics($($impl_generics)*),
-            @ty_generics($($ty_generics)*),
-            @decl_generics($($decl_generics)*),
-            @where($($whr)*),
-            @pinned($($pinned)*),
-            @not_pinned($($not_pinned)*),
-        );
-
-        // We put the rest into this const item, because it then will not be accessible to anything
-        // outside.
-        const _: () = {
-            // We declare this struct which will host all of the projection function for our type.
-            // it will be invariant over all generic parameters which are inherited from the
-            // struct.
-            $vis struct __ThePinData<$($impl_generics)*>
-            where $($whr)*
-            {
-                __phantom: ::core::marker::PhantomData<
-                    fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*>
-                >,
-            }
-
-            impl<$($impl_generics)*> ::core::clone::Clone for __ThePinData<$($ty_generics)*>
-            where $($whr)*
-            {
-                fn clone(&self) -> Self { *self }
-            }
-
-            impl<$($impl_generics)*> ::core::marker::Copy for __ThePinData<$($ty_generics)*>
-            where $($whr)*
-            {}
-
-            // Make all projection functions.
-            $crate::__pin_data!(make_pin_data:
-                @pin_data(__ThePinData),
-                @impl_generics($($impl_generics)*),
-                @ty_generics($($ty_generics)*),
-                @where($($whr)*),
-                @pinned($($pinned)*),
-                @not_pinned($($not_pinned)*),
-            );
-
-            // SAFETY: We have added the correct projection functions above to `__ThePinData` and
-            // we also use the least restrictive generics possible.
-            unsafe impl<$($impl_generics)*>
-                $crate::__internal::HasPinData for $name<$($ty_generics)*>
-            where $($whr)*
-            {
-                type PinData = __ThePinData<$($ty_generics)*>;
-
-                unsafe fn __pin_data() -> Self::PinData {
-                    __ThePinData { __phantom: ::core::marker::PhantomData }
-                }
-            }
-
-            // SAFETY: TODO.
-            unsafe impl<$($impl_generics)*>
-                $crate::__internal::PinData for __ThePinData<$($ty_generics)*>
-            where $($whr)*
-            {
-                type Datee = $name<$($ty_generics)*>;
-            }
-
-            // This struct will be used for the unpin analysis. Since only structurally pinned
-            // fields are relevant whether the struct should implement `Unpin`.
-            #[allow(dead_code)]
-            struct __Unpin <'__pin, $($impl_generics)*>
-            where $($whr)*
-            {
-                __phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
-                __phantom: ::core::marker::PhantomData<
-                    fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*>
-                >,
-                // Only the pinned fields.
-                $($pinned)*
-            }
-
-            #[doc(hidden)]
-            impl<'__pin, $($impl_generics)*> ::core::marker::Unpin for $name<$($ty_generics)*>
-            where
-                __Unpin<'__pin, $($ty_generics)*>: ::core::marker::Unpin,
-                $($whr)*
-            {}
-
-            // We need to disallow normal `Drop` implementation, the exact behavior depends on
-            // whether `PinnedDrop` was specified as the parameter.
-            $crate::__pin_data!(drop_prevention:
-                @name($name),
-                @impl_generics($($impl_generics)*),
-                @ty_generics($($ty_generics)*),
-                @where($($whr)*),
-                @pinned_drop($($pinned_drop)?),
-            );
-        };
-    };
-    // When no `PinnedDrop` was specified, then we have to prevent implementing drop.
-    (drop_prevention:
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @where($($whr:tt)*),
-        @pinned_drop(),
-    ) => {
-        // We prevent this by creating a trait that will be implemented for all types implementing
-        // `Drop`. Additionally we will implement this trait for the struct leading to a conflict,
-        // if it also implements `Drop`
-        trait MustNotImplDrop {}
-        #[expect(drop_bounds)]
-        impl<T: ::core::ops::Drop> MustNotImplDrop for T {}
-        impl<$($impl_generics)*> MustNotImplDrop for $name<$($ty_generics)*>
-        where $($whr)* {}
-        // We also take care to prevent users from writing a useless `PinnedDrop` implementation.
-        // They might implement `PinnedDrop` correctly for the struct, but forget to give
-        // `PinnedDrop` as the parameter to `#[pin_data]`.
-        #[expect(non_camel_case_types)]
-        trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
-        impl<T: $crate::PinnedDrop>
-            UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
-        impl<$($impl_generics)*>
-            UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for $name<$($ty_generics)*>
-        where $($whr)* {}
-    };
-    // When `PinnedDrop` was specified we just implement `Drop` and delegate.
-    (drop_prevention:
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @where($($whr:tt)*),
-        @pinned_drop(PinnedDrop),
-    ) => {
-        impl<$($impl_generics)*> ::core::ops::Drop for $name<$($ty_generics)*>
-        where $($whr)*
-        {
-            fn drop(&mut self) {
-                // SAFETY: Since this is a destructor, `self` will not move after this function
-                // terminates, since it is inaccessible.
-                let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
-                // SAFETY: Since this is a drop function, we can create this token to call the
-                // pinned destructor of this type.
-                let token = unsafe { $crate::__internal::OnlyCallFromDrop::new() };
-                $crate::PinnedDrop::drop(pinned, token);
-            }
-        }
-    };
-    // If some other parameter was specified, we emit a readable error.
-    (drop_prevention:
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @where($($whr:tt)*),
-        @pinned_drop($($rest:tt)*),
-    ) => {
-        compile_error!(
-            "Wrong parameters to `#[pin_data]`, expected nothing or `PinnedDrop`, got '{}'.",
-            stringify!($($rest)*),
-        );
-    };
-    (make_pin_projections:
-        @vis($vis:vis),
-        @name($name:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @decl_generics($($decl_generics:tt)*),
-        @where($($whr:tt)*),
-        @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?),
-        @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?),
-    ) => {
-        $crate::macros::paste! {
-            #[doc(hidden)]
-            $vis struct [< $name Projection >] <'__pin, $($decl_generics)*> {
-                $($(#[$($p_attr)*])* $pvis $p_field : ::core::pin::Pin<&'__pin mut $p_type>,)*
-                $($(#[$($attr)*])* $fvis $field : &'__pin mut $type,)*
-                ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
-            }
-
-            impl<$($impl_generics)*> $name<$($ty_generics)*>
-            where $($whr)*
-            {
-                /// Pin-projects all fields of `Self`.
-                ///
-                /// These fields are structurally pinned:
-                $(#[doc = ::core::concat!(" - `", ::core::stringify!($p_field), "`")])*
-                ///
-                /// These fields are **not** structurally pinned:
-                $(#[doc = ::core::concat!(" - `", ::core::stringify!($field), "`")])*
-                #[inline]
-                $vis fn project<'__pin>(
-                    self: ::core::pin::Pin<&'__pin mut Self>,
-                ) -> [< $name Projection >] <'__pin, $($ty_generics)*> {
-                    // SAFETY: we only give access to `&mut` for fields not structurally pinned.
-                    let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
-                    [< $name Projection >] {
-                        $(
-                            // SAFETY: `$p_field` is structurally pinned.
-                            $(#[$($p_attr)*])*
-                            $p_field : unsafe { ::core::pin::Pin::new_unchecked(&mut this.$p_field) },
-                        )*
-                        $(
-                            $(#[$($attr)*])*
-                            $field : &mut this.$field,
-                        )*
-                        ___pin_phantom_data: ::core::marker::PhantomData,
-                    }
-                }
-            }
-        }
-    };
-    (make_pin_data:
-        @pin_data($pin_data:ident),
-        @impl_generics($($impl_generics:tt)*),
-        @ty_generics($($ty_generics:tt)*),
-        @where($($whr:tt)*),
-        @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?),
-        @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?),
-    ) => {
-        $crate::macros::paste! {
-            // For every field, we create a projection function according to its projection type. If a
-            // field is structurally pinned, then it must be initialized via `PinInit`, if it is not
-            // structurally pinned, then it can be initialized via `Init`.
-            //
-            // The functions are `unsafe` to prevent accidentally calling them.
-            #[allow(dead_code)]
-            #[expect(clippy::missing_safety_doc)]
-            impl<$($impl_generics)*> $pin_data<$($ty_generics)*>
-            where $($whr)*
-            {
-                $(
-                    $(#[$($p_attr)*])*
-                    $pvis unsafe fn $p_field<E>(
-                        self,
-                        slot: *mut $p_type,
-                        init: impl $crate::PinInit<$p_type, E>,
-                    ) -> ::core::result::Result<(), E> {
-                        // SAFETY: TODO.
-                        unsafe { $crate::PinInit::__pinned_init(init, slot) }
-                    }
-
-                    $(#[$($p_attr)*])*
-                    $pvis unsafe fn [<__project_ $p_field>]<'__slot>(
-                        self,
-                        slot: &'__slot mut $p_type,
-                    ) -> ::core::pin::Pin<&'__slot mut $p_type> {
-                        ::core::pin::Pin::new_unchecked(slot)
-                    }
-                )*
-                $(
-                    $(#[$($attr)*])*
-                    $fvis unsafe fn $field<E>(
-                        self,
-                        slot: *mut $type,
-                        init: impl $crate::Init<$type, E>,
-                    ) -> ::core::result::Result<(), E> {
-                        // SAFETY: TODO.
-                        unsafe { $crate::Init::__init(init, slot) }
-                    }
-
-                    $(#[$($attr)*])*
-                    $fvis unsafe fn [<__project_ $field>]<'__slot>(
-                        self,
-                        slot: &'__slot mut $type,
-                    ) -> &'__slot mut $type {
-                        slot
-                    }
-                )*
-            }
-        }
-    };
-}
-
 /// The internal init macro. Do not call manually!
 ///
 /// This is called by the `{try_}{pin_}init!` macros with various inputs.
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 08/15] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (6 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 07/15] rust: pin-init: rewrite `#[pin_data]` " Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 09/15] rust: pin-init: rewrite the initializer macros using `syn` Benno Lossin
                     ` (7 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens
  Cc: rust-for-linux, linux-kernel

The `#[pin_data]` macro uses some auxiliary traits to ensure that a user
does not implement `Drop` for the annotated struct, as that is unsound
and can lead to UB. However, if the struct that is annotated is
`!Sized`, the current bounds do not work, because `Sized` is an implicit
bound for generics.

This is *not* a soundness hole of pin-init, as it currently is
impossible to construct an unsized struct using pin-init.

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2: none
---
 rust/pin-init/internal/src/pin_data.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 3211fb522330..b35953b2daaa 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -228,7 +228,7 @@ fn drop(&mut self) {
             // if it also implements `Drop`
             trait MustNotImplDrop {}
             #[expect(drop_bounds)]
-            impl<T: ::core::ops::Drop> MustNotImplDrop for T {}
+            impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
             impl #impl_generics MustNotImplDrop for #ident #ty_generics
                 #whr
             {}
@@ -237,7 +237,7 @@ impl #impl_generics MustNotImplDrop for #ident #ty_generics
             // `PinnedDrop` as the parameter to `#[pin_data]`.
             #[expect(non_camel_case_types)]
             trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
-            impl<T: ::pin_init::PinnedDrop>
+            impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
                 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
             impl #impl_generics
                 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 09/15] rust: pin-init: rewrite the initializer macros using `syn`
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (7 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 08/15] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 10/15] rust: pin-init: add `#[default_error(<type>)]` attribute to initializer macros Benno Lossin
                     ` (6 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl, Alban Kurti
  Cc: linux-kernel, rust-for-linux

Rewrite the initializer macros `[pin_]init!` using `syn`. No functional
changes intended aside from improved error messages on syntactic and
semantical errors. For example if one forgets to use `<-` with an
initializer (and instead uses `:`):

    impl Bar {
        fn new() -> impl PinInit<Self> { ... }
    }

    impl Foo {
        fn new() -> impl PinInit<Self> {
            pin_init!(Self { bar: Bar::new() })
        }
    }

Then the declarative macro would report:

    error[E0308]: mismatched types
      --> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:9
       |
    14 |     fn new() -> impl PinInit<Self> {
       |                 ------------------ the found opaque type
    ...
    21 |         pin_init!(Self { bar: Bar::new() })
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       |         |
       |         expected `Bar`, found opaque type
       |         arguments to this function are incorrect
       |
       = note:   expected struct `Bar`
               found opaque type `impl pin_init::PinInit<Bar>`
    note: function defined here
      --> $RUST/core/src/ptr/mod.rs
       |
       | pub const unsafe fn write<T>(dst: *mut T, src: T) {
       |                     ^^^^^
       = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info)

And the new error is:

    error[E0308]: mismatched types
      --> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:31
       |
    14 |     fn new() -> impl PinInit<Self> {
       |                 ------------------ the found opaque type
    ...
    21 |         pin_init!(Self { bar: Bar::new() })
       |                          ---  ^^^^^^^^^^ expected `Bar`, found opaque type
       |                          |
       |                          arguments to this function are incorrect
       |
       = note:   expected struct `Bar`
               found opaque type `impl pin_init::PinInit<Bar>`
    note: function defined here
      --> $RUST/core/src/ptr/mod.rs
       |
       | pub const unsafe fn write<T>(dst: *mut T, src: T) {
       |                     ^^^^^

Importantly, this error gives much more accurate span locations,
pointing to the offending field, rather than the entire macro
invocation.

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2:
* fix clippy error
* add missing SPDX-License-Identifier
* improve error handling
* improve default error evaluation
* added missing comment from pre-rewrite code
---
 rust/pin-init/internal/src/init.rs | 445 ++++++++++++++
 rust/pin-init/internal/src/lib.rs  |  21 +
 rust/pin-init/src/lib.rs           |  56 +-
 rust/pin-init/src/macros.rs        | 951 -----------------------------
 4 files changed, 468 insertions(+), 1005 deletions(-)
 create mode 100644 rust/pin-init/internal/src/init.rs
 delete mode 100644 rust/pin-init/src/macros.rs

diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
new file mode 100644
index 000000000000..cd8351152cf1
--- /dev/null
+++ b/rust/pin-init/internal/src/init.rs
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro2::{Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned};
+use syn::{
+    braced,
+    parse::{End, Parse},
+    parse_quote,
+    punctuated::Punctuated,
+    spanned::Spanned,
+    token, Block, Error, Expr, ExprCall, ExprPath, Ident, Path, Token, Type,
+};
+
+pub(crate) struct Initializer {
+    this: Option<This>,
+    path: Path,
+    brace_token: token::Brace,
+    fields: Punctuated<InitializerField, Token![,]>,
+    rest: Option<(Token![..], Expr)>,
+    error: Option<(Token![?], Type)>,
+}
+
+struct This {
+    _and_token: Token![&],
+    ident: Ident,
+    _in_token: Token![in],
+}
+
+enum InitializerField {
+    Value {
+        ident: Ident,
+        value: Option<(Token![:], Expr)>,
+    },
+    Init {
+        ident: Ident,
+        _left_arrow_token: Token![<-],
+        value: Expr,
+    },
+    Code {
+        _underscore_token: Token![_],
+        _colon_token: Token![:],
+        block: Block,
+    },
+}
+
+impl InitializerField {
+    fn ident(&self) -> Option<&Ident> {
+        match self {
+            Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident),
+            Self::Code { .. } => None,
+        }
+    }
+}
+
+pub(crate) fn expand(
+    Initializer {
+        this,
+        path,
+        brace_token,
+        fields,
+        rest,
+        error,
+    }: Initializer,
+    default_error: Option<&'static str>,
+    pinned: bool,
+) -> TokenStream {
+    let mut macro_error = crate::Error::none();
+    let error = error.map_or_else(
+        || {
+            if let Some(default_error) = default_error {
+                syn::parse_str(default_error).unwrap()
+            } else {
+                macro_error.combine(Error::new(
+                    brace_token.span.close(),
+                    "expected `? <type>` after `}`",
+                ));
+                parse_quote!(::core::convert::Infallible)
+            }
+        },
+        |(_, err)| err,
+    );
+    let slot = format_ident!("slot");
+    let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned {
+        (
+            format_ident!("HasPinData"),
+            format_ident!("PinData"),
+            format_ident!("__pin_data"),
+            format_ident!("pin_init_from_closure"),
+        )
+    } else {
+        (
+            format_ident!("HasInitData"),
+            format_ident!("InitData"),
+            format_ident!("__init_data"),
+            format_ident!("init_from_closure"),
+        )
+    };
+    let init_kind = get_init_kind(rest, &mut macro_error);
+    let zeroable_check = match init_kind {
+        InitKind::Normal => quote!(),
+        InitKind::Zeroing => quote! {
+            // The user specified `..Zeroable::zeroed()` at the end of the list of fields.
+            // Therefore we check if the struct implements `Zeroable` and then zero the memory.
+            // This allows us to also remove the check that all fields are present (since we
+            // already set the memory to zero and that is a valid bit pattern).
+            fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
+            where T: ::pin_init::Zeroable
+            {}
+            // Ensure that the struct is indeed `Zeroable`.
+            assert_zeroable(#slot);
+            // SAFETY: The type implements `Zeroable` by the check above.
+            unsafe { ::core::ptr::write_bytes(#slot, 0, 1) };
+        },
+    };
+    let this = match this {
+        None => quote!(),
+        Some(This { ident, .. }) => quote! {
+            // Create the `this` so it can be referenced by the user inside of the
+            // expressions creating the individual fields.
+            let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
+        },
+    };
+    // `mixed_site` ensures that the data is not accessible to the user-controlled code.
+    let data = Ident::new("__data", Span::mixed_site());
+    let init_fields = init_fields(&fields, pinned, &data, &slot);
+    let field_check = make_field_check(&fields, init_kind, &path);
+    quote! {{
+        #macro_error
+        // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
+        // type and shadow it later when we insert the arbitrary user code. That way there will be
+        // no possibility of returning without `unsafe`.
+        struct __InitOk;
+
+        // Get the data about fields from the supplied type.
+        // SAFETY: TODO
+        let #data = unsafe {
+            use ::pin_init::__internal::#has_data_trait;
+            // Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit
+            // generics (which need to be present with that syntax).
+            #path::#get_data()
+        };
+        // Ensure that `#data` really is of type `#data` and help with type inference:
+        let init = ::pin_init::__internal::#data_trait::make_closure::<_, __InitOk, #error>(
+            #data,
+            move |slot| {
+                {
+                    // Shadow the structure so it cannot be used to return early.
+                    struct __InitOk;
+                    #zeroable_check
+                    #this
+                    #init_fields
+                    #field_check
+                }
+                Ok(__InitOk)
+            }
+        );
+        let init = move |slot| -> ::core::result::Result<(), #error> {
+            init(slot).map(|__InitOk| ())
+        };
+        // SAFETY: TODO
+        let init = unsafe { ::pin_init::#init_from_closure::<_, #error>(init) };
+        init
+    }}
+}
+
+enum InitKind {
+    Normal,
+    Zeroing,
+}
+
+fn get_init_kind(rest: Option<(Token![..], Expr)>, macro_error: &mut crate::Error) -> InitKind {
+    let Some((dotdot, expr)) = rest else {
+        return InitKind::Normal;
+    };
+    match &expr {
+        Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => match &**func {
+            Expr::Path(ExprPath {
+                attrs,
+                qself: None,
+                path:
+                    Path {
+                        leading_colon: None,
+                        segments,
+                    },
+            }) if attrs.is_empty()
+                && segments.len() == 2
+                && segments[0].ident == "Zeroable"
+                && segments[0].arguments.is_none()
+                && segments[1].ident == "init_zeroed"
+                && segments[1].arguments.is_none() =>
+            {
+                return InitKind::Zeroing;
+            }
+            _ => {}
+        },
+        _ => {}
+    }
+    macro_error.combine(Error::new(
+        dotdot.span().join(expr.span()).unwrap_or(expr.span()),
+        "expected nothing or `..Zeroable::init_zeroed()`.",
+    ));
+    InitKind::Normal
+}
+
+/// Generate the code that initializes the fields of the struct using the initializers in `field`.
+fn init_fields(
+    fields: &Punctuated<InitializerField, Token![,]>,
+    pinned: bool,
+    data: &Ident,
+    slot: &Ident,
+) -> TokenStream {
+    let mut guards = vec![];
+    let mut res = TokenStream::new();
+    for field in fields {
+        let init = match field {
+            InitializerField::Value { ident, value } => {
+                let mut value_ident = ident.clone();
+                let value_prep = value.as_ref().map(|value| &value.1).map(|value| {
+                    // Setting the span of `value_ident` to `value`'s span improves error messages
+                    // when the type of `value` is wrong.
+                    value_ident.set_span(value.span());
+                    quote!(let #value_ident = #value;)
+                });
+                // Again span for better diagnostics
+                let write = quote_spanned!(ident.span()=> ::core::ptr::write);
+                let accessor = if pinned {
+                    let project_ident = format_ident!("__project_{ident}");
+                    quote! {
+                        // SAFETY: TODO
+                        unsafe { #data.#project_ident(&mut (*#slot).#ident) }
+                    }
+                } else {
+                    quote! {
+                        // SAFETY: TODO
+                        unsafe { &mut (*#slot).#ident }
+                    }
+                };
+                quote! {
+                    {
+                        #value_prep
+                        // SAFETY: TODO
+                        unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) };
+                    }
+                    #[allow(unused_variables)]
+                    let #ident = #accessor;
+                }
+            }
+            InitializerField::Init { ident, value, .. } => {
+                // Again span for better diagnostics
+                let init = format_ident!("init", span = value.span());
+                if pinned {
+                    let project_ident = format_ident!("__project_{ident}");
+                    quote! {
+                        {
+                            let #init = #value;
+                            // SAFETY:
+                            // - `slot` is valid, because we are inside of an initializer closure, we
+                            //   return when an error/panic occurs.
+                            // - We also use `#data` to require the correct trait (`Init` or `PinInit`)
+                            //   for `#ident`.
+                            unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? };
+                        }
+                        // SAFETY: TODO
+                        #[allow(unused_variables)]
+                        let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) };
+                    }
+                } else {
+                    quote! {
+                        {
+                            let #init = #value;
+                            // SAFETY: `slot` is valid, because we are inside of an initializer
+                            // closure, we return when an error/panic occurs.
+                            unsafe {
+                                ::pin_init::Init::__init(
+                                    #init,
+                                    ::core::ptr::addr_of_mut!((*#slot).#ident),
+                                )?
+                            };
+                        }
+                        // SAFETY: TODO
+                        #[allow(unused_variables)]
+                        let #ident = unsafe { &mut (*#slot).#ident };
+                    }
+                }
+            }
+            InitializerField::Code { block: value, .. } => quote!(#[allow(unused_braces)] #value),
+        };
+        res.extend(init);
+        if let Some(ident) = field.ident() {
+            // `mixed_site` ensures that the guard is not accessible to the user-controlled code.
+            let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
+            guards.push(guard.clone());
+            res.extend(quote! {
+                // Create the drop guard:
+                //
+                // We rely on macro hygiene to make it impossible for users to access this local
+                // variable.
+                // SAFETY: We forget the guard later when initialization has succeeded.
+                let #guard = unsafe {
+                    ::pin_init::__internal::DropGuard::new(
+                        ::core::ptr::addr_of_mut!((*slot).#ident)
+                    )
+                };
+            });
+        }
+    }
+    quote! {
+        #res
+        // If execution reaches this point, all fields have been initialized. Therefore we can now
+        // dismiss the guards by forgetting them.
+        #(::core::mem::forget(#guards);)*
+    }
+}
+
+/// Generate the check for ensuring that every field has been initialized.
+fn make_field_check(
+    fields: &Punctuated<InitializerField, Token![,]>,
+    init_kind: InitKind,
+    path: &Path,
+) -> TokenStream {
+    let fields = fields.iter().filter_map(|f| f.ident());
+    match init_kind {
+        InitKind::Normal => quote! {
+            // We use unreachable code to ensure that all fields have been mentioned exactly once,
+            // this struct initializer will still be type-checked and complain with a very natural
+            // error message if a field is forgotten/mentioned more than once.
+            #[allow(unreachable_code, clippy::diverging_sub_expression)]
+            // SAFETY: this code is never executed.
+            let _ = || unsafe {
+                ::core::ptr::write(slot, #path {
+                    #(
+                        #fields: ::core::panic!(),
+                    )*
+                })
+            };
+        },
+        InitKind::Zeroing => quote! {
+            // We use unreachable code to ensure that all fields have been mentioned at most once.
+            // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
+            // be zeroed. This struct initializer will still be type-checked and complain with a
+            // very natural error message if a field is mentioned more than once, or doesn't exist.
+            #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
+            // SAFETY: this code is never executed.
+            let _ = || unsafe {
+                let mut zeroed = ::core::mem::zeroed();
+                // We have to use type inference here to make zeroed have the correct type. This
+                // does not get executed, so it has no effect.
+                ::core::ptr::write(slot, zeroed);
+                zeroed = ::core::mem::zeroed();
+                ::core::ptr::write(slot, #path {
+                    #(
+                        #fields: ::core::panic!(),
+                    )*
+                    ..zeroed
+                })
+            };
+        },
+    }
+}
+
+impl Parse for Initializer {
+    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
+        let this = input.peek(Token![&]).then(|| input.parse()).transpose()?;
+        let path = input.parse()?;
+        let content;
+        let brace_token = braced!(content in input);
+        let mut fields = Punctuated::new();
+        loop {
+            let lh = content.lookahead1();
+            if lh.peek(End) || lh.peek(Token![..]) {
+                break;
+            } else if lh.peek(Ident) || lh.peek(Token![_]) {
+                fields.push_value(content.parse()?);
+                let lh = content.lookahead1();
+                if lh.peek(End) {
+                    break;
+                } else if lh.peek(Token![,]) {
+                    fields.push_punct(content.parse()?);
+                } else {
+                    return Err(lh.error());
+                }
+            } else {
+                return Err(lh.error());
+            }
+        }
+        let rest = content
+            .peek(Token![..])
+            .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?)))
+            .transpose()?;
+        let error = input
+            .peek(Token![?])
+            .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?)))
+            .transpose()?;
+        Ok(Self {
+            this,
+            path,
+            brace_token,
+            fields,
+            rest,
+            error,
+        })
+    }
+}
+
+impl Parse for This {
+    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
+        Ok(Self {
+            _and_token: input.parse()?,
+            ident: input.parse()?,
+            _in_token: input.parse()?,
+        })
+    }
+}
+
+impl Parse for InitializerField {
+    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
+        let lh = input.lookahead1();
+        if lh.peek(Token![_]) {
+            Ok(Self::Code {
+                _underscore_token: input.parse()?,
+                _colon_token: input.parse()?,
+                block: input.parse()?,
+            })
+        } else if lh.peek(Ident) {
+            let ident = input.parse()?;
+            let lh = input.lookahead1();
+            if lh.peek(Token![<-]) {
+                Ok(Self::Init {
+                    ident,
+                    _left_arrow_token: input.parse()?,
+                    value: input.parse()?,
+                })
+            } else if lh.peek(Token![:]) {
+                Ok(Self::Value {
+                    ident,
+                    value: Some((input.parse()?, input.parse()?)),
+                })
+            } else if lh.peek(Token![,]) || lh.peek(End) {
+                Ok(Self::Value { ident, value: None })
+            } else {
+                Err(lh.error())
+            }
+        } else {
+            Err(lh.error())
+        }
+    }
+}
diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 07ea6e0970a0..62af0bc85afd 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -13,6 +13,7 @@
 use proc_macro::TokenStream;
 use syn::parse_macro_input;
 
+mod init;
 mod pin_data;
 mod pinned_drop;
 mod zeroable;
@@ -40,6 +41,26 @@ pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
     ok_or_compile_error(zeroable::maybe_derive(parse_macro_input!(input)))
 }
 
+#[proc_macro]
+pub fn init(input: TokenStream) -> TokenStream {
+    init::expand(
+        parse_macro_input!(input),
+        Some("::core::convert::Infallible"),
+        false,
+    )
+    .into()
+}
+
+#[proc_macro]
+pub fn pin_init(input: TokenStream) -> TokenStream {
+    init::expand(
+        parse_macro_input!(input),
+        Some("::core::convert::Infallible"),
+        true,
+    )
+    .into()
+}
+
 fn ok_or_compile_error(res: syn::Result<proc_macro2::TokenStream>) -> TokenStream {
     match res {
         Ok(stream) => stream,
diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index 0e707f00061f..780b4fead22d 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -297,8 +297,6 @@
 
 #[doc(hidden)]
 pub mod __internal;
-#[doc(hidden)]
-pub mod macros;
 
 #[cfg(any(feature = "std", feature = "alloc"))]
 mod alloc;
@@ -781,32 +779,7 @@ macro_rules! stack_try_pin_init {
 /// ```
 ///
 /// [`NonNull<Self>`]: core::ptr::NonNull
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! pin_init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }) => {
-        $crate::pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? ::core::convert::Infallible)
-    };
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }? $err:ty) => {
-        $crate::__init_internal!(
-            @this($($this)?),
-            @typ($t $(::<$($generics),*>)? ),
-            @fields($($fields)*),
-            @error($err),
-            @data(PinData, use_data),
-            @has_data(HasPinData, __pin_data),
-            @construct_closure(pin_init_from_closure),
-            @munch_fields($($fields)*),
-        )
-    }
-}
+pub use pin_init_internal::pin_init;
 
 /// Construct an in-place, fallible initializer for `struct`s.
 ///
@@ -844,32 +817,7 @@ macro_rules! pin_init {
 /// }
 /// # let _ = Box::init(BigBuf::new());
 /// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }) => {
-        $crate::init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? ::core::convert::Infallible)
-    };
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }? $err:ty) => {
-        $crate::__init_internal!(
-            @this($($this)?),
-            @typ($t $(::<$($generics),*>)?),
-            @fields($($fields)*),
-            @error($err),
-            @data(InitData, /*no use_data*/),
-            @has_data(HasInitData, __init_data),
-            @construct_closure(init_from_closure),
-            @munch_fields($($fields)*),
-        )
-    }
-}
+pub use pin_init_internal::init;
 
 /// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is
 /// structurally pinned.
diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs
deleted file mode 100644
index eea8adc5c7ad..000000000000
--- a/rust/pin-init/src/macros.rs
+++ /dev/null
@@ -1,951 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0 OR MIT
-
-//! This module provides the macros that actually implement the proc-macros `pin_data` and
-//! `pinned_drop`. It also contains `__init_internal`, the implementation of the
-//! `{try_}{pin_}init!` macros.
-//!
-//! These macros should never be called directly, since they expect their input to be
-//! in a certain format which is internal. If used incorrectly, these macros can lead to UB even in
-//! safe code! Use the public facing macros instead.
-//!
-//! This architecture has been chosen because the kernel does not yet have access to `syn` which
-//! would make matters a lot easier for implementing these as proc-macros.
-//!
-//! Since this library and the kernel implementation should diverge as little as possible, the same
-//! approach has been taken here.
-//!
-//! # Macro expansion example
-//!
-//! This section is intended for readers trying to understand the macros in this module and the
-//! `[try_][pin_]init!` macros from `lib.rs`.
-//!
-//! We will look at the following example:
-//!
-//! ```rust,ignore
-//! #[pin_data]
-//! #[repr(C)]
-//! struct Bar<T> {
-//!     #[pin]
-//!     t: T,
-//!     pub x: usize,
-//! }
-//!
-//! impl<T> Bar<T> {
-//!     fn new(t: T) -> impl PinInit<Self> {
-//!         pin_init!(Self { t, x: 0 })
-//!     }
-//! }
-//!
-//! #[pin_data(PinnedDrop)]
-//! struct Foo {
-//!     a: usize,
-//!     #[pin]
-//!     b: Bar<u32>,
-//! }
-//!
-//! #[pinned_drop]
-//! impl PinnedDrop for Foo {
-//!     fn drop(self: Pin<&mut Self>) {
-//!         println!("{self:p} is getting dropped.");
-//!     }
-//! }
-//!
-//! let a = 42;
-//! let initializer = pin_init!(Foo {
-//!     a,
-//!     b <- Bar::new(36),
-//! });
-//! ```
-//!
-//! This example includes the most common and important features of the pin-init API.
-//!
-//! Below you can find individual section about the different macro invocations. Here are some
-//! general things we need to take into account when designing macros:
-//! - use global paths, similarly to file paths, these start with the separator: `::core::panic!()`
-//!   this ensures that the correct item is used, since users could define their own `mod core {}`
-//!   and then their own `panic!` inside to execute arbitrary code inside of our macro.
-//! - macro `unsafe` hygiene: we need to ensure that we do not expand arbitrary, user-supplied
-//!   expressions inside of an `unsafe` block in the macro, because this would allow users to do
-//!   `unsafe` operations without an associated `unsafe` block.
-//!
-//! ## `#[pin_data]` on `Bar`
-//!
-//! This macro is used to specify which fields are structurally pinned and which fields are not. It
-//! is placed on the struct definition and allows `#[pin]` to be placed on the fields.
-//!
-//! Here is the definition of `Bar` from our example:
-//!
-//! ```rust,ignore
-//! #[pin_data]
-//! #[repr(C)]
-//! struct Bar<T> {
-//!     #[pin]
-//!     t: T,
-//!     pub x: usize,
-//! }
-//! ```
-//!
-//! This expands to the following code:
-//!
-//! ```rust,ignore
-//! // Firstly the normal definition of the struct, attributes are preserved:
-//! #[repr(C)]
-//! struct Bar<T> {
-//!     t: T,
-//!     pub x: usize,
-//! }
-//! // Then an anonymous constant is defined, this is because we do not want any code to access the
-//! // types that we define inside:
-//! const _: () = {
-//!     // We define the pin-data carrying struct, it is a ZST and needs to have the same generics,
-//!     // since we need to implement access functions for each field and thus need to know its
-//!     // type.
-//!     struct __ThePinData<T> {
-//!         __phantom: ::core::marker::PhantomData<fn(Bar<T>) -> Bar<T>>,
-//!     }
-//!     // We implement `Copy` for the pin-data struct, since all functions it defines will take
-//!     // `self` by value.
-//!     impl<T> ::core::clone::Clone for __ThePinData<T> {
-//!         fn clone(&self) -> Self {
-//!             *self
-//!         }
-//!     }
-//!     impl<T> ::core::marker::Copy for __ThePinData<T> {}
-//!     // For every field of `Bar`, the pin-data struct will define a function with the same name
-//!     // and accessor (`pub` or `pub(crate)` etc.). This function will take a pointer to the
-//!     // field (`slot`) and a `PinInit` or `Init` depending on the projection kind of the field
-//!     // (if pinning is structural for the field, then `PinInit` otherwise `Init`).
-//!     #[allow(dead_code)]
-//!     impl<T> __ThePinData<T> {
-//!         unsafe fn t<E>(
-//!             self,
-//!             slot: *mut T,
-//!             // Since `t` is `#[pin]`, this is `PinInit`.
-//!             init: impl ::pin_init::PinInit<T, E>,
-//!         ) -> ::core::result::Result<(), E> {
-//!             unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
-//!         }
-//!         pub unsafe fn x<E>(
-//!             self,
-//!             slot: *mut usize,
-//!             // Since `x` is not `#[pin]`, this is `Init`.
-//!             init: impl ::pin_init::Init<usize, E>,
-//!         ) -> ::core::result::Result<(), E> {
-//!             unsafe { ::pin_init::Init::__init(init, slot) }
-//!         }
-//!     }
-//!     // Implement the internal `HasPinData` trait that associates `Bar` with the pin-data struct
-//!     // that we constructed above.
-//!     unsafe impl<T> ::pin_init::__internal::HasPinData for Bar<T> {
-//!         type PinData = __ThePinData<T>;
-//!         unsafe fn __pin_data() -> Self::PinData {
-//!             __ThePinData {
-//!                 __phantom: ::core::marker::PhantomData,
-//!             }
-//!         }
-//!     }
-//!     // Implement the internal `PinData` trait that marks the pin-data struct as a pin-data
-//!     // struct. This is important to ensure that no user can implement a rogue `__pin_data`
-//!     // function without using `unsafe`.
-//!     unsafe impl<T> ::pin_init::__internal::PinData for __ThePinData<T> {
-//!         type Datee = Bar<T>;
-//!     }
-//!     // Now we only want to implement `Unpin` for `Bar` when every structurally pinned field is
-//!     // `Unpin`. In other words, whether `Bar` is `Unpin` only depends on structurally pinned
-//!     // fields (those marked with `#[pin]`). These fields will be listed in this struct, in our
-//!     // case no such fields exist, hence this is almost empty. The two phantomdata fields exist
-//!     // for two reasons:
-//!     // - `__phantom`: every generic must be used, since we cannot really know which generics
-//!     //   are used, we declare all and then use everything here once.
-//!     // - `__phantom_pin`: uses the `'__pin` lifetime and ensures that this struct is invariant
-//!     //   over it. The lifetime is needed to work around the limitation that trait bounds must
-//!     //   not be trivial, e.g. the user has a `#[pin] PhantomPinned` field -- this is
-//!     //   unconditionally `!Unpin` and results in an error. The lifetime tricks the compiler
-//!     //   into accepting these bounds regardless.
-//!     #[allow(dead_code)]
-//!     struct __Unpin<'__pin, T> {
-//!         __phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
-//!         __phantom: ::core::marker::PhantomData<fn(Bar<T>) -> Bar<T>>,
-//!         // Our only `#[pin]` field is `t`.
-//!         t: T,
-//!     }
-//!     #[doc(hidden)]
-//!     impl<'__pin, T> ::core::marker::Unpin for Bar<T>
-//!     where
-//!         __Unpin<'__pin, T>: ::core::marker::Unpin,
-//!     {}
-//!     // Now we need to ensure that `Bar` does not implement `Drop`, since that would give users
-//!     // access to `&mut self` inside of `drop` even if the struct was pinned. This could lead to
-//!     // UB with only safe code, so we disallow this by giving a trait implementation error using
-//!     // a direct impl and a blanket implementation.
-//!     trait MustNotImplDrop {}
-//!     // Normally `Drop` bounds do not have the correct semantics, but for this purpose they do
-//!     // (normally people want to know if a type has any kind of drop glue at all, here we want
-//!     // to know if it has any kind of custom drop glue, which is exactly what this bound does).
-//!     #[expect(drop_bounds)]
-//!     impl<T: ::core::ops::Drop> MustNotImplDrop for T {}
-//!     impl<T> MustNotImplDrop for Bar<T> {}
-//!     // Here comes a convenience check, if one implemented `PinnedDrop`, but forgot to add it to
-//!     // `#[pin_data]`, then this will error with the same mechanic as above, this is not needed
-//!     // for safety, but a good sanity check, since no normal code calls `PinnedDrop::drop`.
-//!     #[expect(non_camel_case_types)]
-//!     trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
-//!     impl<
-//!         T: ::pin_init::PinnedDrop,
-//!     > UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
-//!     impl<T> UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for Bar<T> {}
-//! };
-//! ```
-//!
-//! ## `pin_init!` in `impl Bar`
-//!
-//! This macro creates an pin-initializer for the given struct. It requires that the struct is
-//! annotated by `#[pin_data]`.
-//!
-//! Here is the impl on `Bar` defining the new function:
-//!
-//! ```rust,ignore
-//! impl<T> Bar<T> {
-//!     fn new(t: T) -> impl PinInit<Self> {
-//!         pin_init!(Self { t, x: 0 })
-//!     }
-//! }
-//! ```
-//!
-//! This expands to the following code:
-//!
-//! ```rust,ignore
-//! impl<T> Bar<T> {
-//!     fn new(t: T) -> impl PinInit<Self> {
-//!         {
-//!             // We do not want to allow arbitrary returns, so we declare this type as the `Ok`
-//!             // return type and shadow it later when we insert the arbitrary user code. That way
-//!             // there will be no possibility of returning without `unsafe`.
-//!             struct __InitOk;
-//!             // Get the data about fields from the supplied type.
-//!             // - the function is unsafe, hence the unsafe block
-//!             // - we `use` the `HasPinData` trait in the block, it is only available in that
-//!             //   scope.
-//!             let data = unsafe {
-//!                 use ::pin_init::__internal::HasPinData;
-//!                 Self::__pin_data()
-//!             };
-//!             // Ensure that `data` really is of type `PinData` and help with type inference:
-//!             let init = ::pin_init::__internal::PinData::make_closure::<
-//!                 _,
-//!                 __InitOk,
-//!                 ::core::convert::Infallible,
-//!             >(data, move |slot| {
-//!                 {
-//!                     // Shadow the structure so it cannot be used to return early. If a user
-//!                     // tries to write `return Ok(__InitOk)`, then they get a type error,
-//!                     // since that will refer to this struct instead of the one defined
-//!                     // above.
-//!                     struct __InitOk;
-//!                     // This is the expansion of `t,`, which is syntactic sugar for `t: t,`.
-//!                     {
-//!                         unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).t), t) };
-//!                     }
-//!                     // Since initialization could fail later (not in this case, since the
-//!                     // error type is `Infallible`) we will need to drop this field if there
-//!                     // is an error later. This `DropGuard` will drop the field when it gets
-//!                     // dropped and has not yet been forgotten.
-//!                     let __t_guard = unsafe {
-//!                         ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).t))
-//!                     };
-//!                     // Expansion of `x: 0,`:
-//!                     // Since this can be an arbitrary expression we cannot place it inside
-//!                     // of the `unsafe` block, so we bind it here.
-//!                     {
-//!                         let x = 0;
-//!                         unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).x), x) };
-//!                     }
-//!                     // We again create a `DropGuard`.
-//!                     let __x_guard = unsafe {
-//!                         ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).x))
-//!                     };
-//!                     // Since initialization has successfully completed, we can now forget
-//!                     // the guards. This is not `mem::forget`, since we only have
-//!                     // `&DropGuard`.
-//!                     ::core::mem::forget(__x_guard);
-//!                     ::core::mem::forget(__t_guard);
-//!                     // Here we use the type checker to ensure that every field has been
-//!                     // initialized exactly once, since this is `if false` it will never get
-//!                     // executed, but still type-checked.
-//!                     // Additionally we abuse `slot` to automatically infer the correct type
-//!                     // for the struct. This is also another check that every field is
-//!                     // accessible from this scope.
-//!                     #[allow(unreachable_code, clippy::diverging_sub_expression)]
-//!                     let _ = || {
-//!                         unsafe {
-//!                             ::core::ptr::write(
-//!                                 slot,
-//!                                 Self {
-//!                                     // We only care about typecheck finding every field
-//!                                     // here, the expression does not matter, just conjure
-//!                                     // one using `panic!()`:
-//!                                     t: ::core::panic!(),
-//!                                     x: ::core::panic!(),
-//!                                 },
-//!                             );
-//!                         };
-//!                     };
-//!                 }
-//!                 // We leave the scope above and gain access to the previously shadowed
-//!                 // `__InitOk` that we need to return.
-//!                 Ok(__InitOk)
-//!             });
-//!             // Change the return type from `__InitOk` to `()`.
-//!             let init = move |
-//!                 slot,
-//!             | -> ::core::result::Result<(), ::core::convert::Infallible> {
-//!                 init(slot).map(|__InitOk| ())
-//!             };
-//!             // Construct the initializer.
-//!             let init = unsafe {
-//!                 ::pin_init::pin_init_from_closure::<
-//!                     _,
-//!                     ::core::convert::Infallible,
-//!                 >(init)
-//!             };
-//!             init
-//!         }
-//!     }
-//! }
-//! ```
-//!
-//! ## `#[pin_data]` on `Foo`
-//!
-//! Since we already took a look at `#[pin_data]` on `Bar`, this section will only explain the
-//! differences/new things in the expansion of the `Foo` definition:
-//!
-//! ```rust,ignore
-//! #[pin_data(PinnedDrop)]
-//! struct Foo {
-//!     a: usize,
-//!     #[pin]
-//!     b: Bar<u32>,
-//! }
-//! ```
-//!
-//! This expands to the following code:
-//!
-//! ```rust,ignore
-//! struct Foo {
-//!     a: usize,
-//!     b: Bar<u32>,
-//! }
-//! const _: () = {
-//!     struct __ThePinData {
-//!         __phantom: ::core::marker::PhantomData<fn(Foo) -> Foo>,
-//!     }
-//!     impl ::core::clone::Clone for __ThePinData {
-//!         fn clone(&self) -> Self {
-//!             *self
-//!         }
-//!     }
-//!     impl ::core::marker::Copy for __ThePinData {}
-//!     #[allow(dead_code)]
-//!     impl __ThePinData {
-//!         unsafe fn b<E>(
-//!             self,
-//!             slot: *mut Bar<u32>,
-//!             init: impl ::pin_init::PinInit<Bar<u32>, E>,
-//!         ) -> ::core::result::Result<(), E> {
-//!             unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
-//!         }
-//!         unsafe fn a<E>(
-//!             self,
-//!             slot: *mut usize,
-//!             init: impl ::pin_init::Init<usize, E>,
-//!         ) -> ::core::result::Result<(), E> {
-//!             unsafe { ::pin_init::Init::__init(init, slot) }
-//!         }
-//!     }
-//!     unsafe impl ::pin_init::__internal::HasPinData for Foo {
-//!         type PinData = __ThePinData;
-//!         unsafe fn __pin_data() -> Self::PinData {
-//!             __ThePinData {
-//!                 __phantom: ::core::marker::PhantomData,
-//!             }
-//!         }
-//!     }
-//!     unsafe impl ::pin_init::__internal::PinData for __ThePinData {
-//!         type Datee = Foo;
-//!     }
-//!     #[allow(dead_code)]
-//!     struct __Unpin<'__pin> {
-//!         __phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
-//!         __phantom: ::core::marker::PhantomData<fn(Foo) -> Foo>,
-//!         b: Bar<u32>,
-//!     }
-//!     #[doc(hidden)]
-//!     impl<'__pin> ::core::marker::Unpin for Foo
-//!     where
-//!         __Unpin<'__pin>: ::core::marker::Unpin,
-//!     {}
-//!     // Since we specified `PinnedDrop` as the argument to `#[pin_data]`, we expect `Foo` to
-//!     // implement `PinnedDrop`. Thus we do not need to prevent `Drop` implementations like
-//!     // before, instead we implement `Drop` here and delegate to `PinnedDrop`.
-//!     impl ::core::ops::Drop for Foo {
-//!         fn drop(&mut self) {
-//!             // Since we are getting dropped, no one else has a reference to `self` and thus we
-//!             // can assume that we never move.
-//!             let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
-//!             // Create the unsafe token that proves that we are inside of a destructor, this
-//!             // type is only allowed to be created in a destructor.
-//!             let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
-//!             ::pin_init::PinnedDrop::drop(pinned, token);
-//!         }
-//!     }
-//! };
-//! ```
-//!
-//! ## `#[pinned_drop]` on `impl PinnedDrop for Foo`
-//!
-//! This macro is used to implement the `PinnedDrop` trait, since that trait is `unsafe` and has an
-//! extra parameter that should not be used at all. The macro hides that parameter.
-//!
-//! Here is the `PinnedDrop` impl for `Foo`:
-//!
-//! ```rust,ignore
-//! #[pinned_drop]
-//! impl PinnedDrop for Foo {
-//!     fn drop(self: Pin<&mut Self>) {
-//!         println!("{self:p} is getting dropped.");
-//!     }
-//! }
-//! ```
-//!
-//! This expands to the following code:
-//!
-//! ```rust,ignore
-//! // `unsafe`, full path and the token parameter are added, everything else stays the same.
-//! unsafe impl ::pin_init::PinnedDrop for Foo {
-//!     fn drop(self: Pin<&mut Self>, _: ::pin_init::__internal::OnlyCallFromDrop) {
-//!         println!("{self:p} is getting dropped.");
-//!     }
-//! }
-//! ```
-//!
-//! ## `pin_init!` on `Foo`
-//!
-//! Since we already took a look at `pin_init!` on `Bar`, this section will only show the expansion
-//! of `pin_init!` on `Foo`:
-//!
-//! ```rust,ignore
-//! let a = 42;
-//! let initializer = pin_init!(Foo {
-//!     a,
-//!     b <- Bar::new(36),
-//! });
-//! ```
-//!
-//! This expands to the following code:
-//!
-//! ```rust,ignore
-//! let a = 42;
-//! let initializer = {
-//!     struct __InitOk;
-//!     let data = unsafe {
-//!         use ::pin_init::__internal::HasPinData;
-//!         Foo::__pin_data()
-//!     };
-//!     let init = ::pin_init::__internal::PinData::make_closure::<
-//!         _,
-//!         __InitOk,
-//!         ::core::convert::Infallible,
-//!     >(data, move |slot| {
-//!         {
-//!             struct __InitOk;
-//!             {
-//!                 unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).a), a) };
-//!             }
-//!             let __a_guard = unsafe {
-//!                 ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).a))
-//!             };
-//!             let init = Bar::new(36);
-//!             unsafe { data.b(::core::addr_of_mut!((*slot).b), b)? };
-//!             let __b_guard = unsafe {
-//!                 ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).b))
-//!             };
-//!             ::core::mem::forget(__b_guard);
-//!             ::core::mem::forget(__a_guard);
-//!             #[allow(unreachable_code, clippy::diverging_sub_expression)]
-//!             let _ = || {
-//!                 unsafe {
-//!                     ::core::ptr::write(
-//!                         slot,
-//!                         Foo {
-//!                             a: ::core::panic!(),
-//!                             b: ::core::panic!(),
-//!                         },
-//!                     );
-//!                 };
-//!             };
-//!         }
-//!         Ok(__InitOk)
-//!     });
-//!     let init = move |
-//!         slot,
-//!     | -> ::core::result::Result<(), ::core::convert::Infallible> {
-//!         init(slot).map(|__InitOk| ())
-//!     };
-//!     let init = unsafe {
-//!         ::pin_init::pin_init_from_closure::<_, ::core::convert::Infallible>(init)
-//!     };
-//!     init
-//! };
-//! ```
-
-#[cfg(kernel)]
-pub use ::macros::paste;
-#[cfg(not(kernel))]
-pub use ::paste::paste;
-
-/// The internal init macro. Do not call manually!
-///
-/// This is called by the `{try_}{pin_}init!` macros with various inputs.
-///
-/// This macro has multiple internal call configurations, these are always the very first ident:
-/// - nothing: this is the base case and called by the `{try_}{pin_}init!` macros.
-/// - `with_update_parsed`: when the `..Zeroable::init_zeroed()` syntax has been handled.
-/// - `init_slot`: recursively creates the code that initializes all fields in `slot`.
-/// - `make_initializer`: recursively create the struct initializer that guarantees that every
-///   field has been initialized exactly once.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! __init_internal {
-    (
-        @this($($this:ident)?),
-        @typ($t:path),
-        @fields($($fields:tt)*),
-        @error($err:ty),
-        // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData`
-        // case.
-        @data($data:ident, $($use_data:ident)?),
-        // `HasPinData` or `HasInitData`.
-        @has_data($has_data:ident, $get_data:ident),
-        // `pin_init_from_closure` or `init_from_closure`.
-        @construct_closure($construct_closure:ident),
-        @munch_fields(),
-    ) => {
-        $crate::__init_internal!(with_update_parsed:
-            @this($($this)?),
-            @typ($t),
-            @fields($($fields)*),
-            @error($err),
-            @data($data, $($use_data)?),
-            @has_data($has_data, $get_data),
-            @construct_closure($construct_closure),
-            @init_zeroed(), // Nothing means default behavior.
-        )
-    };
-    (
-        @this($($this:ident)?),
-        @typ($t:path),
-        @fields($($fields:tt)*),
-        @error($err:ty),
-        // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData`
-        // case.
-        @data($data:ident, $($use_data:ident)?),
-        // `HasPinData` or `HasInitData`.
-        @has_data($has_data:ident, $get_data:ident),
-        // `pin_init_from_closure` or `init_from_closure`.
-        @construct_closure($construct_closure:ident),
-        @munch_fields(..Zeroable::init_zeroed()),
-    ) => {
-        $crate::__init_internal!(with_update_parsed:
-            @this($($this)?),
-            @typ($t),
-            @fields($($fields)*),
-            @error($err),
-            @data($data, $($use_data)?),
-            @has_data($has_data, $get_data),
-            @construct_closure($construct_closure),
-            @init_zeroed(()), // `()` means zero all fields not mentioned.
-        )
-    };
-    (
-        @this($($this:ident)?),
-        @typ($t:path),
-        @fields($($fields:tt)*),
-        @error($err:ty),
-        // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData`
-        // case.
-        @data($data:ident, $($use_data:ident)?),
-        // `HasPinData` or `HasInitData`.
-        @has_data($has_data:ident, $get_data:ident),
-        // `pin_init_from_closure` or `init_from_closure`.
-        @construct_closure($construct_closure:ident),
-        @munch_fields($ignore:tt $($rest:tt)*),
-    ) => {
-        $crate::__init_internal!(
-            @this($($this)?),
-            @typ($t),
-            @fields($($fields)*),
-            @error($err),
-            @data($data, $($use_data)?),
-            @has_data($has_data, $get_data),
-            @construct_closure($construct_closure),
-            @munch_fields($($rest)*),
-        )
-    };
-    (with_update_parsed:
-        @this($($this:ident)?),
-        @typ($t:path),
-        @fields($($fields:tt)*),
-        @error($err:ty),
-        // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData`
-        // case.
-        @data($data:ident, $($use_data:ident)?),
-        // `HasPinData` or `HasInitData`.
-        @has_data($has_data:ident, $get_data:ident),
-        // `pin_init_from_closure` or `init_from_closure`.
-        @construct_closure($construct_closure:ident),
-        @init_zeroed($($init_zeroed:expr)?),
-    ) => {{
-        // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
-        // type and shadow it later when we insert the arbitrary user code. That way there will be
-        // no possibility of returning without `unsafe`.
-        struct __InitOk;
-        // Get the data about fields from the supplied type.
-        //
-        // SAFETY: TODO.
-        let data = unsafe {
-            use $crate::__internal::$has_data;
-            // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal
-            // information that is associated to already parsed fragments, so a path fragment
-            // cannot be used in this position. Doing the retokenization results in valid rust
-            // code.
-            $crate::macros::paste!($t::$get_data())
-        };
-        // Ensure that `data` really is of type `$data` and help with type inference:
-        let init = $crate::__internal::$data::make_closure::<_, __InitOk, $err>(
-            data,
-            move |slot| {
-                {
-                    // Shadow the structure so it cannot be used to return early.
-                    struct __InitOk;
-                    // If `$init_zeroed` is present we should zero the slot now and not emit an
-                    // error when fields are missing (since they will be zeroed). We also have to
-                    // check that the type actually implements `Zeroable`.
-                    $({
-                        fn assert_zeroable<T: $crate::Zeroable>(_: *mut T) {}
-                        // Ensure that the struct is indeed `Zeroable`.
-                        assert_zeroable(slot);
-                        // SAFETY: The type implements `Zeroable` by the check above.
-                        unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
-                        $init_zeroed // This will be `()` if set.
-                    })?
-                    // Create the `this` so it can be referenced by the user inside of the
-                    // expressions creating the individual fields.
-                    $(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)?
-                    // Initialize every field.
-                    $crate::__init_internal!(init_slot($($use_data)?):
-                        @data(data),
-                        @slot(slot),
-                        @guards(),
-                        @munch_fields($($fields)*,),
-                    );
-                    // We use unreachable code to ensure that all fields have been mentioned exactly
-                    // once, this struct initializer will still be type-checked and complain with a
-                    // very natural error message if a field is forgotten/mentioned more than once.
-                    #[allow(unreachable_code, clippy::diverging_sub_expression)]
-                    let _ = || {
-                        $crate::__init_internal!(make_initializer:
-                            @slot(slot),
-                            @type_name($t),
-                            @munch_fields($($fields)*,),
-                            @acc(),
-                        );
-                    };
-                }
-                Ok(__InitOk)
-            }
-        );
-        let init = move |slot| -> ::core::result::Result<(), $err> {
-            init(slot).map(|__InitOk| ())
-        };
-        // SAFETY: TODO.
-        let init = unsafe { $crate::$construct_closure::<_, $err>(init) };
-        init
-    }};
-    (init_slot($($use_data:ident)?):
-        @data($data:ident),
-        @slot($slot:ident),
-        @guards($($guards:ident,)*),
-        @munch_fields($(..Zeroable::init_zeroed())? $(,)?),
-    ) => {
-        // Endpoint of munching, no fields are left. If execution reaches this point, all fields
-        // have been initialized. Therefore we can now dismiss the guards by forgetting them.
-        $(::core::mem::forget($guards);)*
-    };
-    (init_slot($($use_data:ident)?):
-        @data($data:ident),
-        @slot($slot:ident),
-        @guards($($guards:ident,)*),
-        // arbitrary code block
-        @munch_fields(_: { $($code:tt)* }, $($rest:tt)*),
-    ) => {
-        { $($code)* }
-        $crate::__init_internal!(init_slot($($use_data)?):
-            @data($data),
-            @slot($slot),
-            @guards($($guards,)*),
-            @munch_fields($($rest)*),
-        );
-    };
-    (init_slot($use_data:ident): // `use_data` is present, so we use the `data` to init fields.
-        @data($data:ident),
-        @slot($slot:ident),
-        @guards($($guards:ident,)*),
-        // In-place initialization syntax.
-        @munch_fields($field:ident <- $val:expr, $($rest:tt)*),
-    ) => {
-        let init = $val;
-        // Call the initializer.
-        //
-        // SAFETY: `slot` is valid, because we are inside of an initializer closure, we
-        // return when an error/panic occurs.
-        // We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`.
-        unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? };
-        // SAFETY:
-        // - the project function does the correct field projection,
-        // - the field has been initialized,
-        // - the reference is only valid until the end of the initializer.
-        #[allow(unused_variables)]
-        let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
-
-        // Create the drop guard:
-        //
-        // We rely on macro hygiene to make it impossible for users to access this local variable.
-        // We use `paste!` to create new hygiene for `$field`.
-        $crate::macros::paste! {
-            // SAFETY: We forget the guard later when initialization has succeeded.
-            let [< __ $field _guard >] = unsafe {
-                $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
-            };
-
-            $crate::__init_internal!(init_slot($use_data):
-                @data($data),
-                @slot($slot),
-                @guards([< __ $field _guard >], $($guards,)*),
-                @munch_fields($($rest)*),
-            );
-        }
-    };
-    (init_slot(): // No `use_data`, so we use `Init::__init` directly.
-        @data($data:ident),
-        @slot($slot:ident),
-        @guards($($guards:ident,)*),
-        // In-place initialization syntax.
-        @munch_fields($field:ident <- $val:expr, $($rest:tt)*),
-    ) => {
-        let init = $val;
-        // Call the initializer.
-        //
-        // SAFETY: `slot` is valid, because we are inside of an initializer closure, we
-        // return when an error/panic occurs.
-        unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? };
-
-        // SAFETY:
-        // - the field is not structurally pinned, since the line above must compile,
-        // - the field has been initialized,
-        // - the reference is only valid until the end of the initializer.
-        #[allow(unused_variables)]
-        let $field = unsafe { &mut (*$slot).$field };
-
-        // Create the drop guard:
-        //
-        // We rely on macro hygiene to make it impossible for users to access this local variable.
-        // We use `paste!` to create new hygiene for `$field`.
-        $crate::macros::paste! {
-            // SAFETY: We forget the guard later when initialization has succeeded.
-            let [< __ $field _guard >] = unsafe {
-                $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
-            };
-
-            $crate::__init_internal!(init_slot():
-                @data($data),
-                @slot($slot),
-                @guards([< __ $field _guard >], $($guards,)*),
-                @munch_fields($($rest)*),
-            );
-        }
-    };
-    (init_slot(): // No `use_data`, so all fields are not structurally pinned
-        @data($data:ident),
-        @slot($slot:ident),
-        @guards($($guards:ident,)*),
-        // Init by-value.
-        @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
-    ) => {
-        {
-            $(let $field = $val;)?
-            // Initialize the field.
-            //
-            // SAFETY: The memory at `slot` is uninitialized.
-            unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
-        }
-
-        #[allow(unused_variables)]
-        // SAFETY:
-        // - the field is not structurally pinned, since no `use_data` was required to create this
-        //   initializer,
-        // - the field has been initialized,
-        // - the reference is only valid until the end of the initializer.
-        let $field = unsafe { &mut (*$slot).$field };
-
-        // Create the drop guard:
-        //
-        // We rely on macro hygiene to make it impossible for users to access this local variable.
-        // We use `paste!` to create new hygiene for `$field`.
-        $crate::macros::paste! {
-            // SAFETY: We forget the guard later when initialization has succeeded.
-            let [< __ $field _guard >] = unsafe {
-                $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
-            };
-
-            $crate::__init_internal!(init_slot():
-                @data($data),
-                @slot($slot),
-                @guards([< __ $field _guard >], $($guards,)*),
-                @munch_fields($($rest)*),
-            );
-        }
-    };
-    (init_slot($use_data:ident):
-        @data($data:ident),
-        @slot($slot:ident),
-        @guards($($guards:ident,)*),
-        // Init by-value.
-        @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
-    ) => {
-        {
-            $(let $field = $val;)?
-            // Initialize the field.
-            //
-            // SAFETY: The memory at `slot` is uninitialized.
-            unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
-        }
-        // SAFETY:
-        // - the project function does the correct field projection,
-        // - the field has been initialized,
-        // - the reference is only valid until the end of the initializer.
-        #[allow(unused_variables)]
-        let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
-
-        // Create the drop guard:
-        //
-        // We rely on macro hygiene to make it impossible for users to access this local variable.
-        // We use `paste!` to create new hygiene for `$field`.
-        $crate::macros::paste! {
-            // SAFETY: We forget the guard later when initialization has succeeded.
-            let [< __ $field _guard >] = unsafe {
-                $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
-            };
-
-            $crate::__init_internal!(init_slot($use_data):
-                @data($data),
-                @slot($slot),
-                @guards([< __ $field _guard >], $($guards,)*),
-                @munch_fields($($rest)*),
-            );
-        }
-    };
-    (make_initializer:
-        @slot($slot:ident),
-        @type_name($t:path),
-        @munch_fields(_: { $($code:tt)* }, $($rest:tt)*),
-        @acc($($acc:tt)*),
-    ) => {
-        // code blocks are ignored for the initializer check
-        $crate::__init_internal!(make_initializer:
-            @slot($slot),
-            @type_name($t),
-            @munch_fields($($rest)*),
-            @acc($($acc)*),
-        );
-    };
-    (make_initializer:
-        @slot($slot:ident),
-        @type_name($t:path),
-        @munch_fields(..Zeroable::init_zeroed() $(,)?),
-        @acc($($acc:tt)*),
-    ) => {
-        // Endpoint, nothing more to munch, create the initializer. Since the users specified
-        // `..Zeroable::init_zeroed()`, the slot will already have been zeroed and all field that have
-        // not been overwritten are thus zero and initialized. We still check that all fields are
-        // actually accessible by using the struct update syntax ourselves.
-        // We are inside of a closure that is never executed and thus we can abuse `slot` to
-        // get the correct type inference here:
-        #[allow(unused_assignments)]
-        unsafe {
-            let mut zeroed = ::core::mem::zeroed();
-            // We have to use type inference here to make zeroed have the correct type. This does
-            // not get executed, so it has no effect.
-            ::core::ptr::write($slot, zeroed);
-            zeroed = ::core::mem::zeroed();
-            // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal
-            // information that is associated to already parsed fragments, so a path fragment
-            // cannot be used in this position. Doing the retokenization results in valid rust
-            // code.
-            $crate::macros::paste!(
-                ::core::ptr::write($slot, $t {
-                    $($acc)*
-                    ..zeroed
-                });
-            );
-        }
-    };
-    (make_initializer:
-        @slot($slot:ident),
-        @type_name($t:path),
-        @munch_fields($(,)?),
-        @acc($($acc:tt)*),
-    ) => {
-        // Endpoint, nothing more to munch, create the initializer.
-        // Since we are in the closure that is never called, this will never get executed.
-        // We abuse `slot` to get the correct type inference here:
-        //
-        // SAFETY: TODO.
-        unsafe {
-            // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal
-            // information that is associated to already parsed fragments, so a path fragment
-            // cannot be used in this position. Doing the retokenization results in valid rust
-            // code.
-            $crate::macros::paste!(
-                ::core::ptr::write($slot, $t {
-                    $($acc)*
-                });
-            );
-        }
-    };
-    (make_initializer:
-        @slot($slot:ident),
-        @type_name($t:path),
-        @munch_fields($field:ident <- $val:expr, $($rest:tt)*),
-        @acc($($acc:tt)*),
-    ) => {
-        $crate::__init_internal!(make_initializer:
-            @slot($slot),
-            @type_name($t),
-            @munch_fields($($rest)*),
-            @acc($($acc)* $field: ::core::panic!(),),
-        );
-    };
-    (make_initializer:
-        @slot($slot:ident),
-        @type_name($t:path),
-        @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
-        @acc($($acc:tt)*),
-    ) => {
-        $crate::__init_internal!(make_initializer:
-            @slot($slot),
-            @type_name($t),
-            @munch_fields($($rest)*),
-            @acc($($acc)* $field: ::core::panic!(),),
-        );
-    };
-}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 10/15] rust: pin-init: add `#[default_error(<type>)]` attribute to initializer macros
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (8 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 09/15] rust: pin-init: rewrite the initializer macros using `syn` Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 11/15] rust: init: use `#[default_error(err)]` for the " Benno Lossin
                     ` (5 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

The `#[default_error(<type>)]` attribute can be used to supply a default
type as the error used for the `[pin_]init!` macros. This way one can
easily define custom `try_[pin_]init!` variants that default to your
project specific error type. Just write the following declarative macro:

    macro_rules! try_init {
        ($($args:tt)*) => {
            ::pin_init::init!(
                #[default_error(YourCustomErrorType)]
                $($args)*
            )
        }
    }

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2:
* improve error handling
* improve parsing and default error evaluation
---
 rust/pin-init/internal/src/init.rs | 42 ++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index cd8351152cf1..1e6f7aa1c1aa 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/internal/src/init.rs
@@ -6,10 +6,11 @@
     parse_quote,
     punctuated::Punctuated,
     spanned::Spanned,
-    token, Block, Error, Expr, ExprCall, ExprPath, Ident, Path, Token, Type,
+    token, Attribute, Block, Error, Expr, ExprCall, ExprPath, Ident, Path, Token, Type,
 };
 
 pub(crate) struct Initializer {
+    attrs: Vec<InitializerAttribute>,
     this: Option<This>,
     path: Path,
     brace_token: token::Brace,
@@ -50,8 +51,17 @@ fn ident(&self) -> Option<&Ident> {
     }
 }
 
+enum InitializerAttribute {
+    DefaultError(DefaultErrorAttribute),
+}
+
+struct DefaultErrorAttribute {
+    ty: Type,
+}
+
 pub(crate) fn expand(
     Initializer {
+        attrs,
         this,
         path,
         brace_token,
@@ -65,7 +75,16 @@ pub(crate) fn expand(
     let mut macro_error = crate::Error::none();
     let error = error.map_or_else(
         || {
-            if let Some(default_error) = default_error {
+            if let Some(default_error) = attrs.iter().fold(None, |acc, attr| {
+                #[expect(irrefutable_let_patterns)]
+                if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr {
+                    Some(ty.clone())
+                } else {
+                    acc
+                }
+            }) {
+                default_error
+            } else if let Some(default_error) = default_error {
                 syn::parse_str(default_error).unwrap()
             } else {
                 macro_error.combine(Error::new(
@@ -358,6 +377,7 @@ fn make_field_check(
 
 impl Parse for Initializer {
     fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
+        let attrs = input.call(Attribute::parse_outer)?;
         let this = input.peek(Token![&]).then(|| input.parse()).transpose()?;
         let path = input.parse()?;
         let content;
@@ -389,7 +409,19 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
             .peek(Token![?])
             .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?)))
             .transpose()?;
+        let attrs = attrs
+            .into_iter()
+            .map(|a| {
+                if a.path().is_ident("default_error") {
+                    a.parse_args::<DefaultErrorAttribute>()
+                        .map(InitializerAttribute::DefaultError)
+                } else {
+                    Err(syn::Error::new_spanned(a, "unknown initializer attribute"))
+                }
+            })
+            .collect::<Result<Vec<_>, _>>()?;
         Ok(Self {
+            attrs,
             this,
             path,
             brace_token,
@@ -400,6 +432,12 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
     }
 }
 
+impl Parse for DefaultErrorAttribute {
+    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
+        Ok(Self { ty: input.parse()? })
+    }
+}
+
 impl Parse for This {
     fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
         Ok(Self {
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 11/15] rust: init: use `#[default_error(err)]` for the initializer macros
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (9 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 10/15] rust: pin-init: add `#[default_error(<type>)]` attribute to initializer macros Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-12 19:14     ` Gary Guo
  2026-01-11 12:25   ` [PATCH v2 12/15] rust: pin-init: internal: init: add support for attributes on initializer fields Benno Lossin
                     ` (4 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

Initializer macros should use this attribute instead of manually parsing
the macro's input. This is because the syntax is now parsed using `syn`,
which permits more complex constructs to be parsed. In addition, this
ensures that the kernel's initializer marcos will have the exact same
syntax as the ones from pin-init.

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2: none
---
 rust/kernel/init.rs | 40 ++++++++++++----------------------------
 1 file changed, 12 insertions(+), 28 deletions(-)

diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs
index 917f7ef001fd..7a0d4559d7b5 100644
--- a/rust/kernel/init.rs
+++ b/rust/kernel/init.rs
@@ -219,20 +219,12 @@ fn init<E>(init: impl Init<T, E>, flags: Flags) -> error::Result<Self>
 /// [`Error`]: crate::error::Error
 #[macro_export]
 macro_rules! try_init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }) => {
-        ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? $crate::error::Error)
-    };
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }? $err:ty) => {
-        ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? $err)
-    };
+    ($($args:tt)*) => {
+        ::pin_init::init!(
+            #[default_error($crate::error::Error)]
+            $($args)*
+        )
+    }
 }
 
 /// Construct an in-place, fallible pinned initializer for `struct`s.
@@ -279,18 +271,10 @@ macro_rules! try_init {
 /// [`Error`]: crate::error::Error
 #[macro_export]
 macro_rules! try_pin_init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }) => {
-        ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? $crate::error::Error)
-    };
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }? $err:ty) => {
-        ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? $err)
-    };
+    ($($args:tt)*) => {
+        ::pin_init::pin_init!(
+            #[default_error($crate::error::Error)]
+            $($args)*
+        )
+    }
 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 12/15] rust: pin-init: internal: init: add support for attributes on initializer fields
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (10 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 11/15] rust: init: use `#[default_error(err)]` for the " Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 13/15] rust: pin-init: internal: init: add escape hatch for referencing initialized fields Benno Lossin
                     ` (3 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

Initializer fields ought to support the same attributes that are allowed
in struct initializers on fields. For example, `cfg` or lint levels such
as `expect`, `allow` etc. Add parsing support for these attributes using
syn to initializer fields and adjust the macro expansion accordingly.

Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2:
* only attach cfg attributes to non-user controlled code for fields
---
 rust/pin-init/internal/src/init.rs | 69 ++++++++++++++++++++++++------
 1 file changed, 55 insertions(+), 14 deletions(-)

diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index 1e6f7aa1c1aa..c5e24ab1c3e0 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/internal/src/init.rs
@@ -25,7 +25,12 @@ struct This {
     _in_token: Token![in],
 }
 
-enum InitializerField {
+struct InitializerField {
+    attrs: Vec<Attribute>,
+    kind: InitializerKind,
+}
+
+enum InitializerKind {
     Value {
         ident: Ident,
         value: Option<(Token![:], Expr)>,
@@ -42,7 +47,7 @@ enum InitializerField {
     },
 }
 
-impl InitializerField {
+impl InitializerKind {
     fn ident(&self) -> Option<&Ident> {
         match self {
             Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident),
@@ -227,10 +232,16 @@ fn init_fields(
     slot: &Ident,
 ) -> TokenStream {
     let mut guards = vec![];
+    let mut guard_attrs = vec![];
     let mut res = TokenStream::new();
-    for field in fields {
-        let init = match field {
-            InitializerField::Value { ident, value } => {
+    for InitializerField { attrs, kind } in fields {
+        let cfgs = {
+            let mut cfgs = attrs.clone();
+            cfgs.retain(|attr| attr.path().is_ident("cfg") || attr.path().is_ident("cfg_attr"));
+            cfgs
+        };
+        let init = match kind {
+            InitializerKind::Value { ident, value } => {
                 let mut value_ident = ident.clone();
                 let value_prep = value.as_ref().map(|value| &value.1).map(|value| {
                     // Setting the span of `value_ident` to `value`'s span improves error messages
@@ -253,21 +264,24 @@ fn init_fields(
                     }
                 };
                 quote! {
+                    #(#attrs)*
                     {
                         #value_prep
                         // SAFETY: TODO
                         unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) };
                     }
+                    #(#cfgs)*
                     #[allow(unused_variables)]
                     let #ident = #accessor;
                 }
             }
-            InitializerField::Init { ident, value, .. } => {
+            InitializerKind::Init { ident, value, .. } => {
                 // Again span for better diagnostics
                 let init = format_ident!("init", span = value.span());
                 if pinned {
                     let project_ident = format_ident!("__project_{ident}");
                     quote! {
+                        #(#attrs)*
                         {
                             let #init = #value;
                             // SAFETY:
@@ -277,12 +291,14 @@ fn init_fields(
                             //   for `#ident`.
                             unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? };
                         }
+                        #(#cfgs)*
                         // SAFETY: TODO
                         #[allow(unused_variables)]
                         let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) };
                     }
                 } else {
                     quote! {
+                        #(#attrs)*
                         {
                             let #init = #value;
                             // SAFETY: `slot` is valid, because we are inside of an initializer
@@ -294,20 +310,25 @@ fn init_fields(
                                 )?
                             };
                         }
+                        #(#cfgs)*
                         // SAFETY: TODO
                         #[allow(unused_variables)]
                         let #ident = unsafe { &mut (*#slot).#ident };
                     }
                 }
             }
-            InitializerField::Code { block: value, .. } => quote!(#[allow(unused_braces)] #value),
+            InitializerKind::Code { block: value, .. } => quote! {
+                #(#attrs)*
+                #[allow(unused_braces)]
+                #value
+            },
         };
         res.extend(init);
-        if let Some(ident) = field.ident() {
+        if let Some(ident) = kind.ident() {
             // `mixed_site` ensures that the guard is not accessible to the user-controlled code.
             let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
-            guards.push(guard.clone());
             res.extend(quote! {
+                #(#cfgs)*
                 // Create the drop guard:
                 //
                 // We rely on macro hygiene to make it impossible for users to access this local
@@ -319,13 +340,18 @@ fn init_fields(
                     )
                 };
             });
+            guards.push(guard);
+            guard_attrs.push(cfgs);
         }
     }
     quote! {
         #res
         // If execution reaches this point, all fields have been initialized. Therefore we can now
         // dismiss the guards by forgetting them.
-        #(::core::mem::forget(#guards);)*
+        #(
+            #(#guard_attrs)*
+            ::core::mem::forget(#guards);
+        )*
     }
 }
 
@@ -335,7 +361,10 @@ fn make_field_check(
     init_kind: InitKind,
     path: &Path,
 ) -> TokenStream {
-    let fields = fields.iter().filter_map(|f| f.ident());
+    let field_attrs = fields
+        .iter()
+        .filter_map(|f| f.kind.ident().map(|_| &f.attrs));
+    let field_name = fields.iter().filter_map(|f| f.kind.ident());
     match init_kind {
         InitKind::Normal => quote! {
             // We use unreachable code to ensure that all fields have been mentioned exactly once,
@@ -346,7 +375,8 @@ fn make_field_check(
             let _ = || unsafe {
                 ::core::ptr::write(slot, #path {
                     #(
-                        #fields: ::core::panic!(),
+                        #(#field_attrs)*
+                        #field_name: ::core::panic!(),
                     )*
                 })
             };
@@ -366,7 +396,8 @@ fn make_field_check(
                 zeroed = ::core::mem::zeroed();
                 ::core::ptr::write(slot, #path {
                     #(
-                        #fields: ::core::panic!(),
+                        #(#field_attrs)*
+                        #field_name: ::core::panic!(),
                     )*
                     ..zeroed
                 })
@@ -387,7 +418,7 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
             let lh = content.lookahead1();
             if lh.peek(End) || lh.peek(Token![..]) {
                 break;
-            } else if lh.peek(Ident) || lh.peek(Token![_]) {
+            } else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Token![#]) {
                 fields.push_value(content.parse()?);
                 let lh = content.lookahead1();
                 if lh.peek(End) {
@@ -449,6 +480,16 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
 }
 
 impl Parse for InitializerField {
+    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
+        let attrs = input.call(Attribute::parse_outer)?;
+        Ok(Self {
+            attrs,
+            kind: input.parse()?,
+        })
+    }
+}
+
+impl Parse for InitializerKind {
     fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
         let lh = input.lookahead1();
         if lh.peek(Token![_]) {
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 13/15] rust: pin-init: internal: init: add escape hatch for referencing initialized fields
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (11 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 12/15] rust: pin-init: internal: init: add support for attributes on initializer fields Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-11 12:25   ` [PATCH v2 14/15] rust: pin-init: internal: init: simplify Zeroable safety check Benno Lossin
                     ` (2 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: Janne Grunau, rust-for-linux, linux-kernel

The initializer macro emits mutable references for already initialized
fields, which allows modifying or accessing them later in code blocks or
when initializing other fields. This behavior results in compiler errors
when combining with packed structs, since those do not permit creating
references to misaligned fields. For example:

    #[repr(C, packed)]
    struct Foo {
        a: i8,
        b: i32,
    }

    fn main() {
        let _ = init!(Foo { a: -42, b: 42 });
    }

This will lead to an error like this:

    error[E0793]: reference to field of packed struct is unaligned
      --> tests/ui/compile-fail/init/packed_struct.rs:10:13
       |
    10 |     let _ = init!(Foo { a: -42, b: 42 });
       |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       |
       = note: this struct is 1-byte aligned, but the type of this field may require higher alignment
       = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
       = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
       = note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)

This was requested by Janne Grunau [1] and will most certainly be used
by the kernel when we eventually end up with trying to initialize packed
structs.

Thus add an initializer attribute `#[disable_initialized_field_access]`
that does what the name suggests: do not generate references to already
initialized fields.

There is space for future work: add yet another attribute which can be
applied on fields of initializers that ask for said field to be made
accessible. We can add that when the need arises.

Requested-by: Janne Grunau <j@jannau.net>
Link: https://lore.kernel.org/all/20251206170214.GE1097212@robin.jannau.net [1]
Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2:
* silence clippy warning
---
 rust/pin-init/internal/src/init.rs | 76 +++++++++++++++++++++---------
 1 file changed, 53 insertions(+), 23 deletions(-)

diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index c5e24ab1c3e0..40df01635a3a 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/internal/src/init.rs
@@ -56,8 +56,10 @@ fn ident(&self) -> Option<&Ident> {
     }
 }
 
+#[expect(clippy::large_enum_variant)]
 enum InitializerAttribute {
     DefaultError(DefaultErrorAttribute),
+    DisableInitializedFieldAccess,
 }
 
 struct DefaultErrorAttribute {
@@ -81,7 +83,6 @@ pub(crate) fn expand(
     let error = error.map_or_else(
         || {
             if let Some(default_error) = attrs.iter().fold(None, |acc, attr| {
-                #[expect(irrefutable_let_patterns)]
                 if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr {
                     Some(ty.clone())
                 } else {
@@ -144,7 +145,15 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
     };
     // `mixed_site` ensures that the data is not accessible to the user-controlled code.
     let data = Ident::new("__data", Span::mixed_site());
-    let init_fields = init_fields(&fields, pinned, &data, &slot);
+    let init_fields = init_fields(
+        &fields,
+        pinned,
+        !attrs
+            .iter()
+            .any(|attr| matches!(attr, InitializerAttribute::DisableInitializedFieldAccess)),
+        &data,
+        &slot,
+    );
     let field_check = make_field_check(&fields, init_kind, &path);
     quote! {{
         #macro_error
@@ -228,6 +237,7 @@ fn get_init_kind(rest: Option<(Token![..], Expr)>, macro_error: &mut crate::Erro
 fn init_fields(
     fields: &Punctuated<InitializerField, Token![,]>,
     pinned: bool,
+    generate_initialized_accessors: bool,
     data: &Ident,
     slot: &Ident,
 ) -> TokenStream {
@@ -263,6 +273,13 @@ fn init_fields(
                         unsafe { &mut (*#slot).#ident }
                     }
                 };
+                let accessor = generate_initialized_accessors.then(|| {
+                    quote! {
+                        #(#cfgs)*
+                        #[allow(unused_variables)]
+                        let #ident = #accessor;
+                    }
+                });
                 quote! {
                     #(#attrs)*
                     {
@@ -270,37 +287,31 @@ fn init_fields(
                         // SAFETY: TODO
                         unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) };
                     }
-                    #(#cfgs)*
-                    #[allow(unused_variables)]
-                    let #ident = #accessor;
+                    #accessor
                 }
             }
             InitializerKind::Init { ident, value, .. } => {
                 // Again span for better diagnostics
                 let init = format_ident!("init", span = value.span());
-                if pinned {
+                let (value_init, accessor) = if pinned {
                     let project_ident = format_ident!("__project_{ident}");
-                    quote! {
-                        #(#attrs)*
-                        {
-                            let #init = #value;
+                    (
+                        quote! {
                             // SAFETY:
                             // - `slot` is valid, because we are inside of an initializer closure, we
                             //   return when an error/panic occurs.
                             // - We also use `#data` to require the correct trait (`Init` or `PinInit`)
                             //   for `#ident`.
                             unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? };
-                        }
-                        #(#cfgs)*
-                        // SAFETY: TODO
-                        #[allow(unused_variables)]
-                        let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) };
-                    }
+                        },
+                        quote! {
+                            // SAFETY: TODO
+                            unsafe { #data.#project_ident(&mut (*#slot).#ident) }
+                        },
+                    )
                 } else {
-                    quote! {
-                        #(#attrs)*
-                        {
-                            let #init = #value;
+                    (
+                        quote! {
                             // SAFETY: `slot` is valid, because we are inside of an initializer
                             // closure, we return when an error/panic occurs.
                             unsafe {
@@ -309,12 +320,27 @@ fn init_fields(
                                     ::core::ptr::addr_of_mut!((*#slot).#ident),
                                 )?
                             };
-                        }
+                        },
+                        quote! {
+                            // SAFETY: TODO
+                            unsafe { &mut (*#slot).#ident }
+                        },
+                    )
+                };
+                let accessor = generate_initialized_accessors.then(|| {
+                    quote! {
                         #(#cfgs)*
-                        // SAFETY: TODO
                         #[allow(unused_variables)]
-                        let #ident = unsafe { &mut (*#slot).#ident };
+                        let #ident = #accessor;
+                    }
+                });
+                quote! {
+                    #(#attrs)*
+                    {
+                        let #init = #value;
+                        #value_init
                     }
+                    #accessor
                 }
             }
             InitializerKind::Code { block: value, .. } => quote! {
@@ -446,6 +472,10 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
                 if a.path().is_ident("default_error") {
                     a.parse_args::<DefaultErrorAttribute>()
                         .map(InitializerAttribute::DefaultError)
+                } else if a.path().is_ident("disable_initialized_field_access") {
+                    a.meta
+                        .require_path_only()
+                        .map(|_| InitializerAttribute::DisableInitializedFieldAccess)
                 } else {
                     Err(syn::Error::new_spanned(a, "unknown initializer attribute"))
                 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 14/15] rust: pin-init: internal: init: simplify Zeroable safety check
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (12 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 13/15] rust: pin-init: internal: init: add escape hatch for referencing initialized fields Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-12 19:13     ` Gary Guo
  2026-01-11 12:25   ` [PATCH v2 15/15] MAINTAINERS: add Gary Guo to pin-init Benno Lossin
  2026-01-14 15:48   ` [PATCH v2 00/15] `syn` rewrite of pin-init Andreas Hindborg
  15 siblings, 1 reply; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

The `Zeroable` type check uses a small dance with a raw pointer to aid
type inference. It turns out that this is not necessary and type
inference is powerful enough to resolve any ambiguity. Thus remove it.

Suggested-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2: added this patch
---
 rust/pin-init/internal/src/init.rs | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index 40df01635a3a..17ae7b2e4f70 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/internal/src/init.rs
@@ -415,17 +415,12 @@ fn make_field_check(
             #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
             // SAFETY: this code is never executed.
             let _ = || unsafe {
-                let mut zeroed = ::core::mem::zeroed();
-                // We have to use type inference here to make zeroed have the correct type. This
-                // does not get executed, so it has no effect.
-                ::core::ptr::write(slot, zeroed);
-                zeroed = ::core::mem::zeroed();
                 ::core::ptr::write(slot, #path {
                     #(
                         #(#field_attrs)*
                         #field_name: ::core::panic!(),
                     )*
-                    ..zeroed
+                    ..::core::mem::zeroed()
                 })
             };
         },
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v2 15/15] MAINTAINERS: add Gary Guo to pin-init
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (13 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 14/15] rust: pin-init: internal: init: simplify Zeroable safety check Benno Lossin
@ 2026-01-11 12:25   ` Benno Lossin
  2026-01-14 15:48   ` [PATCH v2 00/15] `syn` rewrite of pin-init Andreas Hindborg
  15 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-11 12:25 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: linux-kernel, rust-for-linux

Gary has been involved in pin-init since the very beginning. He created
his own version before I even joined Rust-for-Linux, contributed ideas
to my solution, and reviewed it. With the removal of the declarative
macro mess, he is also going to be a maintainer of pin-init.

Acked-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Benno Lossin <lossin@kernel.org>
---
Changes in v2: none
---
 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index dc731d37c8fe..fb6534339b75 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22916,6 +22916,7 @@ F:	rust/kernel/num/
 
 RUST [PIN-INIT]
 M:	Benno Lossin <lossin@kernel.org>
+M:	Gary Guo <gary@garyguo.net>
 L:	rust-for-linux@vger.kernel.org
 S:	Maintained
 W:	https://rust-for-linux.com/pin-init
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v2 01/15] rust: pin-init: remove `try_` versions of the initializer macros
  2026-01-11 12:24   ` [PATCH v2 01/15] rust: pin-init: remove `try_` versions of the initializer macros Benno Lossin
@ 2026-01-12 12:57     ` Gary Guo
  0 siblings, 0 replies; 23+ messages in thread
From: Gary Guo @ 2026-01-12 12:57 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl
  Cc: rust-for-linux, linux-kernel

On Sun Jan 11, 2026 at 12:24 PM GMT, Benno Lossin wrote:
> The `try_[pin_]init!` versions of the initializer macros are
> superfluous. Instead of forcing the user to always write an error in
> `try_[pin_]init!` and not allowing one in `[pin_]init!`, combine them
> into `[pin_]init!` that defaults the error to
> `core::convert::Infallible`, but also allows to specify a custom one.
>
> Projects using pin-init still can provide their own defaulting
> initializers using the `try_` prefix by using the `#[default_error]`
> attribute added in a future patch.
>
> [ Adjust the definition of the kernel's version of the `try_`
>   initializer macros - Benno]
>
> Signed-off-by: Benno Lossin <lossin@kernel.org>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
> Changes in v2: none
> ---
>  rust/kernel/init.rs                     |   8 +-
>  rust/pin-init/README.md                 |   2 +-
>  rust/pin-init/examples/linked_list.rs   |  19 ++--
>  rust/pin-init/examples/pthread_mutex.rs |  10 +-
>  rust/pin-init/src/lib.rs                | 118 ++++--------------------
>  5 files changed, 35 insertions(+), 122 deletions(-)

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v2 02/15] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests
  2026-01-11 12:25   ` [PATCH v2 02/15] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests Benno Lossin
@ 2026-01-12 15:55     ` Gary Guo
  0 siblings, 0 replies; 23+ messages in thread
From: Gary Guo @ 2026-01-12 15:55 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl
  Cc: rust-for-linux, linux-kernel

On Sun Jan 11, 2026 at 12:25 PM GMT, Benno Lossin wrote:
> The `syn` approach requires use of `::pin_init::...` instead of the
> `$crate::...` construct available to declarative macros. To be able to
> use the `pin_init` crate from itself (which includes doc tests), we have
> to declare it as such.
> 
> Signed-off-by: Benno Lossin <lossin@kernel.org>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
> Changes in v2: none
> ---
>  rust/pin-init/src/lib.rs | 5 +++++
>  1 file changed, 5 insertions(+)


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v2 04/15] rust: pin-init: internal: add utility API for syn error handling
  2026-01-11 12:25   ` [PATCH v2 04/15] rust: pin-init: internal: add utility API for syn error handling Benno Lossin
@ 2026-01-12 16:14     ` Gary Guo
  2026-01-13  8:17       ` Benno Lossin
  0 siblings, 1 reply; 23+ messages in thread
From: Gary Guo @ 2026-01-12 16:14 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens
  Cc: rust-for-linux, linux-kernel

On Sun Jan 11, 2026 at 12:25 PM GMT, Benno Lossin wrote:
> Add a function to turn a `syn::Result<TokenStream>` into a
> `TokenStream`, either containing the error or the normal stream.
>
> Add a nullable custom error type wrapping `syn::Error`.
>
> Signed-off-by: Benno Lossin <lossin@kernel.org>
> ---
> Changes in v2: added this patch
> ---
>  rust/pin-init/internal/src/lib.rs | 44 +++++++++++++++++++++++++++++++
>  1 file changed, 44 insertions(+)
>
> diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
> index 4c4dc639ce82..21f5e33486ce 100644
> --- a/rust/pin-init/internal/src/lib.rs
> +++ b/rust/pin-init/internal/src/lib.rs
> @@ -36,3 +36,47 @@ pub fn derive_zeroable(input: TokenStream) -> TokenStream {
>  pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
>      zeroable::maybe_derive(input.into()).into()
>  }
> +
> +#[expect(dead_code)]
> +fn ok_or_compile_error(res: syn::Result<proc_macro2::TokenStream>) -> TokenStream {
> +    match res {
> +        Ok(stream) => stream,
> +        Err(error) => error.into_compile_error(),
> +    }
> +    .into()
> +}
> +
> +pub(crate) struct Error(Option<syn::Error>);

I don't think it's a good idea to call a type `Error` when it could just be no
error.

I think what I propose in https://lore.kernel.org/all/DFK7ITVQ97RL.2SZ2ANDIQ39H3@garyguo.net
is better than what's implemented here.

Best,
Gary

> +
> +impl From<syn::Error> for Error {
> +    fn from(value: syn::Error) -> Self {
> +        Self(Some(value))
> +    }
> +}
> +
> +impl Error {
> +    #[expect(dead_code)]
> +    pub(crate) fn none() -> Self {
> +        Self(None)
> +    }
> +
> +    #[expect(dead_code)]
> +    pub(crate) fn combine(&mut self, error: impl Into<Self>) {
> +        let error = error.into();
> +        if let Some(this) = self.0.as_mut() {
> +            if let Some(error) = error.0 {
> +                this.combine(error);
> +            }
> +        } else {
> +            self.0 = error.0;
> +        }
> +    }
> +}
> +
> +impl quote::ToTokens for Error {
> +    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
> +        if let Some(error) = self.0.as_ref() {
> +            quote::ToTokens::to_tokens(&error.to_compile_error(), tokens);
> +        }
> +    }
> +}


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v2 14/15] rust: pin-init: internal: init: simplify Zeroable safety check
  2026-01-11 12:25   ` [PATCH v2 14/15] rust: pin-init: internal: init: simplify Zeroable safety check Benno Lossin
@ 2026-01-12 19:13     ` Gary Guo
  0 siblings, 0 replies; 23+ messages in thread
From: Gary Guo @ 2026-01-12 19:13 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

On Sun Jan 11, 2026 at 12:25 PM GMT, Benno Lossin wrote:
> The `Zeroable` type check uses a small dance with a raw pointer to aid
> type inference. It turns out that this is not necessary and type
> inference is powerful enough to resolve any ambiguity. Thus remove it.
> 
> Suggested-by: Gary Guo <gary@garyguo.net>
> Signed-off-by: Benno Lossin <lossin@kernel.org>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
> Changes in v2: added this patch
> ---
>  rust/pin-init/internal/src/init.rs | 7 +------
>  1 file changed, 1 insertion(+), 6 deletions(-)


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v2 11/15] rust: init: use `#[default_error(err)]` for the initializer macros
  2026-01-11 12:25   ` [PATCH v2 11/15] rust: init: use `#[default_error(err)]` for the " Benno Lossin
@ 2026-01-12 19:14     ` Gary Guo
  0 siblings, 0 replies; 23+ messages in thread
From: Gary Guo @ 2026-01-12 19:14 UTC (permalink / raw)
  To: Benno Lossin, Gary Guo, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

On Sun Jan 11, 2026 at 12:25 PM GMT, Benno Lossin wrote:
> Initializer macros should use this attribute instead of manually parsing
> the macro's input. This is because the syntax is now parsed using `syn`,
> which permits more complex constructs to be parsed. In addition, this
> ensures that the kernel's initializer marcos will have the exact same
> syntax as the ones from pin-init.
> 
> Signed-off-by: Benno Lossin <lossin@kernel.org>

Reviewed-by: Gary Guo <gary@garyguo.net>

> ---
> Changes in v2: none
> ---
>  rust/kernel/init.rs | 40 ++++++++++++----------------------------
>  1 file changed, 12 insertions(+), 28 deletions(-)


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v2 04/15] rust: pin-init: internal: add utility API for syn error handling
  2026-01-12 16:14     ` Gary Guo
@ 2026-01-13  8:17       ` Benno Lossin
  0 siblings, 0 replies; 23+ messages in thread
From: Benno Lossin @ 2026-01-13  8:17 UTC (permalink / raw)
  To: Gary Guo, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Fiona Behrens
  Cc: rust-for-linux, linux-kernel

On Mon Jan 12, 2026 at 5:14 PM CET, Gary Guo wrote:
> I don't think it's a good idea to call a type `Error` when it could just be no
> error.
>
> I think what I propose in https://lore.kernel.org/all/DFK7ITVQ97RL.2SZ2ANDIQ39H3@garyguo.net
> is better than what's implemented here.

I don't use the fatal error, so I opted to make `emit` return unit:

    https://github.com/Rust-for-Linux/pin-init/pull/89/changes/b0a242334802695342f2095657dbe62c8841eb0c

What do you think?

Cheers,
Benno

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v2 00/15] `syn` rewrite of pin-init
  2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
                     ` (14 preceding siblings ...)
  2026-01-11 12:25   ` [PATCH v2 15/15] MAINTAINERS: add Gary Guo to pin-init Benno Lossin
@ 2026-01-14 15:48   ` Andreas Hindborg
  15 siblings, 0 replies; 23+ messages in thread
From: Andreas Hindborg @ 2026-01-14 15:48 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: linux-kernel, rust-for-linux

"Benno Lossin" <lossin@kernel.org> writes:

> Rewrite the proc-macros of pin-init by using the `syn` crate for Rust
> syntax parsing. This series has been a long way coming. At the very
> start of pin-init, I initially implemented everything using syn, since
> parsing is so much easier with it. Now after several years it is finally
> time to remove the dreaded 1600 lines of declarative macros required to
> parse and expand the initializer syntax.
>
> The move to syn is not only a blessing for the maintenance work, but
> also improves the implementation of new features. This series includes
> many such improvements.
>
> * Patch 1, 2, 3 and 4 prepare for the rewrite. The first removes the
>   superfluous `try_` variants of the initializer macros from pin-init.
>   Note that the kernel defines its own, so no code changes in the kernel
>   are required. The second patch allows using `::pin_init` in the
>   pin-init crate. The third adds the syn dependency and cleans up some
>   old workarounds and new clippy warnings. The fourth adds better error
>   handling on top of `syn`.
> * Patch 5, 6, 7, and 9 rewrite the derive macros for Zeroable,
>   `#[pinned_drop]` attribute macro, `#[pin_data]` attribute macro, and
>   the initializer macros respectively using `syn`.
> * Patch 8 ensures soundness in the future by fixing generic bounds in
>   generated code by `#[pin_data]`.
> * Patch 10 adds the `#[default_error(type)]` attribute to initializer
>   macros allowing them to specify a default error that is used when no
>   error is manually specified.
> * Patch 11 uses `#[default_error(type)]` in the definition of the
>   kernel's `try_` variants of the initializer macros (which defaults to
>   the kernel's `Error` type).
> * Patch 12 allows putting attributes on fields in initializer macros
>   (for example `cfg` attributes).
> * Patch 13 adds `#[disable_initialized_field_access]` to support packed
>   structs.
> * Patch 14 simplifies the code generated by the initializer macros.
> * Patch 15 adds Gary as a maintainer.
>
> In addition to the new features, using syn results in much cleaner error
> messages and more accurate span information. The code is much easier to
> read as well and hopefully easier to understand as well.
>
> As always, tests that ensure the correctness of the macro output are
> included and updated in the upstream pin-init repository. Take a look at
> the pull request on GitHub for the diff in the test output:
>
>     https://github.com/Rust-for-Linux/pin-init/pull/89
>
> I would greatly appreciate Tested-by's from people actively using
> pin-init weather in the kernel or outside.
>
> One thing I forgot to mention last time: the commit adding syn support
> to pin-init is also used by Gary's series to rewrite the `macros` crate
> using `syn`:
>
>     https://lore.kernel.org/all/20260107161729.3855851-1-gary@kernel.org
>
> My series can be found at:
>
>     https://github.com/BennoLossin/linux/commits/pin-init/sync
>
> The rust-dev branch in the RfL repository will be updated to contain
> both patch series for ease of use & testing.
>
> Link to v1:
>
>     https://lore.kernel.org/all/20260108135127.3153925-1-lossin@kernel.org
>
> Cheers,
> Benno

Tested with downstream rust null block driver on v6.19-rc5 [1]. No issues encountered, and
conditional fields now work, yay!

    #[pin_data]
    struct NullBlkDevice {
        ...
        #[cfg(CONFIG_BLK_DEV_ZONED)]
        #[pin]
        zoned: zoned::ZoneOptions,
    }

    try_pin_init!(Self {
        ...
        #[cfg(CONFIG_BLK_DEV_ZONED)]
        zoned <- zoned::ZoneOptions::new(
            zoned,
            device_capacity_mib,
            *block_size_bytes,
            zone_size_mib,
            zone_capacity_mib,
            zone_nr_conv,
            zone_max_open,
            zone_max_active,
            zone_append_max_sectors,
        )?,
    }),


Tested-by: Andreas Hindborg <a.hindborg@kernel.org>


Best regards,
Andreas Hindborg


[1] https://github.com/metaspace/linux/tree/rnull-v6.19-rc5


^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2026-01-14 15:48 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <0_JVrevJeDexgBCPJKUDqVKcopDMZyU-kLN9o-1502Av94rIL4O1fyItT1kC8-P0J9FbvciiNqZWf9lzMq6fUg==@protonmail.internalid>
2026-01-11 12:24 ` [PATCH v2 00/15] `syn` rewrite of pin-init Benno Lossin
2026-01-11 12:24   ` [PATCH v2 01/15] rust: pin-init: remove `try_` versions of the initializer macros Benno Lossin
2026-01-12 12:57     ` Gary Guo
2026-01-11 12:25   ` [PATCH v2 02/15] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests Benno Lossin
2026-01-12 15:55     ` Gary Guo
2026-01-11 12:25   ` [PATCH v2 03/15] rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds Benno Lossin
2026-01-11 12:25   ` [PATCH v2 04/15] rust: pin-init: internal: add utility API for syn error handling Benno Lossin
2026-01-12 16:14     ` Gary Guo
2026-01-13  8:17       ` Benno Lossin
2026-01-11 12:25   ` [PATCH v2 05/15] rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn` Benno Lossin
2026-01-11 12:25   ` [PATCH v2 06/15] rust: pin-init: rewrite the `#[pinned_drop]` attribute macro " Benno Lossin
2026-01-11 12:25   ` [PATCH v2 07/15] rust: pin-init: rewrite `#[pin_data]` " Benno Lossin
2026-01-11 12:25   ` [PATCH v2 08/15] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Benno Lossin
2026-01-11 12:25   ` [PATCH v2 09/15] rust: pin-init: rewrite the initializer macros using `syn` Benno Lossin
2026-01-11 12:25   ` [PATCH v2 10/15] rust: pin-init: add `#[default_error(<type>)]` attribute to initializer macros Benno Lossin
2026-01-11 12:25   ` [PATCH v2 11/15] rust: init: use `#[default_error(err)]` for the " Benno Lossin
2026-01-12 19:14     ` Gary Guo
2026-01-11 12:25   ` [PATCH v2 12/15] rust: pin-init: internal: init: add support for attributes on initializer fields Benno Lossin
2026-01-11 12:25   ` [PATCH v2 13/15] rust: pin-init: internal: init: add escape hatch for referencing initialized fields Benno Lossin
2026-01-11 12:25   ` [PATCH v2 14/15] rust: pin-init: internal: init: simplify Zeroable safety check Benno Lossin
2026-01-12 19:13     ` Gary Guo
2026-01-11 12:25   ` [PATCH v2 15/15] MAINTAINERS: add Gary Guo to pin-init Benno Lossin
2026-01-14 15:48   ` [PATCH v2 00/15] `syn` rewrite of pin-init Andreas Hindborg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox