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 <lossin@kernel.org>
Link: https://patch.msgid.link/20261008-dev-selfref-v2-4-e280b3c8fba5@garyguo.net/
Signed-off-by: Gary Guo <gary@garyguo.net>
This commit is contained in:
Gary Guo 2026-09-22 21:42:34 +01:00
parent 9ad9425015
commit 73511e489b
2 changed files with 38 additions and 0 deletions

View File

@ -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 <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
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`.

View File

@ -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<F: EraseLt> Sync for Erase<F>
for<'a> Erase<<F as FnOutput<(&'a (),)>>::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<T: ?Sized>(PhantomPinned, T);
impl<T: ?Sized> Deref for Borrowed<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
&self.1
}
}