All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gary Guo <gary@garyguo.net>
To: "Benno Lossin" <lossin@kernel.org>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>
Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Gary Guo <gary@garyguo.net>
Subject: [PATCH 5/8] rust: pin-init: internal: use marker on drop guard type for pinned fields
Date: Tue, 12 May 2026 13:09:50 +0100	[thread overview]
Message-ID: <20260512-pin-init-sync-v1-5-81963130dfbd@garyguo.net> (raw)
In-Reply-To: <20260512-pin-init-sync-v1-0-81963130dfbd@garyguo.net>

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


  parent reply	other threads:[~2026-05-12 12:10 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Gary Guo [this message]
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
2026-05-14 19:15   ` Gary Guo
2026-05-14 19:31 ` (subset) [PATCH 0/8] rust: pin-init: internal refactors Gary Guo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260512-pin-init-sync-v1-5-81963130dfbd@garyguo.net \
    --to=gary@garyguo.net \
    --cc=a.hindborg@kernel.org \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.