All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Gary Guo" <gary@garyguo.net>
To: "Benno Lossin" <lossin@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"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>,
	"Fiona Behrens" <me@Kloenk.dev>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Alban Kurti" <kurti@invicto.ai>
Cc: <linux-kernel@vger.kernel.org>, <rust-for-linux@vger.kernel.org>
Subject: Re: [PATCH 06/12] rust: pin-init: rewrite `#[pin_data]` using `syn`
Date: Fri, 09 Jan 2026 12:47:36 +0000	[thread overview]
Message-ID: <DFK2IZSN3G5Y.2ESC7WN213SYD@garyguo.net> (raw)
In-Reply-To: <20260108135127.3153925-7-lossin@kernel.org>

On Thu Jan 8, 2026 at 1:50 PM GMT, Benno Lossin wrote:
> Rewrite the attribute macro `#[pin_data]` using `syn`. No functional
> changes intended aside from improved error messages on syntactic and
> semantical errors. For example if one forgets a comma at the end of a
> field:
>
>     #[pin_data]
>     struct Foo {
>         a: Box<Foo>
>         b: Box<Foo>
>     }
>
> The declarative macro reports the following errors:
>
>     error: expected `,`, or `}`, found `b`
>      --> tests/ui/compile-fail/pin_data/missing_comma.rs:5:16
>       |
>     5 |     a: Box<Foo>
>       |                ^ help: try adding a comma: `,`
>
>     error: recursion limit reached while expanding `$crate::__pin_data!`
>      --> tests/ui/compile-fail/pin_data/missing_comma.rs:3:1
>       |
>     3 | #[pin_data]
>       | ^^^^^^^^^^^
>       |
>       = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`)
>       = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)
>
> The new `syn` version reports:
>
>     error: expected `,`, or `}`, found `b`
>      --> tests/ui/compile-fail/pin_data/missing_comma.rs:5:16
>       |
>     5 |     a: Box<Foo>
>       |                ^ help: try adding a comma: `,`
>
>     error: expected `,`
>      --> tests/ui/compile-fail/pin_data/missing_comma.rs:6:5
>       |
>     6 |     b: Box<Foo>
>       |     ^
>
> Signed-off-by: Benno Lossin <lossin@kernel.org>
> ---
>  rust/pin-init/internal/src/helpers.rs  | 149 ------
>  rust/pin-init/internal/src/lib.rs      |  12 +-
>  rust/pin-init/internal/src/pin_data.rs | 645 ++++++++++++++++++++-----
>  rust/pin-init/src/macros.rs            | 574 ----------------------
>  4 files changed, 543 insertions(+), 837 deletions(-)
>  delete mode 100644 rust/pin-init/internal/src/helpers.rs
>
> diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
> index 86a53b37cc66..d1e7ed121860 100644
> --- a/rust/pin-init/internal/src/pin_data.rs
> +++ b/rust/pin-init/internal/src/pin_data.rs
> @@ -1,126 +1,549 @@
>  // SPDX-License-Identifier: Apache-2.0 OR MIT
>  
> -use crate::helpers::{parse_generics, Generics};
> -use proc_macro2::{Group, Punct, Spacing, TokenStream, TokenTree};
> -use quote::quote;
> +use proc_macro2::TokenStream;
> +use quote::{format_ident, quote};
> +use syn::{
> +    parse::{End, Nothing, Parse},
> +    parse_quote, parse_quote_spanned,
> +    spanned::Spanned,
> +    visit_mut::VisitMut,
> +    Error, Field, Ident, Item, ItemStruct, PathSegment, Result, Type, TypePath, WhereClause,
> +};
>  
> -pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
> -    // This proc-macro only does some pre-parsing and then delegates the actual parsing to
> -    // `pin_init::__pin_data!`.
> +pub(crate) mod kw {
> +    syn::custom_keyword!(PinnedDrop);
> +}
> +
> +pub(crate) enum Args {
> +    Nothing(Nothing),
> +    #[allow(dead_code)]
> +    PinnedDrop(kw::PinnedDrop),
> +}
> +
> +impl Parse for Args {
> +    fn parse(input: syn::parse::ParseStream) -> Result<Self> {
> +        let lh = input.lookahead1();
> +        if lh.peek(End) {
> +            input.parse().map(Self::Nothing)

How about make this `impl Parse for Option<Args>` and remove the nothing
variant? It looks a bit weird.

> +        } else if lh.peek(kw::PinnedDrop) {
> +            let res = input.parse().map(Self::PinnedDrop)?;
> +            let lh = input.lookahead1();
> +            if lh.peek(End) {
> +                Ok(res)
> +            } else {
> +                Err(lh.error())
> +            }
> +        } else {
> +            Err(lh.error())
> +        }
> +    }
> +}
> +
> +pub(crate) fn pin_data(args: Args, input: Item) -> Result<TokenStream> {
> +    let mut struct_ = match input {
> +        Item::Struct(struct_) => struct_,
> +        Item::Enum(enum_) => {
> +            return Err(Error::new_spanned(
> +                enum_.enum_token,
> +                "`#[pin_data]` only supports structs for now",
> +            ));
> +        }
> +        Item::Union(union) => {
> +            return Err(Error::new_spanned(
> +                union.union_token,
> +                "`#[pin_data]` only supports structs for now",
> +            ));
> +        }
> +        rest => {
> +            return Err(Error::new_spanned(
> +                rest,
> +                "`#[pin_data]` can only be applied to struct, enum and union defintions",
> +            ));
> +        }
> +    };
> +
> +    // The generics might contain the `Self` type. Since this macro will define a new type with the
> +    // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed
> +    // to this struct definition. Therefore we have to replace `Self` with the concrete name.
> +    let mut replacer = {
> +        let name = &struct_.ident;
> +        let (_, ty_generics, _) = struct_.generics.split_for_impl();
> +        SelfReplacer(parse_quote!(#name #ty_generics))
> +    };
> +    replacer.visit_generics_mut(&mut struct_.generics);
> +    replacer.visit_fields_mut(&mut struct_.fields);
> +
> +    let mut error: Option<Error> = None;

I would probably just collect into a `Vec` and merge at the end.

> +    for field in &struct_.fields {
> +        if !is_field_structurally_pinned(field) && is_phantom_pinned(&field.ty) {
> +            let mut err = Error::new_spanned(
> +                field,
> +                format!(
> +                    "The field `{}` of type `PhantomData` only has an effect \

You mean PhantomPinned?

> +                if it has the `#[pin]` attribute",
> +                    field.ident.as_ref().expect(""),
> +                ),
> +            );
> +            if let Some(mut error) = error.take() {
> +                error.combine(err);
> +                err = error;
> +            }
> +            error = Some(err);
> +        }
> +    }
> +
> +    let unpin_impl = generate_unpin_impl(&struct_);
> +    let drop_impl = generate_drop_impl(&struct_, args);
> +    let projections = generate_projections(&struct_);
> +    let the_pin_data = generate_the_pin_data(&struct_);
> +
> +    strip_pin_annotations(&mut struct_);
> +
> +    let error = error.map(|e| e.into_compile_error());
> +
> +    Ok(quote! {
> +        #struct_
> +        #projections
> +        // We put the rest into this const item, because it then will not be accessible to anything
> +        // outside.
> +        const _: () = {
> +            #the_pin_data
> +            #unpin_impl
> +            #drop_impl
> +        };
> +        #error
> +    })
> +}
> +
> +fn is_phantom_pinned(ty: &Type) -> bool {
> +    match ty {
> +        Type::Path(TypePath { qself: None, path }) => {
> +            // Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user).
> +            if path.segments.len() > 3 {
> +                return false;
> +            }
> +            // If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or
> +            // `::std::marker::PhantomPinned`.
> +            if path.leading_colon.is_some() && path.segments.len() != 3 {
> +                return false;
> +            }
> +            let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
> +            for (actual, expected) in path.segments.iter().rev().zip(expected) {
> +                if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
> +                    return false;
> +                }
> +            }
> +            true
> +        }
> +        _ => false,
> +    }
> +}
> +
> +fn is_field_structurally_pinned(field: &Field) -> bool {
> +    field.attrs.iter().any(|a| a.path().is_ident("pin"))
> +}
>  
> +fn generate_unpin_impl(
> +    ItemStruct {
> +        ident,
> +        generics,
> +        fields,
> +        ..
> +    }: &ItemStruct,
> +) -> TokenStream {
> +    let generics_with_pinlt = {

Can you name this `generics_with_pin_lt`? Otherwise I read it as a misspelled
`pinit` :)

> +        let mut g = generics.clone();
> +        g.params.insert(0, parse_quote!('__pin));
> +        let _ = g.make_where_clause();
> +        g
> +    };
>      let (
> +        impl_generics_with_pinlt,
> +        ty_generics_with_pinlt,
> +        Some(WhereClause {
> +            where_token,
> +            predicates,
> +        }),
> +    ) = generics_with_pinlt.split_for_impl()
> +    else {
> +        unreachable!()
> +    };
> +    let (_, ty_generics, _) = generics.split_for_impl();
> +    let mut pinned_fields = fields
>          .iter()
> +        .filter(|f| is_field_structurally_pinned(f))
> +        .cloned()
> +        .collect::<Vec<_>>();
> +    for field in &mut pinned_fields {
> +        field.attrs.retain(|a| !a.path().is_ident("pin"));
> +    }
> +    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`.
> +        #[allow(dead_code)] // The fields below are never used.
> +        struct __Unpin #generics_with_pinlt
> +        #where_token
> +            #predicates
> +        {
> +            __phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
> +            __phantom: ::core::marker::PhantomData<
> +                fn(#ident #ty_generics) -> #ident #ty_generics
> +            >,
> +            #(#pinned_fields),*
> +        }
> +
> +        #[doc(hidden)]
> +        impl #impl_generics_with_pinlt ::core::marker::Unpin for #ident #ty_generics
> +        #where_token
> +            __Unpin #ty_generics_with_pinlt: ::core::marker::Unpin,
> +            #predicates
> +        {}
> +    }
> +}
> +
> +fn generate_drop_impl(
> +    ItemStruct {
> +        ident, generics, ..
> +    }: &syn::ItemStruct,
> +    args: Args,
> +) -> TokenStream {
> +    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
> +    let has_pinned_drop = matches!(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 {
> +        // When `PinnedDrop` was specified we just implement `Drop` and delegate.
> +        quote! {
> +            impl #impl_generics ::core::ops::Drop for #ident #ty_generics
> +                #whr
> +            {
> +                fn drop(&mut self) {
> +                    // SAFETY: Since this is a destructor, `self` will not move after this function
> +                    // terminates, since it is inaccessible.
> +                    let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
> +                    // SAFETY: Since this is a drop function, we can create this token to call the
> +                    // pinned destructor of this type.
> +                    let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
> +                    ::pin_init::PinnedDrop::drop(pinned, token);
>                  }
> -                Some(res)
>              }
> -            _ => None,
> -        })
> -        .unwrap_or_else(|| {
> -            // If we did not find the name of the struct then we will use `Self` as the replacement
> -            // and add a compile error to ensure it does not compile.
> -            errs.extend(
> -                "::core::compile_error!(\"Could not locate type name.\");"
> -                    .parse::<TokenStream>()
> -                    .unwrap(),
> -            );
> -            "Self".parse::<TokenStream>().unwrap().into_iter().collect()
> -        });
> -    let impl_generics = impl_generics
> -        .into_iter()
> -        .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &mut errs))
> -        .collect::<Vec<_>>();
> -    let mut rest = rest
> -        .into_iter()
> -        .flat_map(|tt| {
> -            // We ignore top level `struct` tokens, since they would emit a compile error.
> -            if matches!(&tt, TokenTree::Ident(i) if i == "struct") {
> -                vec![tt]
> +        }
> +    } else {
> +        // When no `PinnedDrop` was specified, then we have to prevent implementing drop.
> +        quote! {
> +            // We prevent this by creating a trait that will be implemented for all types implementing
> +            // `Drop`. Additionally we will implement this trait for the struct leading to a conflict,
> +            // if it also implements `Drop`
> +            trait MustNotImplDrop {}
> +            #[expect(drop_bounds)]
> +            impl<T: ::core::ops::Drop> MustNotImplDrop for T {}
> +            impl #impl_generics MustNotImplDrop for #ident #ty_generics
> +                #whr
> +            {}
> +            // We also take care to prevent users from writing a useless `PinnedDrop` implementation.
> +            // They might implement `PinnedDrop` correctly for the struct, but forget to give
> +            // `PinnedDrop` as the parameter to `#[pin_data]`.
> +            #[expect(non_camel_case_types)]
> +            trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
> +            impl<T: ::pin_init::PinnedDrop>
> +                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
> +            impl #impl_generics
> +                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
> +                #whr
> +            {}
> +        }
> +    }
> +}
> +
> +fn generate_projections(
> +    ItemStruct {
> +        vis,
> +        ident,
> +        generics,
> +        fields,
> +        ..
> +    }: &ItemStruct,
> +) -> TokenStream {
> +    let (og_impl_gen, og_ty_gen, _) = generics.split_for_impl();

Please make sure the names match the early generate_ function, so impl_generics,
ty_generics for the original ones, and _with_pin_lt for the ones with lifetime
added.

> +    let mut generics = generics.clone();
> +    generics.params.insert(0, parse_quote!('__pin));
> +    let (_, ty_gen, whr) = generics.split_for_impl();
> +    let projection = format_ident!("{ident}Projection");
> +    let this = format_ident!("this");
> +
> +    let (fields_decl, fields_proj) = collect_tuple(fields.iter().map(
> +        |f @ Field {
> +             vis,
> +             ident,
> +             ty,
> +             attrs,
> +             ..
> +         }| {
> +            let mut attrs = attrs.clone();
> +            attrs.retain(|a| !a.path().is_ident("pin"));
> +            let mut no_doc_attrs = attrs.clone();
> +            no_doc_attrs.retain(|a| !a.path().is_ident("doc"));
> +            let ident = ident
> +                .as_ref()
> +                .expect("only structs with named fields are supported");
> +            if is_field_structurally_pinned(f) {
> +                (
> +                    quote!(
> +                        #(#attrs)*
> +                        #vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
> +                    ),
> +                    quote!(
> +                        #(#no_doc_attrs)*
> +                        // SAFETY: this field is structurally pinned.
> +                        #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
> +                    ),
> +                )
>              } else {
> -                replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)
> +                (
> +                    quote!(
> +                        #(#attrs)*
> +                        #vis #ident: &'__pin mut #ty,
> +                    ),
> +                    quote!(
> +                        #(#no_doc_attrs)*
> +                        #ident: &mut #this.#ident,
> +                    ),
> +                )
>              }
> -        })
> -        .collect::<Vec<_>>();
> -    // This should be the body of the struct `{...}`.
> -    let last = rest.pop();
> -    let mut quoted = quote!(::pin_init::__pin_data! {
> -        parse_input:
> -        @args(#args),
> -        @sig(#(#rest)*),
> -        @impl_generics(#(#impl_generics)*),
> -        @ty_generics(#(#ty_generics)*),
> -        @decl_generics(#(#decl_generics)*),
> -        @body(#last),
> -    });
> -    quoted.extend(errs);
> -    quoted
> +        },
> +    ));
> +    let structurally_pinned_fields_docs = fields
> +        .iter()
> +        .filter(|f| is_field_structurally_pinned(f))
> +        .map(|Field { ident, .. }| {
> +            let doc = format!(" - `{}`", ident.as_ref().expect(""));

I'd just use `unwrap` over `expect("")`.

> +            quote!(#[doc = #doc])
> +        });
> +    let not_structurally_pinned_fields_docs = fields
> +        .iter()
> +        .filter(|f| !is_field_structurally_pinned(f))
> +        .map(|Field { ident, .. }| {
> +            let doc = format!(" - `{}`", ident.as_ref().expect(""));
> +            quote!(#[doc = #doc])
> +        });

Instead of building the `#[doc = ...]` streams for each field, I think we should just build the
docs entirely and insert in one go.

> +    let docs = format!(" Pin-projections of [`{ident}`]");
> +    quote! {
> +        #[doc = #docs]
> +        #[allow(dead_code)]
> +        #[doc(hidden)]
> +        #vis struct #projection #generics {
> +            #(#fields_decl)*
> +            ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
> +        }
> +
> +        impl #og_impl_gen #ident #og_ty_gen
> +            #whr
> +        {
> +            /// Pin-projects all fields of `Self`.
> +            ///
> +            /// These fields are structurally pinned:
> +            #(#structurally_pinned_fields_docs)*
> +            ///
> +            /// These fields are **not** structurally pinned:
> +            #(#not_structurally_pinned_fields_docs)*
> +            #[inline]
> +            #vis fn project<'__pin>(
> +                self: ::core::pin::Pin<&'__pin mut Self>,
> +            ) -> #projection #ty_gen {
> +                // SAFETY: we only give access to `&mut` for fields not structurally pinned.
> +                let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
> +                #projection {
> +                    #(#fields_proj)*
> +                    ___pin_phantom_data: ::core::marker::PhantomData,
> +                }
> +            }
> +        }
> +    }
>  }
>  
> +fn generate_the_pin_data(
> +    ItemStruct {
> +        generics,
> +        fields,
> +        ident,
> +        vis,
> +        ..
> +    }: &syn::ItemStruct,
> +) -> TokenStream {
> +    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, then it must be initialized via `PinInit`, if it is
> +    // not structurally pinned, then it can be initialized via `Init`.
> +    //
> +    // The functions are `unsafe` to prevent accidentally calling them.
> +    fn handle_field(
> +        Field {
> +            vis,
> +            ident,
> +            ty,
> +            attrs,
> +            ..
> +        }: &Field,
> +        struct_ident: &Ident,
> +        pinned: bool,
> +    ) -> TokenStream {
> +        let mut attrs = attrs.clone();
> +        attrs.retain(|a| !a.path().is_ident("pin"));
> +        let ident = ident
> +            .as_ref()
> +            .expect("only structs with named fields are supported");
> +        let project_ident = format_ident!("__project_{ident}");
> +        let (init_ty, init_fn, project_ty, project_body, pin_safety) = if 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!(
> +                    #[doc = " - `slot` will not move until it is dropped, i.e. it will be pinned."]

This can just be /// ...

> +                ),
> +            )
> +        } else {
> +            (
> +                quote!(Init),
> +                quote!(__init),
> +                quote!(&'__slot mut #ty),
> +                quote!(slot),
> +                quote!(),
> +            )
> +        };
> +        let slot_safety = format!(
> +            " `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.",
> +        );
> +        quote! {
> +            /// # Safety
> +            ///
> +            /// - `slot` is a valid pointer to uninitialized memory.
> +            /// - the caller does not touch `slot` when `Err` is returned, they are only permitted
> +            ///   to deallocate.
> +            #pin_safety
> +            #(#attrs)*
> +            #vis unsafe fn #ident<E>(
> +                self,
> +                slot: *mut #ty,
> +                init: impl ::pin_init::#init_ty<#ty, E>,
> +            ) -> ::core::result::Result<(), E> {
> +                // SAFETY: this function has the same safety requirements as the __init function
> +                // called below.
> +                unsafe { ::pin_init::#init_ty::#init_fn(init, slot) }
> +            }
> +
> +            /// # Safety
> +            ///
> +            #[doc = #slot_safety]
> +            #(#attrs)*
> +            #vis unsafe fn #project_ident<'__slot>(
> +                self,
> +                slot: &'__slot mut #ty,
> +            ) -> #project_ty {
> +                #project_body
> +            }
> +        }
> +    }
> +
> +    let field_accessors = fields
> +        .iter()
> +        .map(|f| handle_field(f, ident, is_field_structurally_pinned(f)))
> +        .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.
> +        #[doc(hidden)]
> +        #vis struct __ThePinData #generics
> +            #whr
>          {
> -            errs.extend(
> -                format!(
> -                    "::core::compile_error!(\"Cannot use `{i}` inside of struct definition with \
> -                        `#[pin_data]`.\");"
> -                )
> -                .parse::<TokenStream>()
> -                .unwrap()
> -                .into_iter()
> -                .map(|mut tok| {
> -                    tok.set_span(tt.span());
> -                    tok
> -                }),
> -            );
> -            vec![tt]
> -        }
> -        TokenTree::Ident(i) if i == "Self" => struct_name.clone(),
> -        TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt],
> -        TokenTree::Group(g) => vec![TokenTree::Group(Group::new(
> -            g.delimiter(),
> -            g.stream()
> -                .into_iter()
> -                .flat_map(|tt| replace_self_and_deny_type_defs(struct_name, tt, errs))
> -                .collect(),
> -        ))],
> +            __phantom: ::core::marker::PhantomData<
> +                fn(#ident #ty_generics) -> #ident #ty_generics
> +            >,
> +        }
> +
> +        impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
> +            #whr
> +        {
> +            fn clone(&self) -> Self { *self }
> +        }
> +
> +        impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
> +            #whr
> +        {}
> +
> +        #[allow(dead_code)] // Some functions might never be used and private.
> +        #[expect(clippy::missing_safety_doc)]
> +        impl #impl_generics __ThePinData #ty_generics
> +            #whr
> +        {
> +            #field_accessors
> +        }
> +
> +        // SAFETY: We have added the correct projection functions above to `__ThePinData` and
> +        // we also use the least restrictive generics possible.
> +        unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #ident #ty_generics
> +            #whr
> +        {
> +            type PinData = __ThePinData #ty_generics;
> +
> +            unsafe fn __pin_data() -> Self::PinData {
> +                __ThePinData { __phantom: ::core::marker::PhantomData }
> +            }
> +        }
> +
> +        // SAFETY: TODO
> +        unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics
> +            #whr
> +        {
> +            type Datee = #ident #ty_generics;
> +        }
> +    }
> +}
> +
> +fn strip_pin_annotations(struct_: &mut syn::ItemStruct) {
> +    for field in &mut struct_.fields {
> +        field.attrs.retain(|a| !a.path().is_ident("pin"));
> +    }
> +}

Multiple places have similar things for stripping annotations and checking if
structurally pinned. Would it make sense to do this at the very beginning, and
build a `HashSet` of structurally pinned fields, and use that as canonical
source for all generate_ functions?

Best,
Gary

> +
> +struct SelfReplacer(PathSegment);
> +
> +impl VisitMut for SelfReplacer {
> +    fn visit_path_mut(&mut self, i: &mut syn::Path) {
> +        if i.is_ident("Self") {
> +            let span = i.span();
> +            let seg = &self.0;
> +            *i = parse_quote_spanned!(span=> #seg);
> +        } else {
> +            syn::visit_mut::visit_path_mut(self, i);
> +        }
> +    }
> +
> +    fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) {
> +        if seg.ident == "Self" {
> +            let span = seg.span();
> +            let this = &self.0;
> +            *seg = parse_quote_spanned!(span=> #this);
> +        } else {
> +            syn::visit_mut::visit_path_segment_mut(self, seg);
> +        }
> +    }
> +
> +    fn visit_item_mut(&mut self, _: &mut Item) {
> +        // Do not descend into items, since items reset/change what `Self` refers to.
> +    }
> +}
> +
> +// replace with `.collect()` once MSRV is above 1.79
> +fn collect_tuple<A, B>(iter: impl Iterator<Item = (A, B)>) -> (Vec<A>, Vec<B>) {
> +    let mut res_a = vec![];
> +    let mut res_b = vec![];
> +    for (a, b) in iter {
> +        res_a.push(a);
> +        res_b.push(b);
>      }
> +    (res_a, res_b)
>  }


  parent reply	other threads:[~2026-01-09 12:47 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-08 13:50 [PATCH 00/13] `syn` rewrite of pin-init Benno Lossin
2026-01-08 13:50 ` [PATCH 01/12] rust: pin-init: remove `try_` versions of the initializer macros Benno Lossin
2026-01-08 13:50 ` [PATCH 02/12] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests Benno Lossin
2026-01-08 13:50 ` [PATCH 03/12] rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds Benno Lossin
2026-01-08 13:50 ` [PATCH 04/12] rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn` Benno Lossin
2026-01-09 12:02   ` Gary Guo
2026-01-08 13:50 ` [PATCH 05/12] rust: pin-init: rewrite the `#[pinned_drop]` attribute macro " Benno Lossin
2026-01-09 12:12   ` Gary Guo
2026-01-09 15:34     ` Benno Lossin
2026-01-09 16:42       ` Gary Guo
2026-01-08 13:50 ` [PATCH 06/12] rust: pin-init: rewrite `#[pin_data]` " Benno Lossin
2026-01-09  7:45   ` kernel test robot
2026-01-09 12:47   ` Gary Guo [this message]
2026-01-09 16:39     ` Benno Lossin
2026-01-09 16:46       ` Gary Guo
2026-01-10 16:41         ` Benno Lossin
2026-01-10 19:18           ` Gary Guo
2026-01-08 13:50 ` [PATCH 07/12] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Benno Lossin
2026-01-08 13:50 ` [PATCH 08/12] rust: pin-init: rewrite the initializer macros using `syn` Benno Lossin
2026-01-09  8:44   ` kernel test robot
2026-01-09 13:45   ` Gary Guo
2026-01-09 17:24     ` Benno Lossin
2026-01-10 16:21       ` Benno Lossin
2026-01-10 18:14     ` Benno Lossin
2026-01-10 19:20       ` Gary Guo
2026-01-10 23:18         ` Benno Lossin
2026-01-11  1:10           ` Gary Guo
2026-01-11 10:04             ` Benno Lossin
2026-01-08 13:50 ` [PATCH 09/12] rust: pin-init: add `#[default_error(<type>)]` attribute to initializer macros Benno Lossin
2026-01-09 13:52   ` Gary Guo
2026-01-08 13:50 ` [PATCH 10/12] rust: init: use `#[default_error(err)]` for the " Benno Lossin
2026-01-08 13:50 ` [PATCH 11/12] rust: pin-init: internal: init: add support for attributes on initializer fields Benno Lossin
2026-01-09 13:55   ` Gary Guo
2026-01-09 18:02     ` Benno Lossin
2026-01-09 21:16       ` Gary Guo
2026-01-08 13:50 ` [PATCH 12/12] rust: pin-init: internal: init: add escape hatch for referencing initialized fields Benno Lossin
2026-01-09  9:44   ` kernel test robot
2026-01-09 13:58   ` Gary Guo
2026-01-09 18:04     ` Benno Lossin
2026-01-08 13:50 ` [PATCH 13/13] MAINTAINERS: add Gary Guo to pin-init Benno Lossin

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=DFK2IZSN3G5Y.2ESC7WN213SYD@garyguo.net \
    --to=gary@garyguo.net \
    --cc=a.hindborg@kernel.org \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=kurti@invicto.ai \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=me@Kloenk.dev \
    --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.