Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH 0/8] rust: pin-init: internal refactors
@ 2026-05-12 12:09 Gary Guo
  2026-05-12 12:09 ` [PATCH 1/8] rust: pin-init: internal: pin_data: use closure for `handle_field` Gary Guo
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo, Mohamad Alsadhan

This series include some internal refactors to pin-init macros that
simplifies the codebase and allows new features to be built more easily on
top.

This includes the following upstream PRs:

- internal: pin_data: add struct to record field info
  https://github.com/Rust-for-Linux/pin-init/pull/152

- internal: add `PhantomInvariant` and `PhantomInvariantLifetime`
  https://github.com/Rust-for-Linux/pin-init/pull/150

- Rework initialization and accessor generation
  https://github.com/Rust-for-Linux/pin-init/pull/143

There're more improvements/fixes/features upcoming that would benefit from
this refactor, so I would want to land this relatively soon. Benno has
looked at them and is OK with the approach, but didn't have time to do a
full review; if someone else would like to take look at them it'll be
hugely appreciated.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
Gary Guo (7):
      rust: pin-init: internal: pin_data: use closure for `handle_field`
      rust: pin-init: internal: add `PhantomInvariant` and `PhantomInvariantLifetime`
      rust: pin-init: internal: init: handle code blocks early
      rust: pin-init: internal: use marker on drop guard type for pinned fields
      rust: pin-init: internal: make `make_closure` inherent methods
      rust: pin-init: internal: project slots instead of references
      rust: pin-init: internal: project using full slot

Mohamad Alsadhan (1):
      rust: pin-init: internal: pin_data: add struct to record field info

 rust/pin-init/internal/src/init.rs     | 156 ++++++++++--------------
 rust/pin-init/internal/src/pin_data.rs | 195 +++++++++++++-----------------
 rust/pin-init/src/__internal.rs        | 215 +++++++++++++++++++++++++--------
 rust/pin-init/src/lib.rs               |  24 ++--
 4 files changed, 324 insertions(+), 266 deletions(-)
---
base-commit: 430654211d566f86e8ee533ff1b01a42be6b602c
change-id: 20260511-pin-init-sync-e04cf605b097

Best regards,
--  
Gary Guo <gary@garyguo.net>


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

* [PATCH 1/8] rust: pin-init: internal: pin_data: use closure for `handle_field`
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  2026-05-12 12:09 ` [PATCH 2/8] rust: pin-init: internal: pin_data: add struct to record field info Gary Guo
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo

`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.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 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<E>(
-                self,
-                slot: *mut #ty,
-                init: impl ::pin_init::#init_ty<#ty, E>,
-            ) -> ::core::result::Result<(), E> {
-                // SAFETY: this function has the same safety requirements as the __init function
-                // called below.
-                unsafe { ::pin_init::#init_ty::#init_fn(init, slot) }
-            }
-
-            /// # Safety
-            ///
-            #[doc = #slot_safety]
-            #(#attrs)*
-            #vis unsafe fn #project_ident<'__slot>(
-                self,
-                slot: &'__slot mut #ty,
-            ) -> #project_ty {
-                #project_body
-            }
-        }
-    }
-
     let field_accessors = fields
         .iter()
-        .map(|(pinned, field)| handle_field(field, ident, *pinned))
+        .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<E>(
+                    self,
+                    slot: *mut #ty,
+                    init: impl ::pin_init::#init_ty<#ty, E>,
+                ) -> ::core::result::Result<(), E> {
+                    // SAFETY: this function has the same safety requirements as the __init function
+                    // called below.
+                    unsafe { ::pin_init::#init_ty::#init_fn(init, slot) }
+                }
+
+                /// # Safety
+                ///
+                #[doc = #slot_safety]
+                #(#attrs)*
+                #vis unsafe fn #project_ident<'__slot>(
+                    self,
+                    slot: &'__slot mut #ty,
+                ) -> #project_ty {
+                    #project_body
+                }
+            }
+        })
         .collect::<TokenStream>();
     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;
         }
     }
 }

-- 
2.51.2


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

* [PATCH 2/8] rust: pin-init: internal: pin_data: add struct to record field info
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
  2026-05-12 12:09 ` [PATCH 1/8] rust: pin-init: internal: pin_data: use closure for `handle_field` Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  2026-05-12 12:09 ` [PATCH 3/8] rust: pin-init: internal: add `PhantomInvariant` and `PhantomInvariantLifetime` Gary Guo
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo, Mohamad Alsadhan

From: Mohamad Alsadhan <mo@sdhn.cc>

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 <mo@sdhn.cc>
Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
---
 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<Self> {
     }
 }
 
+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<FieldInfo<'_>> = 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),

-- 
2.51.2


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

* [PATCH 3/8] rust: pin-init: internal: add `PhantomInvariant` and `PhantomInvariantLifetime`
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
  2026-05-12 12:09 ` [PATCH 1/8] rust: pin-init: internal: pin_data: use closure for `handle_field` Gary Guo
  2026-05-12 12:09 ` [PATCH 2/8] rust: pin-init: internal: pin_data: add struct to record field info Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  2026-05-12 12:09 ` [PATCH 4/8] rust: pin-init: internal: init: handle code blocks early Gary Guo
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo

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<fn(T) -> 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.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 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<fn(&'__pin ()) -> &'__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::*;
 
-/// See the [nomicon] for what subtyping is. See also [this table].
+/// 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.
 ///
-/// 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.
+/// See the [nomicon] for what subtyping is. See also [this table].
 ///
+/// [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<T> = PhantomData<fn(*mut T) -> *mut T>;
+#[repr(transparent)]
+pub struct PhantomInvariant<T: ?Sized>(PhantomData<fn(T) -> T>);
+
+impl<T: ?Sized> Clone for PhantomInvariant<T> {
+    #[inline(always)]
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<T: ?Sized> Copy for PhantomInvariant<T> {}
+
+impl<T: ?Sized> Default for PhantomInvariant<T> {
+    #[inline(always)]
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<T: ?Sized> PhantomInvariant<T> {
+    #[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<F, T: ?Sized, E>(pub(crate) F, pub(crate) Invariant<(E, T)>);
+pub(crate) struct InitClosure<F, T: ?Sized, E>(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<F, E>(self, f: F) -> F
     }
 }
 
-pub struct AllData<T: ?Sized>(Invariant<T>);
+pub struct AllData<T: ?Sized>(PhantomInvariant<T>);
 
 impl<T: ?Sized> Clone for AllData<T> {
     fn clone(&self) -> Self {
@@ -146,7 +188,7 @@ unsafe impl<T: ?Sized> HasInitData for T {
     type InitData = AllData<T>;
 
     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<F>(self, f: F) -> ChainPinInit<Self, F, T, E>
     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, T: ?Sized, E>(I, F, __internal::Invariant<(E, T)>);
+pub struct ChainPinInit<I, F, T: ?Sized, E>(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<F>(self, f: F) -> ChainInit<Self, F, T, E>
     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, T: ?Sized, E>(I, F, __internal::Invariant<(E, T)>);
+pub struct ChainInit<I, F, T: ?Sized, E>(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<T: ?Sized, E>(
     f: impl FnOnce(*mut T) -> Result<(), E>,
 ) -> impl PinInit<T, E> {
-    __internal::InitClosure(f, PhantomData)
+    __internal::InitClosure(f, __internal::PhantomInvariant::new())
 }
 
 /// Creates a new [`Init<T, E>`] 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<T: ?Sized, E>(
     f: impl FnOnce(*mut T) -> Result<(), E>,
 ) -> impl Init<T, E> {
-    __internal::InitClosure(f, PhantomData)
+    __internal::InitClosure(f, __internal::PhantomInvariant::new())
 }
 
 /// Changes the to be initialized type.

-- 
2.51.2


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

* [PATCH 4/8] rust: pin-init: internal: init: handle code blocks early
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
                   ` (2 preceding siblings ...)
  2026-05-12 12:09 ` [PATCH 3/8] rust: pin-init: internal: add `PhantomInvariant` and `PhantomInvariantLifetime` Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  2026-05-12 12:09 ` [PATCH 5/8] rust: pin-init: internal: use marker on drop guard type for pinned fields Gary Guo
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo

`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 <gary@garyguo.net>
---
 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());
 
-            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
-                    )
-                };
+        // 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()
+            }
+        };
 
-                #(#cfgs)*
-                #[allow(unused_variables)]
-                let #ident = #accessor;
-            });
-            guards.push(guard);
-            guard_attrs.push(cfgs);
-        }
+        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
+                )
+            };
+
+            #(#cfgs)*
+            #[allow(unused_variables)]
+            let #ident = #accessor;
+        });
+        guards.push(guard);
+        guard_attrs.push(cfgs);
     }
     quote! {
         #res

-- 
2.51.2


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

* [PATCH 5/8] rust: pin-init: internal: use marker on drop guard type for pinned fields
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
                   ` (3 preceding siblings ...)
  2026-05-12 12:09 ` [PATCH 4/8] rust: pin-init: internal: init: handle code blocks early Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  2026-05-12 12:09 ` [PATCH 6/8] rust: pin-init: internal: make `make_closure` inherent methods Gary Guo
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo

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 <gary@garyguo.net>
---
 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<T: ?Sized> {
+/// - if `P` is `Pinned`, `ptr` is pinned.
+pub struct DropGuard<P, T: ?Sized> {
     ptr: *mut T,
+    phantom: PhantomData<P>,
 }
 
-impl<T: ?Sized> DropGuard<T> {
+impl<P, T: ?Sized> DropGuard<P, T> {
     /// 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<T: ?Sized> DropGuard<T> {
     ///
     /// - `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<T: ?Sized> DropGuard<Unpinned, T> {
     /// 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<T: ?Sized> Drop for DropGuard<T> {
+impl<T: ?Sized> DropGuard<Pinned, T> {
+    /// 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<P, T: ?Sized> Drop for DropGuard<P, T> {
     #[inline]
     fn drop(&mut self) {
         // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard.

-- 
2.51.2


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

* [PATCH 6/8] rust: pin-init: internal: make `make_closure` inherent methods
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
                   ` (4 preceding siblings ...)
  2026-05-12 12:09 ` [PATCH 5/8] rust: pin-init: internal: use marker on drop guard type for pinned fields Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  2026-05-12 12:09 ` [PATCH 7/8] rust: pin-init: internal: project slots instead of references Gary Guo
  2026-05-12 12:09 ` [PATCH 8/8] rust: pin-init: internal: project using full slot Gary Guo
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo

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 <gary@garyguo.net>
---
 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<T: ?::core::marker::Sized>(_: *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<F, E>(self, f: F) -> F
-    where
-        F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
-    {
-        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<F, E>(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<F, E>(self, f: F) -> F
-    where
-        F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
-    {
-        f
-    }
-}
-
 pub struct AllData<T: ?Sized>(PhantomInvariant<T>);
 
 impl<T: ?Sized> Clone for AllData<T> {
@@ -178,9 +142,15 @@ fn clone(&self) -> Self {
 
 impl<T: ?Sized> Copy for AllData<T> {}
 
-// SAFETY: TODO.
-unsafe impl<T: ?Sized> InitData for AllData<T> {
-    type Datee = T;
+impl<T: ?Sized> AllData<T> {
+    /// Type inference helper function.
+    #[inline(always)]
+    pub fn __make_closure<F, E>(self, f: F) -> F
+    where
+        F: FnOnce(*mut T) -> Result<InitOk, E>,
+    {
+        f
+    }
 }
 
 // SAFETY: TODO.

-- 
2.51.2


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

* [PATCH 7/8] rust: pin-init: internal: project slots instead of references
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
                   ` (5 preceding siblings ...)
  2026-05-12 12:09 ` [PATCH 6/8] rust: pin-init: internal: make `make_closure` inherent methods Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  2026-05-12 12:09 ` [PATCH 8/8] rust: pin-init: internal: project using full slot Gary Guo
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo

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 <gary@garyguo.net>
---
 rust/pin-init/internal/src/init.rs     | 113 +++++++++++----------------------
 rust/pin-init/internal/src/pin_data.rs |  52 ++++-----------
 rust/pin-init/src/__internal.rs        |  77 ++++++++++++++++++++++
 rust/pin-init/src/lib.rs               |  12 ++--
 4 files changed, 132 insertions(+), 122 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..3278a54510e1 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
-                #(#attrs)*
-                #vis unsafe fn #field_name<E>(
-                    self,
-                    slot: *mut #ty,
-                    init: impl ::pin_init::#init_ty<#ty, E>,
-                ) -> ::core::result::Result<(), E> {
-                    // SAFETY: this function has the same safety requirements as the __init function
-                    // called below.
-                    unsafe { ::pin_init::#init_ty::#init_fn(init, slot) }
-                }
-
                 /// # Safety
                 ///
                 /// - `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.
+                ///   describes.
+                /// - `slot` is a valid, properly aligned and points to uninitialized and
+                ///   exclusively accessed memory.
                 #(#attrs)*
-                #vis unsafe fn #project_ident(
+                #[inline(always)]
+                #vis unsafe fn #field_name(
                     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..854fbcaa93f3 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<P, T: ?Sized> {
+    ptr: *mut T,
+    _phantom: PhantomData<P>,
+}
+
+impl<P, T: ?Sized> Slot<P, T> {
+    /// # 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<P, T>
+    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<T: ?Sized> Slot<Unpinned, T> {
+    /// Initialize the field.
+    #[inline(always)]
+    pub fn init<E>(self, init: impl Init<T, E>) -> Result<DropGuard<Unpinned, T>, 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<T: ?Sized> Slot<Pinned, T> {
+    /// Initialize the field.
+    #[inline(always)]
+    pub fn init<E>(self, init: impl PinInit<T, E>) -> Result<DropGuard<Pinned, T>, 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());
         };
     };
 

-- 
2.51.2


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

* [PATCH 8/8] rust: pin-init: internal: project using full slot
  2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
                   ` (6 preceding siblings ...)
  2026-05-12 12:09 ` [PATCH 7/8] rust: pin-init: internal: project slots instead of references Gary Guo
@ 2026-05-12 12:09 ` Gary Guo
  7 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-05-12 12:09 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux, linux-kernel, Gary Guo

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 <gary@garyguo.net>
---
 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 3278a54510e1..a3431863f5d6 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 `#ident` 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)

-- 
2.51.2


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

end of thread, other threads:[~2026-05-12 12:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-12 12:09 [PATCH 0/8] rust: pin-init: internal refactors Gary Guo
2026-05-12 12:09 ` [PATCH 1/8] rust: pin-init: internal: pin_data: use closure for `handle_field` Gary Guo
2026-05-12 12:09 ` [PATCH 2/8] rust: pin-init: internal: pin_data: add struct to record field info Gary Guo
2026-05-12 12:09 ` [PATCH 3/8] rust: pin-init: internal: add `PhantomInvariant` and `PhantomInvariantLifetime` Gary Guo
2026-05-12 12:09 ` [PATCH 4/8] rust: pin-init: internal: init: handle code blocks early Gary Guo
2026-05-12 12:09 ` [PATCH 5/8] rust: pin-init: internal: use marker on drop guard type for pinned fields Gary Guo
2026-05-12 12:09 ` [PATCH 6/8] rust: pin-init: internal: make `make_closure` inherent methods Gary Guo
2026-05-12 12:09 ` [PATCH 7/8] rust: pin-init: internal: project slots instead of references Gary Guo
2026-05-12 12:09 ` [PATCH 8/8] rust: pin-init: internal: project using full slot Gary Guo

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