lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <DFK2IZSN3G5Y.2ESC7WN213SYD@garyguo.net>
Date: Fri, 09 Jan 2026 12:47:36 +0000
From: "Gary Guo" <gary@...yguo.net>
To: "Benno Lossin" <lossin@...nel.org>, "Gary Guo" <gary@...yguo.net>,
 "Miguel Ojeda" <ojeda@...nel.org>, "Boqun Feng" <boqun.feng@...il.com>,
 Björn Roy Baron <bjorn3_gh@...tonmail.com>, "Andreas
 Hindborg" <a.hindborg@...nel.org>, "Alice Ryhl" <aliceryhl@...gle.com>,
 "Trevor Gross" <tmgross@...ch.edu>, "Danilo Krummrich" <dakr@...nel.org>,
 "Fiona Behrens" <me@...enk.dev>, "Greg Kroah-Hartman"
 <gregkh@...uxfoundation.org>, "Alban Kurti" <kurti@...icto.ai>
Cc: <linux-kernel@...r.kernel.org>, <rust-for-linux@...r.kernel.org>
Subject: Re: [PATCH 06/12] rust: pin-init: rewrite `#[pin_data]` using `syn`

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@...nel.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)
>  }


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ