From nobody Sun Feb 8 23:24:46 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2BEE4335065; Wed, 14 Jan 2026 18:20:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768414835; cv=none; b=CngZTWC7fEgBOE4aKlX3HGEDk64d8Dw6pybirhqzMbH2HDetMTwZ/9P9PDsUKakrPvtAgZPT/IMHMxW1+vNz1fFb4lOm5QEHy1ryaT91eEeVTo9kinY3kvv4A0wcWb1AidfMsbBW6HwC47CcEILyUvSAeU6LhErys0DIRS62AI0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768414835; c=relaxed/simple; bh=oNT2vBCXy7f7WhUuM+DHyRi+ZXTw42768aHi2oq3d4Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tqcPN5zaI/T1hjleXWngGtRjbJ3VbTib6tfxOFg1N68H6c5s8NLmi/yU+dRQ4EyJB5il1fswhjIL+oTLcIihmLT9PXLIkCsen0t9PNwt8GIwq5qKC4OM2MqRPPoynaigkP8fXrUrqZ+PkE/NXeyVVsF8xMVQwpnju/WhnPrt11s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kjvWGnFM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kjvWGnFM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 92EF0C19421; Wed, 14 Jan 2026 18:20:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768414832; bh=oNT2vBCXy7f7WhUuM+DHyRi+ZXTw42768aHi2oq3d4Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kjvWGnFMCictMZvXKjemGxjAn8/ByjZUYQZxqXR4GPSOfNj9ITdgoV7W0zjZwUytj 238ZVTp4gyAkHaQ2k71/HXYiJcTZQMOvp7AhTIJmcBaPrJ2KdcjzGSx2Y1XoJUd1nS HIn67SwKjY1JVqV1vzVbCMY7hwtkRVUBuec2N5detkHoDXIdf+0vzUKI0j1RVlaqZM 3gLJH4GXPdFRalSr0tDDpSMkmJ8rVnn6VfmwrGh3096aTjSOICeFknmAa0BPtPCWqM cg78qkGat2MumnybqvPQtdCVstez/IXLGOKFTOo83VFBSiO9G32yWy3O+MjO6x9y6w 5hs7Q9qAfDSUQ== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Fiona Behrens , Tamir Duberstein , Alban Kurti Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 07/15] rust: pin-init: rewrite `#[pin_data]` using `syn` Date: Wed, 14 Jan 2026 19:18:42 +0100 Message-ID: <20260114181934.1782470-8-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114181934.1782470-1-lossin@kernel.org> References: <20260114181934.1782470-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 b: Box } 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 | ^ 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] | ^^^^^^^^^^^ | =3D help: consider increasing the recursion limit by adding a `#![rec= ursion_limit =3D "256"]` attribute to your crate (`$CRATE`) =3D note: this error originates in the macro `$crate::__pin_data` whi= ch comes from the expansion of the attribute macro `pin_data` (in Nightly b= uilds, 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 | ^ help: try adding a comma: `,` error: expected `,` --> tests/ui/compile-fail/pin_data/missing_comma.rs:6:5 | 6 | b: Box | ^ Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- Changes in v3: * use DiagCtxt error handling Changes in v2: * improved error handling * fix clippy warnings * fix typos and variable names * collect the information about the pinned/not pinned fields only once at the beginning --- rust/pin-init/internal/src/helpers.rs | 149 ------ rust/pin-init/internal/src/lib.rs | 7 +- rust/pin-init/internal/src/pin_data.rs | 633 ++++++++++++++++++++----- rust/pin-init/src/macros.rs | 574 ---------------------- 4 files changed, 525 insertions(+), 838 deletions(-) delete mode 100644 rust/pin-init/internal/src/helpers.rs diff --git a/rust/pin-init/internal/src/helpers.rs b/rust/pin-init/internal= /src/helpers.rs deleted file mode 100644 index 90f85eaa4123..000000000000 --- a/rust/pin-init/internal/src/helpers.rs +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT - -use proc_macro2::{TokenStream, TokenTree}; - -/// Parsed generics. -/// -/// See the field documentation for an explanation what each of the fields= represents. -/// -/// # Examples -/// -/// ```rust,ignore -/// # let input =3D todo!(); -/// let (Generics { decl_generics, impl_generics, ty_generics }, rest) =3D= parse_generics(input); -/// quote! { -/// struct Foo<$($decl_generics)*> { -/// // ... -/// } -/// -/// impl<$impl_generics> Foo<$ty_generics> { -/// fn foo() { -/// // ... -/// } -/// } -/// } -/// ``` -pub(crate) struct Generics { - /// The generics with bounds and default values (e.g. `T: Clone, const= N: usize =3D 0`). - /// - /// Use this on type definitions e.g. `struct Foo<$decl_generics> ...`= (or `union`/`enum`). - pub(crate) decl_generics: Vec, - /// The generics with bounds (e.g. `T: Clone, const N: usize`). - /// - /// Use this on `impl` blocks e.g. `impl<$impl_generics> Trait for ...= `. - pub(crate) impl_generics: Vec, - /// The generics without bounds and without default values (e.g. `T, N= `). - /// - /// Use this when you use the type that is declared with these generic= s e.g. - /// `Foo<$ty_generics>`. - pub(crate) ty_generics: Vec, -} - -/// Parses the given `TokenStream` into `Generics` and the rest. -/// -/// The generics are not present in the rest, but a where clause might rem= ain. -pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec) { - // The generics with bounds and default values. - let mut decl_generics =3D vec![]; - // `impl_generics`, the declared generics with their bounds. - let mut impl_generics =3D vec![]; - // Only the names of the generics, without any bounds. - let mut ty_generics =3D vec![]; - // Tokens not related to the generics e.g. the `where` token and defin= ition. - let mut rest =3D vec![]; - // The current level of `<`. - let mut nesting =3D 0; - let mut toks =3D input.into_iter(); - // If we are at the beginning of a generic parameter. - let mut at_start =3D true; - let mut skip_until_comma =3D false; - while let Some(tt) =3D toks.next() { - if nesting =3D=3D 1 && matches!(&tt, TokenTree::Punct(p) if p.as_c= har() =3D=3D '>') { - // Found the end of the generics. - break; - } else if nesting >=3D 1 { - decl_generics.push(tt.clone()); - } - match tt.clone() { - TokenTree::Punct(p) if p.as_char() =3D=3D '<' =3D> { - if nesting >=3D 1 && !skip_until_comma { - // This is inside of the generics and part of some bou= nd. - impl_generics.push(tt); - } - nesting +=3D 1; - } - TokenTree::Punct(p) if p.as_char() =3D=3D '>' =3D> { - // This is a parsing error, so we just end it here. - if nesting =3D=3D 0 { - break; - } else { - nesting -=3D 1; - if nesting >=3D 1 && !skip_until_comma { - // We are still inside of the generics and part of= some bound. - impl_generics.push(tt); - } - } - } - TokenTree::Punct(p) if skip_until_comma && p.as_char() =3D=3D = ',' =3D> { - if nesting =3D=3D 1 { - impl_generics.push(tt.clone()); - impl_generics.push(tt); - skip_until_comma =3D false; - } - } - _ if !skip_until_comma =3D> { - match nesting { - // If we haven't entered the generics yet, we still wa= nt to keep these tokens. - 0 =3D> rest.push(tt), - 1 =3D> { - // Here depending on the token, it might be a gene= ric variable name. - match tt.clone() { - TokenTree::Ident(i) if at_start && i =3D=3D "c= onst" =3D> { - let Some(name) =3D toks.next() else { - // Parsing error. - break; - }; - impl_generics.push(tt); - impl_generics.push(name.clone()); - ty_generics.push(name.clone()); - decl_generics.push(name); - at_start =3D false; - } - TokenTree::Ident(_) if at_start =3D> { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start =3D false; - } - TokenTree::Punct(p) if p.as_char() =3D=3D ',' = =3D> { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start =3D true; - } - // Lifetimes begin with `'`. - TokenTree::Punct(p) if p.as_char() =3D=3D '\''= && at_start =3D> { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - } - // Generics can have default values, we skip t= hese. - TokenTree::Punct(p) if p.as_char() =3D=3D '=3D= ' =3D> { - skip_until_comma =3D true; - } - _ =3D> impl_generics.push(tt), - } - } - _ =3D> impl_generics.push(tt), - } - } - _ =3D> {} - } - } - rest.extend(toks); - ( - Generics { - impl_generics, - decl_generics, - ty_generics, - }, - rest, - ) -} diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src= /lib.rs index a75b99b58189..56dc306e04a9 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -16,14 +16,15 @@ use crate::diagnostics::DiagCtxt; =20 mod diagnostics; -mod helpers; mod pin_data; mod pinned_drop; mod zeroable; =20 #[proc_macro_attribute] -pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { - pin_data::pin_data(inner.into(), item.into()).into() +pub fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { + let args =3D parse_macro_input!(args); + let input =3D parse_macro_input!(input); + DiagCtxt::with(|dcx| pin_data::pin_data(args, input, dcx)).into() } =20 #[proc_macro_attribute] diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/interna= l/src/pin_data.rs index 86a53b37cc66..fb4b8507c01d 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1,126 +1,535 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT =20 -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, + Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility,= WhereClause, +}; =20 -pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStre= am { - // This proc-macro only does some pre-parsing and then delegates the a= ctual parsing to - // `pin_init::__pin_data!`. +use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; =20 - let ( - Generics { - impl_generics, - decl_generics, - ty_generics, - }, - rest, - ) =3D parse_generics(input); - // The struct definition might contain the `Self` type. Since `__pin_d= ata!` will define a new - // type with the same generics and bounds, this poses a problem, since= `Self` will refer to the - // new type as opposed to this struct definition. Therefore we have to= replace `Self` with the - // concrete name. - - // Errors that occur when replacing `Self` with `struct_name`. - let mut errs =3D TokenStream::new(); - // The name of the struct with ty_generics. - let struct_name =3D rest - .iter() - .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i =3D=3D "st= ruct")) - .nth(1) - .and_then(|tt| match tt { - TokenTree::Ident(_) =3D> { - let tt =3D tt.clone(); - let mut res =3D vec![tt]; - if !ty_generics.is_empty() { - // We add this, so it is maximally compatible with e.g= . `Self::CONST` which - // will be replaced by `StructName::<$generics>::CONST= `. - res.push(TokenTree::Punct(Punct::new(':', Spacing::Joi= nt))); - res.push(TokenTree::Punct(Punct::new(':', Spacing::Alo= ne))); - res.push(TokenTree::Punct(Punct::new('<', Spacing::Alo= ne))); - res.extend(ty_generics.iter().cloned()); - res.push(TokenTree::Punct(Punct::new('>', Spacing::Alo= ne))); - } - Some(res) +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<'_>) -> syn::Result { + let lh =3D input.lookahead1(); + if lh.peek(End) { + input.parse().map(Self::Nothing) + } else if lh.peek(kw::PinnedDrop) { + let res =3D input.parse().map(Self::PinnedDrop)?; + let lh =3D input.lookahead1(); + if lh.peek(End) { + Ok(res) + } else { + Err(lh.error()) } - _ =3D> None, + } else { + Err(lh.error()) + } + } +} + +pub(crate) fn pin_data( + args: Args, + input: Item, + dcx: &mut DiagCtxt, +) -> Result { + let mut struct_ =3D match input { + Item::Struct(struct_) =3D> struct_, + Item::Enum(enum_) =3D> { + return Err(dcx.error( + enum_.enum_token, + "`#[pin_data]` only supports structs for now", + )); + } + Item::Union(union) =3D> { + return Err(dcx.error( + union.union_token, + "`#[pin_data]` only supports structs for now", + )); + } + rest =3D> { + return Err(dcx.error( + rest, + "`#[pin_data]` can only be applied to struct, enum and uni= on definitions", + )); + } + }; + + // The generics might contain the `Self` type. Since this macro will d= efine a new type with the + // same generics and bounds, this poses a problem: `Self` will refer t= o the new type as opposed + // to this struct definition. Therefore we have to replace `Self` with= the concrete name. + let mut replacer =3D { + let name =3D &struct_.ident; + let (_, ty_generics, _) =3D 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 fields: Vec<(bool, Field)> =3D struct_ + .fields + .iter() + .cloned() + .map(|mut field| { + let pinned =3D is_field_structurally_pinned(&field); + field.attrs.retain(|a| !a.path().is_ident("pin")); + (pinned, field) }) - .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::() - .unwrap(), + .collect(); + + for (pinned, field) in &fields { + if !pinned && is_phantom_pinned(&field.ty) { + dcx.error( + field, + format!( + "The field `{}` of type `PhantomPinned` only has an ef= fect \ + if it has the `#[pin]` attribute", + field.ident.as_ref().unwrap(), + ), ); - "Self".parse::().unwrap().into_iter().collect() - }); - let impl_generics =3D impl_generics - .into_iter() - .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &= mut errs)) - .collect::>(); - let mut rest =3D 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 =3D=3D "struct") { - vec![tt] - } else { - replace_self_and_deny_type_defs(&struct_name, tt, &mut err= s) + } + } + + let unpin_impl =3D generate_unpin_impl(&struct_.ident, &struct_.generi= cs, &fields); + let drop_impl =3D generate_drop_impl(&struct_.ident, &struct_.generics= , args); + let projections =3D + generate_projections(&struct_.vis, &struct_.ident, &struct_.generi= cs, &fields); + let the_pin_data =3D + generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.gener= ics, &fields); + + strip_pin_annotations(&mut struct_); + + Ok(quote! { + #struct_ + #projections + // We put the rest into this const item, because it then will not = be accessible to anything + // outside. + const _: () =3D { + #the_pin_data + #unpin_impl + #drop_impl + }; + }) +} + +fn is_phantom_pinned(ty: &Type) -> bool { + match ty { + Type::Path(TypePath { qself: None, path }) =3D> { + // Cannot possibly refer to `PhantomPinned` (except alias, but= that's on the user). + if path.segments.len() > 3 { + return false; } - }) - .collect::>(); - // This should be the body of the struct `{...}`. - let last =3D rest.pop(); - let mut quoted =3D 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 + // If there is a `::`, then the path needs to be `::core::mark= er::PhantomPinned` or + // `::std::marker::PhantomPinned`. + if path.leading_colon.is_some() && path.segments.len() !=3D 3 { + return false; + } + let expected: Vec<&[&str]> =3D vec![&["PhantomPinned"], &["mar= ker"], &["core", "std"]]; + for (actual, expected) in path.segments.iter().rev().zip(expec= ted) { + if !actual.arguments.is_empty() || expected.iter().all(|e|= actual.ident !=3D e) { + return false; + } + } + true + } + _ =3D> false, + } } =20 -/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `str= uct` `union` and `impl` -/// keywords. -/// -/// The error is appended to `errs` to allow normal parsing to continue. -fn replace_self_and_deny_type_defs( - struct_name: &Vec, - tt: TokenTree, - errs: &mut TokenStream, -) -> Vec { - match tt { - TokenTree::Ident(ref i) - if i =3D=3D "enum" || i =3D=3D "trait" || i =3D=3D "struct" ||= i =3D=3D "union" || i =3D=3D "impl" =3D> +fn is_field_structurally_pinned(field: &Field) -> bool { + field.attrs.iter().any(|a| a.path().is_ident("pin")) +} + +fn generate_unpin_impl( + ident: &Ident, + generics: &Generics, + fields: &[(bool, Field)], +) -> TokenStream { + let generics_with_pin_lt =3D { + let mut g =3D generics.clone(); + g.params.insert(0, parse_quote!('__pin)); + let _ =3D g.make_where_clause(); + g + }; + let ( + impl_generics_with_pin_lt, + ty_generics_with_pin_lt, + Some(WhereClause { + where_token, + predicates, + }), + ) =3D generics_with_pin_lt.split_for_impl() + else { + unreachable!() + }; + let (_, ty_generics, _) =3D generics.split_for_impl(); + let pinned_fields =3D fields.iter().filter_map(|(b, f)| b.then_some(f)= ); + 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_pin_lt + #where_token + #predicates { - errs.extend( - format!( - "::core::compile_error!(\"Cannot use `{i}` inside of s= truct definition with \ - `#[pin_data]`.\");" + __phantom_pin: ::core::marker::PhantomData &= '__pin ()>, + __phantom: ::core::marker::PhantomData< + fn(#ident #ty_generics) -> #ident #ty_generics + >, + #(#pinned_fields),* + } + + #[doc(hidden)] + impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #= ty_generics + #where_token + __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin, + #predicates + {} + } +} + +fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> T= okenStream { + let (impl_generics, ty_generics, whr) =3D generics.split_for_impl(); + let has_pinned_drop =3D matches!(args, Args::PinnedDrop(_)); + // We need to disallow normal `Drop` implementation, the exact behavio= r depends on whether + // `PinnedDrop` was specified in `args`. + if has_pinned_drop { + // When `PinnedDrop` was specified we just implement `Drop` and de= legate. + 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 =3D unsafe { ::core::pin::Pin::new_unchecke= d(self) }; + // SAFETY: Since this is a drop function, we can creat= e this token to call the + // pinned destructor of this type. + let token =3D unsafe { ::pin_init::__internal::OnlyCal= lFromDrop::new() }; + ::pin_init::PinnedDrop::drop(pinned, token); + } + } + } + } else { + // When no `PinnedDrop` was specified, then we have to prevent imp= lementing drop. + quote! { + // We prevent this by creating a trait that will be implemente= d for all types implementing + // `Drop`. Additionally we will implement this trait for the s= truct leading to a conflict, + // if it also implements `Drop` + trait MustNotImplDrop {} + #[expect(drop_bounds)] + impl 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 + 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( + vis: &Visibility, + ident: &Ident, + generics: &Generics, + fields: &[(bool, Field)], +) -> TokenStream { + let (impl_generics, ty_generics, _) =3D generics.split_for_impl(); + let mut generics =3D generics.clone(); + generics.params.insert(0, parse_quote!('__pin)); + let (_, ty_generics_with_pin_lt, whr) =3D generics.split_for_impl(); + let projection =3D format_ident!("{ident}Projection"); + let this =3D format_ident!("this"); + + let (fields_decl, fields_proj) =3D collect_tuple(fields.iter().map( + |( + pinned, + Field { + vis, + ident, + ty, + attrs, + .. + }, + )| { + let mut attrs =3D attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let mut no_doc_attrs =3D attrs.clone(); + no_doc_attrs.retain(|a| !a.path().is_ident("doc")); + let ident =3D ident + .as_ref() + .expect("only structs with named fields are supported"); + if *pinned { + ( + 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) }, + ), ) - .parse::() - .unwrap() - .into_iter() - .map(|mut tok| { - tok.set_span(tt.span()); - tok - }), - ); - vec![tt] - } - TokenTree::Ident(i) if i =3D=3D "Self" =3D> struct_name.clone(), - TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) = =3D> vec![tt], - TokenTree::Group(g) =3D> 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(), - ))], + } else { + ( + quote!( + #(#attrs)* + #vis #ident: &'__pin mut #ty, + ), + quote!( + #(#no_doc_attrs)* + #ident: &mut #this.#ident, + ), + ) + } + }, + )); + let structurally_pinned_fields_docs =3D fields + .iter() + .filter_map(|(pinned, field)| pinned.then_some(field)) + .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwra= p())); + let not_structurally_pinned_fields_docs =3D fields + .iter() + .filter_map(|(pinned, field)| (!pinned).then_some(field)) + .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwra= p())); + let docs =3D format!(" Pin-projections of [`{ident}`]"); + quote! { + #[doc =3D #docs] + #[allow(dead_code)] + #[doc(hidden)] + #vis struct #projection #generics { + #(#fields_decl)* + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut (= )>, + } + + impl #impl_generics #ident #ty_generics + #whr + { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + #(#[doc =3D #structurally_pinned_fields_docs])* + /// + /// These fields are **not** structurally pinned: + #(#[doc =3D #not_structurally_pinned_fields_docs])* + #[inline] + #vis fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> #projection #ty_generics_with_pin_lt { + // SAFETY: we only give access to `&mut` for fields not st= ructurally pinned. + let #this =3D unsafe { ::core::pin::Pin::get_unchecked_mut= (self) }; + #projection { + #(#fields_proj)* + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + } + } +} + +fn generate_the_pin_data( + vis: &Visibility, + ident: &Ident, + generics: &Generics, + fields: &[(bool, Field)], +) -> TokenStream { + let (impl_generics, ty_generics, whr) =3D generics.split_for_impl(); + + // For every field, we create an initializing projection function acco= rding to its projection + // type. If a field is structurally pinned, then it must be initialize= d 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 =3D attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let ident =3D ident + .as_ref() + .expect("only structs with named fields are supported"); + let project_ident =3D format_ident!("__project_{ident}"); + let (init_ty, init_fn, project_ty, project_body, pin_safety) =3D i= f 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!( + /// - `slot` will not move until it is dropped, i.e. i= t will be pinned. + ), + ) + } else { + ( + quote!(Init), + quote!(__init), + quote!(&'__slot mut #ty), + quote!(slot), + quote!(), + ) + }; + let slot_safety =3D format!( + " `slot` points at the field `{ident}` inside of `{struct_iden= t}`, 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( + 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 =3D #slot_safety] + #(#attrs)* + #vis unsafe fn #project_ident<'__slot>( + self, + slot: &'__slot mut #ty, + ) -> #project_ty { + #project_body + } + } + } + + let field_accessors =3D fields + .iter() + .map(|(pinned, field)| handle_field(field, ident, *pinned)) + .collect::(); + quote! { + // We declare this struct which will host all of the projection fu= nction for our type. It + // will be invariant over all generic parameters which are inherit= ed from the struct. + #[doc(hidden)] + #vis struct __ThePinData #generics + #whr + { + __phantom: ::core::marker::PhantomData< + fn(#ident #ty_generics) -> #ident #ty_generics + >, + } + + impl #impl_generics ::core::clone::Clone for __ThePinData #ty_gene= rics + #whr + { + fn clone(&self) -> Self { *self } + } + + impl #impl_generics ::core::marker::Copy for __ThePinData #ty_gene= rics + #whr + {} + + #[allow(dead_code)] // Some functions might never be used and priv= ate. + #[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 =3D __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 __T= hePinData #ty_generics + #whr + { + type Datee =3D #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")); + } +} + +struct SelfReplacer(PathSegment); + +impl VisitMut for SelfReplacer { + fn visit_path_mut(&mut self, i: &mut syn::Path) { + if i.is_ident("Self") { + let span =3D i.span(); + let seg =3D &self.0; + *i =3D parse_quote_spanned!(span=3D> #seg); + } else { + syn::visit_mut::visit_path_mut(self, i); + } + } + + fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) { + if seg.ident =3D=3D "Self" { + let span =3D seg.span(); + let this =3D &self.0; + *seg =3D parse_quote_spanned!(span=3D> #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(iter: impl Iterator) -> (Vec, V= ec) { + let mut res_a =3D vec![]; + let mut res_b =3D vec![]; + for (a, b) in iter { + res_a.push(a); + res_b.push(b); } + (res_a, res_b) } diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index b80c95612fd6..eea8adc5c7ad 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -503,580 +503,6 @@ #[cfg(not(kernel))] pub use ::paste::paste; =20 -/// This macro first parses the struct definition such that it separates p= inned and not pinned -/// fields. Afterwards it declares the struct and implement the `PinData` = trait safely. -#[doc(hidden)] -#[macro_export] -macro_rules! __pin_data { - // Proc-macro entry point, this is supplied by the proc-macro pre-pars= ing. - (parse_input: - @args($($pinned_drop:ident)?), - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis struct $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @body({ $($fields:tt)* }), - ) =3D> { - // We now use token munching to iterate through all of the fields.= While doing this we - // identify fields marked with `#[pin]`, these fields are the 'pin= ned fields'. The user - // wants these to be structurally pinned. The rest of the fields a= re the - // 'not pinned fields'. Additionally we collect all fields, since = we need them in the right - // order to declare the struct. - // - // In this call we also put some explaining comments for the param= eters. - $crate::__pin_data!(find_pinned_fields: - // Attributes on the struct itself, these will just be propaga= ted to be put onto the - // struct definition. - @struct_attrs($(#[$($struct_attr)*])*), - // The visibility of the struct. - @vis($vis), - // The name of the struct. - @name($name), - // The 'impl generics', the generics that will need to be spec= ified on the struct inside - // of an `impl<$ty_generics>` block. - @impl_generics($($impl_generics)*), - // The 'ty generics', the generics that will need to be specif= ied on the impl blocks. - @ty_generics($($ty_generics)*), - // The 'decl generics', the generics that need to be specified= on the struct - // definition. - @decl_generics($($decl_generics)*), - // The where clause of any impl block and the declaration. - @where($($($whr)*)?), - // The remaining fields tokens that need to be processed. - // We add a `,` at the end to ensure correct parsing. - @fields_munch($($fields)* ,), - // The pinned fields. - @pinned(), - // The not pinned fields. - @not_pinned(), - // All fields. - @fields(), - // The accumulator containing all attributes already parsed. - @accum(), - // Contains `yes` or `` to indicate if `#[pin]` was found on t= he current field. - @is_pinned(), - // The proc-macro argument, this should be `PinnedDrop` or ``. - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We found a PhantomPinned field, this should generally be pinned! - @fields_munch($field:ident : $($($(::)?core::)?marker::)?PhantomPi= nned, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is not pinned. - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) =3D> { - ::core::compile_error!(concat!( - "The field `", - stringify!($field), - "` of type `PhantomPinned` only has an effect, if it has the `= #[pin]` attribute.", - )); - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)* $($accum)* $field: ::core::marker::Phantom= Pinned,), - @not_pinned($($not_pinned)*), - @fields($($fields)* $($accum)* $field: ::core::marker::Phantom= Pinned,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration. - @fields_munch($field:ident : $type:ty, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is pinned. - @is_pinned(yes), - @pinned_drop($($pinned_drop:ident)?), - ) =3D> { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)* $($accum)* $field: $type,), - @not_pinned($($not_pinned)*), - @fields($($fields)* $($accum)* $field: $type,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration. - @fields_munch($field:ident : $type:ty, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is not pinned. - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) =3D> { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)* $($accum)* $field: $type,), - @fields($($fields)* $($accum)* $field: $type,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We found the `#[pin]` attr. - @fields_munch(#[pin] $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) =3D> { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - // We do not include `#[pin]` in the list of attributes, since= it is not actually an - // attribute that is defined somewhere. - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)*), - // Set this to `yes`. - @is_pinned(yes), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration with visibility, for simplicit= y we only munch the - // visibility and put it into `$accum`. - @fields_munch($fvis:vis $field:ident $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) =3D> { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($field $($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)* $fvis), - @is_pinned($($is_pinned)?), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // Some other attribute, just put it into `$accum`. - @fields_munch(#[$($attr:tt)*] $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) =3D> { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)* #[$($attr)*]), - @is_pinned($($is_pinned)?), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the end of the fields, plus an optional additional c= omma, since we added one - // before and the user is also allowed to put a trailing comma. - @fields_munch($(,)?), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) =3D> { - // Declare the struct with all fields in the correct order. - $($struct_attrs)* - $vis struct $name <$($decl_generics)*> - where $($whr)* - { - $($fields)* - } - - $crate::__pin_data!(make_pin_projections: - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - ); - - // We put the rest into this const item, because it then will not = be accessible to anything - // outside. - const _: () =3D { - // We declare this struct which will host all of the projectio= n function for our type. - // it will be invariant over all generic parameters which are = inherited from the - // struct. - $vis struct __ThePinData<$($impl_generics)*> - where $($whr)* - { - __phantom: ::core::marker::PhantomData< - fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*> - >, - } - - impl<$($impl_generics)*> ::core::clone::Clone for __ThePinData= <$($ty_generics)*> - where $($whr)* - { - fn clone(&self) -> Self { *self } - } - - impl<$($impl_generics)*> ::core::marker::Copy for __ThePinData= <$($ty_generics)*> - where $($whr)* - {} - - // Make all projection functions. - $crate::__pin_data!(make_pin_data: - @pin_data(__ThePinData), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @where($($whr)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - ); - - // SAFETY: We have added the correct projection functions abov= e to `__ThePinData` and - // we also use the least restrictive generics possible. - unsafe impl<$($impl_generics)*> - $crate::__internal::HasPinData for $name<$($ty_generics)*> - where $($whr)* - { - type PinData =3D __ThePinData<$($ty_generics)*>; - - unsafe fn __pin_data() -> Self::PinData { - __ThePinData { __phantom: ::core::marker::PhantomData } - } - } - - // SAFETY: TODO. - unsafe impl<$($impl_generics)*> - $crate::__internal::PinData for __ThePinData<$($ty_generic= s)*> - where $($whr)* - { - type Datee =3D $name<$($ty_generics)*>; - } - - // This struct will be used for the unpin analysis. Since only= structurally pinned - // fields are relevant whether the struct should implement `Un= pin`. - #[allow(dead_code)] - struct __Unpin <'__pin, $($impl_generics)*> - where $($whr)* - { - __phantom_pin: ::core::marker::PhantomData &'__pin ()>, - __phantom: ::core::marker::PhantomData< - fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*> - >, - // Only the pinned fields. - $($pinned)* - } - - #[doc(hidden)] - impl<'__pin, $($impl_generics)*> ::core::marker::Unpin for $na= me<$($ty_generics)*> - where - __Unpin<'__pin, $($ty_generics)*>: ::core::marker::Unpin, - $($whr)* - {} - - // We need to disallow normal `Drop` implementation, the exact= behavior depends on - // whether `PinnedDrop` was specified as the parameter. - $crate::__pin_data!(drop_prevention: - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @where($($whr)*), - @pinned_drop($($pinned_drop)?), - ); - }; - }; - // When no `PinnedDrop` was specified, then we have to prevent impleme= nting drop. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(), - ) =3D> { - // We prevent this by creating a trait that will be implemented fo= r all types implementing - // `Drop`. Additionally we will implement this trait for the struc= t leading to a conflict, - // if it also implements `Drop` - trait MustNotImplDrop {} - #[expect(drop_bounds)] - impl MustNotImplDrop for T {} - impl<$($impl_generics)*> MustNotImplDrop for $name<$($ty_generics)= *> - where $($whr)* {} - // We also take care to prevent users from writing a useless `Pinn= edDrop` 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 - UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} - impl<$($impl_generics)*> - UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for $name= <$($ty_generics)*> - where $($whr)* {} - }; - // When `PinnedDrop` was specified we just implement `Drop` and delega= te. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(PinnedDrop), - ) =3D> { - impl<$($impl_generics)*> ::core::ops::Drop for $name<$($ty_generic= s)*> - where $($whr)* - { - fn drop(&mut self) { - // SAFETY: Since this is a destructor, `self` will not mov= e after this function - // terminates, since it is inaccessible. - let pinned =3D unsafe { ::core::pin::Pin::new_unchecked(se= lf) }; - // SAFETY: Since this is a drop function, we can create th= is token to call the - // pinned destructor of this type. - let token =3D unsafe { $crate::__internal::OnlyCallFromDro= p::new() }; - $crate::PinnedDrop::drop(pinned, token); - } - } - }; - // If some other parameter was specified, we emit a readable error. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop($($rest:tt)*), - ) =3D> { - compile_error!( - "Wrong parameters to `#[pin_data]`, expected nothing or `Pinne= dDrop`, got '{}'.", - stringify!($($rest)*), - ); - }; - (make_pin_projections: - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type= :ty),* $(,)?), - @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:t= y),* $(,)?), - ) =3D> { - $crate::macros::paste! { - #[doc(hidden)] - $vis struct [< $name Projection >] <'__pin, $($decl_generics)*= > { - $($(#[$($p_attr)*])* $pvis $p_field : ::core::pin::Pin<&'_= _pin mut $p_type>,)* - $($(#[$($attr)*])* $fvis $field : &'__pin mut $type,)* - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin m= ut ()>, - } - - impl<$($impl_generics)*> $name<$($ty_generics)*> - where $($whr)* - { - /// Pin-projects all fields of `Self`. - /// - /// These fields are structurally pinned: - $(#[doc =3D ::core::concat!(" - `", ::core::stringify!($p_= field), "`")])* - /// - /// These fields are **not** structurally pinned: - $(#[doc =3D ::core::concat!(" - `", ::core::stringify!($fi= eld), "`")])* - #[inline] - $vis fn project<'__pin>( - self: ::core::pin::Pin<&'__pin mut Self>, - ) -> [< $name Projection >] <'__pin, $($ty_generics)*> { - // SAFETY: we only give access to `&mut` for fields no= t structurally pinned. - let this =3D unsafe { ::core::pin::Pin::get_unchecked_= mut(self) }; - [< $name Projection >] { - $( - // SAFETY: `$p_field` is structurally pinned. - $(#[$($p_attr)*])* - $p_field : unsafe { ::core::pin::Pin::new_unch= ecked(&mut this.$p_field) }, - )* - $( - $(#[$($attr)*])* - $field : &mut this.$field, - )* - ___pin_phantom_data: ::core::marker::PhantomData, - } - } - } - } - }; - (make_pin_data: - @pin_data($pin_data:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type= :ty),* $(,)?), - @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:t= y),* $(,)?), - ) =3D> { - $crate::macros::paste! { - // For every field, we create a projection function according = to its projection type. If a - // field is structurally pinned, then it must be initialized v= ia `PinInit`, if it is not - // structurally pinned, then it can be initialized via `Init`. - // - // The functions are `unsafe` to prevent accidentally calling = them. - #[allow(dead_code)] - #[expect(clippy::missing_safety_doc)] - impl<$($impl_generics)*> $pin_data<$($ty_generics)*> - where $($whr)* - { - $( - $(#[$($p_attr)*])* - $pvis unsafe fn $p_field( - self, - slot: *mut $p_type, - init: impl $crate::PinInit<$p_type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::PinInit::__pinned_init(init, slot= ) } - } - - $(#[$($p_attr)*])* - $pvis unsafe fn [<__project_ $p_field>]<'__slot>( - self, - slot: &'__slot mut $p_type, - ) -> ::core::pin::Pin<&'__slot mut $p_type> { - ::core::pin::Pin::new_unchecked(slot) - } - )* - $( - $(#[$($attr)*])* - $fvis unsafe fn $field( - self, - slot: *mut $type, - init: impl $crate::Init<$type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::Init::__init(init, slot) } - } - - $(#[$($attr)*])* - $fvis unsafe fn [<__project_ $field>]<'__slot>( - self, - slot: &'__slot mut $type, - ) -> &'__slot mut $type { - slot - } - )* - } - } - }; -} - /// The internal init macro. Do not call manually! /// /// This is called by the `{try_}{pin_}init!` macros with various inputs. --=20 2.52.0