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: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
Gary Guo <gary@garyguo.net>
Subject: [PATCH 8/8] rust: pin-init: internal: pin_data: parse into `StructInfo` before generating
Date: Tue, 06 Oct 2026 17:29:38 +0200 [thread overview]
Message-ID: <20261006-dev-selfref-dep-v1-8-da716f740d6d@garyguo.net> (raw)
In-Reply-To: <20261006-dev-selfref-dep-v1-0-da716f740d6d@garyguo.net>
Organize parsing and generation into two separate phases, with a single
`StructInfo` in the middle. This allows analysis and processing to happen
with a single struct and avoid having to pass different information into
the token stream generation functions.
A drawback of this approach is that as we consume the `Fields`, we cannot
expand the original struct using `quote!(#struct_)` anymore, and the field
needs to be re-constructed in `generate_struct_def` function. However, this
is only a temporary drawback, as the upcoming pin-init self-referential
feature will require modification of the generated struct anyway.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/pin-init/internal/src/pin_data.rs | 162 +++++++++++++++++++++++----------
1 file changed, 112 insertions(+), 50 deletions(-)
diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 6e18bc9e890a..03e893cf5475 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -8,8 +8,7 @@
punctuated::Punctuated,
spanned::Spanned,
visit_mut::VisitMut,
- Field, Fields, Generics, Ident, Index, Item, ItemStruct, Member, PathSegment, Type, TypePath,
- Visibility,
+ Field, Fields, Generics, Index, Item, ItemStruct, Member, PathSegment, Type, TypePath,
};
use crate::{
@@ -49,12 +48,19 @@ fn to_tokens(&self, tokens: &mut TokenStream) {
}
}
-struct FieldInfo<'a> {
- field: &'a Field,
+struct FieldInfo {
+ field: Field,
member: Member,
pinned: bool,
}
+struct StructInfo {
+ args: Args,
+ struct_: ItemStruct,
+ fields: Vec<FieldInfo>,
+ is_tuple_struct: bool,
+}
+
pub(crate) fn expand_with_cfg(
args: Args,
input: Item,
@@ -141,11 +147,11 @@ fn expand(
replacer.visit_fields_mut(&mut struct_.fields);
let is_tuple_struct = matches!(struct_.fields, Fields::Unnamed(_));
- let fields: Vec<FieldInfo<'_>> = struct_
+ let fields: Vec<FieldInfo> = struct_
.fields
- .iter_mut()
+ .into_iter()
.enumerate()
- .map(|(index, field)| {
+ .map(|(index, mut field)| {
let pinned = field.attrs.extract_single_attr(dcx, "pin").is_some();
assert!(
@@ -161,17 +167,25 @@ fn expand(
};
FieldInfo {
- field: &*field,
+ field,
member,
pinned,
}
})
.collect();
- for field in &fields {
+ struct_.fields = Fields::Unit;
+ let info = StructInfo {
+ args,
+ struct_,
+ fields,
+ is_tuple_struct,
+ };
+
+ for field in &info.fields {
if !field.pinned && is_phantom_pinned(&field.field.ty) {
dcx.warn(
- field.field,
+ &field.field,
format!(
"The field {} of type `PhantomPinned` only has an effect \
if it has the `#[pin]` attribute",
@@ -181,20 +195,14 @@ fn expand(
}
}
- let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
- let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
- let projections = generate_projections(
- &struct_.vis,
- &struct_.ident,
- &struct_.generics,
- is_tuple_struct,
- &fields,
- );
- let the_pin_data =
- generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
+ let struct_def = generate_struct_def(&info);
+ let unpin_impl = generate_unpin_impl(&info);
+ let drop_impl = generate_drop_impl(&info);
+ let projections = generate_projections(&info);
+ let the_pin_data = generate_the_pin_data(&info);
Ok(quote! {
- #struct_
+ #struct_def
// We put the rest into this const item, because it then will not be accessible to anything
// outside.
const _: () = {
@@ -230,17 +238,63 @@ fn is_phantom_pinned(ty: &Type) -> bool {
}
}
-fn generate_unpin_impl(
- ident: &Ident,
- generics: &Generics,
- fields: &[FieldInfo<'_>],
-) -> TokenStream {
+fn generate_struct_def(info: &StructInfo) -> TokenStream {
+ let ItemStruct {
+ attrs,
+ vis,
+ struct_token,
+ ident,
+ generics,
+ fields: _,
+ semi_token,
+ } = &info.struct_;
+
+ let generated_fields = info.fields.iter().map(|field| {
+ let Field {
+ attrs,
+ vis,
+ mutability: _,
+ ident,
+ colon_token,
+ ty,
+ } = &field.field;
+
+ quote! {
+ #(#attrs)* #vis #ident #colon_token #ty
+ }
+ });
+
+ let whr = &generics.where_clause;
+
+ if info.is_tuple_struct {
+ quote!(
+ #(#attrs)*
+ #vis
+ #struct_token #ident #generics (#(#generated_fields,)*) #whr
+ #semi_token
+ )
+ } else {
+ quote!(
+ #(#attrs)*
+ #vis
+ #struct_token #ident #generics #whr {
+ #(#generated_fields,)*
+ }
+ #semi_token
+ )
+ }
+}
+
+fn generate_unpin_impl(info: &StructInfo) -> TokenStream {
+ let ItemStruct {
+ generics, ident, ..
+ } = &info.struct_;
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
let predicates = whr
.map(|x| &x.predicates)
.unwrap_or(const { &Punctuated::new() });
- let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {
+ let pinned_fields = info.fields.iter().filter(|f| f.pinned).map(|f| {
let ident = f.member.as_ident();
let ty = &f.field.ty;
quote!(
@@ -271,9 +325,12 @@ impl #impl_generics ::core::marker::Unpin for #ident #ty_generics
}
}
-fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {
+fn generate_drop_impl(info: &StructInfo) -> TokenStream {
+ let ItemStruct {
+ generics, ident, ..
+ } = &info.struct_;
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
- let has_pinned_drop = matches!(args, Args::PinnedDrop(_));
+ let has_pinned_drop = matches!(info.args, Args::PinnedDrop(_));
// We need to disallow normal `Drop` implementation, the exact behavior depends on whether
// `PinnedDrop` was specified in `args`.
if has_pinned_drop {
@@ -318,13 +375,13 @@ impl #impl_generics
}
}
-fn generate_projections(
- vis: &Visibility,
- ident: &Ident,
- generics: &Generics,
- is_tuple_struct: bool,
- fields: &[FieldInfo<'_>],
-) -> TokenStream {
+fn generate_projections(info: &StructInfo) -> TokenStream {
+ let ItemStruct {
+ vis,
+ ident,
+ generics,
+ ..
+ } = &info.struct_;
let this_lt_generics: Generics = parse_quote!(<'__this>);
let generics_with_this_lt = CombinedGenerics(vec![&this_lt_generics, generics]);
@@ -333,14 +390,15 @@ fn generate_projections(
let this = format_ident!("this");
- let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields
+ let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = info
+ .fields
.iter()
.map(|field| {
let Field { vis, ty, .. } = &field.field;
let member = &field.member;
// The projection of a tuple struct is a tuple struct itself, so its fields are
// positional and must not be named.
- let name = (!is_tuple_struct).then(|| {
+ let name = (!info.is_tuple_struct).then(|| {
let ident = field.member.as_ident();
quote!(#ident:)
});
@@ -367,16 +425,18 @@ fn generate_projections(
}
})
.collect();
- let structurally_pinned_fields_docs = fields
+ let structurally_pinned_fields_docs = info
+ .fields
.iter()
.filter(|f| f.pinned)
.map(|f| format!(" - {}", f.member.display_name()));
- let not_structurally_pinned_fields_docs = fields
+ let not_structurally_pinned_fields_docs = info
+ .fields
.iter()
.filter(|f| !f.pinned)
.map(|f| format!(" - {}", f.member.display_name()));
let docs = format!(" Pin-projections of [`{ident}`]");
- let (projection_def, projection_init) = if is_tuple_struct {
+ let (projection_def, projection_init) = if info.is_tuple_struct {
(
quote! {
#vis struct __Projection #generics_with_this_lt (
@@ -439,22 +499,24 @@ impl #impl_generics #ident #ty_generics
}
}
-fn generate_the_pin_data(
- vis: &Visibility,
- struct_name: &Ident,
- generics: &Generics,
- fields: &[FieldInfo<'_>],
-) -> TokenStream {
+fn generate_the_pin_data(info: &StructInfo) -> TokenStream {
+ let ItemStruct {
+ vis,
+ ident: struct_name,
+ generics,
+ ..
+ } = &info.struct_;
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, 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
+ let field_accessors = info
+ .fields
.iter()
.map(|f| {
- let Field { vis, ty, .. } = f.field;
+ let Field { vis, ty, .. } = &f.field;
let field_name = f.member.as_ident();
let member = &f.member;
let pin_marker = if f.pinned {
--
2.54.0
prev parent reply other threads:[~2026-10-06 15:30 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 15:29 [PATCH 0/8] rust: pin-init: various cleanups to prepare for self-reference Gary Guo
2026-10-06 15:29 ` [PATCH 1/8] rust: pin-init: internal: init: forget guards in reverse order Gary Guo
2026-10-06 15:29 ` [PATCH 2/8] rust: pin-init: internal: init: set span location of guards Gary Guo
2026-10-06 15:29 ` [PATCH 3/8] rust: pin-init: internal: add utility to handle generics merging Gary Guo
2026-10-06 15:29 ` [PATCH 4/8] rust: pin-init: internal: add utility for extract a single attribute Gary Guo
2026-10-06 15:29 ` [PATCH 5/8] rust: pin-init: internal: pin_data: move projection struct into `const _: ()` block Gary Guo
2026-10-06 15:29 ` [PATCH 6/8] rust: pin-init: internal: pin_data: extract `#[cfg]` processing to its own function Gary Guo
2026-10-06 15:29 ` [PATCH 7/8] rust: pin-init: internal: pin_data: rename `'__pin` lifetime to `'__this` Gary Guo
2026-10-06 15:29 ` Gary Guo [this message]
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=20261006-dev-selfref-dep-v1-8-da716f740d6d@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®