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>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
Gary Guo <gary@garyguo.net>
Subject: [PATCH v2 08/20] rust: pin-init: internal: pin_data: implement initialization of borrowed structs
Date: Thu, 08 Oct 2026 20:24:32 +0100 [thread overview]
Message-ID: <20261008-dev-selfref-v2-8-e280b3c8fba5@garyguo.net> (raw)
In-Reply-To: <20261008-dev-selfref-v2-0-e280b3c8fba5@garyguo.net>
We now have the checks to ensure that lifetime relations are what is
expected, we can generate the slot projections in `generate_pin_data` so
self-referential struct can be implemented.
New slot and guard types are defined (`SelfRefSlot` and `SelfRefDropGuard`)
which gives the generated let bindings longer lifetime than the guard
themselves.
Have `__make_closure` take `data` back as an argument. This gives
`#[pin_data]` an opportunity to change the type to add lifetimes.
Higher-ranked trait bound on `__make_closure` is used to ensure that the
initialization closure cannot make arbitrary assumptions of those
lifetimes.
Acked-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/pin-init/internal/src/init.rs | 42 ++++++--
rust/pin-init/internal/src/pin_data.rs | 114 +++++++++++++++++++---
rust/pin-init/src/__internal.rs | 169 ++++++++++++++++++++++++++++++++-
rust/pin-init/src/lib.rs | 1 +
4 files changed, 305 insertions(+), 21 deletions(-)
diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index 80e4dc068d2e..ded2b5283376 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/internal/src/init.rs
@@ -288,8 +288,10 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
},
};
// `mixed_site` ensures that the data is not accessible to the user-controlled code.
- let init_fields = init_fields(&fields, pinned);
+ let init_fields = make_field_init(&fields, pinned, false);
+ let drop_check = make_field_init(&fields, pinned, true);
let field_check = make_field_check(&fields, init_kind, &path);
+
Ok(quote_spanned! { Span::mixed_site() => {
// Get the data about fields from the supplied type.
let data = {
@@ -303,17 +305,29 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
// Ensure that `data` really is of type `data` and help with type inference:
let init = data.__make_closure::<_, #error>(
- move |slot| {
+ move |slot, data_lt| {
#zeroable_check
#this
- #init_fields
+ // Generate init twice, which is mostly identical except for lifetimes.
+ if true {
+ // In this path, we use the field lifetime from HRTB to prevent environment
+ // lifetime from entering the fields, and to ensure that the dependency of
+ // fields is consistent with the field drop of the struct.
+ #init_fields
+ } else {
+ // In this path, we use local lifetime, to make sure that if initialization
+ // fails, the destructor execution will not cause lifetime issues. This is
+ // separate as implied bounds between field lifetimes can be inconsistent with
+ // that of the drop.
+ #drop_check
+ }
#field_check
// SAFETY: we are the `init!` macro that is allowed to call this.
Ok(unsafe { ::pin_init::__internal::InitOk::new() })
}
);
let init = move |slot| -> ::core::result::Result<(), #error> {
- init(slot).map(|__InitOk| ())
+ init(slot, data.__with_lt()).map(|__InitOk| ())
};
// SAFETY: TODO
unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }
@@ -360,7 +374,11 @@ fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKi
}
/// Generate the code that initializes the fields of the struct using the initializers in `field`.
-fn init_fields(fields: &Punctuated<InitializerField, Token![,]>, pinned: bool) -> TokenStream {
+fn make_field_init(
+ fields: &Punctuated<InitializerField, Token![,]>,
+ pinned: bool,
+ dropck: bool,
+) -> TokenStream {
let mut forget_guards = vec![];
let mut res = TokenStream::new();
for InitializerField { attrs, kind } in fields {
@@ -388,13 +406,18 @@ fn init_fields(fields: &Punctuated<InitializerField, Token![,]>, pinned: bool) -
let span = Span::mixed_site().located_at(ident.span());
let slot = if pinned {
+ let data = if !dropck {
+ quote_spanned!(span => data_lt)
+ } else {
+ quote_spanned!(span => data.__with_lt())
+ };
quote_spanned! { span =>
// SAFETY:
// - `slot` is valid and properly aligned.
// - `make_field_check` checks that `&raw mut (*slot).#member` is properly aligned.
// - `make_field_check` prevents `#member` from being used twice, therefore
// `(*slot).#member` is exclusively accessed and has not been initialized.
- (unsafe { data.#ident(slot) })
+ (unsafe { #data.#ident(slot) })
}
} else {
quote_spanned! { span =>
@@ -455,6 +478,11 @@ fn init_fields(fields: &Punctuated<InitializerField, Token![,]>, pinned: bool) -
// A tuple field has no name that could be bound here (the `_0` identifiers are considered
// implementation detail and not user-facing).
+ let let_binding_method = if !dropck {
+ format_ident!("let_binding", span = span)
+ } else {
+ format_ident!("let_binding_in_dropck", span = span)
+ };
let binding = match member {
Member::Named(ident) => quote_spanned! { span =>
#(#cfgs)*
@@ -462,7 +490,7 @@ fn init_fields(fields: &Punctuated<InitializerField, Token![,]>, pinned: bool) -
// struct field.
#[allow(unused_variables, non_snake_case)]
// Include `mut` so that `Pin<&mut T>` bindings can be reborrowed via `.as_mut()`.
- let mut #ident = #guard.let_binding();
+ let mut #ident = #guard.#let_binding_method();
},
Member::Unnamed(_) => quote!(),
};
diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 6a29ec358c30..307d9b36078c 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -61,7 +61,6 @@ enum BorrowedKind {
}
/// Information about a borrowed field.
-#[expect(unused)]
struct BorrowedInfo {
kind: BorrowedKind,
/// Field lifetime for this field.
@@ -974,12 +973,36 @@ fn generate_the_pin_data(info: &StructInfo) -> TokenStream {
generics,
..
} = &info.struct_;
+
+ // Wrap in `CombinedGenerics` because it's ty generics will always output `<>`, so it can be
+ // used with `for`.
+ let field_lts = CombinedGenerics(vec![&info.field_lts]);
+ let generics_with_field_lt = CombinedGenerics(vec![&info.field_lts, generics]);
+
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
+ let (_, field_lt_ty_generics, _) = field_lts.split_for_impl();
+ let (impl_generics_with_lt, ty_generics_with_field_lt, whr_with_field_lt) =
+ generics_with_field_lt.split_for_impl();
+
+ // Wrap each field in a `PhantomInvariant`. For borrowed fields, additionally
+ // use `&#lt mut #ty` so the `lt` becomes associated with `#ty` which deduces
+ // implied bounds.
+ let phantom_fields = info.fields.iter().map(|f| {
+ let ty = &f.field.ty;
+ let ident = f.member.as_ident();
+
+ if let Some(borrowed) = &f.borrowed {
+ let lt = &borrowed.lifetime;
+ quote!(
+ #ident: ::pin_init::__internal::PhantomInvariant<&#lt mut #ty>,
+ )
+ } else {
+ quote!(
+ #ident: ::pin_init::__internal::PhantomInvariant<#ty>,
+ )
+ }
+ });
- // For every field, we create an initializing projection function according to its projection
- // 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 = info
.fields
.iter()
@@ -992,6 +1015,30 @@ fn generate_the_pin_data(info: &StructInfo) -> TokenStream {
} else {
quote!(Unpinned)
};
+
+ let (slot_ty, slot_arg) = match &f.borrowed {
+ None => (quote!(Slot), quote!()),
+ Some(BorrowedInfo {
+ kind: BorrowedKind::Shared,
+ lifetime,
+ }) => (
+ // For borrowed fields, create a `SelfRefSlot`, which after initialization
+ // turns into a `SelfRefDropGuard` instead of `DropGuard`.
+ //
+ // They're mostly the same, except that `SelfRefDropGuard` returns `&'field T`
+ // instead of `&'guard T` for let bindings; this allows it to be used to be
+ // used to initialize other fields.
+ //
+ // The soundness of doing so relies on fact that `__make_init` requires a
+ // higher-ranked trait bound on the closure. Within the closure (which is the
+ // caller of the generated slot projection functions here), it can make no
+ // assumptions on the lifetime except for those implied by the struct's bounds,
+ // and we have validated them in `generate_drop_check`.
+ quote!(SelfRefSlot),
+ quote!(#lifetime,),
+ ),
+ };
+
quote! {
/// # Safety
///
@@ -1006,19 +1053,54 @@ fn generate_the_pin_data(info: &StructInfo) -> TokenStream {
#vis unsafe fn #field_name(
self,
slot: *mut #struct_name #ty_generics,
- ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {
+ ) -> ::pin_init::__internal::#slot_ty<
+ #slot_arg ::pin_init::__internal::#pin_marker, #ty
+ > {
+ // CAST: `as _` is needed to convert types wrapped inside `SelfRef`.
// 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(&raw mut (*slot).#member) }
+ // - If `#slot_ty` is `SelfRefSlot`, the lifetime `#lt` represents that of the
+ // field.
+ unsafe { ::pin_init::__internal::#slot_ty::new(&raw mut (*slot).#member as _) }
}
}
})
.collect::<TokenStream>();
+
quote! {
- // We declare this struct which will host all of the projection function for our type. It
- // will be invariant over all generic parameters which are inherited from the struct.
+ // We declare this struct which will host all of the projection function for our type.
+ #[doc(hidden)]
+ #[allow(non_snake_case)]
+ #vis struct __PinDataLt #generics_with_field_lt
+ #whr_with_field_lt
+ {
+ #(#phantom_fields)*
+ __pin_phantom: ::core::marker::PhantomData<#struct_name #ty_generics>,
+ }
+
+ impl #impl_generics_with_lt ::core::clone::Clone for __PinDataLt #ty_generics_with_field_lt
+ #whr_with_field_lt
+ {
+ fn clone(&self) -> Self { *self }
+ }
+
+ impl #impl_generics_with_lt ::core::marker::Copy for __PinDataLt #ty_generics_with_field_lt
+ #whr_with_field_lt
+ {}
+
+ #[allow(dead_code)] // Some functions might never be used and private.
+ #[expect(clippy::missing_safety_doc)]
+ impl #impl_generics_with_lt __PinDataLt #ty_generics_with_field_lt
+ #whr_with_field_lt
+ {
+ #field_accessors
+ }
+
+ // Declare a type that serves as the entry point of interaction with the `pin_init!` macro.
+ // We use this type instead of defining methods directly on user's type to avoid possibility
+ // of name conflicts.
#[doc(hidden)]
#vis struct __ThePinData #generics
#whr
@@ -1037,7 +1119,6 @@ impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
#whr
{}
- #[allow(dead_code)] // Some functions might never be used and private.
impl #impl_generics __ThePinData #ty_generics
#whr
{
@@ -1045,13 +1126,20 @@ impl #impl_generics __ThePinData #ty_generics
#[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: for #field_lt_ty_generics ::core::ops::FnOnce(
+ *mut #struct_name #ty_generics,
+ __PinDataLt #ty_generics_with_field_lt
+ ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>,
{
f
}
- #field_accessors
+ #[inline(always)]
+ #vis fn __with_lt #field_lts(self) -> __PinDataLt #ty_generics_with_field_lt {
+ // Generate a zeroed to avoid naming all fields.
+ // SAFETY: `__PinDataLt` only contains phantom fields.
+ unsafe { ::core::mem::zeroed() }
+ }
}
// SAFETY: We have added the correct projection functions above to `__ThePinData` and
diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs
index 32bd6d8c39a1..df079e68ec1e 100644
--- a/rust/pin-init/src/__internal.rs
+++ b/rust/pin-init/src/__internal.rs
@@ -109,10 +109,15 @@ impl<T: ?Sized> InitData<T> {
#[inline(always)]
pub fn __make_closure<F, E>(self, f: F) -> F
where
- F: FnOnce(*mut T) -> Result<InitOk, E>,
+ F: FnOnce(*mut T, Self) -> Result<InitOk, E>,
{
f
}
+
+ #[inline(always)]
+ pub fn __with_lt(self) -> Self {
+ self
+ }
}
/// Stack initializer helper type. Use [`stack_pin_init`] instead of this primitive.
@@ -322,6 +327,12 @@ pub fn let_binding(&mut self) -> &mut T {
// SAFETY: Per type invariant.
unsafe { &mut *self.ptr }
}
+
+ /// Create a let binding for accessor use in dropck.
+ #[inline]
+ pub fn let_binding_in_dropck(&mut self) -> &mut T {
+ self.let_binding()
+ }
}
impl<T: ?Sized> DropGuard<Pinned, T> {
@@ -332,6 +343,12 @@ pub fn let_binding(&mut self) -> Pin<&mut T> {
// pinned per type invariant.
unsafe { Pin::new_unchecked(&mut *self.ptr) }
}
+
+ /// Create a let binding for accessor use in dropck.
+ #[inline]
+ pub fn let_binding_in_dropck(&mut self) -> Pin<&mut T> {
+ self.let_binding()
+ }
}
impl<P, T: ?Sized> Drop for DropGuard<P, T> {
@@ -342,6 +359,156 @@ fn drop(&mut self) {
}
}
+/// Represent an uninitialized field in a pinned struct that will be referenced by other fields.
+///
+/// # Invariants
+///
+/// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed memory
+/// and will live longer than `'a`.
+/// - If `P` is `Pinned`, then `ptr` is structurally pinned.
+pub struct SelfRefSlot<'a, P, T: ?Sized> {
+ pub ptr: *mut T,
+ pub _phantom: PhantomData<(P, &'a mut T)>,
+}
+
+impl<'a, P, T: ?Sized> SelfRefSlot<'a, P, T> {
+ /// # Safety
+ ///
+ /// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed
+ /// memory and will live longer than `'a`.
+ /// - If `P` is `Pinned`, then `ptr` is structurally pinned.
+ #[inline]
+ pub unsafe fn new(ptr: *mut T) -> Self {
+ // INVARIANT: Per safety requirement.
+ Self {
+ ptr,
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Initialize the field by value.
+ #[inline]
+ pub fn write(self, value: T) -> SelfRefDropGuard<'a, 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, properly aligned and live longer than `'a` 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 { SelfRefDropGuard::new(self.ptr) }
+ }
+}
+
+impl<'a, T: ?Sized> SelfRefSlot<'a, Unpinned, T> {
+ /// Initialize the field.
+ #[inline]
+ pub fn init<E>(self, init: impl Init<T, E>) -> Result<SelfRefDropGuard<'a, 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, properly aligned and live longer than `'a` per type invariant.
+ // - `*self.ptr` is initialized above and the ownership is transferred to the guard.
+ Ok(unsafe { SelfRefDropGuard::new(self.ptr) })
+ }
+}
+
+impl<'a, T: ?Sized> SelfRefSlot<'a, Pinned, T> {
+ /// Initialize the field.
+ #[inline]
+ pub fn init<E>(self, init: impl PinInit<T, E>) -> Result<SelfRefDropGuard<'a, Pinned, T>, E> {
+ // SAFETY:
+ // - `ptr` is valid
+ // - 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` (but only `Pin<&mut T>`) it has been dropped.
+ unsafe { init.__init(self.ptr)? };
+
+ // SAFETY:
+ // - `self.ptr` is valid, properly aligned and live longer than `'a` per type invariant.
+ // - `*self.ptr` is initialized above and the ownership is transferred to the guard.
+ Ok(unsafe { SelfRefDropGuard::new(self.ptr) })
+ }
+}
+/// When a value of this type is dropped, it drops a `T`.
+///
+/// Can be forgotten to prevent the drop.
+///
+/// # Invariants
+///
+/// - `ptr` is valid, properly aligned and live longer than `'a`.
+/// - `*ptr` is initialized and owned by this guard.
+/// - if `P` is `Pinned`, `ptr` is pinned.
+pub struct SelfRefDropGuard<'a, P, T: ?Sized> {
+ ptr: *mut T,
+ phantom: PhantomData<(P, &'a mut T)>,
+}
+
+impl<'a, P, T: ?Sized> SelfRefDropGuard<'a, P, T> {
+ /// Creates a drop guard and transfer the ownership of the pointer content.
+ ///
+ /// The ownership is only relinquished if the guard is forgotten via [`core::mem::forget`].
+ ///
+ /// # Safety
+ ///
+ /// - `ptr` is valid, properly aligned and live longer than `'a`.
+ /// - `*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,
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl<'a, T: ?Sized> SelfRefDropGuard<'a, Unpinned, T> {
+ /// Create a let binding for accessor use.
+ #[inline]
+ pub fn let_binding(&mut self) -> &'a T {
+ // SAFETY: Per type invariant.
+ unsafe { &*self.ptr }
+ }
+
+ /// Create a let binding for accessor use in dropck.
+ #[inline]
+ pub fn let_binding_in_dropck(&mut self) -> &T {
+ self.let_binding()
+ }
+}
+
+impl<'a, T: ?Sized> SelfRefDropGuard<'a, Pinned, T> {
+ /// Create a let binding for accessor use.
+ #[inline]
+ pub fn let_binding(&mut self) -> Pin<&'a T> {
+ // SAFETY: `self.ptr` is valid, properly aligned, live longer than `'a`, initialized,
+ // exclusively accessible and pinned per type invariant.
+ unsafe { Pin::new_unchecked(&*self.ptr) }
+ }
+
+ /// Create a let binding for accessor use in dropck.
+ #[inline]
+ pub fn let_binding_in_dropck(&mut self) -> Pin<&T> {
+ self.let_binding()
+ }
+}
+
+impl<P, T: ?Sized> Drop for SelfRefDropGuard<'_, P, T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard.
+ unsafe { ptr::drop_in_place(self.ptr) }
+ }
+}
+
/// Token used by `PinnedDrop` to prevent calling the function without creating this unsafely
/// created struct. This is needed, because the `drop` function is safe, but should not be called
/// manually.
diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index d1ed0561bb27..9ffa76434310 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -923,6 +923,7 @@ macro_rules! assert_pinned {
let data = <$ty as $crate::__internal::HasInitData>::__init_data();
let data = $crate::__internal::HasPinData::__pin_data(data);
_ = data
+ .__with_lt()
.$field(ptr)
.init($crate::__internal::AlwaysFail::<$field_ty>::new());
};
--
2.54.0
next prev parent reply other threads:[~2026-10-08 19:27 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-08 19:24 [PATCH v2 00/20] rust: pin-init: create self references safely Gary Guo
2026-10-08 19:24 ` [PATCH v2 01/20] kbuild: rust: allow `clippy::comparison_chain` globally Gary Guo
2026-10-11 9:40 ` Miguel Ojeda
2026-10-08 19:24 ` [PATCH v2 02/20] rust: pin-init: internal: pin_data: infer self-referential struct Gary Guo
2026-10-08 19:24 ` [PATCH v2 03/20] rust: pin-init: internal: pin_data: rewrite fields that borrow others Gary Guo
2026-10-08 19:24 ` [PATCH v2 04/20] rust: pin-init: internal: pin_data: pin borrowed fields with wrapper Gary Guo
2026-10-08 19:24 ` [PATCH v2 05/20] rust: pin-init: internal: pin_data: teach drop check about generics that cannot dangle Gary Guo
2026-10-08 19:24 ` [PATCH v2 06/20] rust: pin-init: internal: pin_data: self-referential drop order checks Gary Guo
2026-10-08 19:24 ` [PATCH v2 07/20] rust: pin-init: internal: pin_data: check covariance of self-referential fields Gary Guo
2026-10-08 19:24 ` Gary Guo [this message]
2026-10-08 19:24 ` [PATCH v2 09/20] rust: pin-init: internal: pin_data: project " Gary Guo
2026-10-08 19:24 ` [PATCH v2 10/20] rust: pin-init: internal: pin_data: add `with_project` method Gary Guo
2026-10-08 19:24 ` [PATCH v2 11/20] rust: pin-init: internal: pin_data: enable self-referential support Gary Guo
2026-10-08 19:24 ` [PATCH v2 12/20] rust: pin-init: internal: pin_data: allow lifetime to be shortened per field drop order Gary Guo
2026-10-08 19:24 ` [PATCH v2 13/20] rust: pin-init: internal: pin_data: parse explicit `#[borrowed]` annotation Gary Guo
2026-10-08 19:24 ` [PATCH v2 14/20] rust: pin-init: internal: pin_data: support mutable borrows Gary Guo
2026-10-08 19:24 ` [PATCH v2 15/20] rust: pin-init: internal: pin_data: parse explicit `#[uses]` annotation Gary Guo
2026-10-08 19:24 ` [PATCH v2 16/20] rust: pin-init: internal: pin_data: make field lifetime invariance imply type invariance Gary Guo
2026-10-08 19:24 ` [PATCH v2 17/20] rust: pin-init: internal: pin_data: complete invariant borrow support Gary Guo
2026-10-08 19:24 ` [PATCH v2 18/20] rust: pin-init: internal: pin_data: perform AST lifetime replacement if possible Gary Guo
2026-10-08 19:24 ` [PATCH v2 19/20] rust: pin-init: internal: pin_data: support shared projection Gary Guo
2026-10-08 19:24 ` [PATCH v2 20/20] rust: pin-init: internal: pin_data: support existential lifetimes Gary Guo
2026-10-11 10:37 ` [PATCH v2 00/20] rust: pin-init: create self references safely 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=20261008-dev-selfref-v2-8-e280b3c8fba5@garyguo.net \
--to=gary@garyguo.net \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/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.