From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2686B29ACC0; Fri, 16 Jan 2026 10:55:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560952; cv=none; b=eZg5Lef58vmF/ndiU7OwdR/Nj/TRCOq0oM0wb7Z/y53M8DMlRfjeNaNjhu3/+AXb1Duvmbod11zcLY8ZGkbBdhrFGxglyFSDmjArNj7VDuIH7x/GmrIqE5W77eLpEg+GrIrwOMOrVgiQjRfb554p2XkUakk0ki/hgWc+OZt5L7g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560952; c=relaxed/simple; bh=TPh+XUwlVAKMZ5uWAYgAY7bxrOgtlRJ70mwGUv5jGqg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=vGPq+djdfLXyiTOIIvyQb4WytdmkNgI3MxuSJZaIkKlzqgi7QzBk0Y1v4+zVIP190SZLxyonn7nKtH0HwxRezRaIDEkd3TtfIgHr9eiBgmd+UPW+QV5EMEhjUqYMf2lGW76rbFNWOr6QiuxWR3Sn94ff/S7y/C0desuNfmz7V7M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PimokD+1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PimokD+1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 01953C116C6; Fri, 16 Jan 2026 10:55:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560951; bh=TPh+XUwlVAKMZ5uWAYgAY7bxrOgtlRJ70mwGUv5jGqg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PimokD+1RcHL3wTR/SJsskSi16N0b2beHvsZDe9AAWTHzfAjjppXyeXIWw+yzxcdl HJvBo7TY4GcMjSmJHcocjlGdIRGH8QQ/nK9xCLHlkg39Ov+3VUe/pgimEnqUqEC7v5 +dyIoo6k8Fr1xtSzeSrK38x4yfbIYANpfwrlkH3Axll0rTCO5hvwJAVOkAErMxmkOB o5eECxY1VIalZMwHrEs+vgjZh7r/T/tAUbGb1lt9zZS2USMBhAlVjlKDaNHdJmTRar irof6Rt+6IvN8vx6e4ESE+cZbix00e/a9Xc4EcgPtgjlVQvUG/UdMA4ddTLECuo+dF RhuG7/mtpLpDw== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Christian Schrefl Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 01/15] rust: pin-init: remove `try_` versions of the initializer macros Date: Fri, 16 Jan 2026 11:54:16 +0100 Message-ID: <20260116105514.3794384-2-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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] Reviewed-by: Gary Guo Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: none 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)* }) =3D> { - ::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) =3D> { - ::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)* }) =3D> { - ::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) =3D> { - ::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 { =20 impl DriverData { fn new() -> impl PinInit { - 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 @@ =20 use core::{ cell::Cell, - convert::Infallible, marker::PhantomPinned, pin::Pin, ptr::{self, NonNull}, @@ -31,31 +30,31 @@ pub struct ListHead { =20 impl ListHead { #[inline] - pub fn new() -> impl PinInit { - try_pin_init!(&this in Self { + pub fn new() -> impl PinInit { + pin_init!(&this in Self { next: unsafe { Link::new_unchecked(this) }, prev: unsafe { Link::new_unchecked(this) }, pin: PhantomPinned, - }? Infallible) + }) } =20 #[inline] #[allow(dead_code)] - pub fn insert_next(list: &ListHead) -> impl PinInit = + '_ { - try_pin_init!(&this in Self { + pub fn insert_next(list: &ListHead) -> impl PinInit + '_ { + pin_init!(&this in Self { prev: list.next.prev().replace(unsafe { Link::new_unchecked(th= is)}), next: list.next.replace(unsafe { Link::new_unchecked(this)}), pin: PhantomPinned, - }? Infallible) + }) } =20 #[inline] - pub fn insert_prev(list: &ListHead) -> impl PinInit = + '_ { - try_pin_init!(&this in Self { + pub fn insert_prev(list: &ListHead) -> impl PinInit + '_ { + pin_init!(&this in Self { next: list.prev.next().replace(unsafe { Link::new_unchecked(th= is)}), prev: list.prev.replace(unsafe { Link::new_unchecked(this)}), pin: PhantomPinned, - }? Infallible) + }) } =20 #[inline] diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/exampl= es/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, 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) } =20 #[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 { -//! 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 =3D try_pin_init!(Foo { +/// stack_try_pin_init!(let foo: Foo =3D 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 =3D? try_pin_init!(Foo { +/// stack_try_pin_init!(let foo: Foo =3D? pin_init!(Foo { /// a <- CMutex::new(42), /// b: Box::try_new(Bar { /// x: 64, @@ -584,10 +584,10 @@ macro_rules! stack_try_pin_init { }; } =20 -/// 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 differe= nt 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` initialize= r: /// @@ -783,54 +783,10 @@ macro_rules! pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) =3D> { - $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 initi= alizer to stop -/// initialization and return the error. -/// -/// IMPORTANT: if you have `unsafe` code inside of the initializer you hav= e 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 u= se. -/// -/// # Examples -/// -/// ```rust -/// # #![feature(allocator_api)] -/// # #[path =3D "../examples/error.rs"] mod error; use error::Error; -/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, init_zero= ed}; -/// -/// #[pin_data] -/// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, -/// small: [u8; 1024 * 1024], -/// ptr: *mut u8, -/// } -/// -/// impl BigBuf { -/// fn new() -> impl PinInit { -/// try_pin_init!(Self { -/// big: Box::init(init_zeroed())?, -/// small: [0; 1024 * 1024], -/// ptr: core::ptr::null_mut(), -/// }? Error) -/// } -/// } -/// # let _ =3D Box::pin_init(BigBuf::new()); -/// ``` -// For a detailed example of how this macro works, see the module document= ation 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) =3D> { @@ -847,10 +803,10 @@ macro_rules! try_pin_init { } } =20 -/// 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 differe= nt error, then use -/// [`try_init!`]. +/// This macro defaults the error to [`Infallible`]; if you need a differe= nt one, write `? Error` +/// at the end, after the struct initializer. /// /// The syntax is identical to [`pin_init!`] and its safety caveats also a= pply: /// - `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)* }) =3D> { - $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 cu= stom 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 init= ializer. -/// -/// # 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 { -/// try_init!(Self { -/// big: Box::init(init_zeroed())?, -/// small: [0; 1024 * 1024], -/// }? AllocError) -/// } -/// } -/// # let _ =3D Box::init(BigBuf::new()); -/// ``` -// For a detailed example of how this macro works, see the module document= ation 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) =3D> { @@ -1410,14 +1324,14 @@ pub fn pin_init_array_from_fn( /// fn init_foo() -> impl PinInit { /// pin_init_scope(|| { /// let bar =3D 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`, the= n it will run the -/// initializer returned by the [`try_pin_init!`] invocation. +/// initializer returned by the [`pin_init!`] invocation. pub fn pin_init_scope(make_init: F) -> impl PinInit where F: FnOnce() -> Result, @@ -1453,14 +1367,14 @@ pub fn pin_init_scope(make_init: F) -> = impl PinInit /// fn init_foo() -> impl Init { /// init_scope(|| { /// let bar =3D 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`, the= n it will run the -/// initializer returned by the [`try_init!`] invocation. +/// initializer returned by the [`init!`] invocation. pub fn init_scope(make_init: F) -> impl Init where F: FnOnce() -> Result, --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DC53E356A22; Fri, 16 Jan 2026 10:55:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560955; cv=none; b=d/SLoQLYkHvMBwan1KU6OFZ+PCKdFRx4aB/GWAlRWPCWkJWLWGQnNKy5Dry+zhsnucC+jFbsY9wuYdBk+yYhhSkFxA6T6HZ0d1yEtn/S7UaO7rKb2DzxBKqpnXceMSIbips0UeCtSteM9MsL2kWBcH8Dk15GuCREaGyaRgRRRiQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560955; c=relaxed/simple; bh=nmMLuroM0g1vhgYaajNuwlEGX4J8jLH4/9NeYtTNe30=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EmUuz9p0zyOpEGxBuVnHaexA+I3AOxocGTpzw/FJipTKMHFLM3oKp3abGHfW89yesFIm6kLSNlQB5WLvCPXlhGSWOlqQV/nV5cv1CDm3RoLJgfFbYMuCdZPKlHDAhtI6NwXDozAXTCdvccFZm7XFV1o0Q6E5c4NTs95XjrYzmBc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nOf10bf9; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nOf10bf9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CA4C8C19425; Fri, 16 Jan 2026 10:55:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560955; bh=nmMLuroM0g1vhgYaajNuwlEGX4J8jLH4/9NeYtTNe30=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nOf10bf9yVedLSqaqnGYM9szkNc0hOqgrNjdMF3Zz5jwTA/wY3Fw/Iih3W+/86rr+ Eq9yMPgvJvcTe7SYZnFp7nNHiADZW7kaxMDazJr5QHc5WeyXsWkvDLBQi8vPv2Vc4Q NHpjFHLuSY1TLcXOafDvinN5w8cY7qPJe91gwQCG2EKvjR/aINNYd6krLT32pZsRZK dIzbOKDlwrX+0eodyhXaKXKpvYiCtLzmin6YwigxCDZlU2dh4/o84tb2PxvPMdT7ND BevTWnZ7oiDbzne5TP3K91sOMrxUx6/0yvvePgypysvicUoqLTBjpzX9OLOuQWTSZ6 v0Ci9Q/9DWPEg== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Christian Schrefl Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 02/15] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests Date: Fri, 16 Jan 2026 11:54:17 +0100 Message-ID: <20260116105514.3794384-3-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Reviewed-by: Gary Guo Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: none 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}, }; =20 +// 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)] --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD14B30B50B; Fri, 16 Jan 2026 10:56:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560963; cv=none; b=qJTkYuugokT22hYXJ4Yfcindj/IK7h4JbLJXXca4Urmp8Pa0TA/BMbwNrr+fPw4a02rtIu6Jy2rUtcTYJrCE+bszNtUL8qoIlEHiG7DuiVUt7o/zqk3J9B7af0X+BIPLSpcm6MmGeBSTe2OaaauCNw6VEE8oVh/nsfmbTmAi9Ug= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560963; c=relaxed/simple; bh=Gt/Z9WFhbCWP3tL7LkU/S2wAXx2/PnsVLFCDcWb5BB8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qCMlTp6CORb11gMevxKKuXOPDExl/XFIqRKEnEPX4taVXxY2wEEuHDHbmIuaXuQvEyYACsvF9WzF/2QBQH0mJLvEm05a20mb8K+lFw/QWPujOfgR/2ufJQrOfBguXTiTSLA5wiBXjUzTQxKBSRFvQsZJgRTGevP2qJ6M4C9yHoQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PH1UUFMC; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PH1UUFMC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 11753C116C6; Fri, 16 Jan 2026 10:56:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560963; bh=Gt/Z9WFhbCWP3tL7LkU/S2wAXx2/PnsVLFCDcWb5BB8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PH1UUFMC5HjvJasfGKctCuZ2BDGOTKHWBSlmbu0vgyHQRcZaSqZBVRIDlPI2E+ygJ qom+s8lq8xHi0j9aIAmV0C6TUhXWdlOVTenFDIqwrubxhIU0pW0CFrTFo/uDP49uM6 4GMxLIf90msEqeHX9lnJQ3Pd6QgUNJIL/G/4SUOEDJcM98xePiMRrPaJuGEFTCWtzS 3WY3tfecfBcDrboLl22dJUmZ0jmn01YtAW2JE4wnmnLiC2Ksa4ypLhLwexHbRFx/pl id38J2Nfe1jHhrwAuDJbf9wBokXFJcmebS2dOzcsYwO5+ZqLAQ73yxQ5Z0xArcpYwt RO7wXLUtjiADA== From: Benno Lossin To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Tamir Duberstein Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 03/15] rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds Date: Fri, 16 Jan 2026 11:54:18 +0100 Message-ID: <20260116105514.3794384-4-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" `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 Signed-off-by: Gary Guo Reviewed-by: Tamir Duberstein Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- This commit is also used in Gary's series to rewrite the `macros` crate using syn:=20 https://lore.kernel.org/all/20260112170919.1888584-1-gary@kernel.org Changes in v4: none Changes in v3: none 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 =20 rustdoc-pin_init_internal: private rustdoc_host =3D yes rustdoc-pin_init_internal: private rustc_target_flags =3D --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) =20 rustdoc-pin_init: private rustdoc_host =3D yes @@ -273,9 +274,10 @@ rusttestlib-macros: $(src)/macros/lib.rs \ +$(call if_changed,rustc_test_library) =20 rusttestlib-pin_init_internal: private rustc_target_flags =3D --cfg kernel= \ - --extern proc_macro + --extern proc_macro --extern proc_macro2 --extern quote --extern syn rusttestlib-pin_init_internal: private rustc_test_library_proc =3D 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) =20 rusttestlib-pin_init: private rustc_target_flags =3D --extern pin_init_int= ernal \ @@ -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) =20 -$(obj)/$(libpin_init_internal_name): private rustc_target_flags =3D --cfg = kernel -$(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs F= ORCE +$(obj)/$(libpin_init_internal_name): private rustc_target_flags =3D --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 FOR= CE +$(call if_changed_dep,rustc_procmacro) =20 quiet_cmd_rustc_library =3D $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QU= IET)) 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 =20 -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; - -use proc_macro::{TokenStream, TokenTree}; +use proc_macro2::{TokenStream, TokenTree}; =20 /// Parsed generics. /// @@ -101,7 +98,7 @@ pub(crate) fn parse_generics(input: TokenStream) -> (Gen= erics, Vec) { 1 =3D> { // Here depending on the token, it might be a gene= ric variable name. match tt.clone() { - TokenTree::Ident(i) if at_start && i.to_string= () =3D=3D "const" =3D> { + TokenTree::Ident(i) if at_start && i =3D=3D "c= onst" =3D> { let Some(name) =3D 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. =20 #![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 kerne= l 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)] =20 use proc_macro::TokenStream; =20 -#[cfg(kernel)] -#[path =3D "../../../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/interna= l/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 =20 -#[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; =20 pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStre= am { // This proc-macro only does some pre-parsing and then delegates the a= ctual parsing to @@ -28,7 +26,7 @@ pub(crate) fn pin_data(args: TokenStream, input: TokenStr= eam) -> TokenStream { // The name of the struct with ty_generics. let struct_name =3D rest .iter() - .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i.to_string(= ) =3D=3D "struct")) + .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i =3D=3D "st= ruct")) .nth(1) .and_then(|tt| match tt { TokenTree::Ident(_) =3D> { @@ -65,7 +63,7 @@ pub(crate) fn pin_data(args: TokenStream, input: TokenStr= eam) -> 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() =3D=3D "= struct") { + if matches!(&tt, TokenTree::Ident(i) if i =3D=3D "struct") { vec![tt] } else { replace_self_and_deny_type_defs(&struct_name, tt, &mut err= s) @@ -98,11 +96,7 @@ fn replace_self_and_deny_type_defs( ) -> Vec { match tt { TokenTree::Ident(ref i) - if i.to_string() =3D=3D "enum" - || i.to_string() =3D=3D "trait" - || i.to_string() =3D=3D "struct" - || i.to_string() =3D=3D "union" - || i.to_string() =3D=3D "impl" =3D> + if i =3D=3D "enum" || i =3D=3D "trait" || i =3D=3D "struct" ||= i =3D=3D "union" || i =3D=3D "impl" =3D> { errs.extend( format!( @@ -119,7 +113,7 @@ fn replace_self_and_deny_type_defs( ); vec![tt] } - TokenTree::Ident(i) if i.to_string() =3D=3D "Self" =3D> struct_nam= e.clone(), + TokenTree::Ident(i) if i =3D=3D "Self" =3D> struct_name.clone(), TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) = =3D> vec![tt], TokenTree::Group(g) =3D> vec![TokenTree::Group(Group::new( g.delimiter(), diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/inte= rnal/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 =20 -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; - -use proc_macro::{TokenStream, TokenTree}; +use proc_macro2::{TokenStream, TokenTree}; +use quote::quote; =20 pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> Token= Stream { let mut toks =3D input.into_iter().collect::>(); assert!(!toks.is_empty()); // Ensure that we have an `impl` item. - assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() =3D=3D= "impl")); + assert!(matches!(&toks[0], TokenTree::Ident(i) if i =3D=3D "impl")); // Ensure that we are implementing `PinnedDrop`. let mut nesting: usize =3D 0; let mut pinned_drop_idx =3D None; @@ -27,7 +25,7 @@ pub(crate) fn pinned_drop(_args: TokenStream, input: Toke= nStream) -> TokenStream if i >=3D 1 && nesting =3D=3D 0 { // Found the end of the generics, this should be `PinnedDrop`. assert!( - matches!(tt, TokenTree::Ident(i) if i.to_string() =3D=3D "= PinnedDrop"), + matches!(tt, TokenTree::Ident(i) if i =3D=3D "PinnedDrop"), "expected 'PinnedDrop', found: '{tt:?}'" ); pinned_drop_idx =3D Some(i); diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/interna= l/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 =20 -#[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; =20 pub(crate) fn parse_zeroable_derive_input( input: TokenStream, diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_anal= yzer.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, exte= rnal_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=3D["kernel"], is_proc_macro=3DTrue, ) --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0C9B3361DA6; Fri, 16 Jan 2026 10:56:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560970; cv=none; b=quFOcVOfK2F5vbAWxoTe5XggrpwKfykw5/+aJERGlV51K27Sq5hMK2nfJlDMv+mpOjZWYreULr74wHhnUHG4fDnJeFIalr6ZH70TlbZKzTeC5BHKS+U2PRnfceEx6ZxWc2dnrurrQKJ1/78tRK+TNPKx4GgflSaCERJiWkO1VxU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560970; c=relaxed/simple; bh=2HO2uxQMrUwOkvVnkU1kk3+Ww6OJwdAR1b0X+yVwyUE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hEQGa2+q2FUtldQAZH5tr0fm9Gn1Ekv9Vi2+QHKFChR2F4oFy8ew4AgkbPOvInxP7ZJSSppB7qOgHUflpnYngEKsWGV6wP87H7QD0TNIsatkl+vzVaJJLxX+eeznIlH0adlDQAB/M3b2fOV+66wpzYUOKUSEwQzDt9qYHlo4r+4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LZkHFTIf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LZkHFTIf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 39D33C19422; Fri, 16 Jan 2026 10:56:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560969; bh=2HO2uxQMrUwOkvVnkU1kk3+Ww6OJwdAR1b0X+yVwyUE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LZkHFTIfuZGioTG6Psrn25zWorZcZzIL41i5paERGLfPx6TztaPfyCd98O81qFY4Z zvH5G3iaTiGi/GZVL2l4N8PaIf8ciQ9pg2eedmhHFbkGAapCkttQvrcLuLw9RtWRW0 soRAJSC7QK++rzqhgCM9mWIxX7lYU1zu5GrZysGb7mfufLr2a0gGFKlc03CmZ3ZrOc B8PUg9VFploJy9XtrkYYcSlaod5HEFNjYMMvkvk7MqatnuszfiIbll2FY/Q+ivoVl7 MMuSSqieWY8G+wIG+ik2ufQM0CDm8uN0hxvVNeMWb7xAem9M/x2Ac05Q/xbeZ941WH nt+Ux56d2M+yw== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v4 04/15] rust: pin-init: internal: add utility API for syn error handling Date: Fri, 16 Jan 2026 11:54:19 +0100 Message-ID: <20260116105514.3794384-5-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The API is similar to diagnostics handling in rustc and uses a `ErrorGuaranteed` value to signify that an error has been emitted. It supports both fatal errors (which abort the macro expansion immediately by returning `Err(ErrorGuaranteed)`) and non-fatal ones at at generation time. These errors are appended to the token stream after generation has finished normally. This allows giving good errors while still expanding most of the code as expected to avoid the user encountering additional errors (for example missing definitions). Suggested-by: Gary Guo Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: switch to Gary's suggestion Changes in v2: added this patch --- rust/pin-init/internal/src/diagnostics.rs | 32 +++++++++++++++++++++++ rust/pin-init/internal/src/lib.rs | 1 + 2 files changed, 33 insertions(+) create mode 100644 rust/pin-init/internal/src/diagnostics.rs diff --git a/rust/pin-init/internal/src/diagnostics.rs b/rust/pin-init/inte= rnal/src/diagnostics.rs new file mode 100644 index 000000000000..555876c01bab --- /dev/null +++ b/rust/pin-init/internal/src/diagnostics.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::fmt::Display; + +use proc_macro2::TokenStream; +use syn::{spanned::Spanned, Error}; + +pub(crate) struct DiagCtxt(TokenStream); +pub(crate) struct ErrorGuaranteed(()); + +impl DiagCtxt { + #[expect(dead_code)] + pub(crate) fn error(&mut self, span: impl Spanned, msg: impl Display) = -> ErrorGuaranteed { + let error =3D Error::new(span.span(), msg); + self.0.extend(error.into_compile_error()); + ErrorGuaranteed(()) + } + + #[expect(dead_code)] + pub(crate) fn with( + fun: impl FnOnce(&mut DiagCtxt) -> Result, + ) -> TokenStream { + let mut dcx =3D Self(TokenStream::new()); + match fun(&mut dcx) { + Ok(mut stream) =3D> { + stream.extend(dcx.0); + stream + } + Err(ErrorGuaranteed(())) =3D> dcx.0, + } + } +} diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src= /lib.rs index 4c4dc639ce82..0e1a4724549d 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -12,6 +12,7 @@ =20 use proc_macro::TokenStream; =20 +mod diagnostics; mod helpers; mod pin_data; mod pinned_drop; --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AF81029A31C; Fri, 16 Jan 2026 10:56:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560976; cv=none; b=kPWOgjsyyRgvTwRiadGzbjWaMkMeSNuAm4G4rpqQ6gVedOWfsJhwglz5QGXHNUQWu92qY4lkS6a2HveKTixGhLME/nMNnDnwW50tTdv2gMPUrLgLulW4ksYY+2t97+u7ukKXZgMx0eK3m0nz4pRlenoLyHFBZPAynbEplIc/Xac= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560976; c=relaxed/simple; bh=fiDW6G3EQnPiVzsNLBIcEj/zG499ajVn8YFYr0W09sI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hGIw8fAzb2oMKjKtMmSyd4X8rKZFVXk0RT47bpZc22doh+k7STIuGVqgCxwdmMwgB9J3rcikqUOTjHzwreAuPH+st/0AbxaFTKsZWjOvinmsilLryQOshMjwe9PJ6eYLIHQtVbGVhkmVyvfrr5QN01EyzbflFKgZGbVMpv2cCDc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qcIsMVDQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qcIsMVDQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D516CC116C6; Fri, 16 Jan 2026 10:56:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560976; bh=fiDW6G3EQnPiVzsNLBIcEj/zG499ajVn8YFYr0W09sI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qcIsMVDQvLRMNt43rz+ihvZrBfZN7+r8xq0PPH2YFNR1HCmV5m7Tqx2mcnZRK1NOJ wpWNRTgUXIcpj/qiDbsmleHY7+Rxr+xIl7HeEQCGrCL2w1TOvvrvbpQKGnTQgxWNtv JBRO+nOR5kQMTkB0FAM2OC5D3+Moomd2OroyEGhAFVxsM3vEFp0Fw51bvU1s7Hg62A gR7kOqts6vekB5d7fp/lQIeZDVm6xQaE/cifB0jmjLCcj6uhV6him/H7rnqw7BBQk0 Fm2LcPEnPM4sIJEIoUKdbzyipDncpk9CjwCqEuqysEVcnFEboOVnxYOYkCnHnf4/gd 4T4G9UgVvaIJA== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Alban Kurti Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 05/15] rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn` Date: Fri, 16 Jan 2026 11:54:20 +0100 Message-ID: <20260116105514.3794384-6-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: * use DiagCtxt error handling Changes in v2: * improved error handling --- rust/pin-init/internal/src/diagnostics.rs | 2 - rust/pin-init/internal/src/lib.rs | 9 +- rust/pin-init/internal/src/zeroable.rs | 155 ++++++++++------------ rust/pin-init/src/macros.rs | 124 ----------------- 4 files changed, 74 insertions(+), 216 deletions(-) diff --git a/rust/pin-init/internal/src/diagnostics.rs b/rust/pin-init/inte= rnal/src/diagnostics.rs index 555876c01bab..3bdb477c2f2b 100644 --- a/rust/pin-init/internal/src/diagnostics.rs +++ b/rust/pin-init/internal/src/diagnostics.rs @@ -9,14 +9,12 @@ pub(crate) struct ErrorGuaranteed(()); =20 impl DiagCtxt { - #[expect(dead_code)] pub(crate) fn error(&mut self, span: impl Spanned, msg: impl Display) = -> ErrorGuaranteed { let error =3D Error::new(span.span(), msg); self.0.extend(error.into_compile_error()); ErrorGuaranteed(()) } =20 - #[expect(dead_code)] pub(crate) fn with( fun: impl FnOnce(&mut DiagCtxt) -> Result, ) -> TokenStream { diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src= /lib.rs index 0e1a4724549d..4cc9b7b0cda1 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -11,6 +11,9 @@ #![allow(missing_docs)] =20 use proc_macro::TokenStream; +use syn::parse_macro_input; + +use crate::diagnostics::DiagCtxt; =20 mod diagnostics; mod helpers; @@ -30,10 +33,12 @@ pub fn pinned_drop(args: TokenStream, input: TokenStrea= m) -> TokenStream { =20 #[proc_macro_derive(Zeroable)] pub fn derive_zeroable(input: TokenStream) -> TokenStream { - zeroable::derive(input.into()).into() + let input =3D parse_macro_input!(input); + DiagCtxt::with(|dcx| zeroable::derive(input, dcx)).into() } =20 #[proc_macro_derive(MaybeZeroable)] pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream { - zeroable::maybe_derive(input.into()).into() + let input =3D parse_macro_input!(input); + DiagCtxt::with(|dcx| zeroable::maybe_derive(input, dcx)).into() } diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/interna= l/src/zeroable.rs index d8a5ef3883f4..05683319b0f7 100644 --- a/rust/pin-init/internal/src/zeroable.rs +++ b/rust/pin-init/internal/src/zeroable.rs @@ -1,99 +1,78 @@ // SPDX-License-Identifier: GPL-2.0 =20 -use crate::helpers::{parse_generics, Generics}; -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::TokenStream; use quote::quote; +use syn::{parse_quote, Data, DeriveInput, Field, Fields}; =20 -pub(crate) fn parse_zeroable_derive_input( - input: TokenStream, -) -> ( - Vec, - Vec, - Vec, - Option, -) { - let ( - Generics { - impl_generics, - decl_generics: _, - ty_generics, - }, - mut rest, - ) =3D parse_generics(input); - // This should be the body of the struct `{...}`. - let last =3D rest.pop(); - // Now we insert `Zeroable` as a bound for every generic parameter in = `impl_generics`. - let mut new_impl_generics =3D Vec::with_capacity(impl_generics.len()); - // Are we inside of a generic where we want to add `Zeroable`? - let mut in_generic =3D !impl_generics.is_empty(); - // Have we already inserted `Zeroable`? - let mut inserted =3D false; - // Level of `<>` nestings. - let mut nested =3D 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 =3D=3D 0 && p.as_char() =3D=3D '= ,' =3D> { - if in_generic && !inserted { - new_impl_generics.extend(quote! { : ::pin_init::Zeroab= le }); - } - in_generic =3D true; - inserted =3D false; - new_impl_generics.push(tt); - } - // If we find `'`, then we are entering a lifetime. - TokenTree::Punct(p) if nested =3D=3D 0 && p.as_char() =3D=3D '= \'' =3D> { - in_generic =3D false; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if nested =3D=3D 0 && p.as_char() =3D=3D '= :' =3D> { - new_impl_generics.push(tt); - if in_generic { - new_impl_generics.extend(quote! { ::pin_init::Zeroable= + }); - inserted =3D true; - } - } - TokenTree::Punct(p) if p.as_char() =3D=3D '<' =3D> { - nested +=3D 1; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if p.as_char() =3D=3D '>' =3D> { - assert!(nested > 0); - nested -=3D 1; - new_impl_generics.push(tt); - } - _ =3D> new_impl_generics.push(tt), +use crate::{diagnostics::ErrorGuaranteed, DiagCtxt}; + +pub(crate) fn derive( + input: DeriveInput, + dcx: &mut DiagCtxt, +) -> Result { + let fields =3D match input.data { + Data::Struct(data_struct) =3D> data_struct.fields, + Data::Union(data_union) =3D> Fields::Named(data_union.fields), + Data::Enum(data_enum) =3D> { + return Err(dcx.error(data_enum.enum_token, "cannot derive `Zer= oable` for an enum")); } + }; + let name =3D input.ident; + let mut generics =3D 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) =3D generics.split_for_impl(); + let field_type =3D fields.iter().map(|field| &field.ty); + Ok(quote! { + // SAFETY: Every field type implements `Zeroable` and padding byte= s may be zero. + #[automatically_derived] + unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen + #whr + {} + const _: () =3D { + fn assert_zeroable() {} + fn ensure_zeroable #impl_gen () + #whr + { + #( + assert_zeroable::<#field_type>(); + )* + } + }; + }) } =20 -pub(crate) fn derive(input: TokenStream) -> TokenStream { - let (rest, new_impl_generics, ty_generics, last) =3D parse_zeroable_de= rive_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, + dcx: &mut DiagCtxt, +) -> Result { + let fields =3D match input.data { + Data::Struct(data_struct) =3D> data_struct.fields, + Data::Union(data_union) =3D> Fields::Named(data_union.fields), + Data::Enum(data_enum) =3D> { + return Err(dcx.error(data_enum.enum_token, "cannot derive `Zer= oable` for an enum")); + } + }; + let name =3D input.ident; + let mut generics =3D 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) =3D parse_zeroable_de= rive_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 . + .push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable)); } + let (impl_gen, ty_gen, whr) =3D generics.split_for_impl(); + Ok(quote! { + // SAFETY: Every field type implements `Zeroable` and padding byte= s 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(_: *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 - ),* $(,)? - }), - ) =3D> { - // SAFETY: Every field type implements `Zeroable` and padding byte= s may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_g= enerics)*> - where - $($($whr)*)? - {} - const _: () =3D { - fn assert_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 - ),* $(,)? - }), - ) =3D> { - // SAFETY: Every field type implements `Zeroable` and padding byte= s may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_g= enerics)*> - where - $($($whr)*)? - {} - const _: () =3D { - fn assert_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 - ),* $(,)? - }), - ) =3D> { - // SAFETY: Every field type implements `Zeroable` and padding byte= s may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_g= enerics)*> - where - $( - // the `for<'__dummy>` HRTB makes this not error without t= he `trivial_bounds` - // feature . - $field_ty: for<'__dummy> $crate::Zeroable, - )* - $($($whr)*)? - {} - }; - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis union $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) =3D> { - // SAFETY: Every field type implements `Zeroable` and padding byte= s may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_g= enerics)*> - where - $( - // the `for<'__dummy>` HRTB makes this not error without t= he `trivial_bounds` - // feature . - $field_ty: for<'__dummy> $crate::Zeroable, - )* - $($($whr)*)? - {} - }; -} --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4E8CF36165D; Fri, 16 Jan 2026 10:56:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560983; cv=none; b=gruTCcM8GCX9JQRxJCBx68IVNN52g81nPU0hsz+F+xEOOoP9nD95SK3mrTEPNDX9n1ZvUj15yO2knOCESJ8oMGWxdBVPMUpwm4hej4jqLCpUxpSRjSGq58T9DrzhtPhwWtxicFRPDs6YD5meS2zXph+VAabbZv0BAEXP3Ofi3Gw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560983; c=relaxed/simple; bh=RMgFvoTUsA+/qhPbx192DvHCWGVexyrysiUSMdjVqxE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ng5G8zs/+XR96iVqVZSqRpLpc7O5AzB9gY8ztZ+RghoPGjjZomPDhqAk9M1wVdb4ejQTSQf4PAk9xZWfy61NgBQz/JYS5mj81tj3ZK/zPO8RiDnVkT/y6X0b7o9x87R9A1Qtp4H8EWJkeiWfGz6/UBw03OuNwfwjumnzUpdV1uc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=rA4cNi+N; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="rA4cNi+N" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B21F7C116C6; Fri, 16 Jan 2026 10:56:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560982; bh=RMgFvoTUsA+/qhPbx192DvHCWGVexyrysiUSMdjVqxE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rA4cNi+Ne0k6QX0Jd1NqJBbNN76koPBDj82hwtzJDewYiBQIb80iuRO/GH9BXQ9QB R9aAxz8fsCQVHmrPGoSlGVg286g6tGf6BxDbKkK2MdwEE6L3tiS1iIF08csYnlCXPz rd1xRtEkG9/Zoeqp7ux4ZRuWMQAQkCyNOgqul1+k3W/q6VFOy1fDTGf1QH28bmvIZX 4YFEUb4D2hgYnwa5P687n8lWQFIRsWIaxwa6Tn+/aO2cBjJagLEpPNbec8NuJgYHTE uSUGJkB++tRTejijk0gLKpmTAIbpqfP7/Ezx/eVbNXTBmfNBct9Bsolt9s+/YawanN mesi+RS/jp4gg== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Tamir Duberstein , Alban Kurti Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 06/15] rust: pin-init: rewrite the `#[pinned_drop]` attribute macro using `syn` Date: Fri, 16 Jan 2026 11:54:21 +0100 Message-ID: <20260116105514.3794384-7-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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)*) { | ^^ =3D 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 | =3D help: implement the missing item: `fn drop(self: Pin<&mut Self>, = _: OnlyCallFromDrop) { todo!() }` Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: * use DiagCtxt error handling 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 | 86 +++++++++++++---------- rust/pin-init/src/macros.rs | 28 -------- 3 files changed, 52 insertions(+), 66 deletions(-) diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src= /lib.rs index 4cc9b7b0cda1..a75b99b58189 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -28,7 +28,9 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) ->= TokenStream { =20 #[proc_macro_attribute] pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { - pinned_drop::pinned_drop(args.into(), input.into()).into() + let args =3D parse_macro_input!(args); + let input =3D parse_macro_input!(input); + DiagCtxt::with(|dcx| pinned_drop::pinned_drop(args, input, dcx)).into() } =20 #[proc_macro_derive(Zeroable)] diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/inte= rnal/src/pinned_drop.rs index cf8cd1c42984..a20ac314ca82 100644 --- a/rust/pin-init/internal/src/pinned_drop.rs +++ b/rust/pin-init/internal/src/pinned_drop.rs @@ -1,49 +1,61 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT =20 -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::TokenStream; use quote::quote; +use syn::{parse::Nothing, parse_quote, spanned::Spanned, ImplItem, ItemImp= l, Token}; =20 -pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> Token= Stream { - let mut toks =3D input.into_iter().collect::>(); - assert!(!toks.is_empty()); - // Ensure that we have an `impl` item. - assert!(matches!(&toks[0], TokenTree::Ident(i) if i =3D=3D "impl")); - // Ensure that we are implementing `PinnedDrop`. - let mut nesting: usize =3D 0; - let mut pinned_drop_idx =3D None; - for (i, tt) in toks.iter().enumerate() { - match tt { - TokenTree::Punct(p) if p.as_char() =3D=3D '<' =3D> { - nesting +=3D 1; +use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; + +pub(crate) fn pinned_drop( + _args: Nothing, + mut input: ItemImpl, + dcx: &mut DiagCtxt, +) -> Result { + if let Some(unsafety) =3D input.unsafety { + dcx.error(unsafety, "implementing `PinnedDrop` is safe"); + } + input.unsafety =3D Some(Token![unsafe](input.impl_token.span)); + match &mut input.trait_ { + Some((not, path, _for)) =3D> { + if let Some(not) =3D not { + dcx.error(not, "cannot implement `!PinnedDrop`"); } - TokenTree::Punct(p) if p.as_char() =3D=3D '>' =3D> { - nesting =3D nesting.checked_sub(1).unwrap(); - continue; + for (seg, expected) in path + .segments + .iter() + .rev() + .zip(["PinnedDrop", "pin_init", ""]) + { + if expected.is_empty() || seg.ident !=3D expected { + dcx.error(seg, "bad import path for `PinnedDrop`"); + } + if !seg.arguments.is_none() { + dcx.error(&seg.arguments, "unexpected arguments for `P= innedDrop` path"); + } } - _ =3D> {} + *path =3D parse_quote!(::pin_init::PinnedDrop); } - if i >=3D 1 && nesting =3D=3D 0 { - // Found the end of the generics, this should be `PinnedDrop`. - assert!( - matches!(tt, TokenTree::Ident(i) if i =3D=3D "PinnedDrop"), - "expected 'PinnedDrop', found: '{tt:?}'" + None =3D> { + let span =3D input + .impl_token + .span + .join(input.self_ty.span()) + .unwrap_or(input.impl_token.span); + dcx.error( + span, + "expected `impl ... PinnedDrop for ...`, got inherent impl= ", ); - pinned_drop_idx =3D Some(i); - break; } } - let idx =3D 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)) =3D toks.pop() { - let last =3D 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) =3D item { + if fn_item.sig.ident =3D=3D "drop" { + fn_item + .sig + .inputs + .push(parse_quote!(_: ::pin_init::__internal::OnlyCall= FromDrop)); + } + } } + Ok(quote!(#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; =20 -/// 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)* - } - ), - ) =3D> { - // SAFETY: TODO. - unsafe $($impl_sig)* { - // Inherit all attributes and the type/ident tokens for the si= gnature. - $(#[$($attr)*])* - fn drop($($sig)*, _: $crate::__internal::OnlyCallFromDrop) { - $($inner)* - } - } - } -} - /// This macro first parses the struct definition such that it separates p= inned and not pinned /// fields. Afterwards it declares the struct and implement the `PinData` = trait safely. #[doc(hidden)] --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4F3F5363C75; Fri, 16 Jan 2026 10:56:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560992; cv=none; b=cWAmhZSh9Acp8vrWhk5X7EymHMr1iuHg7ydaGyYrCD/ldzn6CaNEAoXNj3EFCnnFHRwtBXK9vXT5pbZNkld9h5IP9Nwvo9pukLI8YGmJ3cqaGvwogcLx8PvG1hQlCqMYDm1sruZQ0wrzvuYWDSQcnvNTsH48g/yqloURhglK9aA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560992; c=relaxed/simple; bh=Qq6ym4PksmlC1gTodlqs15cBEbaF64qrEE5E9Vnq1Ck=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rPdpQc4hfbYnLdK3nsYjHjHNEG3FYu3K1Q5tNj3m49UFixe1f3+hZmgE5ftoiuD8YTipqb4kR4HGl5vVBRX9GT1i0xNrsrS/kubx2HP207auZmOIkVl7a5aWnSKdylaHIUn0r7sbhK+a23SXuNFXJPyhFeP6PgrozdQk86RNfII= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=vJCKdt+P; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="vJCKdt+P" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5C83CC116C6; Fri, 16 Jan 2026 10:56:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560991; bh=Qq6ym4PksmlC1gTodlqs15cBEbaF64qrEE5E9Vnq1Ck=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vJCKdt+PnZqxYYMCyizwWOoABQzWgjmYklRuBQ4VO7P49+teeP3gkpAl9sQN+jY5G oHIPZutLDQM0ZqHxKyGeDQUdenefYN7iwKBtBrreVItFwSgXv+UfdQaLrzicC4bnxD 7XCjEWwMtrmM3DyVGHrRPnlIYfy1FEBNoMhf0bDtJnc++D09lSuELDqCcayydKXoB5 I2yyWzpcciZJgsuaB9mRdQCaChz2dSha2m1v7gp8VfVIdZur2u09tG4p7NSvrbIt0P Lg1nqM420Y+DF8wnV5Wxh+UGRU6CV/ZLOaZAZlt1lj3IRFFeqLLaWUklUERarh9tX/ mEKRezXrHLjfg== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Tamir Duberstein , Alban Kurti Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v4 07/15] rust: pin-init: rewrite `#[pin_data]` using `syn` Date: Fri, 16 Jan 2026 11:54:22 +0100 Message-ID: <20260116105514.3794384-8-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 b: Box } 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 | ^ 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] | ^^^^^^^^^^^ | =3D help: consider increasing the recursion limit by adding a `#![rec= ursion_limit =3D "256"]` attribute to your crate (`$CRATE`) =3D note: this error originates in the macro `$crate::__pin_data` whi= ch comes from the expansion of the attribute macro `pin_data` (in Nightly b= uilds, 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 | ^ help: try adding a comma: `,` error: expected `,` --> tests/ui/compile-fail/pin_data/missing_comma.rs:6:5 | 6 | b: Box | ^ Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin Reviewed-by: Gary Guo --- Changes in v4: * improve parsing * consolidate generics handling * improve handling of `#[pin]` attributes Changes in v3: * use DiagCtxt error handling 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 | 7 +- rust/pin-init/internal/src/pin_data.rs | 611 ++++++++++++++++++++----- rust/pin-init/src/macros.rs | 574 ----------------------- 4 files changed, 503 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 =3D todo!(); -/// let (Generics { decl_generics, impl_generics, ty_generics }, rest) =3D= 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 =3D 0`). - /// - /// Use this on type definitions e.g. `struct Foo<$decl_generics> ...`= (or `union`/`enum`). - pub(crate) decl_generics: Vec, - /// 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, - /// The generics without bounds and without default values (e.g. `T, N= `). - /// - /// Use this when you use the type that is declared with these generic= s e.g. - /// `Foo<$ty_generics>`. - pub(crate) ty_generics: Vec, -} - -/// Parses the given `TokenStream` into `Generics` and the rest. -/// -/// The generics are not present in the rest, but a where clause might rem= ain. -pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec) { - // The generics with bounds and default values. - let mut decl_generics =3D vec![]; - // `impl_generics`, the declared generics with their bounds. - let mut impl_generics =3D vec![]; - // Only the names of the generics, without any bounds. - let mut ty_generics =3D vec![]; - // Tokens not related to the generics e.g. the `where` token and defin= ition. - let mut rest =3D vec![]; - // The current level of `<`. - let mut nesting =3D 0; - let mut toks =3D input.into_iter(); - // If we are at the beginning of a generic parameter. - let mut at_start =3D true; - let mut skip_until_comma =3D false; - while let Some(tt) =3D toks.next() { - if nesting =3D=3D 1 && matches!(&tt, TokenTree::Punct(p) if p.as_c= har() =3D=3D '>') { - // Found the end of the generics. - break; - } else if nesting >=3D 1 { - decl_generics.push(tt.clone()); - } - match tt.clone() { - TokenTree::Punct(p) if p.as_char() =3D=3D '<' =3D> { - if nesting >=3D 1 && !skip_until_comma { - // This is inside of the generics and part of some bou= nd. - impl_generics.push(tt); - } - nesting +=3D 1; - } - TokenTree::Punct(p) if p.as_char() =3D=3D '>' =3D> { - // This is a parsing error, so we just end it here. - if nesting =3D=3D 0 { - break; - } else { - nesting -=3D 1; - if nesting >=3D 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() =3D=3D = ',' =3D> { - if nesting =3D=3D 1 { - impl_generics.push(tt.clone()); - impl_generics.push(tt); - skip_until_comma =3D false; - } - } - _ if !skip_until_comma =3D> { - match nesting { - // If we haven't entered the generics yet, we still wa= nt to keep these tokens. - 0 =3D> rest.push(tt), - 1 =3D> { - // Here depending on the token, it might be a gene= ric variable name. - match tt.clone() { - TokenTree::Ident(i) if at_start && i =3D=3D "c= onst" =3D> { - let Some(name) =3D 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 =3D false; - } - TokenTree::Ident(_) if at_start =3D> { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start =3D false; - } - TokenTree::Punct(p) if p.as_char() =3D=3D ',' = =3D> { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start =3D true; - } - // Lifetimes begin with `'`. - TokenTree::Punct(p) if p.as_char() =3D=3D '\''= && at_start =3D> { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - } - // Generics can have default values, we skip t= hese. - TokenTree::Punct(p) if p.as_char() =3D=3D '=3D= ' =3D> { - skip_until_comma =3D true; - } - _ =3D> impl_generics.push(tt), - } - } - _ =3D> impl_generics.push(tt), - } - } - _ =3D> {} - } - } - 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 a75b99b58189..56dc306e04a9 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -16,14 +16,15 @@ use crate::diagnostics::DiagCtxt; =20 mod diagnostics; -mod helpers; mod pin_data; mod pinned_drop; mod zeroable; =20 #[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 { + let args =3D parse_macro_input!(args); + let input =3D parse_macro_input!(input); + DiagCtxt::with(|dcx| pin_data::pin_data(args, input, dcx)).into() } =20 #[proc_macro_attribute] diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/interna= l/src/pin_data.rs index 86a53b37cc66..11ea3f8d8a1b 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1,126 +1,513 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT =20 -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, + Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility,= WhereClause, +}; =20 -pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStre= am { - // This proc-macro only does some pre-parsing and then delegates the a= ctual parsing to - // `pin_init::__pin_data!`. +use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; =20 +pub(crate) mod kw { + syn::custom_keyword!(PinnedDrop); +} + +pub(crate) enum Args { + Nothing(Nothing), + #[allow(dead_code)] + PinnedDrop(kw::PinnedDrop), +} + +impl Parse for Args { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let lh =3D input.lookahead1(); + if lh.peek(End) { + input.parse().map(Self::Nothing) + } else if lh.peek(kw::PinnedDrop) { + input.parse().map(Self::PinnedDrop) + } else { + Err(lh.error()) + } + } +} + +pub(crate) fn pin_data( + args: Args, + input: Item, + dcx: &mut DiagCtxt, +) -> Result { + let mut struct_ =3D match input { + Item::Struct(struct_) =3D> struct_, + Item::Enum(enum_) =3D> { + return Err(dcx.error( + enum_.enum_token, + "`#[pin_data]` only supports structs for now", + )); + } + Item::Union(union) =3D> { + return Err(dcx.error( + union.union_token, + "`#[pin_data]` only supports structs for now", + )); + } + rest =3D> { + return Err(dcx.error( + rest, + "`#[pin_data]` can only be applied to struct, enum and uni= on definitions", + )); + } + }; + + // The generics might contain the `Self` type. Since this macro will d= efine a new type with the + // same generics and bounds, this poses a problem: `Self` will refer t= o the new type as opposed + // to this struct definition. Therefore we have to replace `Self` with= the concrete name. + let mut replacer =3D { + let name =3D &struct_.ident; + let (_, ty_generics, _) =3D 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)> =3D struct_ + .fields + .iter_mut() + .map(|field| { + let len =3D field.attrs.len(); + field.attrs.retain(|a| !a.path().is_ident("pin")); + (len !=3D field.attrs.len(), &*field) + }) + .collect(); + + for (pinned, field) in &fields { + if !pinned && is_phantom_pinned(&field.ty) { + dcx.error( + field, + format!( + "The field `{}` of type `PhantomPinned` only has an ef= fect \ + if it has the `#[pin]` attribute", + field.ident.as_ref().unwrap(), + ), + ); + } + } + + let unpin_impl =3D generate_unpin_impl(&struct_.ident, &struct_.generi= cs, &fields); + let drop_impl =3D generate_drop_impl(&struct_.ident, &struct_.generics= , args); + let projections =3D + generate_projections(&struct_.vis, &struct_.ident, &struct_.generi= cs, &fields); + let the_pin_data =3D + generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.gener= ics, &fields); + + Ok(quote! { + #struct_ + #projections + // We put the rest into this const item, because it then will not = be accessible to anything + // outside. + const _: () =3D { + #the_pin_data + #unpin_impl + #drop_impl + }; + }) +} + +fn is_phantom_pinned(ty: &Type) -> bool { + match ty { + Type::Path(TypePath { qself: None, path }) =3D> { + // 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::mark= er::PhantomPinned` or + // `::std::marker::PhantomPinned`. + if path.leading_colon.is_some() && path.segments.len() !=3D 3 { + return false; + } + let expected: Vec<&[&str]> =3D vec![&["PhantomPinned"], &["mar= ker"], &["core", "std"]]; + for (actual, expected) in path.segments.iter().rev().zip(expec= ted) { + if !actual.arguments.is_empty() || expected.iter().all(|e|= actual.ident !=3D e) { + return false; + } + } + true + } + _ =3D> false, + } +} + +fn generate_unpin_impl( + ident: &Ident, + generics: &Generics, + fields: &[(bool, &Field)], +) -> TokenStream { + let (_, ty_generics, _) =3D generics.split_for_impl(); + let mut generics_with_pin_lt =3D generics.clone(); + generics_with_pin_lt.params.insert(0, parse_quote!('__pin)); + generics_with_pin_lt.make_where_clause(); let ( - Generics { - impl_generics, - decl_generics, - ty_generics, - }, - rest, - ) =3D parse_generics(input); - // The struct definition might contain the `Self` type. Since `__pin_d= ata!` 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 =3D TokenStream::new(); - // The name of the struct with ty_generics. - let struct_name =3D rest - .iter() - .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i =3D=3D "st= ruct")) - .nth(1) - .and_then(|tt| match tt { - TokenTree::Ident(_) =3D> { - let tt =3D tt.clone(); - let mut res =3D 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::Joi= nt))); - res.push(TokenTree::Punct(Punct::new(':', Spacing::Alo= ne))); - res.push(TokenTree::Punct(Punct::new('<', Spacing::Alo= ne))); - res.extend(ty_generics.iter().cloned()); - res.push(TokenTree::Punct(Punct::new('>', Spacing::Alo= ne))); + impl_generics_with_pin_lt, + ty_generics_with_pin_lt, + Some(WhereClause { + where_token, + predicates, + }), + ) =3D generics_with_pin_lt.split_for_impl() + else { + unreachable!() + }; + let pinned_fields =3D 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 &= '__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) -> T= okenStream { + let (impl_generics, ty_generics, whr) =3D generics.split_for_impl(); + let has_pinned_drop =3D matches!(args, Args::PinnedDrop(_)); + // We need to disallow normal `Drop` implementation, the exact behavio= r depends on whether + // `PinnedDrop` was specified in `args`. + if has_pinned_drop { + // When `PinnedDrop` was specified we just implement `Drop` and de= legate. + 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 =3D unsafe { ::core::pin::Pin::new_unchecke= d(self) }; + // SAFETY: Since this is a drop function, we can creat= e this token to call the + // pinned destructor of this type. + let token =3D unsafe { ::pin_init::__internal::OnlyCal= lFromDrop::new() }; + ::pin_init::PinnedDrop::drop(pinned, token); } - Some(res) } - _ =3D> 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::() - .unwrap(), - ); - "Self".parse::().unwrap().into_iter().collect() - }); - let impl_generics =3D impl_generics - .into_iter() - .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &= mut errs)) - .collect::>(); - let mut rest =3D 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 =3D=3D "struct") { - vec![tt] + } + } else { + // When no `PinnedDrop` was specified, then we have to prevent imp= lementing drop. + quote! { + // We prevent this by creating a trait that will be implemente= d for all types implementing + // `Drop`. Additionally we will implement this trait for the s= truct leading to a conflict, + // if it also implements `Drop` + trait MustNotImplDrop {} + #[expect(drop_bounds)] + impl 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 + 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, _) =3D generics.split_for_impl(); + let mut generics_with_pin_lt =3D generics.clone(); + generics_with_pin_lt.params.insert(0, parse_quote!('__pin)); + let (_, ty_generics_with_pin_lt, whr) =3D generics_with_pin_lt.split_f= or_impl(); + let projection =3D format_ident!("{ident}Projection"); + let this =3D format_ident!("this"); + + let (fields_decl, fields_proj) =3D collect_tuple(fields.iter().map( + |( + pinned, + Field { + vis, + ident, + ty, + attrs, + .. + }, + )| { + let mut attrs =3D attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let mut no_doc_attrs =3D attrs.clone(); + no_doc_attrs.retain(|a| !a.path().is_ident("doc")); + let ident =3D 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 err= s) + ( + quote!( + #(#attrs)* + #vis #ident: &'__pin mut #ty, + ), + quote!( + #(#no_doc_attrs)* + #ident: &mut #this.#ident, + ), + ) } - }) - .collect::>(); - // This should be the body of the struct `{...}`. - let last =3D rest.pop(); - let mut quoted =3D 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 =3D fields + .iter() + .filter_map(|(pinned, field)| pinned.then_some(field)) + .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwra= p())); + let not_structurally_pinned_fields_docs =3D fields + .iter() + .filter_map(|(pinned, field)| (!pinned).then_some(field)) + .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwra= p())); + let docs =3D format!(" Pin-projections of [`{ident}`]"); + quote! { + #[doc =3D #docs] + #[allow(dead_code)] + #[doc(hidden)] + #vis struct #projection #generics_with_pin_lt { + #(#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 =3D #structurally_pinned_fields_docs])* + /// + /// These fields are **not** structurally pinned: + #(#[doc =3D #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 st= ructurally pinned. + let #this =3D unsafe { ::core::pin::Pin::get_unchecked_mut= (self) }; + #projection { + #(#fields_proj)* + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + } + } } =20 -/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `str= uct` `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, - tt: TokenTree, - errs: &mut TokenStream, -) -> Vec { - match tt { - TokenTree::Ident(ref i) - if i =3D=3D "enum" || i =3D=3D "trait" || i =3D=3D "struct" ||= i =3D=3D "union" || i =3D=3D "impl" =3D> +fn generate_the_pin_data( + vis: &Visibility, + ident: &Ident, + generics: &Generics, + fields: &[(bool, &Field)], +) -> TokenStream { + let (impl_generics, ty_generics, whr) =3D generics.split_for_impl(); + + // For every field, we create an initializing projection function acco= rding to its projection + // type. If a field is structurally pinned, then it must be initialize= d 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 =3D attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let ident =3D ident + .as_ref() + .expect("only structs with named fields are supported"); + let project_ident =3D format_ident!("__project_{ident}"); + let (init_ty, init_fn, project_ty, project_body, pin_safety) =3D i= f 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. i= t will be pinned. + ), + ) + } else { + ( + quote!(Init), + quote!(__init), + quote!(&'__slot mut #ty), + quote!(slot), + quote!(), + ) + }; + let slot_safety =3D format!( + " `slot` points at the field `{ident}` inside of `{struct_iden= t}`, 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( + 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 =3D #slot_safety] + #(#attrs)* + #vis unsafe fn #project_ident<'__slot>( + self, + slot: &'__slot mut #ty, + ) -> #project_ty { + #project_body + } + } + } + + let field_accessors =3D fields + .iter() + .map(|(pinned, field)| handle_field(field, ident, *pinned)) + .collect::(); + quote! { + // We declare this struct which will host all of the projection fu= nction for our type. It + // will be invariant over all generic parameters which are inherit= ed from the struct. + #[doc(hidden)] + #vis struct __ThePinData #generics + #whr { - errs.extend( - format!( - "::core::compile_error!(\"Cannot use `{i}` inside of s= truct definition with \ - `#[pin_data]`.\");" - ) - .parse::() - .unwrap() - .into_iter() - .map(|mut tok| { - tok.set_span(tt.span()); - tok - }), - ); - vec![tt] - } - TokenTree::Ident(i) if i =3D=3D "Self" =3D> struct_name.clone(), - TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) = =3D> vec![tt], - TokenTree::Group(g) =3D> 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_gene= rics + #whr + { + fn clone(&self) -> Self { *self } + } + + impl #impl_generics ::core::marker::Copy for __ThePinData #ty_gene= rics + #whr + {} + + #[allow(dead_code)] // Some functions might never be used and priv= ate. + #[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 =3D __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 __T= hePinData #ty_generics + #whr + { + type Datee =3D #ident #ty_generics; + } + } +} + +struct SelfReplacer(PathSegment); + +impl VisitMut for SelfReplacer { + fn visit_path_mut(&mut self, i: &mut syn::Path) { + if i.is_ident("Self") { + let span =3D i.span(); + let seg =3D &self.0; + *i =3D parse_quote_spanned!(span=3D> #seg); + } else { + syn::visit_mut::visit_path_mut(self, i); + } + } + + fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) { + if seg.ident =3D=3D "Self" { + let span =3D seg.span(); + let this =3D &self.0; + *seg =3D parse_quote_spanned!(span=3D> #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(iter: impl Iterator) -> (Vec, V= ec) { + let mut res_a =3D vec![]; + let mut res_b =3D 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; =20 -/// This macro first parses the struct definition such that it separates p= inned 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-pars= ing. - (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)* }), - ) =3D> { - // 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 'pin= ned fields'. The user - // wants these to be structurally pinned. The rest of the fields a= re 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 param= eters. - $crate::__pin_data!(find_pinned_fields: - // Attributes on the struct itself, these will just be propaga= ted 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 spec= ified on the struct inside - // of an `impl<$ty_generics>` block. - @impl_generics($($impl_generics)*), - // The 'ty generics', the generics that will need to be specif= ied 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 t= he 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::)?PhantomPi= nned, $($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)?), - ) =3D> { - ::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::Phantom= Pinned,), - @not_pinned($($not_pinned)*), - @fields($($fields)* $($accum)* $field: ::core::marker::Phantom= Pinned,), - @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)?), - ) =3D> { - $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)?), - ) =3D> { - $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)?), - ) =3D> { - $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 simplicit= y 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)?), - ) =3D> { - $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)?), - ) =3D> { - $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 c= omma, 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)?), - ) =3D> { - // 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 _: () =3D { - // We declare this struct which will host all of the projectio= n 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 abov= e 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 =3D __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_generic= s)*> - where $($whr)* - { - type Datee =3D $name<$($ty_generics)*>; - } - - // This struct will be used for the unpin analysis. Since only= structurally pinned - // fields are relevant whether the struct should implement `Un= pin`. - #[allow(dead_code)] - struct __Unpin <'__pin, $($impl_generics)*> - where $($whr)* - { - __phantom_pin: ::core::marker::PhantomData &'__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 $na= me<$($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 impleme= nting drop. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(), - ) =3D> { - // We prevent this by creating a trait that will be implemented fo= r all types implementing - // `Drop`. Additionally we will implement this trait for the struc= t leading to a conflict, - // if it also implements `Drop` - trait MustNotImplDrop {} - #[expect(drop_bounds)] - impl MustNotImplDrop for T {} - impl<$($impl_generics)*> MustNotImplDrop for $name<$($ty_generics)= *> - where $($whr)* {} - // We also take care to prevent users from writing a useless `Pinn= edDrop` 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 - 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 delega= te. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(PinnedDrop), - ) =3D> { - impl<$($impl_generics)*> ::core::ops::Drop for $name<$($ty_generic= s)*> - where $($whr)* - { - fn drop(&mut self) { - // SAFETY: Since this is a destructor, `self` will not mov= e after this function - // terminates, since it is inaccessible. - let pinned =3D unsafe { ::core::pin::Pin::new_unchecked(se= lf) }; - // SAFETY: Since this is a drop function, we can create th= is token to call the - // pinned destructor of this type. - let token =3D unsafe { $crate::__internal::OnlyCallFromDro= p::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)*), - ) =3D> { - compile_error!( - "Wrong parameters to `#[pin_data]`, expected nothing or `Pinne= dDrop`, 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:t= y),* $(,)?), - ) =3D> { - $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 m= ut ()>, - } - - impl<$($impl_generics)*> $name<$($ty_generics)*> - where $($whr)* - { - /// Pin-projects all fields of `Self`. - /// - /// These fields are structurally pinned: - $(#[doc =3D ::core::concat!(" - `", ::core::stringify!($p_= field), "`")])* - /// - /// These fields are **not** structurally pinned: - $(#[doc =3D ::core::concat!(" - `", ::core::stringify!($fi= eld), "`")])* - #[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 no= t structurally pinned. - let this =3D 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_unch= ecked(&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:t= y),* $(,)?), - ) =3D> { - $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 v= ia `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( - 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( - 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. --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8E8B23624C7; Fri, 16 Jan 2026 10:56:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560995; cv=none; b=NZwxb8b+KDSPi0TKjMxFjhoF6whDibpvKmKMO7RmHLodWS23dWERPaSKeGmM+MuyKycKjsEKD+S7hE8DS7qbEc2UB2wFpmB2sEmUC5eH5RjJrk5s5wJ84aFLBcu1LkSSyIkO2hLKX+AiQIV9MU3fD+ZxFYx1tHoMk7jzL4hwt+g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768560995; c=relaxed/simple; bh=//3KCgtf0P/XZavEN+NB6p/Jsmc2AsGhSyNxc5LgbPk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QzVaYz0FmE7Rj7/ccmV0zoYB89Dbm2yaRVcPycT2YzLP4IxNW+iazsrMDGsd0J3zxeUGMiOv+ViX8v8eyDUc2P5NWncgUcKE3Ed6B/eSHnr0ophsceukK+qCReCTlXC1W7vy+UrBtb1mazuRmDk5DYjcfZB+XWniZPdDIzJkmi0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ias/88Ax; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ias/88Ax" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 92B46C19422; Fri, 16 Jan 2026 10:56:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768560995; bh=//3KCgtf0P/XZavEN+NB6p/Jsmc2AsGhSyNxc5LgbPk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ias/88AxwMnwv5vngj56GeF9HTmzE2yRtL5PyzrjM1J7I2VWGXG/L0/hcE+tD8PSB YJoYUpNEvMgZParqQ8NQsCpaUK6S/kYQUVHjGBAuD9m0DchzOCgCG7Z8cDxLIE0RmY ttOSQ9RwP9SmykXNKmJB6grfZc6xqmoBTlKUTAan0xMrL7lzbqmRmyu9vaSptTA7QP LqGOVva6ympityUemwi0Iw0WTxBizy8HbawtcUo9zjznUQxyiMESsJnTYTO7w12ybD j7kpf3+qYc8DfkfleUb7KT+Y4QE7j1GuzxR9qbttrrOJj6xHvkgy4SJhujFl/vZLy9 mmSGFu/3YtsrQ== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 08/15] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Date: Fri, 16 Jan 2026 11:54:23 +0100 Message-ID: <20260116105514.3794384-9-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: none 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/interna= l/src/pin_data.rs index 11ea3f8d8a1b..7d871236b49c 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -215,7 +215,7 @@ fn drop(&mut self) { // if it also implements `Drop` trait MustNotImplDrop {} #[expect(drop_bounds)] - impl MustNotImplDrop for T {} + impl MustNotImp= lDrop for T {} impl #impl_generics MustNotImplDrop for #ident #ty_generics #whr {} @@ -224,7 +224,7 @@ impl #impl_generics MustNotImplDrop for #ident #ty_gene= rics // `PinnedDrop` as the parameter to `#[pin_data]`. #[expect(non_camel_case_types)] trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} - impl + impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T= {} impl #impl_generics UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #= ident #ty_generics --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8472D356A22; Fri, 16 Jan 2026 10:56:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561007; cv=none; b=M6KNx6QXFmvTwe8cNAOVgdClWZzWYVG6c3P0ZMW+LWL0GwHAZUPUMFfCidlFw8jhGUUKvlGG8vosoxUYgfg+rgQejZr4+9vvVPUqQiBGAgyEKTW7xq1nY6qqYlhosvtGpXa89eXHwgWgM4yXDR6zAQBo0X+G/iGQiNaeW8O96lQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561007; c=relaxed/simple; bh=RTVfkVpaGEoRe6Eplbt+LOygCQp8HaWcl0LdAj9dFv4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AZw1BKLOHEf4G/AFKWf2FujSoKmxjohYZksezcdMdCU003XtCc3Obpju1+MaDXrSkVXxyVu3CbWx2nigNld7LhhqRWYf5ABWHSRK49GAHOtRvJbJrXyALnWOuO4wiuAZQiFqncMGLOy+ASn6FvJcFgfAfAkoAzRVFt0FSXNpXWM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=txmmAqjX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="txmmAqjX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BBFEEC116C6; Fri, 16 Jan 2026 10:56:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768561005; bh=RTVfkVpaGEoRe6Eplbt+LOygCQp8HaWcl0LdAj9dFv4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=txmmAqjXqXkyv7SAzuRWzDcNVqyZ7v4lrvZ1DUxUxojXeVgc85TGptiS3gHpuZLGO AcYx+IronWpFsYOcAS9tA/P57qFicCZqvVBdvP3d1spO1oOcyPacCMI6gU/ZG+bSOV LPlqDjAApgNcSJdWp64rneasCktYbreIqoTGOS1HBfZ7ynOaMaR81kPdeHc0VFobCl RSh4YUxZAV8COnxQPrWeo0/+eLD2QOh1UFn8LsdKHTnrbX1LVKguYQwICONjvtS2Fk R8WoTFyPFdWrM3m6nOytk+uZCAOoHHrUtAVq3oIPXWwsC06cKSy49wzE7RwkvhPdor uP3mR2P4p9Hww== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Christian Schrefl , Alban Kurti Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v4 09/15] rust: pin-init: rewrite the initializer macros using `syn` Date: Fri, 16 Jan 2026 11:54:24 +0100 Message-ID: <20260116105514.3794384-10-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 { ... } } impl Foo { fn new() -> impl PinInit { 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 { | ------------------ the found opaque type ... 21 | pin_init!(Self { bar: Bar::new() }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | expected `Bar`, found opaque type | arguments to this function are incorrect | =3D note: expected struct `Bar` found opaque type `impl pin_init::PinInit` note: function defined here --> $RUST/core/src/ptr/mod.rs | | pub const unsafe fn write(dst: *mut T, src: T) { | ^^^^^ =3D note: this error originates in the macro `$crate::__init_interna= l` which comes from the expansion of the macro `pin_init` (in Nightly build= s, 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 { | ------------------ the found opaque type ... 21 | pin_init!(Self { bar: Bar::new() }) | --- ^^^^^^^^^^ expected `Bar`, found opa= que type | | | arguments to this function are incorrect | =3D note: expected struct `Bar` found opaque type `impl pin_init::PinInit` note: function defined here --> $RUST/core/src/ptr/mod.rs | | pub const unsafe fn write(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. Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: * use DiagCtxt error handling 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 | 13 + rust/pin-init/src/lib.rs | 56 +- rust/pin-init/src/macros.rs | 951 ----------------------------- 4 files changed, 460 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/sr= c/init.rs new file mode 100644 index 000000000000..b0eb66224341 --- /dev/null +++ b/rust/pin-init/internal/src/init.rs @@ -0,0 +1,445 @@ +// 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, Expr, ExprCall, ExprPath, Ident, Path, Token, Type, +}; + +use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; + +pub(crate) struct Initializer { + this: Option, + path: Path, + brace_token: token::Brace, + fields: Punctuated, + 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, .. } =3D> Some= (ident), + Self::Code { .. } =3D> None, + } + } +} + +pub(crate) fn expand( + Initializer { + this, + path, + brace_token, + fields, + rest, + error, + }: Initializer, + default_error: Option<&'static str>, + pinned: bool, + dcx: &mut DiagCtxt, +) -> Result { + let error =3D error.map_or_else( + || { + if let Some(default_error) =3D default_error { + syn::parse_str(default_error).unwrap() + } else { + dcx.error(brace_token.span.close(), "expected `? ` a= fter `}`"); + parse_quote!(::core::convert::Infallible) + } + }, + |(_, err)| err, + ); + let slot =3D format_ident!("slot"); + let (has_data_trait, data_trait, get_data, init_from_closure) =3D if p= inned { + ( + 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 =3D get_init_kind(rest, dcx); + let zeroable_check =3D match init_kind { + InitKind::Normal =3D> quote!(), + InitKind::Zeroing =3D> 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 patt= ern). + fn assert_zeroable(_: *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 =3D match this { + None =3D> quote!(), + Some(This { ident, .. }) =3D> quote! { + // Create the `this` so it can be referenced by the user insid= e of the + // expressions creating the individual fields. + let #ident =3D unsafe { ::core::ptr::NonNull::new_unchecked(sl= ot) }; + }, + }; + // `mixed_site` ensures that the data is not accessible to the user-co= ntrolled code. + let data =3D Ident::new("__data", Span::mixed_site()); + let init_fields =3D init_fields(&fields, pinned, &data, &slot); + let field_check =3D make_field_check(&fields, init_kind, &path); + Ok(quote! {{ + // We do not want to allow arbitrary returns, so we declare this t= ype 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 =3D unsafe { + use ::pin_init::__internal::#has_data_trait; + // Can't use `<#path as #has_data_trait>::#get_data`, since th= e 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 typ= e inference: + let init =3D ::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 =3D move |slot| -> ::core::result::Result<(), #error> { + init(slot).map(|__InitOk| ()) + }; + // SAFETY: TODO + let init =3D unsafe { ::pin_init::#init_from_closure::<_, #error>(= init) }; + init + }}) +} + +enum InitKind { + Normal, + Zeroing, +} + +fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> = InitKind { + let Some((dotdot, expr)) =3D rest else { + return InitKind::Normal; + }; + match &expr { + Expr::Call(ExprCall { func, args, .. }) if args.is_empty() =3D> ma= tch &**func { + Expr::Path(ExprPath { + attrs, + qself: None, + path: + Path { + leading_colon: None, + segments, + }, + }) if attrs.is_empty() + && segments.len() =3D=3D 2 + && segments[0].ident =3D=3D "Zeroable" + && segments[0].arguments.is_none() + && segments[1].ident =3D=3D "init_zeroed" + && segments[1].arguments.is_none() =3D> + { + return InitKind::Zeroing; + } + _ =3D> {} + }, + _ =3D> {} + } + dcx.error( + 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, + pinned: bool, + data: &Ident, + slot: &Ident, +) -> TokenStream { + let mut guards =3D vec![]; + let mut res =3D TokenStream::new(); + for field in fields { + let init =3D match field { + InitializerField::Value { ident, value } =3D> { + let mut value_ident =3D ident.clone(); + let value_prep =3D value.as_ref().map(|value| &value.1).ma= p(|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 =3D #value;) + }); + // Again span for better diagnostics + let write =3D quote_spanned!(ident.span()=3D> ::core::ptr:= :write); + let accessor =3D if pinned { + let project_ident =3D 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 =3D #accessor; + } + } + InitializerField::Init { ident, value, .. } =3D> { + // Again span for better diagnostics + let init =3D format_ident!("init", span =3D value.span()); + if pinned { + let project_ident =3D format_ident!("__project_{ident}= "); + quote! { + { + let #init =3D #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 correc= t trait (`Init` or `PinInit`) + // for `#ident`. + unsafe { #data.#ident(::core::ptr::addr_of_mut= !((*#slot).#ident), #init)? }; + } + // SAFETY: TODO + #[allow(unused_variables)] + let #ident =3D unsafe { #data.#project_ident(&mut = (*#slot).#ident) }; + } + } else { + quote! { + { + let #init =3D #value; + // SAFETY: `slot` is valid, because we are ins= ide of an initializer + // closure, we return when an error/panic occu= rs. + unsafe { + ::pin_init::Init::__init( + #init, + ::core::ptr::addr_of_mut!((*#slot).#id= ent), + )? + }; + } + // SAFETY: TODO + #[allow(unused_variables)] + let #ident =3D unsafe { &mut (*#slot).#ident }; + } + } + } + InitializerField::Code { block: value, .. } =3D> quote!(#[allo= w(unused_braces)] #value), + }; + res.extend(init); + if let Some(ident) =3D field.ident() { + // `mixed_site` ensures that the guard is not accessible to th= e user-controlled code. + let guard =3D format_ident!("__{ident}_guard", span =3D Span::= mixed_site()); + guards.push(guard.clone()); + res.extend(quote! { + // Create the drop guard: + // + // We rely on macro hygiene to make it impossible for user= s to access this local + // variable. + // SAFETY: We forget the guard later when initialization h= as succeeded. + let #guard =3D unsafe { + ::pin_init::__internal::DropGuard::new( + ::core::ptr::addr_of_mut!((*slot).#ident) + ) + }; + }); + } + } + quote! { + #res + // If execution reaches this point, all fields have been initializ= ed. 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, + init_kind: InitKind, + path: &Path, +) -> TokenStream { + let fields =3D fields.iter().filter_map(|f| f.ident()); + match init_kind { + InitKind::Normal =3D> quote! { + // We use unreachable code to ensure that all fields have been= mentioned exactly once, + // this struct initializer will still be type-checked and comp= lain with a very natural + // error message if a field is forgotten/mentioned more than o= nce. + #[allow(unreachable_code, clippy::diverging_sub_expression)] + // SAFETY: this code is never executed. + let _ =3D || unsafe { + ::core::ptr::write(slot, #path { + #( + #fields: ::core::panic!(), + )* + }) + }; + }, + InitKind::Zeroing =3D> 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-check= ed and complain with a + // very natural error message if a field is mentioned more tha= n once, or doesn't exist. + #[allow(unreachable_code, clippy::diverging_sub_expression, un= used_assignments)] + // SAFETY: this code is never executed. + let _ =3D || unsafe { + let mut zeroed =3D ::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 =3D ::core::mem::zeroed(); + ::core::ptr::write(slot, #path { + #( + #fields: ::core::panic!(), + )* + ..zeroed + }) + }; + }, + } +} + +impl Parse for Initializer { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let this =3D input.peek(Token![&]).then(|| input.parse()).transpos= e()?; + let path =3D input.parse()?; + let content; + let brace_token =3D braced!(content in input); + let mut fields =3D Punctuated::new(); + loop { + let lh =3D 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 =3D 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 =3D content + .peek(Token![..]) + .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse(= )?))) + .transpose()?; + let error =3D 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 { + 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 { + let lh =3D 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 =3D input.parse()?; + let lh =3D 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 56dc306e04a9..08372c8f65f0 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -16,6 +16,7 @@ use crate::diagnostics::DiagCtxt; =20 mod diagnostics; +mod init; mod pin_data; mod pinned_drop; mod zeroable; @@ -45,3 +46,15 @@ pub fn maybe_derive_zeroable(input: TokenStream) -> Toke= nStream { let input =3D parse_macro_input!(input); DiagCtxt::with(|dcx| zeroable::maybe_derive(input, dcx)).into() } +#[proc_macro] +pub fn init(input: TokenStream) -> TokenStream { + let input =3D parse_macro_input!(input); + DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infall= ible"), false, dcx)) + .into() +} + +#[proc_macro] +pub fn pin_init(input: TokenStream) -> TokenStream { + let input =3D parse_macro_input!(input); + DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infall= ible"), true, dcx)).into() +} 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 @@ =20 #[doc(hidden)] pub mod __internal; -#[doc(hidden)] -pub mod macros; =20 #[cfg(any(feature =3D "std", feature =3D "alloc"))] mod alloc; @@ -781,32 +779,7 @@ macro_rules! stack_try_pin_init { /// ``` /// /// [`NonNull`]: core::ptr::NonNull -// For a detailed example of how this macro works, see the module document= ation of the hidden -// module `macros` inside of `macros.rs`. -#[macro_export] -macro_rules! pin_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) =3D> { - $crate::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? ::core::convert::Infallible) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) =3D> { - $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; =20 /// Construct an in-place, fallible initializer for `struct`s. /// @@ -844,32 +817,7 @@ macro_rules! pin_init { /// } /// # let _ =3D Box::init(BigBuf::new()); /// ``` -// For a detailed example of how this macro works, see the module document= ation of the hidden -// module `macros` inside of `macros.rs`. -#[macro_export] -macro_rules! init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) =3D> { - $crate::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? ::core::convert::Infallible) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) =3D> { - $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; =20 /// 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-macro= s `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 macr= os 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 lit= tle as possible, the same -//! approach has been taken here. -//! -//! # Macro expansion example -//! -//! This section is intended for readers trying to understand the macros i= n 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 { -//! #[pin] -//! t: T, -//! pub x: usize, -//! } -//! -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! pin_init!(Self { t, x: 0 }) -//! } -//! } -//! -//! #[pin_data(PinnedDrop)] -//! struct Foo { -//! a: usize, -//! #[pin] -//! b: Bar, -//! } -//! -//! #[pinned_drop] -//! impl PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! -//! let a =3D 42; -//! let initializer =3D pin_init!(Foo { -//! a, -//! b <- Bar::new(36), -//! }); -//! ``` -//! -//! This example includes the most common and important features of the pi= n-init API. -//! -//! Below you can find individual section about the different macro invoca= tions. 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 sepa= rator: `::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 arbi= trary, user-supplied -//! expressions inside of an `unsafe` block in the macro, because this w= ould 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 { -//! #[pin] -//! t: T, -//! pub x: usize, -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! // Firstly the normal definition of the struct, attributes are preserv= ed: -//! #[repr(C)] -//! struct Bar { -//! t: T, -//! pub x: usize, -//! } -//! // Then an anonymous constant is defined, this is because we do not wa= nt any code to access the -//! // types that we define inside: -//! const _: () =3D { -//! // We define the pin-data carrying struct, it is a ZST and needs t= o have the same generics, -//! // since we need to implement access functions for each field and = thus need to know its -//! // type. -//! struct __ThePinData { -//! __phantom: ::core::marker::PhantomData) -> Bar>, -//! } -//! // We implement `Copy` for the pin-data struct, since all function= s it defines will take -//! // `self` by value. -//! impl ::core::clone::Clone for __ThePinData { -//! fn clone(&self) -> Self { -//! *self -//! } -//! } -//! impl ::core::marker::Copy for __ThePinData {} -//! // For every field of `Bar`, the pin-data struct will define a fun= ction with the same name -//! // and accessor (`pub` or `pub(crate)` etc.). This function will t= ake a pointer to the -//! // field (`slot`) and a `PinInit` or `Init` depending on the proje= ction kind of the field -//! // (if pinning is structural for the field, then `PinInit` otherwi= se `Init`). -//! #[allow(dead_code)] -//! impl __ThePinData { -//! unsafe fn t( -//! self, -//! slot: *mut T, -//! // Since `t` is `#[pin]`, this is `PinInit`. -//! init: impl ::pin_init::PinInit, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } -//! } -//! pub unsafe fn x( -//! self, -//! slot: *mut usize, -//! // Since `x` is not `#[pin]`, this is `Init`. -//! init: impl ::pin_init::Init, -//! ) -> ::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 ::pin_init::__internal::HasPinData for Bar { -//! type PinData =3D __ThePinData; -//! 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 ::pin_init::__internal::PinData for __ThePinData= { -//! type Datee =3D Bar; -//! } -//! // Now we only want to implement `Unpin` for `Bar` when every stru= cturally 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 liste= d 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 real= ly 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` fi= eld -- this is -//! // unconditionally `!Unpin` and results in an error. The lifetim= e tricks the compiler -//! // into accepting these bounds regardless. -//! #[allow(dead_code)] -//! struct __Unpin<'__pin, T> { -//! __phantom_pin: ::core::marker::PhantomData &= '__pin ()>, -//! __phantom: ::core::marker::PhantomData) -> Bar>, -//! // Our only `#[pin]` field is `t`. -//! t: T, -//! } -//! #[doc(hidden)] -//! impl<'__pin, T> ::core::marker::Unpin for Bar -//! where -//! __Unpin<'__pin, T>: ::core::marker::Unpin, -//! {} -//! // Now we need to ensure that `Bar` does not implement `Drop`, sin= ce that would give users -//! // access to `&mut self` inside of `drop` even if the struct was p= inned. This could lead to -//! // UB with only safe code, so we disallow this by giving a trait i= mplementation error using -//! // a direct impl and a blanket implementation. -//! trait MustNotImplDrop {} -//! // Normally `Drop` bounds do not have the correct semantics, but f= or this purpose they do -//! // (normally people want to know if a type has any kind of drop gl= ue at all, here we want -//! // to know if it has any kind of custom drop glue, which is exactl= y what this bound does). -//! #[expect(drop_bounds)] -//! impl MustNotImplDrop for T {} -//! impl MustNotImplDrop for Bar {} -//! // 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 a= bove, 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 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for B= ar {} -//! }; -//! ``` -//! -//! ## `pin_init!` in `impl Bar` -//! -//! This macro creates an pin-initializer for the given struct. It require= s that the struct is -//! annotated by `#[pin_data]`. -//! -//! Here is the impl on `Bar` defining the new function: -//! -//! ```rust,ignore -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! pin_init!(Self { t, x: 0 }) -//! } -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! { -//! // We do not want to allow arbitrary returns, so we declar= e this type as the `Ok` -//! // return type and shadow it later when we insert the arbi= trary user code. That way -//! // there will be no possibility of returning without `unsa= fe`. -//! 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 o= nly available in that -//! // scope. -//! let data =3D unsafe { -//! use ::pin_init::__internal::HasPinData; -//! Self::__pin_data() -//! }; -//! // Ensure that `data` really is of type `PinData` and help= with type inference: -//! let init =3D ::pin_init::__internal::PinData::make_closure= ::< -//! _, -//! __InitOk, -//! ::core::convert::Infallible, -//! >(data, move |slot| { -//! { -//! // Shadow the structure so it cannot be used to re= turn 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 syntact= ic sugar for `t: t,`. -//! { -//! unsafe { ::core::ptr::write(::core::addr_of_mu= t!((*slot).t), t) }; -//! } -//! // Since initialization could fail later (not in t= his case, since the -//! // error type is `Infallible`) we will need to dro= p this field if there -//! // is an error later. This `DropGuard` will drop t= he field when it gets -//! // dropped and has not yet been forgotten. -//! let __t_guard =3D unsafe { -//! ::pin_init::__internal::DropGuard::new(::core:= :addr_of_mut!((*slot).t)) -//! }; -//! // Expansion of `x: 0,`: -//! // Since this can be an arbitrary expression we ca= nnot place it inside -//! // of the `unsafe` block, so we bind it here. -//! { -//! let x =3D 0; -//! unsafe { ::core::ptr::write(::core::addr_of_mu= t!((*slot).x), x) }; -//! } -//! // We again create a `DropGuard`. -//! let __x_guard =3D 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 eve= ry field has been -//! // initialized exactly once, since this is `if fal= se` it will never get -//! // executed, but still type-checked. -//! // Additionally we abuse `slot` to automatically i= nfer 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_ex= pression)] -//! let _ =3D || { -//! unsafe { -//! ::core::ptr::write( -//! slot, -//! Self { -//! // We only care about typecheck fi= nding every field -//! // here, the expression does not m= atter, just conjure -//! // one using `panic!()`: -//! t: ::core::panic!(), -//! x: ::core::panic!(), -//! }, -//! ); -//! }; -//! }; -//! } -//! // We leave the scope above and gain access to the pre= viously shadowed -//! // `__InitOk` that we need to return. -//! Ok(__InitOk) -//! }); -//! // Change the return type from `__InitOk` to `()`. -//! let init =3D move | -//! slot, -//! | -> ::core::result::Result<(), ::core::convert::Infallibl= e> { -//! init(slot).map(|__InitOk| ()) -//! }; -//! // Construct the initializer. -//! let init =3D 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 w= ill 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, -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! struct Foo { -//! a: usize, -//! b: Bar, -//! } -//! const _: () =3D { -//! struct __ThePinData { -//! __phantom: ::core::marker::PhantomData 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( -//! self, -//! slot: *mut Bar, -//! init: impl ::pin_init::PinInit, E>, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } -//! } -//! unsafe fn a( -//! self, -//! slot: *mut usize, -//! init: impl ::pin_init::Init, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::Init::__init(init, slot) } -//! } -//! } -//! unsafe impl ::pin_init::__internal::HasPinData for Foo { -//! type PinData =3D __ThePinData; -//! unsafe fn __pin_data() -> Self::PinData { -//! __ThePinData { -//! __phantom: ::core::marker::PhantomData, -//! } -//! } -//! } -//! unsafe impl ::pin_init::__internal::PinData for __ThePinData { -//! type Datee =3D Foo; -//! } -//! #[allow(dead_code)] -//! struct __Unpin<'__pin> { -//! __phantom_pin: ::core::marker::PhantomData &= '__pin ()>, -//! __phantom: ::core::marker::PhantomData Foo>, -//! b: Bar, -//! } -//! #[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` i= mplementations like -//! // before, instead we implement `Drop` here and delegate to `Pinne= dDrop`. -//! impl ::core::ops::Drop for Foo { -//! fn drop(&mut self) { -//! // Since we are getting dropped, no one else has a referen= ce to `self` and thus we -//! // can assume that we never move. -//! let pinned =3D unsafe { ::core::pin::Pin::new_unchecked(se= lf) }; -//! // 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 =3D unsafe { ::pin_init::__internal::OnlyCallFro= mDrop::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 tra= it is `unsafe` and has an -//! extra parameter that should not be used at all. The macro hides that p= arameter. -//! -//! 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 e= lse stays the same. -//! unsafe impl ::pin_init::PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>, _: ::pin_init::__internal::OnlyCallF= romDrop) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! ``` -//! -//! ## `pin_init!` on `Foo` -//! -//! Since we already took a look at `pin_init!` on `Bar`, this section wil= l only show the expansion -//! of `pin_init!` on `Foo`: -//! -//! ```rust,ignore -//! let a =3D 42; -//! let initializer =3D pin_init!(Foo { -//! a, -//! b <- Bar::new(36), -//! }); -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! let a =3D 42; -//! let initializer =3D { -//! struct __InitOk; -//! let data =3D unsafe { -//! use ::pin_init::__internal::HasPinData; -//! Foo::__pin_data() -//! }; -//! let init =3D ::pin_init::__internal::PinData::make_closure::< -//! _, -//! __InitOk, -//! ::core::convert::Infallible, -//! >(data, move |slot| { -//! { -//! struct __InitOk; -//! { -//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slo= t).a), a) }; -//! } -//! let __a_guard =3D unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of= _mut!((*slot).a)) -//! }; -//! let init =3D Bar::new(36); -//! unsafe { data.b(::core::addr_of_mut!((*slot).b), b)? }; -//! let __b_guard =3D 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 _ =3D || { -//! unsafe { -//! ::core::ptr::write( -//! slot, -//! Foo { -//! a: ::core::panic!(), -//! b: ::core::panic!(), -//! }, -//! ); -//! }; -//! }; -//! } -//! Ok(__InitOk) -//! }); -//! let init =3D move | -//! slot, -//! | -> ::core::result::Result<(), ::core::convert::Infallible> { -//! init(slot).map(|__InitOk| ()) -//! }; -//! let init =3D unsafe { -//! ::pin_init::pin_init_from_closure::<_, ::core::convert::Infall= ible>(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 ha= s been handled. -/// - `init_slot`: recursively creates the code that initializes all field= s in `slot`. -/// - `make_initializer`: recursively create the struct initializer that g= uarantees 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 pres= ent 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(), - ) =3D> { - $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 pres= ent 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()), - ) =3D> { - $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 pres= ent 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)*), - ) =3D> { - $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 pres= ent 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)?), - ) =3D> {{ - // We do not want to allow arbitrary returns, so we declare this t= ype 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 =3D unsafe { - use $crate::__internal::$has_data; - // Here we abuse `paste!` to retokenize `$t`. Declarative macr= os have some internal - // information that is associated to already parsed fragments,= so a path fragment - // cannot be used in this position. Doing the retokenization r= esults 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 =3D $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 slo= t now and not emit an - // error when fields are missing (since they will be z= eroed). We also have to - // check that the type actually implements `Zeroable`. - $({ - fn assert_zeroable(_: *mut T)= {} - // Ensure that the struct is indeed `Zeroable`. - assert_zeroable(slot); - // SAFETY: The type implements `Zeroable` by the c= heck 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 us= er inside of the - // expressions creating the individual fields. - $(let $this =3D unsafe { ::core::ptr::NonNull::new_unc= hecked(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 h= ave been mentioned exactly - // once, this struct initializer will still be type-ch= ecked and complain with a - // very natural error message if a field is forgotten/= mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expres= sion)] - let _ =3D || { - $crate::__init_internal!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); - }; - } - Ok(__InitOk) - } - ); - let init =3D move |slot| -> ::core::result::Result<(), $err> { - init(slot).map(|__InitOk| ()) - }; - // SAFETY: TODO. - let init =3D 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())? $(,)?), - ) =3D> { - // 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)*), - ) =3D> { - { $($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)*), - ) =3D> { - let init =3D $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initialize= r 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 =3D $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 acc= ess this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has s= ucceeded. - let [< __ $field _guard >] =3D unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mu= t!((*$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)*), - ) =3D> { - let init =3D $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initialize= r closure, we - // return when an error/panic occurs. - unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$s= lot).$field))? }; - - // SAFETY: - // - the field is not structurally pinned, since the line above mu= st compile, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - #[allow(unused_variables)] - let $field =3D unsafe { &mut (*$slot).$field }; - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to acc= ess this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has s= ucceeded. - let [< __ $field _guard >] =3D unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mu= t!((*$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 pin= ned - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // Init by-value. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) =3D> { - { - $(let $field =3D $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 =3D unsafe { &mut (*$slot).$field }; - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to acc= ess this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has s= ucceeded. - let [< __ $field _guard >] =3D unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mu= t!((*$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)*), - ) =3D> { - { - $(let $field =3D $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 =3D $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 acc= ess this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has s= ucceeded. - let [< __ $field _guard >] =3D unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mu= t!((*$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)*), - ) =3D> { - // 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)*), - ) =3D> { - // Endpoint, nothing more to munch, create the initializer. Since = the users specified - // `..Zeroable::init_zeroed()`, the slot will already have been ze= roed and all field that have - // not been overwritten are thus zero and initialized. We still ch= eck 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 c= an abuse `slot` to - // get the correct type inference here: - #[allow(unused_assignments)] - unsafe { - let mut zeroed =3D ::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 =3D ::core::mem::zeroed(); - // Here we abuse `paste!` to retokenize `$t`. Declarative macr= os have some internal - // information that is associated to already parsed fragments,= so a path fragment - // cannot be used in this position. Doing the retokenization r= esults 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)*), - ) =3D> { - // Endpoint, nothing more to munch, create the initializer. - // Since we are in the closure that is never called, this will nev= er get executed. - // We abuse `slot` to get the correct type inference here: - // - // SAFETY: TODO. - unsafe { - // Here we abuse `paste!` to retokenize `$t`. Declarative macr= os have some internal - // information that is associated to already parsed fragments,= so a path fragment - // cannot be used in this position. Doing the retokenization r= esults 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)*), - ) =3D> { - $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)*), - ) =3D> { - $crate::__init_internal!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; -} --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 208D12D97AB; Fri, 16 Jan 2026 10:56:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561011; cv=none; b=GAf5JIu2FUcRqttm7dzUGFrXaqcWSRjCxGotrLp7Z8kbWjFUkYFYbJ5zvqVZ0AYzJuYNU9tSAh9Zhi8mqkMtvpImOj5qxzTaXiH8Je4JIYUsoch8+d6Kp+fuSeHQ/QSkTsCw7VecZ5owkWulWAdzLzUZpn9GtuoqJxZtmNygktU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561011; c=relaxed/simple; bh=TrkjD2Z1MxMUvkAPR7dzHDAOCxrY3mCAREqpeXaqNYY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jCy82b+vjHJBBRJq3tOgMEOB5yqfWW7GnqH8/TG80yg21Q+eu4mwweINfQXvsGNzSS2zjeGvLMwHYM8z9DX+fbZI4oJq5SFytNRNcTGypgUZJOITeUtGt3OsMztu/ETGvj1IOi8cKlgYrMlUrHXOm1sUyoNm8ezbrUmlQwi6ZIE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CmnLzU7K; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CmnLzU7K" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4B5C6C116C6; Fri, 16 Jan 2026 10:56:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768561010; bh=TrkjD2Z1MxMUvkAPR7dzHDAOCxrY3mCAREqpeXaqNYY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CmnLzU7KYKofkfZpewIhOgXTVTulfisyDOPOxtRIlERCZ5Sw1XGsGoWSfQ4LtoeUN OwHswfzgJe5AHWijH4k06IYKe9MVZYZd8zGUHvda/vKR1PUGTkLgneLKEd2J9M+Db+ S1UFZlY40EpegQP9FAQrHaz1FJwVex0t+gqdPRAKaPlVN1xg2TIdMyebVTgA7J7pyt yImZsCkHIVUfI8gikuAQ3OAYgSYWs4jsytqIVd9JhQPPWTzuok6m899uslGEiPMsNH 2kEvK2B/pX2M6W5rDN/9as7lIKJzW/dtjksJJNRILOLzDiBuG/GOjEdynkWpXtRSyA cyR3WwAK/37fw== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 10/15] rust: pin-init: add `#[default_error()]` attribute to initializer macros Date: Fri, 16 Jan 2026 11:54:25 +0100 Message-ID: <20260116105514.3794384-11-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The `#[default_error()]` 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)*) =3D> { ::pin_init::init!( #[default_error(YourCustomErrorType)] $($args)* ) } } Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: * box big value instead of later ignoring the clippy warning Changes in v3: none Changes in v2: * improve error handling * improve parsing and default error evaluation --- rust/pin-init/internal/src/init.rs | 44 ++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/sr= c/init.rs index b0eb66224341..21cddcd9e9f8 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -8,12 +8,13 @@ parse_quote, punctuated::Punctuated, spanned::Spanned, - token, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type, + token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token,= Type, }; =20 use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; =20 pub(crate) struct Initializer { + attrs: Vec, this: Option, path: Path, brace_token: token::Brace, @@ -54,8 +55,17 @@ fn ident(&self) -> Option<&Ident> { } } =20 +enum InitializerAttribute { + DefaultError(DefaultErrorAttribute), +} + +struct DefaultErrorAttribute { + ty: Box, +} + pub(crate) fn expand( Initializer { + attrs, this, path, brace_token, @@ -69,14 +79,23 @@ pub(crate) fn expand( ) -> Result { let error =3D error.map_or_else( || { - if let Some(default_error) =3D default_error { + if let Some(default_error) =3D attrs.iter().fold(None, |acc, a= ttr| { + #[expect(irrefutable_let_patterns)] + if let InitializerAttribute::DefaultError(DefaultErrorAttr= ibute { ty }) =3D attr { + Some(ty.clone()) + } else { + acc + } + }) { + default_error + } else if let Some(default_error) =3D default_error { syn::parse_str(default_error).unwrap() } else { dcx.error(brace_token.span.close(), "expected `? ` a= fter `}`"); parse_quote!(::core::convert::Infallible) } }, - |(_, err)| err, + |(_, err)| Box::new(err), ); let slot =3D format_ident!("slot"); let (has_data_trait, data_trait, get_data, init_from_closure) =3D if p= inned { @@ -358,6 +377,7 @@ fn make_field_check( =20 impl Parse for Initializer { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let attrs =3D input.call(Attribute::parse_outer)?; let this =3D input.peek(Token![&]).then(|| input.parse()).transpos= e()?; let path =3D input.parse()?; let content; @@ -389,7 +409,19 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::R= esult { .peek(Token![?]) .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?))) .transpose()?; + let attrs =3D attrs + .into_iter() + .map(|a| { + if a.path().is_ident("default_error") { + a.parse_args::() + .map(InitializerAttribute::DefaultError) + } else { + Err(syn::Error::new_spanned(a, "unknown initializer at= tribute")) + } + }) + .collect::, _>>()?; Ok(Self { + attrs, this, path, brace_token, @@ -400,6 +432,12 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::R= esult { } } =20 +impl Parse for DefaultErrorAttribute { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + Ok(Self { ty: input.parse()? }) + } +} + impl Parse for This { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { Ok(Self { --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2190F3624A6; Fri, 16 Jan 2026 10:56:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561014; cv=none; b=dPK6vCRuNFUgPddR0O6VqHxYwj/6rNMPrjBxTZ/Um8nYRV5YlErbFLFNB1hPmf1MpJAodWztgTVaPtKGwZeReLcSN+iFTxyF2lwr7Vn2myE0JVZoxGbeVvtqe1+kWfjdkfh4I+IKLf9ACAmED7Huho12FOtULY7ZgEGwJg+TriU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561014; c=relaxed/simple; bh=Y7i76KqVvN6l6Mxe4wX5BLAhY5LdtCKrn3IlV+9eqGk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=S+fnOKSLWkfybG0GA8w+nvq3qnXvmmRdpj/04XuWYuIe0XC3+rLFqwfdDE4uR89z2FTRnbGOoDU0H3g5bxsSL9DJQUFKnux6UXNeZr1pZ3xpYoo1WDiCG0+bHtWM/z4YkFdFOpHp6cKwe5MDebtAKa+qKbyA+bD5hk3ZK9iKjmk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kjih0Eyj; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kjih0Eyj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 509A1C116C6; Fri, 16 Jan 2026 10:56:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768561013; bh=Y7i76KqVvN6l6Mxe4wX5BLAhY5LdtCKrn3IlV+9eqGk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kjih0EyjyfvyKD5Vp2RT9pk05Iv/wqy3OlUQcaVeHnaTK37irtJndKqKwAdGHzaaH bhvuSwZm/a3TrXpG5p3YiFM4Nbf/7MwLqDIVH/fDmsGotzGbuUW6qMiHSCuVUMEKur wwLk9iyautTnIF5/aNNGQzl5LMINc2VCY29IY+h9BUntzMopigcvMJuHslcUOfGQRk PwusTiCwbFn7c6Iw1UzUzD5KZW8HU5ebxbYiAbiO6Yq+tE6LqgiQyxl1WyoHRqu+rw GxbBIDe6bAwEbW8nyprX/cno+Y7g20SjGoqYZSMlNOOl+bwh7bk+HeGT+fvyE80vHp AiEWkxjEikEvA== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 11/15] rust: init: use `#[default_error(err)]` for the initializer macros Date: Fri, 16 Jan 2026 11:54:26 +0100 Message-ID: <20260116105514.3794384-12-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: none 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(init: impl Init, flags: Flags) -> er= ror::Result /// [`Error`]: crate::error::Error #[macro_export] macro_rules! try_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) =3D> { - ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $crate::error::Error) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) =3D> { - ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $err) - }; + ($($args:tt)*) =3D> { + ::pin_init::init!( + #[default_error($crate::error::Error)] + $($args)* + ) + } } =20 /// 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)* - }) =3D> { - ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $crate::error::Error) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) =3D> { - ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $err) - }; + ($($args:tt)*) =3D> { + ::pin_init::pin_init!( + #[default_error($crate::error::Error)] + $($args)* + ) + } } --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B45E362139; Fri, 16 Jan 2026 10:56:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561019; cv=none; b=csS5v+v0oteXYnieNskFfC52CRbFwxgTzP1OjkNcVQUhBQHhbZOqz2l+/9gFZJZ+LGa/KyYyKEsXrgdaCUT35eD9Yc3JazeHzekT2dRGYJNCDOUUUQ2vjinJ7v01Ul+xbVYSIrbpLKFHPpsGzD5XlwEswQM38cVUvErO4xCdGFY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561019; c=relaxed/simple; bh=zTWWrnrn35uvzPV7Pn6rPOqWEDNjBI7ImqWlfO6yJEA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mBzJt7oWvK/fRXPFsjih7Cyb8VhXmsSn1CtW8Rkzg7WC+N2xBXgsN6CoJA5UoAmjDi/w20XnSOsUgDvq7rVowtW88atnfsQm3NN5ZAP26GUP0Et4lEyTn98AejZlgq7xip0eAD3mz8iurK57YGMi4k+/EMymG7rKgyl6+idbEt4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CNcd7qOf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CNcd7qOf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E1CDBC19425; Fri, 16 Jan 2026 10:56:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768561019; bh=zTWWrnrn35uvzPV7Pn6rPOqWEDNjBI7ImqWlfO6yJEA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CNcd7qOf1YBvpWCeBUrAAtGnVvWi1GSX481jXgs0FjMAi5CQK+2pFVrcVFAStHb6W BWgwXaGIrARg5o/qlJxteRU2NdfTZAqUWjkcpfbOTtYyn9+zXploWKvRLrK6gUfFbr 4DKowbvM+kZPxHzBciO6tLfcyx3Peaz41v5waSuteTt/DefqkIbE9lcizq+MLszmcB 95gyeqTJmWi6xpfNnri0jNJLOYQvoESi4oqXcO/510FqhGNBOW/Mapymm0YDNSsWT0 KRwgc3ImPuuxqvjPwueYcOGoi13wS/LAlamyXrbb/csAtShN69ZskeJRftZXRTf+yG OJifgOgAz/E6Q== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 12/15] rust: pin-init: internal: init: add support for attributes on initializer fields Date: Fri, 16 Jan 2026 11:54:27 +0100 Message-ID: <20260116105514.3794384-13-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- Changes in v4: * remove inclusion of `#[cfg_attr(...)]` Changes in v3: none 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/sr= c/init.rs index 21cddcd9e9f8..e8b93b444f7e 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -29,7 +29,12 @@ struct This { _in_token: Token![in], } =20 -enum InitializerField { +struct InitializerField { + attrs: Vec, + kind: InitializerKind, +} + +enum InitializerKind { Value { ident: Ident, value: Option<(Token![:], Expr)>, @@ -46,7 +51,7 @@ enum InitializerField { }, } =20 -impl InitializerField { +impl InitializerKind { fn ident(&self) -> Option<&Ident> { match self { Self::Value { ident, .. } | Self::Init { ident, .. } =3D> Some= (ident), @@ -227,10 +232,16 @@ fn init_fields( slot: &Ident, ) -> TokenStream { let mut guards =3D vec![]; + let mut guard_attrs =3D vec![]; let mut res =3D TokenStream::new(); - for field in fields { - let init =3D match field { - InitializerField::Value { ident, value } =3D> { + for InitializerField { attrs, kind } in fields { + let cfgs =3D { + let mut cfgs =3D attrs.clone(); + cfgs.retain(|attr| attr.path().is_ident("cfg")); + cfgs + }; + let init =3D match kind { + InitializerKind::Value { ident, value } =3D> { let mut value_ident =3D ident.clone(); let value_prep =3D value.as_ref().map(|value| &value.1).ma= p(|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 =3D #accessor; } } - InitializerField::Init { ident, value, .. } =3D> { + InitializerKind::Init { ident, value, .. } =3D> { // Again span for better diagnostics let init =3D format_ident!("init", span =3D value.span()); if pinned { let project_ident =3D format_ident!("__project_{ident}= "); quote! { + #(#attrs)* { let #init =3D #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 =3D unsafe { #data.#project_ident(&mut = (*#slot).#ident) }; } } else { quote! { + #(#attrs)* { let #init =3D #value; // SAFETY: `slot` is valid, because we are ins= ide of an initializer @@ -294,20 +310,25 @@ fn init_fields( )? }; } + #(#cfgs)* // SAFETY: TODO #[allow(unused_variables)] let #ident =3D unsafe { &mut (*#slot).#ident }; } } } - InitializerField::Code { block: value, .. } =3D> quote!(#[allo= w(unused_braces)] #value), + InitializerKind::Code { block: value, .. } =3D> quote! { + #(#attrs)* + #[allow(unused_braces)] + #value + }, }; res.extend(init); - if let Some(ident) =3D field.ident() { + if let Some(ident) =3D kind.ident() { // `mixed_site` ensures that the guard is not accessible to th= e user-controlled code. let guard =3D format_ident!("__{ident}_guard", span =3D 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 user= s 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 initializ= ed. Therefore we can now // dismiss the guards by forgetting them. - #(::core::mem::forget(#guards);)* + #( + #(#guard_attrs)* + ::core::mem::forget(#guards); + )* } } =20 @@ -335,7 +361,10 @@ fn make_field_check( init_kind: InitKind, path: &Path, ) -> TokenStream { - let fields =3D fields.iter().filter_map(|f| f.ident()); + let field_attrs =3D fields + .iter() + .filter_map(|f| f.kind.ident().map(|_| &f.attrs)); + let field_name =3D fields.iter().filter_map(|f| f.kind.ident()); match init_kind { InitKind::Normal =3D> quote! { // We use unreachable code to ensure that all fields have been= mentioned exactly once, @@ -346,7 +375,8 @@ fn make_field_check( let _ =3D || unsafe { ::core::ptr::write(slot, #path { #( - #fields: ::core::panic!(), + #(#field_attrs)* + #field_name: ::core::panic!(), )* }) }; @@ -366,7 +396,8 @@ fn make_field_check( zeroed =3D ::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::Re= sult { let lh =3D 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(Toke= n![#]) { fields.push_value(content.parse()?); let lh =3D content.lookahead1(); if lh.peek(End) { @@ -449,6 +480,16 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::R= esult { } =20 impl Parse for InitializerField { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let attrs =3D input.call(Attribute::parse_outer)?; + Ok(Self { + attrs, + kind: input.parse()?, + }) + } +} + +impl Parse for InitializerKind { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let lh =3D input.lookahead1(); if lh.peek(Token![_]) { --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3949E3624A4; Fri, 16 Jan 2026 10:57:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561025; cv=none; b=j2rnDysbUEFgDxgee21jY8m3NLm2Tm1ThgMztSTirFXLvnL4x/N2pTh/kOj5IrzSY81N6pO8GzatFQM0S7uDtdtqQ34kP9kvT2JMsw90FMH4W+hmJXEaeJkwuDm1+FuOaNc1aD7mRKI+tNMIfJMsl14d8w50Dk7tc4/7UvhNrDs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561025; c=relaxed/simple; bh=+ZNGZcw7QXQ4Plr6NH4Dvb5BPCU1y1cgTuX6+tv6c/A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TPZzdepEV602hzaznRUF6mmn1x5sNIIPj1vKTr9EdYiF6LnT4QfDtaWisTey3xlZMNjmIx2K7b/WuRJhJtYZEoe4XR4dtU4q3jLZfkiZS/Rm2CS00zArH31TeqTJqBJChpxZjHlIAwe0lIHO/2HINCI11x1onhD14e0TwJsX4ks= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hjXLDx+u; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hjXLDx+u" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9068EC19422; Fri, 16 Jan 2026 10:57:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768561025; bh=+ZNGZcw7QXQ4Plr6NH4Dvb5BPCU1y1cgTuX6+tv6c/A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hjXLDx+uOEeg1RO3zvH6267IxWurU9Ry1/ScNAeHbkI588NMegRaLXgShs/+BTX3Y UVjnfVenrfslTLvA7Cnp4JDjPCTgEd/B6rKxhTJId1SRif6SFZtLklDBy1sTEMX9Oi YBoVLC0dGZTGI90unQFPukligoPQAmiy7/fdAzOEejFRwXA9K9OiopvGP84nRBEtd/ f5Z1wboeWVbv99dVbEpgWp81ERKln02ZQn2c8Eai0vkGeCo9GB3b8ZsxhGvhSrdkkn yEcpjsPBKno0clt9/ElWeREPX1GLZQD+fuM7jHyxtD+Fl60BEGvPoV9i7of3CapqAB znwCxMAYDnXNQ== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: Janne Grunau , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 13/15] rust: pin-init: internal: init: add escape hatch for referencing initialized fields Date: Fri, 16 Jan 2026 11:54:28 +0100 Message-ID: <20260116105514.3794384-14-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 _ =3D 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 _ =3D init!(Foo { a: -42, b: 42 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | =3D note: this struct is 1-byte aligned, but the type of this field = may require higher alignment =3D note: creating a misaligned reference is undefined behavior (eve= n if that reference is never dereferenced) =3D help: copy the field contents to a local variable, or replace th= e reference with a raw pointer and use `read_unaligned`/`write_unaligned` (= loads and stores via `*p` must be properly aligned even when using raw poin= ters) =3D note: this error originates in the macro `init` (in Nightly buil= ds, 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 Link: https://lore.kernel.org/all/20251206170214.GE1097212@robin.jannau.net= [1] Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: none Changes in v2: * silence clippy warning --- rust/pin-init/internal/src/init.rs | 75 +++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/sr= c/init.rs index e8b93b444f7e..ed2e1462e176 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -62,6 +62,7 @@ fn ident(&self) -> Option<&Ident> { =20 enum InitializerAttribute { DefaultError(DefaultErrorAttribute), + DisableInitializedFieldAccess, } =20 struct DefaultErrorAttribute { @@ -85,7 +86,6 @@ pub(crate) fn expand( let error =3D error.map_or_else( || { if let Some(default_error) =3D attrs.iter().fold(None, |acc, a= ttr| { - #[expect(irrefutable_let_patterns)] if let InitializerAttribute::DefaultError(DefaultErrorAttr= ibute { ty }) =3D attr { Some(ty.clone()) } else { @@ -145,7 +145,15 @@ fn assert_zeroable(_: *mut = T) }; // `mixed_site` ensures that the data is not accessible to the user-co= ntrolled code. let data =3D Ident::new("__data", Span::mixed_site()); - let init_fields =3D init_fields(&fields, pinned, &data, &slot); + let init_fields =3D init_fields( + &fields, + pinned, + !attrs + .iter() + .any(|attr| matches!(attr, InitializerAttribute::DisableInitia= lizedFieldAccess)), + &data, + &slot, + ); let field_check =3D make_field_check(&fields, init_kind, &path); Ok(quote! {{ // We do not want to allow arbitrary returns, so we declare this t= ype as the `Ok` return @@ -228,6 +236,7 @@ fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx:= &mut DiagCtxt) -> InitKi fn init_fields( fields: &Punctuated, pinned: bool, + generate_initialized_accessors: bool, data: &Ident, slot: &Ident, ) -> TokenStream { @@ -263,6 +272,13 @@ fn init_fields( unsafe { &mut (*#slot).#ident } } }; + let accessor =3D generate_initialized_accessors.then(|| { + quote! { + #(#cfgs)* + #[allow(unused_variables)] + let #ident =3D #accessor; + } + }); quote! { #(#attrs)* { @@ -270,37 +286,31 @@ fn init_fields( // SAFETY: TODO unsafe { #write(::core::ptr::addr_of_mut!((*#slot)= .#ident), #value_ident) }; } - #(#cfgs)* - #[allow(unused_variables)] - let #ident =3D #accessor; + #accessor } } InitializerKind::Init { ident, value, .. } =3D> { // Again span for better diagnostics let init =3D format_ident!("init", span =3D value.span()); - if pinned { + let (value_init, accessor) =3D if pinned { let project_ident =3D format_ident!("__project_{ident}= "); - quote! { - #(#attrs)* - { - let #init =3D #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 correc= t 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 =3D unsafe { #data.#project_ident(&mut = (*#slot).#ident) }; - } + }, + quote! { + // SAFETY: TODO + unsafe { #data.#project_ident(&mut (*#slot).#i= dent) } + }, + ) } else { - quote! { - #(#attrs)* - { - let #init =3D #value; + ( + quote! { // SAFETY: `slot` is valid, because we are ins= ide of an initializer // closure, we return when an error/panic occu= rs. unsafe { @@ -309,12 +319,27 @@ fn init_fields( ::core::ptr::addr_of_mut!((*#slot).#id= ent), )? }; - } + }, + quote! { + // SAFETY: TODO + unsafe { &mut (*#slot).#ident } + }, + ) + }; + let accessor =3D generate_initialized_accessors.then(|| { + quote! { #(#cfgs)* - // SAFETY: TODO #[allow(unused_variables)] - let #ident =3D unsafe { &mut (*#slot).#ident }; + let #ident =3D #accessor; + } + }); + quote! { + #(#attrs)* + { + let #init =3D #value; + #value_init } + #accessor } } InitializerKind::Code { block: value, .. } =3D> quote! { @@ -446,6 +471,10 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::R= esult { if a.path().is_ident("default_error") { a.parse_args::() .map(InitializerAttribute::DefaultError) + } else if a.path().is_ident("disable_initialized_field_acc= ess") { + a.meta + .require_path_only() + .map(|_| InitializerAttribute::DisableInitializedF= ieldAccess) } else { Err(syn::Error::new_spanned(a, "unknown initializer at= tribute")) } --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 393D535E526; Fri, 16 Jan 2026 10:57:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561033; cv=none; b=gRV5JuCIhRKBmmxAx02srMvS3jESOLy36kKHcovJpE/nkgbWyOtNBy1pdeGnzoIFIWiYygFaPO5bnj2vs7oid4hAGWlsQ9HviAnJP2OFWBbZGooAHciXVm5era4ujDWg2RHkT3vH9P0R9tmEnAVfj/b7H9yuoW5Nj+dMAdt5nqg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561033; c=relaxed/simple; bh=2AQPEo9XqhbgrhubYNt7efKElv59azpT5rRvRhjuwV4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DVfA9p/3QLa78mRn0ImNBN0a3rWqko9tennQSEkksXH7ZhxHPXosLVX9bt8Nivew2J7HeFKnRV59EYtAA2MTqPTZPl3UMCYMSMkoo8QWDfdnZgFJgMsGgDPPZyUCKztBjQWIY6O6ZJYPASjqycghpyNJMhv8AsVOsTPtbrFaVYY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pmtUwxd4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="pmtUwxd4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6A382C116C6; Fri, 16 Jan 2026 10:57:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768561030; bh=2AQPEo9XqhbgrhubYNt7efKElv59azpT5rRvRhjuwV4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pmtUwxd4W/virfB6gOq2FngredYnOWiUYqYTtD7vCiSN4/hNAiH5Zy8uCOm13Krhd Nj+3WCtxfON5vjagbdUjjhVipw1m0YRH3EqDfl+4fu2fyDU+XgyLs82b+BeLWukwGm dluaTqkSO87XPpgTuuTiGbTsK3WG+HbN4ADFxmHYy55/qJBcAbG0ZLTZ3tbP7vRxai GuDwxrIa8w8Sohf7UjMZ8EM3v1FqnTu3Fmg8xIBOKa2doSuvuwYv4wARYOsm1p2ID9 Upd65FG1UNUqHcQox7R6XI8ZE0VcSSlp4lm3aOWSmtHSDZUl39LCeMuu2ZDIic3d8D D0vI3jiIROXrg== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 14/15] rust: pin-init: internal: init: simplify Zeroable safety check Date: Fri, 16 Jan 2026 11:54:29 +0100 Message-ID: <20260116105514.3794384-15-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- Changes in v4: none Changes in v3: none 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/sr= c/init.rs index ed2e1462e176..42936f915a07 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -414,17 +414,12 @@ fn make_field_check( #[allow(unreachable_code, clippy::diverging_sub_expression, un= used_assignments)] // SAFETY: this code is never executed. let _ =3D || unsafe { - let mut zeroed =3D ::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 =3D ::core::mem::zeroed(); ::core::ptr::write(slot, #path { #( #(#field_attrs)* #field_name: ::core::panic!(), )* - ..zeroed + ..::core::mem::zeroed() }) }; }, --=20 2.52.0 From nobody Sat Feb 7 17:55:53 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 20CED3624D9; Fri, 16 Jan 2026 10:57:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561034; cv=none; b=EksICCvsCou8+k7aasxozQuiPQLubIGoN2IStkQOljLqO4rJbXdNQNr58GyKCEmu9UfeLK7YenLED77ZGIwdirOlQi4QCWjxr3/DiIIkyyTGyBZAL+qcMfOnr6Hiij4npZ8LcPtqJJOAVH76Ny9nCeOuj1FPH+GlmDTOuE3Ty7w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768561034; c=relaxed/simple; bh=DZPNKFi1bCdtsVKSpjxpLcW13Dhy/4zUvnc2Q9YKxYc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=q6+TSoUxymGLRT+rEpT2uLzNV8qaxOs+De9/CZO8S0vHo/PQ41BHARI0sGgbVBiaAJrFRlm0adHb8gCXO5J+ePVNqQVhgxoqVr8xUrR9+43nYtDYvERPQHkltlDfjoIdj/J7xKPLeKVI+A7AAzyFYE0405FM5QVIRfB64K01xJA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kbsfllZf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kbsfllZf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6B51DC19422; Fri, 16 Jan 2026 10:57:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768561033; bh=DZPNKFi1bCdtsVKSpjxpLcW13Dhy/4zUvnc2Q9YKxYc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kbsfllZfO82CY6ZukfcBd76q6s8AxfSwnW0zt2Cts9gqTN9eS78n20GRbVho5ztJg 7/rOFyJpm+csOAP4vi3TMwOpHLtx2X+r8EK++J18wBTsi25POb6+vE5zEQ79UlIn5G bfdv9mCepvSYwIePBgvql+oDdvEsxFA9nA3to0UDIyiuUfIzQTB7tWU6BPhNarTDG0 GTcwHSPgDnS/lF7BPuoGtY9Q5LvNK4+sGD+U3SpfKFCxnRop29wPTioPSWqSpKVu8P E3LMuOQ3SMp7mj+P7aK/3C0X08y/Bc7f9oUbW4s/Vo0qAVFIWPLfHGAqNL7glDRBMT I7tVb2XIiHTxQ== From: Benno Lossin To: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v4 15/15] MAINTAINERS: add Gary Guo to pin-init Date: Fri, 16 Jan 2026 11:54:30 +0100 Message-ID: <20260116105514.3794384-16-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116105514.3794384-1-lossin@kernel.org> References: <20260116105514.3794384-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Signed-off-by: Benno Lossin --- 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/ =20 RUST [PIN-INIT] M: Benno Lossin +M: Gary Guo L: rust-for-linux@vger.kernel.org S: Maintained W: https://rust-for-linux.com/pin-init --=20 2.52.0