From 73511e489bb0cb41d65a99b6f9df4f73a2f4827a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 22 Sep 2026 21:42:34 +0100 Subject: [PATCH] rust: pin-init: internal: pin_data: pin borrowed fields with wrapper For fields that are borrowed, a mutable reference to the struct no longer mean that it has the permission to access these fields. Therefore, the memory that they refer to must be pinned. Wrap these fields inside a `Borrowed` struct which pins it. They may be accessed directly (if they're not themselves referencing other struct fields), so implement a `Deref`. As such fields are always pinned, there is no need to generate a conditional `Unpin` implementations that implements `Unpin` when all fields are. Simply generate a never satisfiable `Unpin` implementation to prevent user from adding their own. Acked-by: Benno Lossin Link: https://patch.msgid.link/20261008-dev-selfref-v2-4-e280b3c8fba5@garyguo.net/ Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 19 +++++++++++++++++++ rust/pin-init/src/__internal.rs | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 36678843251c..a3e492c455da 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -433,6 +433,10 @@ fn generate_struct_def(info: &StructInfo) -> TokenStream { ty = quote!(::pin_init::__internal::Erase<#ty>); }; + if field.borrowed.is_some() { + ty = quote!(::pin_init::__internal::Borrowed<#ty>); + } + quote! { #(#attrs)* #vis #ident #colon_token #ty } @@ -468,6 +472,20 @@ fn generate_unpin_impl(info: &StructInfo) -> TokenStream { .map(|x| &x.predicates) .unwrap_or(const { &Punctuated::new() }); + if info.self_referential { + // Self-referential structs must always be pinned. + return quote! { + #[doc(hidden)] + impl #impl_generics ::core::marker::Unpin for #ident #ty_generics + where + // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` + // feature . + for<'__dummy> ::core::marker::PhantomPinned: ::core::marker::Unpin, + #predicates + {} + }; + } + let pinned_fields = info.fields.iter().filter(|f| f.pinned).map(|f| { let ident = f.member.as_ident(); let ty = &f.field.ty; @@ -475,6 +493,7 @@ fn generate_unpin_impl(info: &StructInfo) -> TokenStream { #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`. diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 0a8247473cbc..74dc23506d97 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -5,6 +5,9 @@ //! These items must not be used outside of this crate and the pin-init-internal crate located at //! `../internal`. +use core::marker::PhantomPinned; +use core::ops::Deref; + use super::*; /// Zero-sized type used to mark a type as invariant. @@ -462,3 +465,19 @@ unsafe impl Sync for Erase for<'a> Erase<>::Output>: Sync, { } + +/// Wrapper for borrowed fields. +/// +/// This should be switched to `UnsafePinned` when it is stable. +/// NOTE: This type needs to be covariant; Rust's 1.89+'s `UnsafePinned` is invariant. +#[repr(transparent)] +pub struct Borrowed(PhantomPinned, T); + +impl Deref for Borrowed { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &T { + &self.1 + } +}