From be7ea321a2d3a3d4151c9304f50fa1fe26487a81 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 28 Apr 2026 14:10:50 +0100 Subject: [PATCH 01/27] rust: pin-init: examples: mark as `#[inline]` all `From::from()`s for `Error` There was a recent request in kernel [1] to mark as `#[inline]` the simple `From::from()` functions implemented for `Error`. Thus mark all of the existing impl From<...> for Error { fn from(err: ...) -> Self { ... } } functions as `#[inline]`. While in pin-init crate the relevant code is just examples, it nevertheless does not hurt to use good practice for them. Suggested-by: Gary Guo Link: https://lore.kernel.org/all/8403c8b7a832b5274743816eb77abfa4@garyguo.net/ [1] Signed-off-by: Alistair Francis Reviewed-by: Gary Guo [ Reworded commit message - Gary ] Link: https://patch.msgid.link/20260428-pin-init-sync-v1-1-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/error.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/pin-init/examples/error.rs b/rust/pin-init/examples/error.rs index 8f4e135eb8ba..96f095398e8d 100644 --- a/rust/pin-init/examples/error.rs +++ b/rust/pin-init/examples/error.rs @@ -11,6 +11,7 @@ pub struct Error; impl From for Error { + #[inline] fn from(e: Infallible) -> Self { match e {} } @@ -18,6 +19,7 @@ fn from(e: Infallible) -> Self { #[cfg(feature = "alloc")] impl From for Error { + #[inline] fn from(_: AllocError) -> Self { Self } From 531fdc2c341bded76817cef935c5fe97d88d643b Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 28 Apr 2026 14:10:51 +0100 Subject: [PATCH 02/27] rust: pin-init: bump minimum Rust version to 1.82 Following the kernel minimum version bump in commit f32fb9c58a5b ("rust: bump Rust minimum supported version to 1.85.0 (Debian Trixie)"), bump pin-init's minimum Rust version to 1.82. This removes the `lint_reasons` feature which is stabilized in 1.81 and the `raw_ref_ops` and `new_uninit` features which are stabilized in 1.82. Given we do not use any features that are stabilized in 1.82..=1.85 range, and pin-init crate is useful for other projects which may have their own MSRV requirements, the minimum version is not straightly bumped to 1.85. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260428-pin-init-sync-v1-2-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/big_struct_in_place.rs | 3 --- rust/pin-init/examples/linked_list.rs | 2 -- rust/pin-init/examples/mutex.rs | 2 -- rust/pin-init/examples/pthread_mutex.rs | 2 -- rust/pin-init/examples/static_init.rs | 2 -- rust/pin-init/internal/src/lib.rs | 1 - rust/pin-init/src/lib.rs | 6 ------ 7 files changed, 18 deletions(-) diff --git a/rust/pin-init/examples/big_struct_in_place.rs b/rust/pin-init/examples/big_struct_in_place.rs index 80f89b5f8fd6..c05139927486 100644 --- a/rust/pin-init/examples/big_struct_in_place.rs +++ b/rust/pin-init/examples/big_struct_in_place.rs @@ -1,8 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))] - use pin_init::*; // Struct with size over 1GiB diff --git a/rust/pin-init/examples/linked_list.rs b/rust/pin-init/examples/linked_list.rs index 119169e4dc41..424585fe226d 100644 --- a/rust/pin-init/examples/linked_list.rs +++ b/rust/pin-init/examples/linked_list.rs @@ -2,8 +2,6 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))] use core::{ cell::Cell, diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index d53671f0edb8..8ed2d3219eb1 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -2,8 +2,6 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))] #![allow(clippy::missing_safety_doc)] use core::{ diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs index f3b5cc9b7134..4a66316471af 100644 --- a/rust/pin-init/examples/pthread_mutex.rs +++ b/rust/pin-init/examples/pthread_mutex.rs @@ -3,8 +3,6 @@ // inspired by #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))] #[cfg(not(windows))] mod pthread_mtx { diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index f7e53d1a5ae6..906b96c5d4b9 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -2,8 +2,6 @@ #![allow(clippy::undocumented_unsafe_blocks)] #![cfg_attr(feature = "alloc", feature(allocator_api))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))] #![allow(unused_imports)] use core::{ diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index b08dfe003031..60d5093f3128 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -6,7 +6,6 @@ //! `pin-init` proc macros. -#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))] // Documentation is done in the pin-init crate instead. #![allow(missing_docs)] diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 64eec095c859..4f50994bd268 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -263,12 +263,6 @@ //! [`impl Init`]: crate::Init //! [Rust-for-Linux]: https://rust-for-linux.com/ -#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))] -#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))] -#![cfg_attr( - all(any(feature = "alloc", feature = "std"), USE_RUSTC_FEATURES), - feature(new_uninit) -)] #![forbid(missing_docs, unsafe_op_in_unsafe_fn)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "alloc", feature(allocator_api))] From 04828a538da2bed3150116929306f849a3337a37 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 28 Apr 2026 14:10:52 +0100 Subject: [PATCH 03/27] rust: pin-init: cleanup `Zeroable` and `ZeroableOptions` Place definitions and implementations (incl. macro invocations) of the `Zeroable` trait first in the relevant section of `src/lib.rs`, followed by the ZeroableOption trait and its implementations. Rename `impl_non_zero_int_zeroable_option` to `impl_zeroable_option` for consistency. This commit should not introduce any functional changes. Signed-off-by: Mohamad Alsadhan Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260428-pin-init-sync-v1-3-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 4f50994bd268..e34c9bdb88c3 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1517,27 +1517,6 @@ fn zeroed() -> Self } } -/// Marker trait for types that allow `Option` to be set to all zeroes in order to write -/// `None` to that location. -/// -/// # Safety -/// -/// The implementer needs to ensure that `unsafe impl Zeroable for Option {}` is sound. -pub unsafe trait ZeroableOption {} - -// SAFETY: by the safety requirement of `ZeroableOption`, this is valid. -unsafe impl Zeroable for Option {} - -// SAFETY: `Option<&T>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for &T {} -// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for &mut T {} -// SAFETY: `Option>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for NonNull {} - /// Create an initializer for a zeroed `T`. /// /// The returned initializer will write `0x00` to every byte of the given `slot`. @@ -1643,6 +1622,27 @@ unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*) impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J); +/// Marker trait for types that allow `Option` to be set to all zeroes in order to write +/// `None` to that location. +/// +/// # Safety +/// +/// The implementer needs to ensure that `unsafe impl Zeroable for Option {}` is sound. +pub unsafe trait ZeroableOption {} + +// SAFETY: by the safety requirement of `ZeroableOption`, this is valid. +unsafe impl Zeroable for Option {} + +// SAFETY: `Option<&T>` is part of the option layout optimization guarantee: +// . +unsafe impl ZeroableOption for &T {} +// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee: +// . +unsafe impl ZeroableOption for &mut T {} +// SAFETY: `Option>` is part of the option layout optimization guarantee: +// . +unsafe impl ZeroableOption for NonNull {} + macro_rules! impl_fn_zeroable_option { ([$($abi:literal),* $(,)?] $args:tt) => { $(impl_fn_zeroable_option!({extern $abi} $args);)* @@ -1668,14 +1668,14 @@ unsafe impl<$ret, $($rest),*> ZeroableOption for $($prefix)* fn($($rest),*) -> $ impl_fn_zeroable_option!(["Rust", "C"] { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U }); -macro_rules! impl_non_zero_int_zeroable_option { +macro_rules! impl_zeroable_option { ($($int:ty),* $(,)?) => { // SAFETY: Safety comment written in the macro invocation. $(unsafe impl ZeroableOption for $int {})* }; } -impl_non_zero_int_zeroable_option! { +impl_zeroable_option! { // SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee: // ). NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, From de54c2cb052952df2a6f91617471d1f02f213d4e Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 28 Apr 2026 14:10:53 +0100 Subject: [PATCH 04/27] rust: pin-init: extend `impl_zeroable_option` macro to handle generics Improve impl_zeroable_option macro to handle generic impls for types like `&T`, `&mut T`, `NonNull`, and others (for which `Option` is guaranteed to be zeroable) with similar approach to `impl_zeroable`. Also, update old declarations to use generics e.g. `NonZeroU8` to `NonZero`. Signed-off-by: Mohamad Alsadhan Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260428-pin-init-sync-v1-4-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index e34c9bdb88c3..9b76cf5597c6 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1633,16 +1633,6 @@ pub unsafe trait ZeroableOption {} // SAFETY: by the safety requirement of `ZeroableOption`, this is valid. unsafe impl Zeroable for Option {} -// SAFETY: `Option<&T>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for &T {} -// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for &mut T {} -// SAFETY: `Option>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for NonNull {} - macro_rules! impl_fn_zeroable_option { ([$($abi:literal),* $(,)?] $args:tt) => { $(impl_fn_zeroable_option!({extern $abi} $args);)* @@ -1669,17 +1659,26 @@ unsafe impl<$ret, $($rest),*> ZeroableOption for $($prefix)* fn($($rest),*) -> $ impl_fn_zeroable_option!(["Rust", "C"] { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U }); macro_rules! impl_zeroable_option { - ($($int:ty),* $(,)?) => { - // SAFETY: Safety comment written in the macro invocation. - $(unsafe impl ZeroableOption for $int {})* + ($($({$($generics:tt)*})? $t:ty, )*) => { + // SAFETY: Safety comments written in the macro invocation. + $(unsafe impl$($($generics)*)? ZeroableOption for $t {})* }; } impl_zeroable_option! { + // SAFETY: `Option<&T>` is part of the option layout optimization guarantee: + // . + {} &T, + // SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee: + // . + {} &mut T, + // SAFETY: `Option>` is part of the option layout optimization guarantee: + // . + {} NonNull, // SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee: // ). - NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, - NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, + NonZero, NonZero, NonZero, NonZero, NonZero, NonZero, + NonZero, NonZero, NonZero, NonZero, NonZero, NonZero, } /// This trait allows creating an instance of `Self` which contains exactly one From 5edf8ac281ad04433c606917a1a9e88bf8fa10d8 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 28 Apr 2026 14:10:54 +0100 Subject: [PATCH 05/27] rust: pin-init: internal: add missing where clause to projection types `#[pin_data]` failed to propagate the struct's `where` clause to the generated projection struct. As a result, bounds written in a `where` clause could be dropped during expansion, causing type errors when fields depended on those bounds. Fix this by adding the missing `where` clause to the generated projection struct. Reported-by: Andreas Hindborg Closes: https://rust-for-linux.zulipchat.com/#narrow/channel/561532-pin-init/topic/generic.20bounds.20and.20.60.23.5Bpin_data.5D.60/with/578381591 Signed-off-by: Mohamad Alsadhan Reviewed-by: Gary Guo [ Reworded commit message - Gary ] Link: https://patch.msgid.link/20260428-pin-init-sync-v1-5-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 7d871236b49c..6b1b8f26379a 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -304,7 +304,9 @@ fn generate_projections( #[doc = #docs] #[allow(dead_code)] #[doc(hidden)] - #vis struct #projection #generics_with_pin_lt { + #vis struct #projection #generics_with_pin_lt + #whr + { #(#fields_decl)* ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, } From c64c793d9a162768a78f98f99774b5df6d360d0b Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 28 Apr 2026 14:10:55 +0100 Subject: [PATCH 06/27] rust: pin-init: internal: remove redundant `#[pin]` filtering The `generate_projections` and `generate_the_pin_data` function already receive filtered field lists, they do not need to filter out `#[pin]` again. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260428-pin-init-sync-v1-6-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 6b1b8f26379a..76cd11bf28eb 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -258,8 +258,6 @@ fn generate_projections( .. }, )| { - let mut attrs = attrs.clone(); - attrs.retain(|a| !a.path().is_ident("pin")); let mut no_doc_attrs = attrs.clone(); no_doc_attrs.retain(|a| !a.path().is_ident("doc")); let ident = ident @@ -360,8 +358,6 @@ fn handle_field( struct_ident: &Ident, pinned: bool, ) -> TokenStream { - let mut attrs = attrs.clone(); - attrs.retain(|a| !a.path().is_ident("pin")); let ident = ident .as_ref() .expect("only structs with named fields are supported"); From 38a07ef981983d3ed48f781d861d81fe1475f8df Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 28 Apr 2026 14:10:56 +0100 Subject: [PATCH 07/27] rust: pin-init: internal: adjust license identifier of `zeroable.rs` The pin-init crate has been licensed under `Apache-2.0 OR MIT` since the beginning. I introduced in commit 071cedc84e90 ("rust: add derive macro for `Zeroable`") `zeroable.rs` with incompatible GPL-2.0 SPDX identifier. The file has not been modified by other authors, so relicense it under the above license. Signed-off-by: Benno Lossin [ Reworded commit message - Gary ] Link: https://patch.msgid.link/20260428-pin-init-sync-v1-7-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/zeroable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs index 05683319b0f7..b11feaeb1ca6 100644 --- a/rust/pin-init/internal/src/zeroable.rs +++ b/rust/pin-init/internal/src/zeroable.rs @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: Apache-2.0 OR MIT use proc_macro2::TokenStream; use quote::quote; From 0ba33edb310541ed6194fb27841db1a12b90a02c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 28 Apr 2026 14:10:57 +0100 Subject: [PATCH 08/27] rust: pin-init: fix badge URL in README The old CI workflow has been deleted ~1 year ago. Fix the URL to point to the correct one. Link: https://patch.msgid.link/20260428-pin-init-sync-v1-8-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md index 9095d6661ff6..2312c9e75f8c 100644 --- a/rust/pin-init/README.md +++ b/rust/pin-init/README.md @@ -3,7 +3,7 @@ [![Dependency status](https://deps.rs/repo/github/Rust-for-Linux/pin-init/status.svg)](https://deps.rs/repo/github/Rust-for-Linux/pin-init) ![License](https://img.shields.io/crates/l/pin-init) [![Toolchain](https://img.shields.io/badge/toolchain-nightly-red)](#nightly-only) -![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Rust-for-Linux/pin-init/test.yml) +![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Rust-for-Linux/pin-init/ci.yml) # `pin-init` > [!NOTE] From 3dc01266cdf02b7abf733b022f6b936be567fc17 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 28 Apr 2026 14:10:58 +0100 Subject: [PATCH 09/27] rust: pin-init: cleanup workaround for old Rust compiler The workaround mentions it's for Rust versions before 1.81. The minimum is now 1.82, thus clean up. Link: https://patch.msgid.link/20260428-pin-init-sync-v1-9-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 9 +-------- rust/pin-init/src/lib.rs | 18 ++---------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 487ee0013faf..b0bfe44695e1 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -172,14 +172,7 @@ fn assert_zeroable(_: *mut T) init(slot).map(|__InitOk| ()) }; // SAFETY: TODO - let init = unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }; - // FIXME: this let binding is required to avoid a compiler error (cycle when computing the - // opaque type returned by this function) before Rust 1.81. Remove after MSRV bump. - #[allow( - clippy::let_and_return, - reason = "some clippy versions warn about the let binding" - )] - init + unsafe { ::pin_init::#init_from_closure::<_, #error>(init) } }}) } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 9b76cf5597c6..80c476e605f7 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1139,14 +1139,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl PinInit { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. - let res = unsafe { pin_init_from_closure(|ptr: *mut U| init.__pinned_init(ptr.cast::())) }; - // FIXME: this let binding is required to avoid a compiler error (cycle when computing the opaque - // type returned by this function) before Rust 1.81. Remove after MSRV bump. - #[allow( - clippy::let_and_return, - reason = "some clippy versions warn about the let binding" - )] - res + unsafe { pin_init_from_closure(|ptr: *mut U| init.__pinned_init(ptr.cast::())) } } /// Changes the to be initialized type. @@ -1158,14 +1151,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { pub const unsafe fn cast_init(init: impl Init) -> impl Init { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. - let res = unsafe { init_from_closure(|ptr: *mut U| init.__init(ptr.cast::())) }; - // FIXME: this let binding is required to avoid a compiler error (cycle when computing the opaque - // type returned by this function) before Rust 1.81. Remove after MSRV bump. - #[allow( - clippy::let_and_return, - reason = "some clippy versions warn about the let binding" - )] - res + unsafe { init_from_closure(|ptr: *mut U| init.__init(ptr.cast::())) } } /// An initializer that leaves the memory uninitialized. From e5cece935531997cd9903c129cc9a2471ed05a4b Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 28 Apr 2026 14:10:59 +0100 Subject: [PATCH 10/27] rust: pin-init: internal: turn `PhantomPinned` error into warnings The `PhantomPinned` detection is just a lint, and is emitted as an error because there is no `compile_warning!()` macro, and `proc-macro-diagnostics` is not stable. Use of `#[deprecated = ""]` attribute to approximate custom proc-macro warnings. A new line is added before message for visual clarity. An example warning with this trick looks like this: warning: use of deprecated function `_::warn`: The field `pin` of type `PhantomPinned` only has an effect if it has the `#[pin]` attribute --> test.rs:9:5 | 9 | pin: marker::PhantomPinned, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Suggested-by: Benno Lossin Link: https://github.com/Rust-for-Linux/pin-init/issues/51 Link: https://patch.msgid.link/20260428-pin-init-sync-v1-10-07f9bd3859fb@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/diagnostics.rs | 14 ++++++++++++++ rust/pin-init/internal/src/pin_data.rs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/rust/pin-init/internal/src/diagnostics.rs b/rust/pin-init/internal/src/diagnostics.rs index 3bdb477c2f2b..c7d9b3e624fc 100644 --- a/rust/pin-init/internal/src/diagnostics.rs +++ b/rust/pin-init/internal/src/diagnostics.rs @@ -3,6 +3,7 @@ use std::fmt::Display; use proc_macro2::TokenStream; +use quote::quote_spanned; use syn::{spanned::Spanned, Error}; pub(crate) struct DiagCtxt(TokenStream); @@ -15,6 +16,19 @@ pub(crate) fn error(&mut self, span: impl Spanned, msg: impl Display) -> ErrorGu ErrorGuaranteed(()) } + pub(crate) fn warn(&mut self, span: impl Spanned, msg: impl Display) { + // Have the message start on a new line for visual clarity. + let msg = format!("\n{}", msg); + self.0.extend(quote_spanned!(span.span() => + // Approximate using deprecated warning while `proc_macro_diagnostic` is unstable. + const _: () = { + #[deprecated = #msg] + const fn warn() {} + warn(); + }; + )); + } + pub(crate) fn with( fun: impl FnOnce(&mut DiagCtxt) -> Result, ) -> TokenStream { diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 76cd11bf28eb..163a31ed1556 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -85,7 +85,7 @@ pub(crate) fn pin_data( for (pinned, field) in &fields { if !pinned && is_phantom_pinned(&field.ty) { - dcx.error( + dcx.warn( field, format!( "The field `{}` of type `PhantomPinned` only has an effect \ From faed81945d0bfcced81a2738d9681a265d41079a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 1 May 2026 14:44:45 +0100 Subject: [PATCH 11/27] rust: pin-init: internal: remove `collect_tuple` polyfill after MSRV bump Tuples implement `FromIterator` since Rust 1.79. Remove the `collect_tuple` polyfill now the MSRV is above 1.79. To avoid over-identing the closure, I move the `Field` destructure from the closure parameter to a let binding. This keeps the diff small. Link: https://patch.msgid.link/20260501134445.3809731-1-gary@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 27 ++++++++------------------ 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 163a31ed1556..be3d97a38225 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -247,17 +247,17 @@ fn generate_projections( let projection = format_ident!("{ident}Projection"); let this = format_ident!("this"); - let (fields_decl, fields_proj) = collect_tuple(fields.iter().map( - |( - pinned, - Field { + let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields + .iter() + .map(|(pinned, field)| { + let Field { vis, ident, ty, attrs, .. - }, - )| { + } = field; + let mut no_doc_attrs = attrs.clone(); no_doc_attrs.retain(|a| !a.path().is_ident("doc")); let ident = ident @@ -287,8 +287,8 @@ fn generate_projections( ), ) } - }, - )); + }) + .collect(); let structurally_pinned_fields_docs = fields .iter() .filter_map(|(pinned, field)| pinned.then_some(field)) @@ -498,14 +498,3 @@ 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, Vec) { - let mut res_a = vec![]; - let mut res_b = vec![]; - for (a, b) in iter { - res_a.push(a); - res_b.push(b); - } - (res_a, res_b) -} From 430654211d566f86e8ee533ff1b01a42be6b602c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 5 May 2026 12:51:37 +0100 Subject: [PATCH 12/27] rust: pin-init: examples: fix `useless_borrows_in_formatting` clippy warning Clippy 1.97 introduces new `useless_borrows_in_formatting` warning which fires on the examples as we have `&*expr` where the format macro takes reference already. Remove the extra borrow. Link: https://patch.msgid.link/20260505115138.2466966-1-gary@kernel.org Signed-off-by: Gary Guo --- rust/pin-init/examples/mutex.rs | 2 +- rust/pin-init/examples/pthread_mutex.rs | 2 +- rust/pin-init/examples/static_init.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index 8ed2d3219eb1..35ecb5f68dc3 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -218,7 +218,7 @@ fn main() { for h in handles { h.join().expect("thread panicked"); } - println!("{:?}", &*mtx.lock()); + println!("{:?}", *mtx.lock()); assert_eq!(*mtx.lock(), workload * thread_count * 2); } } diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs index 4a66316471af..00f457e68827 100644 --- a/rust/pin-init/examples/pthread_mutex.rs +++ b/rust/pin-init/examples/pthread_mutex.rs @@ -177,7 +177,7 @@ fn main() { for h in handles { h.join().expect("thread panicked"); } - println!("{:?}", &*mtx.lock()); + println!("{:?}", *mtx.lock()); assert_eq!(*mtx.lock(), workload * thread_count * 2); } } diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index 906b96c5d4b9..58cd4241b78c 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -117,7 +117,7 @@ fn main() { for h in handles { h.join().expect("thread panicked"); } - println!("{:?}, {:?}", &*mtx.lock(), &*COUNT.lock()); + println!("{:?}, {:?}", *mtx.lock(), *COUNT.lock()); assert_eq!(*mtx.lock(), workload * thread_count * 2); } } From 1e648c22abfe448d284314a0d05622a242ba4eac Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 12 May 2026 13:09:46 +0100 Subject: [PATCH 13/27] rust: pin-init: internal: pin_data: use closure for `handle_field` `handle_field` is currently a function, which precludes it from referencing things in the scope of the parent function. Given that it's only called once, inline its contents to the closure that invokes it instead, so it can directly reference `struct_name` without having to pass in as argument. Link: https://patch.msgid.link/20260512-pin-init-sync-v1-1-81963130dfbd@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 144 ++++++++++++------------- 1 file changed, 70 insertions(+), 74 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index be3d97a38225..1a7098a4c6e0 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -336,7 +336,7 @@ impl #impl_generics #ident #ty_generics fn generate_the_pin_data( vis: &Visibility, - ident: &Ident, + struct_name: &Ident, generics: &Generics, fields: &[(bool, &Field)], ) -> TokenStream { @@ -347,78 +347,74 @@ fn generate_the_pin_data( // 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 ident = ident - .as_ref() - .expect("only structs with named fields are supported"); - let project_ident = format_ident!("__project_{ident}"); - let (init_ty, init_fn, project_ty, project_body, pin_safety) = if pinned { - ( - quote!(PinInit), - quote!(__pinned_init), - quote!(::core::pin::Pin<&'__slot mut #ty>), - // SAFETY: this field is structurally pinned. - quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }), - quote!( - /// - `slot` will not move until it is dropped, i.e. it will be pinned. - ), - ) - } else { - ( - quote!(Init), - quote!(__init), - quote!(&'__slot mut #ty), - quote!(slot), - quote!(), - ) - }; - let slot_safety = format!( - " `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.", - ); - quote! { - /// # Safety - /// - /// - `slot` is a valid pointer to uninitialized memory. - /// - the caller does not touch `slot` when `Err` is returned, they are only permitted - /// to deallocate. - #pin_safety - #(#attrs)* - #vis unsafe fn #ident( - self, - slot: *mut #ty, - init: impl ::pin_init::#init_ty<#ty, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: this function has the same safety requirements as the __init function - // called below. - unsafe { ::pin_init::#init_ty::#init_fn(init, slot) } - } - - /// # Safety - /// - #[doc = #slot_safety] - #(#attrs)* - #vis unsafe fn #project_ident<'__slot>( - self, - slot: &'__slot mut #ty, - ) -> #project_ty { - #project_body - } - } - } - let field_accessors = fields .iter() - .map(|(pinned, field)| handle_field(field, ident, *pinned)) + .map(|(pinned, field)| { + let Field { + vis, + ident, + ty, + attrs, + .. + } = field; + + let field_name = ident + .as_ref() + .expect("only structs with named fields are supported"); + let project_ident = format_ident!("__project_{field_name}"); + let (init_ty, init_fn, project_ty, project_body, pin_safety) = if *pinned { + ( + quote!(PinInit), + quote!(__pinned_init), + quote!(::core::pin::Pin<&'__slot mut #ty>), + // SAFETY: this field is structurally pinned. + quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }), + quote!( + /// - `slot` will not move until it is dropped, i.e. it will be pinned. + ), + ) + } else { + ( + quote!(Init), + quote!(__init), + quote!(&'__slot mut #ty), + quote!(slot), + quote!(), + ) + }; + let slot_safety = format!( + " `slot` points at the field `{field_name}` inside of `{struct_name}`, 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 #field_name( + self, + slot: *mut #ty, + init: impl ::pin_init::#init_ty<#ty, E>, + ) -> ::core::result::Result<(), E> { + // SAFETY: this function has the same safety requirements as the __init function + // called below. + unsafe { ::pin_init::#init_ty::#init_fn(init, slot) } + } + + /// # Safety + /// + #[doc = #slot_safety] + #(#attrs)* + #vis unsafe fn #project_ident<'__slot>( + self, + slot: &'__slot mut #ty, + ) -> #project_ty { + #project_body + } + } + }) .collect::(); quote! { // We declare this struct which will host all of the projection function for our type. It @@ -428,7 +424,7 @@ fn handle_field( #whr { __phantom: ::core::marker::PhantomData< - fn(#ident #ty_generics) -> #ident #ty_generics + fn(#struct_name #ty_generics) -> #struct_name #ty_generics >, } @@ -452,7 +448,7 @@ impl #impl_generics __ThePinData #ty_generics // 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 + unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name #ty_generics #whr { type PinData = __ThePinData #ty_generics; @@ -466,7 +462,7 @@ unsafe fn __pin_data() -> Self::PinData { unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics #whr { - type Datee = #ident #ty_generics; + type Datee = #struct_name #ty_generics; } } } From 4b60f38cd2a4b2d3288377f25100f8d67015988a Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 12 May 2026 13:09:47 +0100 Subject: [PATCH 14/27] rust: pin-init: internal: pin_data: add struct to record field info Introduce `FieldInfo` struct to encapsulate field and other relevant data, instead of carrying a pair of `(pinned, field)` in all places. This allows us to add more information to the struct in the future. Signed-off-by: Mohamad Alsadhan Co-developed-by: Gary Guo Link: https://patch.msgid.link/20260512-pin-init-sync-v1-2-81963130dfbd@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 53 ++++++++++++++++---------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 1a7098a4c6e0..0199d0143308 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -35,6 +35,11 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { } } +struct FieldInfo<'a> { + field: &'a Field, + pinned: bool, +} + pub(crate) fn pin_data( args: Args, input: Item, @@ -73,24 +78,30 @@ pub(crate) fn pin_data( replacer.visit_generics_mut(&mut struct_.generics); replacer.visit_fields_mut(&mut struct_.fields); - let fields: Vec<(bool, &Field)> = struct_ + let fields: Vec> = struct_ .fields .iter_mut() .map(|field| { let len = field.attrs.len(); field.attrs.retain(|a| !a.path().is_ident("pin")); - (len != field.attrs.len(), &*field) + let pinned = len != field.attrs.len(); + + FieldInfo { + field: &*field, + pinned, + } }) .collect(); - for (pinned, field) in &fields { - if !pinned && is_phantom_pinned(&field.ty) { + for field in &fields { + let ident = field.field.ident.as_ref().unwrap(); + + if !field.pinned && is_phantom_pinned(&field.field.ty) { dcx.warn( - field, + field.field, format!( - "The field `{}` of type `PhantomPinned` only has an effect \ + "The field `{ident}` of type `PhantomPinned` only has an effect \ if it has the `#[pin]` attribute", - field.ident.as_ref().unwrap(), ), ); } @@ -143,7 +154,7 @@ fn is_phantom_pinned(ty: &Type) -> bool { fn generate_unpin_impl( ident: &Ident, generics: &Generics, - fields: &[(bool, &Field)], + fields: &[FieldInfo<'_>], ) -> TokenStream { let (_, ty_generics, _) = generics.split_for_impl(); let mut generics_with_pin_lt = generics.clone(); @@ -160,7 +171,7 @@ fn generate_unpin_impl( else { unreachable!() }; - let pinned_fields = fields.iter().filter_map(|(b, f)| b.then_some(f)); + let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| f.field); 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`. @@ -238,7 +249,7 @@ fn generate_projections( vis: &Visibility, ident: &Ident, generics: &Generics, - fields: &[(bool, &Field)], + fields: &[FieldInfo<'_>], ) -> TokenStream { let (impl_generics, ty_generics, _) = generics.split_for_impl(); let mut generics_with_pin_lt = generics.clone(); @@ -249,21 +260,21 @@ fn generate_projections( let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields .iter() - .map(|(pinned, field)| { + .map(|field| { let Field { vis, ident, ty, attrs, .. - } = field; + } = &field.field; let mut no_doc_attrs = attrs.clone(); no_doc_attrs.retain(|a| !a.path().is_ident("doc")); let ident = ident .as_ref() .expect("only structs with named fields are supported"); - if *pinned { + if field.pinned { ( quote!( #(#attrs)* @@ -291,12 +302,12 @@ fn generate_projections( .collect(); let structurally_pinned_fields_docs = fields .iter() - .filter_map(|(pinned, field)| pinned.then_some(field)) - .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap())); + .filter(|f| f.pinned) + .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); let not_structurally_pinned_fields_docs = fields .iter() - .filter_map(|(pinned, field)| (!pinned).then_some(field)) - .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap())); + .filter(|f| !f.pinned) + .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); let docs = format!(" Pin-projections of [`{ident}`]"); quote! { #[doc = #docs] @@ -338,7 +349,7 @@ fn generate_the_pin_data( vis: &Visibility, struct_name: &Ident, generics: &Generics, - fields: &[(bool, &Field)], + fields: &[FieldInfo<'_>], ) -> TokenStream { let (impl_generics, ty_generics, whr) = generics.split_for_impl(); @@ -349,20 +360,20 @@ fn generate_the_pin_data( // The functions are `unsafe` to prevent accidentally calling them. let field_accessors = fields .iter() - .map(|(pinned, field)| { + .map(|f| { let Field { vis, ident, ty, attrs, .. - } = field; + } = f.field; let field_name = ident .as_ref() .expect("only structs with named fields are supported"); let project_ident = format_ident!("__project_{field_name}"); - let (init_ty, init_fn, project_ty, project_body, pin_safety) = if *pinned { + let (init_ty, init_fn, project_ty, project_body, pin_safety) = if f.pinned { ( quote!(PinInit), quote!(__pinned_init), From fea304ec875454360a3be106e0baad96032bf9fe Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 12 May 2026 13:09:48 +0100 Subject: [PATCH 15/27] rust: pin-init: internal: add `PhantomInvariant` and `PhantomInvariantLifetime` Currently, the `pin_init` library has an `Invariant` type alias, and it is instantiated using `PhantomData`. Generated code from `pin_data` on the other hand cannot access the crate-local type alias, so it generates `PhantomData T>` directly. This is all very inconsistent, despite the exact same use case of ensuring invariance. Add `PhantomInvariant` and `PhantomInvariantLifetime` and switch all users that need to express the concept of invariance to use these. They're polyfills of unstable types in the same names in the Rust standard library. Link: https://patch.msgid.link/20260512-pin-init-sync-v1-3-81963130dfbd@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 12 ++---- rust/pin-init/src/__internal.rs | 56 ++++++++++++++++++++++---- rust/pin-init/src/lib.rs | 12 +++--- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 0199d0143308..44d0bd18e4ae 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -180,10 +180,8 @@ 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 - >, + __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>, + __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>, #(#pinned_fields),* } @@ -434,9 +432,7 @@ fn generate_the_pin_data( #vis struct __ThePinData #generics #whr { - __phantom: ::core::marker::PhantomData< - fn(#struct_name #ty_generics) -> #struct_name #ty_generics - >, + __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>, } impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics @@ -465,7 +461,7 @@ unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name # type PinData = __ThePinData #ty_generics; unsafe fn __pin_data() -> Self::PinData { - __ThePinData { __phantom: ::core::marker::PhantomData } + __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() } } } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 5720a621aed7..e54d90a4742e 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -7,20 +7,62 @@ use super::*; +/// Zero-sized type used to mark a type as invariant. +/// +/// This is a polyfill for the [unstable type] in the standard library of the same name. +/// /// See the [nomicon] for what subtyping is. See also [this table]. /// -/// The reason for not using `PhantomData<*mut T>` is that that type never implements [`Send`] and -/// [`Sync`]. Hence `fn(*mut T) -> *mut T` is used, as that type always implements them. -/// +/// [unstable type]: https://doc.rust-lang.org/nightly/std/marker/struct.PhantomInvariant.html /// [nomicon]: https://doc.rust-lang.org/nomicon/subtyping.html /// [this table]: https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns -pub(crate) type Invariant = PhantomData *mut T>; +#[repr(transparent)] +pub struct PhantomInvariant(PhantomData T>); + +impl Clone for PhantomInvariant { + #[inline(always)] + fn clone(&self) -> Self { + *self + } +} + +impl Copy for PhantomInvariant {} + +impl Default for PhantomInvariant { + #[inline(always)] + fn default() -> Self { + Self::new() + } +} + +impl PhantomInvariant { + #[inline(always)] + pub const fn new() -> Self { + Self(PhantomData) + } +} + +/// Zero-sized type used to mark a lifetime as invariant. +/// +/// This is a polyfill for the [unstable type] in the standard library of the same name. +/// +/// [unstable type]: https://doc.rust-lang.org/nightly/std/marker/struct.PhantomInvariantLifetime.html +#[repr(transparent)] +#[derive(Clone, Copy, Default)] +pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>); + +impl PhantomInvariantLifetime<'_> { + #[inline(always)] + pub const fn new() -> Self { + Self(PhantomInvariant::new()) + } +} /// Module-internal type implementing `PinInit` and `Init`. /// /// It is unsafe to create this type, since the closure needs to fulfill the same safety /// requirement as the `__pinned_init`/`__init` functions. -pub(crate) struct InitClosure(pub(crate) F, pub(crate) Invariant<(E, T)>); +pub(crate) struct InitClosure(pub(crate) F, pub(crate) PhantomInvariant<(E, T)>); // SAFETY: While constructing the `InitClosure`, the user promised that it upholds the // `__init` invariants. @@ -126,7 +168,7 @@ fn make_closure(self, f: F) -> F } } -pub struct AllData(Invariant); +pub struct AllData(PhantomInvariant); impl Clone for AllData { fn clone(&self) -> Self { @@ -146,7 +188,7 @@ unsafe impl HasInitData for T { type InitData = AllData; unsafe fn __init_data() -> Self::InitData { - AllData(PhantomData) + AllData(PhantomInvariant::new()) } } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 80c476e605f7..4098c65d63c3 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -947,12 +947,12 @@ fn pin_chain(self, f: F) -> ChainPinInit where F: FnOnce(Pin<&mut T>) -> Result<(), E>, { - ChainPinInit(self, f, PhantomData) + ChainPinInit(self, f, __internal::PhantomInvariant::new()) } } /// An initializer returned by [`PinInit::pin_chain`]. -pub struct ChainPinInit(I, F, __internal::Invariant<(E, T)>); +pub struct ChainPinInit(I, F, __internal::PhantomInvariant<(E, T)>); // SAFETY: The `__pinned_init` function is implemented such that it // - returns `Ok(())` on successful initialization, @@ -1055,12 +1055,12 @@ fn chain(self, f: F) -> ChainInit where F: FnOnce(&mut T) -> Result<(), E>, { - ChainInit(self, f, PhantomData) + ChainInit(self, f, __internal::PhantomInvariant::new()) } } /// An initializer returned by [`Init::chain`]. -pub struct ChainInit(I, F, __internal::Invariant<(E, T)>); +pub struct ChainInit(I, F, __internal::PhantomInvariant<(E, T)>); // SAFETY: The `__init` function is implemented such that it // - returns `Ok(())` on successful initialization, @@ -1108,7 +1108,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { pub const unsafe fn pin_init_from_closure( f: impl FnOnce(*mut T) -> Result<(), E>, ) -> impl PinInit { - __internal::InitClosure(f, PhantomData) + __internal::InitClosure(f, __internal::PhantomInvariant::new()) } /// Creates a new [`Init`] from the given closure. @@ -1127,7 +1127,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { pub const unsafe fn init_from_closure( f: impl FnOnce(*mut T) -> Result<(), E>, ) -> impl Init { - __internal::InitClosure(f, PhantomData) + __internal::InitClosure(f, __internal::PhantomInvariant::new()) } /// Changes the to be initialized type. From df1827babd665ea7039383dbc5c671b66a65c1ec Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 12 May 2026 13:09:49 +0100 Subject: [PATCH 16/27] rust: pin-init: internal: init: handle code blocks early `InitializerKind::Code` is a special case where it does not initialize a field, and thus generate no guard and accessors. Handle it earlier and make the rest of the code more linear. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 100 ++++++++++++++++------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index b0bfe44695e1..7eda1bcf0f28 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -231,6 +231,20 @@ fn init_fields( cfgs.retain(|attr| attr.path().is_ident("cfg")); cfgs }; + + let ident = match kind { + InitializerKind::Value { ident, .. } => ident, + InitializerKind::Init { ident, .. } => ident, + InitializerKind::Code { block, .. } => { + res.extend(quote! { + #(#attrs)* + #[allow(unused_braces)] + #block + }); + continue; + } + }; + let init = match kind { InitializerKind::Value { ident, value } => { let mut value_ident = ident.clone(); @@ -283,55 +297,51 @@ fn init_fields( } } } - InitializerKind::Code { block: value, .. } => quote! { - #(#attrs)* - #[allow(unused_braces)] - #value - }, + InitializerKind::Code { .. } => unreachable!(), }; - res.extend(init); - if let Some(ident) = kind.ident() { - // `mixed_site` ensures that the guard is not accessible to the user-controlled code. - let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - // NOTE: The reference is derived from the guard so that it only lives as long as the - // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` - // like the unaligned field guard, it will become effectively `'static`. - let accessor = if pinned { - let project_ident = format_ident!("__project_{ident}"); - quote! { - // SAFETY: the initialization is pinned. - unsafe { #data.#project_ident(#guard.let_binding()) } - } - } else { - quote! { - #guard.let_binding() - } + // `mixed_site` ensures that the guard is not accessible to the user-controlled code. + let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); + + // NOTE: The reference is derived from the guard so that it only lives as long as the + // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` + // like the unaligned field guard, it will become effectively `'static`. + let accessor = if pinned { + let project_ident = format_ident!("__project_{ident}"); + quote! { + // SAFETY: the initialization is pinned. + unsafe { #data.#project_ident(#guard.let_binding()) } + } + } else { + quote! { + #guard.let_binding() + } + }; + + res.extend(quote! { + #init + + #(#cfgs)* + // Create the drop guard. + // + // SAFETY: + // - `&raw mut (*slot).#ident` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. + // - `(*slot).#ident` has been initialized above. + // - We only need the ownership to the pointee back when initialization has + // succeeded, where we `forget` the guard. + let mut #guard = unsafe { + ::pin_init::__internal::DropGuard::new( + &raw mut (*slot).#ident + ) }; - res.extend(quote! { - #(#cfgs)* - // Create the drop guard. - // - // SAFETY: - // - `&raw mut (*slot).#ident` is valid. - // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - let mut #guard = unsafe { - ::pin_init::__internal::DropGuard::new( - &raw mut (*slot).#ident - ) - }; - - #(#cfgs)* - #[allow(unused_variables)] - let #ident = #accessor; - }); - guards.push(guard); - guard_attrs.push(cfgs); - } + #(#cfgs)* + #[allow(unused_variables)] + let #ident = #accessor; + }); + guards.push(guard); + guard_attrs.push(cfgs); } quote! { #res From 27693a56e8a697f78db535aad2d5267f286e1f51 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 12 May 2026 13:09:50 +0100 Subject: [PATCH 17/27] rust: pin-init: internal: use marker on drop guard type for pinned fields Instead of projecting the created reference, simply create drop guards with different marker types and have the `let_binding()` method of guards of different marker produce different type instead. This allows more flexible lifetime as this is now controlled by the guard. This will be needed when implementing self-referential fields. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 47 ++++++++++++++------------ rust/pin-init/internal/src/pin_data.rs | 35 +++++++++---------- rust/pin-init/src/__internal.rs | 30 +++++++++++++--- 3 files changed, 68 insertions(+), 44 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 7eda1bcf0f28..a0b3c3790d43 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -303,18 +303,31 @@ fn init_fields( // `mixed_site` ensures that the guard is not accessible to the user-controlled code. let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - // NOTE: The reference is derived from the guard so that it only lives as long as the - // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` - // like the unaligned field guard, it will become effectively `'static`. - let accessor = if pinned { + let guard_creation = if pinned { let project_ident = format_ident!("__project_{ident}"); quote! { - // SAFETY: the initialization is pinned. - unsafe { #data.#project_ident(#guard.let_binding()) } + // SAFETY: + // - `&raw mut (*slot).#ident` points to the `#ident` field of `slot`. + // - `&raw mut (*slot).#ident` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. + // - `(*slot).#ident` has been initialized above. + // - We only need the ownership to the pointee back when initialization has + // succeeded, where we `forget` the guard. + unsafe { #data.#project_ident(&raw mut (*slot).#ident) } } } else { quote! { - #guard.let_binding() + // SAFETY: + // - `&raw mut (*slot).#ident` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. + // - `(*slot).#ident` has been initialized above. + // - We only need the ownership to the pointee back when initialization has + // succeeded, where we `forget` the guard. + unsafe { + ::pin_init::__internal::DropGuard::<::pin_init::__internal::Unpinned, _>::new( + &raw mut (*slot).#ident + ) + } } }; @@ -322,24 +335,16 @@ fn init_fields( #init #(#cfgs)* - // Create the drop guard. - // - // SAFETY: - // - `&raw mut (*slot).#ident` is valid. - // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - let mut #guard = unsafe { - ::pin_init::__internal::DropGuard::new( - &raw mut (*slot).#ident - ) - }; + let mut #guard = #guard_creation; #(#cfgs)* + // NOTE: The reference is derived from the guard so that it only lives as long as the + // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` + // like the unaligned field guard, it will become effectively `'static`. #[allow(unused_variables)] - let #ident = #accessor; + let #ident = #guard.let_binding(); }); + guards.push(guard); guard_attrs.push(cfgs); } diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 44d0bd18e4ae..90f6b05b957c 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -371,29 +371,18 @@ fn generate_the_pin_data( .as_ref() .expect("only structs with named fields are supported"); let project_ident = format_ident!("__project_{field_name}"); - let (init_ty, init_fn, project_ty, project_body, pin_safety) = if f.pinned { + let (init_ty, init_fn, pin_marker, pin_safety) = if 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!(Pinned), quote!( /// - `slot` will not move until it is dropped, i.e. it will be pinned. ), ) } else { - ( - quote!(Init), - quote!(__init), - quote!(&'__slot mut #ty), - quote!(slot), - quote!(), - ) + (quote!(Init), quote!(__init), quote!(Unpinned), quote!()) }; - let slot_safety = format!( - " `slot` points at the field `{field_name}` inside of `{struct_name}`, which is pinned.", - ); quote! { /// # Safety /// @@ -414,13 +403,21 @@ fn generate_the_pin_data( /// # Safety /// - #[doc = #slot_safety] + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. #(#attrs)* - #vis unsafe fn #project_ident<'__slot>( + #vis unsafe fn #project_ident( self, - slot: &'__slot mut #ty, - ) -> #project_ty { - #project_body + slot: *mut #ty, + ) -> ::pin_init::__internal::DropGuard<::pin_init::__internal::#pin_marker, #ty> { + // SAFETY: + // - If `#pin_marker` is `Pinned`, the corresponding field is structurally + // pinned. + // - Other safety requirements follows the safety requirement. + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } } }) diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index e54d90a4742e..010e8bfc6cd3 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -277,6 +277,10 @@ struct Foo { println!("{value:?}"); } +// Marker types that determines type of `DropGuard`'s let bindings. +pub struct Pinned; +pub struct Unpinned; + /// When a value of this type is dropped, it drops a `T`. /// /// Can be forgotten to prevent the drop. @@ -285,11 +289,13 @@ struct Foo { /// /// - `ptr` is valid and properly aligned. /// - `*ptr` is initialized and owned by this guard. -pub struct DropGuard { +/// - if `P` is `Pinned`, `ptr` is pinned. +pub struct DropGuard { ptr: *mut T, + phantom: PhantomData

, } -impl DropGuard { +impl DropGuard { /// Creates a drop guard and transfer the ownership of the pointer content. /// /// The ownership is only relinguished if the guard is forgotten via [`core::mem::forget`]. @@ -298,12 +304,18 @@ impl DropGuard { /// /// - `ptr` is valid and properly aligned. /// - `*ptr` is initialized, and the ownership is transferred to this guard. + /// - if `P` is `Pinned`, `ptr` is pinned. #[inline] pub unsafe fn new(ptr: *mut T) -> Self { // INVARIANT: By safety requirement. - Self { ptr } + Self { + ptr, + phantom: PhantomData, + } } +} +impl DropGuard { /// Create a let binding for accessor use. #[inline] pub fn let_binding(&mut self) -> &mut T { @@ -312,7 +324,17 @@ pub fn let_binding(&mut self) -> &mut T { } } -impl Drop for DropGuard { +impl DropGuard { + /// Create a let binding for accessor use. + #[inline] + pub fn let_binding(&mut self) -> Pin<&mut T> { + // SAFETY: `self.ptr` is valid, properly aligned, initialized, exclusively accessible and + // pinned per type invariant. + unsafe { Pin::new_unchecked(&mut *self.ptr) } + } +} + +impl Drop for DropGuard { #[inline] fn drop(&mut self) { // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard. From 57b0a0d7e5a063edceb50bffa648b49591112896 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 12 May 2026 13:09:51 +0100 Subject: [PATCH 18/27] rust: pin-init: internal: make `make_closure` inherent methods The `InitData` and `PinData` traits do not need to exist, the inference helpers could be inherent methods instead. There is no risk for calling the wrong methods even when user defines it, as inherent methods take priority over trait methods. With this change, it unlocks the possibility of attaching additional bounds to the method per type, which is not possible for trait methods. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 7 +--- rust/pin-init/internal/src/pin_data.rs | 17 +++++---- rust/pin-init/src/__internal.rs | 52 ++++++-------------------- 3 files changed, 23 insertions(+), 53 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index a0b3c3790d43..11affa76d1fc 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -103,17 +103,15 @@ pub(crate) fn expand( |(_, err)| Box::new(err), ); let slot = format_ident!("slot"); - let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned { + let (has_data_trait, get_data, init_from_closure) = if pinned { ( format_ident!("HasPinData"), - format_ident!("PinData"), format_ident!("__pin_data"), format_ident!("pin_init_from_closure"), ) } else { ( format_ident!("HasInitData"), - format_ident!("InitData"), format_ident!("__init_data"), format_ident!("init_from_closure"), ) @@ -157,8 +155,7 @@ fn assert_zeroable(_: *mut T) #path::#get_data() }; // Ensure that `#data` really is of type `#data` and help with type inference: - let init = ::pin_init::__internal::#data_trait::make_closure::<_, #error>( - #data, + let init = #data.__make_closure::<_, #error>( move |slot| { #zeroable_check #this diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 90f6b05b957c..713a43c27826 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -447,6 +447,16 @@ impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics impl #impl_generics __ThePinData #ty_generics #whr { + /// Type inference helper function. + #[inline(always)] + #vis fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: FnOnce(*mut #struct_name #ty_generics) -> + ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } + #field_accessors } @@ -461,13 +471,6 @@ unsafe fn __pin_data() -> Self::PinData { __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() } } } - - // SAFETY: TODO - unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics - #whr - { - type Datee = #struct_name #ty_generics; - } } } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 010e8bfc6cd3..d7fdcfef41d2 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -113,30 +113,12 @@ pub unsafe fn new() -> Self { /// /// Only the `init` module is allowed to use this trait. pub unsafe trait HasPinData { - type PinData: PinData; + type PinData; #[expect(clippy::missing_safety_doc)] unsafe fn __pin_data() -> Self::PinData; } -/// Marker trait for pinning data of structs. -/// -/// # Safety -/// -/// Only the `init` module is allowed to use this trait. -pub unsafe trait PinData: Copy { - type Datee: ?Sized + HasPinData; - - /// Type inference helper function. - #[inline(always)] - fn make_closure(self, f: F) -> F - where - F: FnOnce(*mut Self::Datee) -> Result, - { - f - } -} - /// This trait is automatically implemented for every type. It aims to provide the same type /// inference help as `HasPinData`. /// @@ -144,30 +126,12 @@ fn make_closure(self, f: F) -> F /// /// Only the `init` module is allowed to use this trait. pub unsafe trait HasInitData { - type InitData: InitData; + type InitData; #[expect(clippy::missing_safety_doc)] unsafe fn __init_data() -> Self::InitData; } -/// Same function as `PinData`, but for arbitrary data. -/// -/// # Safety -/// -/// Only the `init` module is allowed to use this trait. -pub unsafe trait InitData: Copy { - type Datee: ?Sized + HasInitData; - - /// Type inference helper function. - #[inline(always)] - fn make_closure(self, f: F) -> F - where - F: FnOnce(*mut Self::Datee) -> Result, - { - f - } -} - pub struct AllData(PhantomInvariant); impl Clone for AllData { @@ -178,9 +142,15 @@ fn clone(&self) -> Self { impl Copy for AllData {} -// SAFETY: TODO. -unsafe impl InitData for AllData { - type Datee = T; +impl AllData { + /// Type inference helper function. + #[inline(always)] + pub fn __make_closure(self, f: F) -> F + where + F: FnOnce(*mut T) -> Result, + { + f + } } // SAFETY: TODO. From 5483a97dd2dfcaf8540dba0a80b68a400c4c1861 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 12 May 2026 13:09:52 +0100 Subject: [PATCH 19/27] rust: pin-init: internal: project slots instead of references By projecting slots, the `pin_init!` and `init!` code path can be more unified. This also reduces the amount of macro-generated code and shifts them to the shared infrastructure. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 113 ++++++++----------------- rust/pin-init/internal/src/pin_data.rs | 54 +++--------- rust/pin-init/src/__internal.rs | 77 +++++++++++++++++ rust/pin-init/src/lib.rs | 12 +-- 4 files changed, 133 insertions(+), 123 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 11affa76d1fc..e6f5ea06f91b 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote, quote_spanned}; +use quote::{format_ident, quote}; use syn::{ braced, parse::{End, Parse}, @@ -242,102 +242,61 @@ fn init_fields( } }; - let init = match kind { - InitializerKind::Value { ident, value } => { - let mut value_ident = ident.clone(); - let value_prep = value.as_ref().map(|value| &value.1).map(|value| { - // Setting the span of `value_ident` to `value`'s span improves error messages - // when the type of `value` is wrong. - value_ident.set_span(value.span()); - quote!(let #value_ident = #value;) - }); - // Again span for better diagnostics - let write = quote_spanned!(ident.span()=> ::core::ptr::write); - quote! { - #(#attrs)* - { - #value_prep - // SAFETY: TODO - unsafe { #write(&raw mut (*#slot).#ident, #value_ident) }; - } - } - } - InitializerKind::Init { ident, value, .. } => { - // Again span for better diagnostics - let init = format_ident!("init", span = value.span()); - let value_init = if pinned { - quote! { - // SAFETY: - // - `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - // - We also use `#data` to require the correct trait (`Init` or `PinInit`) - // for `#ident`. - unsafe { #data.#ident(&raw mut (*#slot).#ident, #init)? }; - } - } else { - quote! { - // SAFETY: `slot` is valid, because we are inside of an initializer - // closure, we return when an error/panic occurs. - unsafe { - ::pin_init::Init::__init( - #init, - &raw mut (*#slot).#ident, - )? - }; - } - }; - quote! { - #(#attrs)* - { - let #init = #value; - #value_init - } - } - } - InitializerKind::Code { .. } => unreachable!(), - }; - - // `mixed_site` ensures that the guard is not accessible to the user-controlled code. - let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - - let guard_creation = if pinned { - let project_ident = format_ident!("__project_{ident}"); + let slot = if pinned { quote! { // SAFETY: // - `&raw mut (*slot).#ident` points to the `#ident` field of `slot`. // - `&raw mut (*slot).#ident` is valid. // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - unsafe { #data.#project_ident(&raw mut (*slot).#ident) } + // - `make_field_check` prevents `#ident` from being used twice, therefore + // `(*slot).#ident` is exclusively accessed and has not been initialized. + (unsafe { #data.#ident(&raw mut (*#slot).#ident) }) } } else { quote! { + // For `init!()` macro, everything is unpinned. // SAFETY: // - `&raw mut (*slot).#ident` is valid. // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - unsafe { - ::pin_init::__internal::DropGuard::<::pin_init::__internal::Unpinned, _>::new( - &raw mut (*slot).#ident + // - `make_field_check` prevents `#ident` from being used twice, therefore + // `(*slot).#ident` is exclusively accessed and has not been initialized. + (unsafe { + ::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new( + &raw mut (*#slot).#ident ) + }) + } + }; + + // `mixed_site` ensures that the guard is not accessible to the user-controlled code. + let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); + + let init = match kind { + InitializerKind::Value { ident, value } => { + let value = value + .as_ref() + .map(|(_, value)| quote!(#value)) + .unwrap_or_else(|| quote!(#ident)); + + quote! { + #(#attrs)* + let mut #guard = #slot.write(#value); + } } + InitializerKind::Init { value, .. } => { + quote! { + #(#attrs)* + let mut #guard = #slot.init(#value)?; + } + } + InitializerKind::Code { .. } => unreachable!(), }; res.extend(quote! { #init #(#cfgs)* - let mut #guard = #guard_creation; - - #(#cfgs)* - // NOTE: The reference is derived from the guard so that it only lives as long as the - // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` - // like the unaligned field guard, it will become effectively `'static`. #[allow(unused_variables)] let #ident = #guard.let_binding(); }); diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 713a43c27826..926f84a014c6 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -352,10 +352,9 @@ fn generate_the_pin_data( let (impl_generics, ty_generics, whr) = generics.split_for_impl(); // For every field, we create an initializing projection function according to its projection - // type. If a field is structurally pinned, then it must be initialized via `PinInit`, if it is - // not structurally pinned, then it can be initialized via `Init`. - // - // The functions are `unsafe` to prevent accidentally calling them. + // type. If a field is structurally pinned, we create a `Slot` with `Pinned` which must be + // initialized via `PinInit`; if it is not structurally pinned, then we create a `Slot` with + // `Unpinned` which allows initialization via `Init`. let field_accessors = fields .iter() .map(|f| { @@ -370,54 +369,29 @@ fn generate_the_pin_data( let field_name = ident .as_ref() .expect("only structs with named fields are supported"); - let project_ident = format_ident!("__project_{field_name}"); - let (init_ty, init_fn, pin_marker, pin_safety) = if f.pinned { - ( - quote!(PinInit), - quote!(__pinned_init), - quote!(Pinned), - quote!( - /// - `slot` will not move until it is dropped, i.e. it will be pinned. - ), - ) + let pin_marker = if f.pinned { + quote!(Pinned) } else { - (quote!(Init), quote!(__init), quote!(Unpinned), quote!()) + quote!(Unpinned) }; 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 + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. #(#attrs)* - #vis unsafe fn #field_name( + #[inline(always)] + #vis unsafe fn #field_name( 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 - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - #(#attrs)* - #vis unsafe fn #project_ident( - self, - slot: *mut #ty, - ) -> ::pin_init::__internal::DropGuard<::pin_init::__internal::#pin_marker, #ty> { + ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> { // SAFETY: // - If `#pin_marker` is `Pinned`, the corresponding field is structurally // pinned. // - Other safety requirements follows the safety requirement. - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(slot) } } } }) diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index d7fdcfef41d2..540add6cee8a 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -251,6 +251,83 @@ struct Foo { pub struct Pinned; pub struct Unpinned; +/// Represent an uninitialized field. +/// +/// # Invariants +/// +/// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed memory. +/// - If `P` is `Pinned`, then `ptr` is structurally pinned. +pub struct Slot { + ptr: *mut T, + _phantom: PhantomData

, +} + +impl Slot { + /// # Safety + /// + /// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed + /// memory. + /// - If `P` is `Pinned`, then `ptr` is structurally pinned. + #[inline(always)] + pub unsafe fn new(ptr: *mut T) -> Self { + // INVARIANT: Per safety requirement. + Self { + ptr, + _phantom: PhantomData, + } + } + + /// Initialize the field by value. + #[inline(always)] + pub fn write(self, value: T) -> DropGuard + where + T: Sized, + { + // SAFETY: `self.ptr` is a valid and aligned pointer for write. + unsafe { self.ptr.write(value) } + // SAFETY: + // - `self.ptr` is valid and properly aligned per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + // - If `P` is `Pinned`, `self.ptr` is pinned. + unsafe { DropGuard::new(self.ptr) } + } +} + +impl Slot { + /// Initialize the field. + #[inline(always)] + pub fn init(self, init: impl Init) -> Result, E> { + // SAFETY: + // - `self.ptr` is valid and properly aligned. + // - when `Err` is returned, we also propagate the error without touching `slot`; + // also `self` is consumed so it cannot be touched further. + unsafe { init.__init(self.ptr)? }; + + // SAFETY: + // - `self.ptr` is valid and properly aligned per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + Ok(unsafe { DropGuard::new(self.ptr) }) + } +} + +impl Slot { + /// Initialize the field. + #[inline(always)] + pub fn init(self, init: impl PinInit) -> Result, E> { + // SAFETY: + // - `self.ptr` is valid and properly aligned. + // - when `Err` is returned, we also propagate the error without touching `ptr`; + // also `self` is consumed so it cannot be touched further. + // - the drop guard will not hand out `&mut` (only `Pin<&mut T>`). + unsafe { init.__pinned_init(self.ptr)? }; + + // SAFETY: + // - `self.ptr` is valid, properly aligned and pinned per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + Ok(unsafe { DropGuard::new(self.ptr) }) + } +} + /// When a value of this type is dropped, it drops a `T`. /// /// Can be forgotten to prevent the drop. diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 4098c65d63c3..e891d65cc469 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -867,12 +867,12 @@ macro_rules! stack_try_pin_init { #[macro_export] macro_rules! assert_pinned { ($ty:ty, $field:ident, $field_ty:ty, inline) => { - let _ = move |ptr: *mut $field_ty| { - // SAFETY: This code is unreachable. - let data = unsafe { <$ty as $crate::__internal::HasPinData>::__pin_data() }; - let init = $crate::__internal::AlwaysFail::<$field_ty>::new(); - // SAFETY: This code is unreachable. - unsafe { data.$field(ptr, init) }.ok(); + // SAFETY: This code is unreachable. + let _ = move |ptr: *mut $field_ty| unsafe { + let data = <$ty as $crate::__internal::HasPinData>::__pin_data(); + _ = data + .$field(ptr) + .init($crate::__internal::AlwaysFail::<$field_ty>::new()); }; }; From 6fb5912c92926025a7e205d53feb73c3478c79a1 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 12 May 2026 13:09:53 +0100 Subject: [PATCH 20/27] rust: pin-init: internal: project using full slot Instead of projecting using pointer to a field project the full slot. This further shifts the code generation from the initializer site to the struct definition site, which means less code is generated overall. It also makes the safety comment easier to justify, as now the projection is done by the `#[pin_data]` macro which has full visibility of pinnedness of fields. The field alignment could also be checked on the `#[pin_data]` side; however, since `init!()` macro works for other type of structs, we cannot remove the alignment check from `init!`/`pin_init!` side anyway, so I opted to still keep the alignment check in init.rs. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 5 ++--- rust/pin-init/internal/src/pin_data.rs | 12 ++++++------ rust/pin-init/src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index e6f5ea06f91b..699b105570a5 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -245,12 +245,11 @@ fn init_fields( let slot = if pinned { quote! { // SAFETY: - // - `&raw mut (*slot).#ident` points to the `#ident` field of `slot`. - // - `&raw mut (*slot).#ident` is valid. + // - `slot` is valid and properly aligned. // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. // - `make_field_check` prevents `#ident` from being used twice, therefore // `(*slot).#ident` is exclusively accessed and has not been initialized. - (unsafe { #data.#ident(&raw mut (*#slot).#ident) }) + (unsafe { #data.#ident(#slot) }) } } else { quote! { diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 926f84a014c6..2284256a6220 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -377,21 +377,21 @@ fn generate_the_pin_data( quote! { /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #(#attrs)* #[inline(always)] #vis unsafe fn #field_name( self, - slot: *mut #ty, + slot: *mut #struct_name #ty_generics, ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> { // SAFETY: // - If `#pin_marker` is `Pinned`, the corresponding field is structurally // pinned. // - Other safety requirements follows the safety requirement. - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) } } } }) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index e891d65cc469..c9e2cbe27915 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -868,7 +868,7 @@ macro_rules! stack_try_pin_init { macro_rules! assert_pinned { ($ty:ty, $field:ident, $field_ty:ty, inline) => { // SAFETY: This code is unreachable. - let _ = move |ptr: *mut $field_ty| unsafe { + let _ = move |ptr: *mut $ty| unsafe { let data = <$ty as $crate::__internal::HasPinData>::__pin_data(); _ = data .$field(ptr) From 28ba76f374bf5fec9e5bf5035ffe90e4c6b05682 Mon Sep 17 00:00:00 2001 From: Martin Kletzander Date: Wed, 27 May 2026 18:19:52 +0100 Subject: [PATCH 21/27] rust: pin-init: internal: pin_data: filter non-`#[cfg]` attr in generated code When using a macro with custom attributes in a `#[pin_data]` struct it can mess up the generated code. The generated code needs nothing more than the `#[cfg]` attribute, thus strip away all other attributes. [ Rebased and updated to only include `#[cfg]` instead of both `#[cfg]` and `#[doc]`; doc is not needed for the generated hidden items. - Gary ] Signed-off-by: Martin Kletzander Co-developed-by: Gary Guo Link: https://patch.msgid.link/20260527-pin-init-sync-v1-1-e20335ed2501@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 50 ++++++++++++++------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 2284256a6220..9ca8235ed3d6 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -7,7 +7,7 @@ parse_quote, parse_quote_spanned, spanned::Spanned, visit_mut::VisitMut, - Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, + Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, }; use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; @@ -38,6 +38,7 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { struct FieldInfo<'a> { field: &'a Field, pinned: bool, + cfg_attrs: Vec<&'a Attribute>, } pub(crate) fn pin_data( @@ -86,9 +87,16 @@ pub(crate) fn pin_data( field.attrs.retain(|a| !a.path().is_ident("pin")); let pinned = len != field.attrs.len(); + let cfg_attrs = field + .attrs + .iter() + .filter(|a| a.path().is_ident("cfg")) + .collect(); + FieldInfo { field: &*field, pinned, + cfg_attrs, } }) .collect(); @@ -171,7 +179,15 @@ fn generate_unpin_impl( else { unreachable!() }; - let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| f.field); + let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| { + let ident = f.field.ident.as_ref().unwrap(); + let ty = &f.field.ty; + let cfg_attrs = &f.cfg_attrs; + quote!( + #(#cfg_attrs)* + #ident: #ty + ) + }); 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`. @@ -259,27 +275,20 @@ fn generate_projections( let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields .iter() .map(|field| { - let Field { - vis, - ident, - ty, - attrs, - .. - } = &field.field; + let Field { vis, ident, ty, .. } = &field.field; + let cfg_attrs = &field.cfg_attrs; - let mut no_doc_attrs = attrs.clone(); - no_doc_attrs.retain(|a| !a.path().is_ident("doc")); let ident = ident .as_ref() .expect("only structs with named fields are supported"); if field.pinned { ( quote!( - #(#attrs)* + #(#cfg_attrs)* #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, ), quote!( - #(#no_doc_attrs)* + #(#cfg_attrs)* // SAFETY: this field is structurally pinned. #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) }, ), @@ -287,11 +296,11 @@ fn generate_projections( } else { ( quote!( - #(#attrs)* + #(#cfg_attrs)* #vis #ident: &'__pin mut #ty, ), quote!( - #(#no_doc_attrs)* + #(#cfg_attrs)* #ident: &mut #this.#ident, ), ) @@ -358,13 +367,8 @@ fn generate_the_pin_data( let field_accessors = fields .iter() .map(|f| { - let Field { - vis, - ident, - ty, - attrs, - .. - } = f.field; + let Field { vis, ident, ty, .. } = f.field; + let cfg_attrs = &f.cfg_attrs; let field_name = ident .as_ref() @@ -381,7 +385,7 @@ fn generate_the_pin_data( /// - `(*slot).#field_name` is properly aligned. /// - `(*slot).#field_name` points to uninitialized and exclusively accessed /// memory. - #(#attrs)* + #(#cfg_attrs)* #[inline(always)] #vis unsafe fn #field_name( self, From e6405dca10de4136a880d218b015663a41d92a54 Mon Sep 17 00:00:00 2001 From: Mirko Adzic Date: Wed, 27 May 2026 18:19:53 +0100 Subject: [PATCH 22/27] rust: pin-init: internal: suppress `non_snake_case` lint in `#[pin_data]` Allows `non_snake_case` lint on struct fields generated by `#[pin_data]`. Since the same warning will be reported by the compiler on the struct definition, having extra warnings for the generated code is unnecessary and confusing. Signed-off-by: Mirko Adzic Link: https://patch.msgid.link/20260527-pin-init-sync-v1-2-e20335ed2501@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 9ca8235ed3d6..9fbbd25bcaac 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -191,7 +191,10 @@ fn generate_unpin_impl( 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. + #[allow( + dead_code, // The fields below are never used. + non_snake_case // The warning will be emitted on the struct definition. + )] struct __Unpin #generics_with_pin_lt #where_token #predicates @@ -318,7 +321,9 @@ fn generate_projections( let docs = format!(" Pin-projections of [`{ident}`]"); quote! { #[doc = #docs] - #[allow(dead_code)] + // Allow `non_snake_case` since the same warning will be emitted on + // the struct definition. + #[allow(dead_code, non_snake_case)] #[doc(hidden)] #vis struct #projection #generics_with_pin_lt #whr @@ -386,6 +391,9 @@ fn generate_the_pin_data( /// - `(*slot).#field_name` points to uninitialized and exclusively accessed /// memory. #(#cfg_attrs)* + // Allow `non_snake_case` since the same warning will be emitted on + // the struct definition. + #[allow(non_snake_case)] #[inline(always)] #vis unsafe fn #field_name( self, From 5423ef9d4db852835746001d0840231227bb0e39 Mon Sep 17 00:00:00 2001 From: Mirko Adzic Date: Wed, 27 May 2026 18:19:54 +0100 Subject: [PATCH 23/27] rust: pin-init: internal: suppress `non_snake_case` lint in `[pin_]init!` Allows `non_snake_case` lint on local variables generated in `[pin_]init!`. Conceptually the identifiers in `[pin_]init!` just references the field names, and are not defining them, so the warning should not be generated, similar to how constructing a struct with non-snake-case field names do no generate these warnings. Reported-by: Gary Guo Closes: https://github.com/Rust-for-Linux/pin-init/issues/125 Closes: https://lore.kernel.org/rust-for-linux/DGTBJBIVFZ2K.2F1ZEFGY0G7NK@garyguo.net/ Fixes: 42415d163e5d ("rust: pin-init: add references to previously initialized fields") Signed-off-by: Mirko Adzic [ Reworded commit message - Gary ] Link: https://patch.msgid.link/20260527-pin-init-sync-v1-3-e20335ed2501@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 699b105570a5..041a84593730 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -296,7 +296,9 @@ fn init_fields( #init #(#cfgs)* - #[allow(unused_variables)] + // Allow `non_snake_case` since the same warning is going to be reported for the struct + // field. + #[allow(unused_variables, non_snake_case)] let #ident = #guard.let_binding(); }); From 2a02b4f96f7bb77ae4cef5d41494f83d01d022dd Mon Sep 17 00:00:00 2001 From: Xiaobo Liu Date: Wed, 27 May 2026 18:19:55 +0100 Subject: [PATCH 24/27] rust: pin-init: docs: fix typos in MaybeZeroable documentation Signed-off-by: Xiaobo Liu Link: https://patch.msgid.link/20260527-pin-init-sync-v1-4-e20335ed2501@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index c9e2cbe27915..84099474324e 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -431,7 +431,7 @@ /// ``` /// use pin_init::MaybeZeroable; /// -/// // implmements `Zeroable` +/// // implements `Zeroable` /// #[derive(MaybeZeroable)] /// pub struct DriverData { /// pub(crate) id: i64, @@ -439,7 +439,7 @@ /// len: usize, /// } /// -/// // does not implmement `Zeroable` +/// // does not implement `Zeroable` /// #[derive(MaybeZeroable)] /// pub struct DriverData2 { /// pub(crate) id: i64, From 79bc923ae2a60a227907697abab4ed26c3fc3670 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 27 May 2026 18:19:56 +0100 Subject: [PATCH 25/27] rust: pin-init: move `InitClosure` out from `__internal` The `__internal` module is for exposing internal items publicly to procedural macros (pin-init-internal). Types that are crate-local only can just have proper visibility and does not need to be in `__internal`. The type name of `InitClosure` can often shows up in symbol names, this reduces the length slightly. Link: https://patch.msgid.link/20260527-pin-init-sync-v1-5-e20335ed2501@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/__internal.rs | 30 ----------------------------- rust/pin-init/src/lib.rs | 34 +++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 540add6cee8a..56dc655e323e 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -58,36 +58,6 @@ pub const fn new() -> Self { } } -/// Module-internal type implementing `PinInit` and `Init`. -/// -/// It is unsafe to create this type, since the closure needs to fulfill the same safety -/// requirement as the `__pinned_init`/`__init` functions. -pub(crate) struct InitClosure(pub(crate) F, pub(crate) PhantomInvariant<(E, T)>); - -// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the -// `__init` invariants. -unsafe impl Init for InitClosure -where - F: FnOnce(*mut T) -> Result<(), E>, -{ - #[inline] - unsafe fn __init(self, slot: *mut T) -> Result<(), E> { - (self.0)(slot) - } -} - -// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the -// `__pinned_init` invariants. -unsafe impl PinInit for InitClosure -where - F: FnOnce(*mut T) -> Result<(), E>, -{ - #[inline] - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { - (self.0)(slot) - } -} - /// Token type to signify successful initialization. /// /// Can only be constructed via the unsafe [`Self::new`] function. The initializer macros use this diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 84099474324e..9732af32795c 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1092,6 +1092,36 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { } } +/// Implement `PinInit` and `Init` for closures. +/// +/// It is unsafe to create this type, since the closure needs to fulfill the same safety +/// requirement as the `__pinned_init`/`__init` functions. +struct InitClosure(F, __internal::PhantomInvariant<(E, T)>); + +// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the +// `__init` invariants. +unsafe impl Init for InitClosure +where + F: FnOnce(*mut T) -> Result<(), E>, +{ + #[inline] + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { + (self.0)(slot) + } +} + +// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the +// `__pinned_init` invariants. +unsafe impl PinInit for InitClosure +where + F: FnOnce(*mut T) -> Result<(), E>, +{ + #[inline] + unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + (self.0)(slot) + } +} + /// Creates a new [`PinInit`] from the given closure. /// /// # Safety @@ -1108,7 +1138,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { pub const unsafe fn pin_init_from_closure( f: impl FnOnce(*mut T) -> Result<(), E>, ) -> impl PinInit { - __internal::InitClosure(f, __internal::PhantomInvariant::new()) + InitClosure(f, __internal::PhantomInvariant::new()) } /// Creates a new [`Init`] from the given closure. @@ -1127,7 +1157,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { pub const unsafe fn init_from_closure( f: impl FnOnce(*mut T) -> Result<(), E>, ) -> impl Init { - __internal::InitClosure(f, __internal::PhantomInvariant::new()) + InitClosure(f, __internal::PhantomInvariant::new()) } /// Changes the to be initialized type. From f85906616ec70d1d0178073b16ebd1c7f6ff1ee7 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 27 May 2026 18:19:57 +0100 Subject: [PATCH 26/27] rust: pin-init: remove `E` from `InitClosure` Move `E` from type to trait impl block. This greatly shortens the monomorphized type names. The `__pinned_init` function name is only slightly shortened as it still encodes the `E` as part of `PinInit` in the symbol. `T` cannot be moved to trait impl block otherwise it will start to conflict with the `impl Init for T` as Rust cannot deduce that there're no types that fulfill `T: FnOnce(*mut T)`. Link: https://patch.msgid.link/20260527-pin-init-sync-v1-6-e20335ed2501@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 9732af32795c..fd40c8f244a1 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1096,11 +1096,11 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { /// /// It is unsafe to create this type, since the closure needs to fulfill the same safety /// requirement as the `__pinned_init`/`__init` functions. -struct InitClosure(F, __internal::PhantomInvariant<(E, T)>); +struct InitClosure(F, __internal::PhantomInvariant); // SAFETY: While constructing the `InitClosure`, the user promised that it upholds the // `__init` invariants. -unsafe impl Init for InitClosure +unsafe impl Init for InitClosure where F: FnOnce(*mut T) -> Result<(), E>, { @@ -1112,7 +1112,7 @@ unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: While constructing the `InitClosure`, the user promised that it upholds the // `__pinned_init` invariants. -unsafe impl PinInit for InitClosure +unsafe impl PinInit for InitClosure where F: FnOnce(*mut T) -> Result<(), E>, { From d2f309227952e73966682f348161094e40eb6440 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 8 May 2026 16:29:49 +0100 Subject: [PATCH 27/27] rust: pin_init: internal: use `loop {}` to produce never value In the `init!`/`pin_init!` macros, we rely on a trick that assigns never (`!`) values to all mentioned fields in never-executed code to let the compiler check that all fields have been initialized. Currently we use `::core::panic!()` to produce this value, but before Rust 1.91.0, it creates outlined `panic_cold_explicit` functions which do not get removed by the optimizer, thus leaving dead code behind in the binary. This has been fixed by [1], which lands in Rust 1.91.0+, higher than the kernel minimum version 1.85.0. This causes ~200 dead `panic_cold_explicit` instances being included in the binary, with ~90 of them from nova-core's usage of pin-init. Work around the issue by using `loop {}` which creates the never value without macro expansion or function call at all. All instances of `panic_cold_explicit` outside libcore are removed by this change in my kernel build. Link: https://github.com/rust-lang/rust/pull/145304 [1] Link: https://patch.msgid.link/20260508152950.833635-1-gary@kernel.org Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 041a84593730..28d30805d06b 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -354,7 +354,7 @@ fn make_field_check( ::core::ptr::write(slot, #path { #( #(#field_attrs)* - #field_name: ::core::panic!(), + #field_name: loop {}, )* #zeroing_trailer })