rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] add `syn` versions of pin-init macros
@ 2025-03-04 22:56 Benno Lossin
  2025-03-04 22:56 ` [RFC PATCH 1/6] rust: pin-init: internal: add syn version of the `Zeroable` derive macro Benno Lossin
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Benno Lossin @ 2025-03-04 22:56 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich
  Cc: rust-for-linux

Change the implementations of all pin-init macros to use `syn` [1]. It
is a library to make parsing Rust code in proc-macros simple. Since the
kernel does not yet depend on syn, only the user-space version will
benefit from this change now. However, we will soon add `syn` as a
dependency using the new build system. `syn` is used by almost all
proc-macros in the Rust ecosystem and even by the Rust compiler itself!

There are a lot of benefits to writing proc-macros using `syn`.
Parsing is a lot more readable and thus maintainable. The benefit in
simplicity can then be taken advantage of by extending the syntax. I
plan to support tuple structs soon. Another benefit is the increased
readability in the errors on malformed macro-input. Currently they are
very bad, because they come from token-munching declarative macros,
which are notorious for bad error messages. See the respective commit
messages for examples.

---
Cheers,
Benno

[1]: https://crates.io/crates/syn

Benno Lossin (6):
  rust: pin-init: internal: add syn version of the `Zeroable` derive
    macro
  rust: pin-init: internal: add syn version of `pinned_drop` proc macro
  rust: pin-init: internal: add syn version of the `pin_data` proc macro
  rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro
  rust: pin-init: allow doctests to refer to the pin-init crate
  rust: pin-init: internal: add syn version of `[try_][pin_]init!`
    macros

 rust/pin-init/internal/src/init.rs            | 404 ++++++++++++++++++
 rust/pin-init/internal/src/lib.rs             |  96 ++++-
 rust/pin-init/internal/src/syn_pin_data.rs    | 313 ++++++++++++++
 rust/pin-init/internal/src/syn_pinned_drop.rs |  77 ++++
 rust/pin-init/internal/src/syn_zeroable.rs    |  63 +++
 rust/pin-init/src/__internal.rs               |  62 +++
 rust/pin-init/src/lib.rs                      |  69 +--
 rust/pin-init/src/macros.rs                   |   4 +-
 8 files changed, 1020 insertions(+), 68 deletions(-)
 create mode 100644 rust/pin-init/internal/src/init.rs
 create mode 100644 rust/pin-init/internal/src/syn_pin_data.rs
 create mode 100644 rust/pin-init/internal/src/syn_pinned_drop.rs
 create mode 100644 rust/pin-init/internal/src/syn_zeroable.rs


base-commit: 0cdd06928bd97cdbfa7061a52cc91901c84d9b2f
-- 
2.48.1



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH 1/6] rust: pin-init: internal: add syn version of the `Zeroable` derive macro
  2025-03-04 22:56 [RFC PATCH 0/6] add `syn` versions of pin-init macros Benno Lossin
@ 2025-03-04 22:56 ` Benno Lossin
  2025-03-04 22:57 ` [RFC PATCH 2/6] rust: pin-init: internal: add syn version of `pinned_drop` proc macro Benno Lossin
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Benno Lossin @ 2025-03-04 22:56 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: linux-kernel, rust-for-linux

Implement the `Zeroable` derive macro using syn to simplify parsing by
not going through an additional declarative macro.

The syn version is only enabled in the user-space version and disabled
in the kernel until syn becomes available there.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
---
 rust/pin-init/internal/src/lib.rs          |  5 ++
 rust/pin-init/internal/src/syn_zeroable.rs | 63 ++++++++++++++++++++++
 2 files changed, 68 insertions(+)
 create mode 100644 rust/pin-init/internal/src/syn_zeroable.rs

diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index babe5e878550..5019efe22662 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -30,6 +30,11 @@
 mod helpers;
 mod pin_data;
 mod pinned_drop;
+#[cfg(kernel)]
+mod zeroable;
+
+#[cfg(not(kernel))]
+#[path = "syn_zeroable.rs"]
 mod zeroable;
 
 #[proc_macro_attribute]
diff --git a/rust/pin-init/internal/src/syn_zeroable.rs b/rust/pin-init/internal/src/syn_zeroable.rs
new file mode 100644
index 000000000000..90ea8fa63c83
--- /dev/null
+++ b/rust/pin-init/internal/src/syn_zeroable.rs
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{
+    parse_macro_input, parse_quote, Data, DataStruct, DeriveInput, Error, GenericParam, Result,
+    TypeParam, WherePredicate,
+};
+
+pub(crate) fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let raw = input.clone().into();
+    do_derive(parse_macro_input!(input as DeriveInput), raw)
+        .unwrap_or_else(|e| e.into_compile_error())
+        .into()
+}
+
+fn do_derive(
+    DeriveInput {
+        ident,
+        mut generics,
+        data,
+        ..
+    }: DeriveInput,
+    raw_input: TokenStream,
+) -> Result<TokenStream> {
+    let Data::Struct(DataStruct { fields, .. }) = data else {
+        return Err(Error::new_spanned(
+            raw_input,
+            "`Zeroable` can only be derived for structs.",
+        ));
+    };
+    let field_ty = fields.iter().map(|f| &f.ty);
+    let zeroable_bounds = generics
+        .params
+        .iter()
+        .filter_map(|p| match p {
+            GenericParam::Type(TypeParam { ident, .. }) => {
+                Some(parse_quote!(#ident: ::pin_init::Zeroable))
+            }
+            _ => None,
+        })
+        .collect::<Vec<WherePredicate>>();
+    generics
+        .make_where_clause()
+        .predicates
+        .extend(zeroable_bounds);
+    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
+    Ok(quote! {
+        // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
+        #[automatically_derived]
+        unsafe impl #impl_generics ::pin_init::Zeroable for #ident #ty_generics
+            #whr
+        {}
+        const _: () = {
+            fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {}
+            fn ensure_zeroable #impl_generics ()
+                #whr
+            {
+                #(assert_zeroable::<#field_ty>();)*
+            }
+        };
+    })
+}
-- 
2.48.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC PATCH 2/6] rust: pin-init: internal: add syn version of `pinned_drop` proc macro
  2025-03-04 22:56 [RFC PATCH 0/6] add `syn` versions of pin-init macros Benno Lossin
  2025-03-04 22:56 ` [RFC PATCH 1/6] rust: pin-init: internal: add syn version of the `Zeroable` derive macro Benno Lossin
@ 2025-03-04 22:57 ` Benno Lossin
  2025-03-04 22:57 ` [RFC PATCH 3/6] rust: pin-init: internal: add syn version of the `pin_data` " Benno Lossin
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Benno Lossin @ 2025-03-04 22:57 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: linux-kernel, rust-for-linux

Implement the `pinned_drop` attribute macro using syn to simplify
parsing by not going through an additional declarative macro.
This not only simplifies the code by a lot, increasing maintainability
and making it easier to implement new features. But also improves the
user experience by improving the error messages one gets when giving
incorrect inputs to the macro.
For example in this piece of code, there is a `drop` function missing:

    use pin_init::*;

    #[pin_data(PinnedDrop)]
    struct Foo {}

    #[pinned_drop]
    impl PinnedDrop for Foo {}

But this error is communicated very poorly in the declarative macro
version:

    error: no rules expected `)`
      |
    6 | #[pinned_drop]
      | ^^^^^^^^^^^^^^ no rules expected this token in macro call
      |
    note: while trying to match keyword `fn`
     --> src/macros.rs
      |
      |             fn drop($($sig:tt)*) {
      |             ^^
      = note: this error originates in the attribute macro `pinned_drop` (in Nightly builds, run with -Z macro-backtrace for more info)

    error[E0277]: the trait bound `Foo: PinnedDrop` is not satisfied
      |
    3 | #[pin_data(PinnedDrop)]
      | ^^^^^^^^^^^^^^^^^^^^^^^
      | |
      | the trait `PinnedDrop` is not implemented for `Foo`
      | required by a bound introduced by this call
      |
      = 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 syn version is much more concise and right to the point:

    error[E0046]: not all trait items implemented, missing: `drop`
      |
    7 | impl PinnedDrop for Foo {}
      | ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation
      |
      = help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }`

Another example is the following:

    use pin_init::*;
    use std::pin::Pin;

    #[pin_data(PinnedDrop)]
    struct Foo {}

    #[pinned_drop]
    impl PinnedDrop for Foo {
        fn drop(self: Pin<&mut Self>) {}

        const BAZ: usize = 0;
    }

It produces this error in the declarative macro version:

    error: no rules expected keyword `const`
       |
    10 |     const BAZ: usize = 0;
       |     ^^^^^ no rules expected this token in macro call
       |
    note: while trying to match `)`
      --> src/macros.rs
       |
       |         ),
       |         ^

    error[E0277]: the trait bound `Foo: PinnedDrop` is not satisfied
      |
    3 | #[pin_data(PinnedDrop)]
      | ^^^^^^^^^^^^^^^^^^^^^^^
      | |
      | the trait `PinnedDrop` is not implemented for `Foo`
      | required by a bound introduced by this call
      |
      = 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)

In the syn version, we get instead:

    error[E0438]: const `BAZ` is not a member of trait `pinned_init::PinnedDrop`
       |
    11 |     const BAZ: usize = 0;
       |     ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pinned_init::PinnedDrop`

The syn version is only enabled in the user-space version and disabled
in the kernel until syn becomes available there.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
---
 rust/pin-init/internal/src/lib.rs             |  4 +
 rust/pin-init/internal/src/syn_pinned_drop.rs | 77 +++++++++++++++++++
 2 files changed, 81 insertions(+)
 create mode 100644 rust/pin-init/internal/src/syn_pinned_drop.rs

diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 5019efe22662..761fbf8b9c68 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -29,10 +29,14 @@
 
 mod helpers;
 mod pin_data;
+#[cfg(kernel)]
 mod pinned_drop;
 #[cfg(kernel)]
 mod zeroable;
 
+#[cfg(not(kernel))]
+#[path = "syn_pinned_drop.rs"]
+mod pinned_drop;
 #[cfg(not(kernel))]
 #[path = "syn_zeroable.rs"]
 mod zeroable;
diff --git a/rust/pin-init/internal/src/syn_pinned_drop.rs b/rust/pin-init/internal/src/syn_pinned_drop.rs
new file mode 100644
index 000000000000..1e70ee29adba
--- /dev/null
+++ b/rust/pin-init/internal/src/syn_pinned_drop.rs
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{
+    parse_macro_input, parse_quote, spanned::Spanned, Error, ImplItem, ImplItemFn, ItemImpl,
+    Result, Token,
+};
+
+pub(crate) fn pinned_drop(
+    args: proc_macro::TokenStream,
+    input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    parse_macro_input!(args as syn::parse::Nothing);
+    do_impl(parse_macro_input!(input as ItemImpl))
+        .unwrap_or_else(|e| e.into_compile_error())
+        .into()
+}
+
+fn do_impl(mut input: ItemImpl) -> Result<TokenStream> {
+    let Some((_, path, _)) = &mut input.trait_ else {
+        return Err(Error::new_spanned(
+            input,
+            "expected an `impl` block implementing `PinnedDrop`",
+        ));
+    };
+    if !is_pinned_drop(path) {
+        return Err(Error::new_spanned(
+            input,
+            "expected an `impl` block implementing `PinnedDrop`",
+        ));
+    }
+    let mut error = None;
+    if let Some(unsafety) = input.unsafety.take() {
+        error = Some(
+            Error::new_spanned(
+                unsafety,
+                "implementing the trait `PinnedDrop` via `#[pinned_drop]` is not unsafe",
+            )
+            .into_compile_error(),
+        );
+    }
+    input.unsafety = Some(Token![unsafe](input.impl_token.span()));
+    if path.segments.len() != 2 {
+        path.segments.insert(0, parse_quote!(pin_init));
+    }
+    path.leading_colon.get_or_insert(Token![::](path.span()));
+    for item in &mut input.items {
+        match item {
+            ImplItem::Fn(ImplItemFn { sig, .. }) if sig.ident == "drop" => {
+                sig.inputs
+                    .push(parse_quote!(_: ::pin_init::__internal::OnlyCallFromDrop));
+            }
+            _ => {}
+        }
+    }
+    Ok(quote! {
+        #error
+        #input
+    })
+}
+
+fn is_pinned_drop(path: &syn::Path) -> bool {
+    if path.segments.len() > 2 {
+        return false;
+    }
+    // If there is a `::`, then the path needs to be `::pin_init::PinnedDrop`.
+    if path.leading_colon.is_some() && path.segments.len() != 2 {
+        return false;
+    }
+    for (actual, expected) in path.segments.iter().rev().zip(["PinnedDrop", "pin_init"]) {
+        if actual.ident != expected {
+            return false;
+        }
+    }
+    true
+}
-- 
2.48.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC PATCH 3/6] rust: pin-init: internal: add syn version of the `pin_data` proc macro
  2025-03-04 22:56 [RFC PATCH 0/6] add `syn` versions of pin-init macros Benno Lossin
  2025-03-04 22:56 ` [RFC PATCH 1/6] rust: pin-init: internal: add syn version of the `Zeroable` derive macro Benno Lossin
  2025-03-04 22:57 ` [RFC PATCH 2/6] rust: pin-init: internal: add syn version of `pinned_drop` proc macro Benno Lossin
@ 2025-03-04 22:57 ` Benno Lossin
  2025-03-04 22:57 ` [RFC PATCH 4/6] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Benno Lossin
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Benno Lossin @ 2025-03-04 22:57 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: linux-kernel, rust-for-linux

Implement the `pin_data` attribute macro using syn to simplify parsing
by not going through an additional declarative macro.
This not only simplifies the code by a lot, increasing maintainability
and making it easier to implement new features. But also improves the
user experience by improving the error messages one gets when giving
incorrect inputs to the macro.
For example, annotating a function with `pin_data` is not allowed:

    use pin_init::*;

    #[pin_data]
    fn foo() {}

This results in the following rather unwieldy error with the declarative
version:

    error: no rules expected keyword `fn`
      |
    4 | fn foo() {}
      | ^^ no rules expected this token in macro call
      |
    note: while trying to match keyword `struct`
     --> src/macros.rs
      |
      |             $vis:vis struct $name:ident
      |                      ^^^^^^

    error: Could not locate type name.
      |
    3 | #[pin_data]
      | ^^^^^^^^^^^
      |
      = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)

The syn version gives this very concise error:

    error: expected `struct`
      |
    4 | fn foo() {}
      | ^^

The syn version is only enabled in the user-space version and disabled
in the kernel until syn becomes available there.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
---
 rust/pin-init/internal/src/lib.rs          |   9 +-
 rust/pin-init/internal/src/syn_pin_data.rs | 313 +++++++++++++++++++++
 2 files changed, 318 insertions(+), 4 deletions(-)
 create mode 100644 rust/pin-init/internal/src/syn_pin_data.rs

diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 761fbf8b9c68..9ef6178e410f 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -23,17 +23,18 @@
 #[path = "../../../macros/quote.rs"]
 #[macro_use]
 mod quote;
-#[cfg(not(kernel))]
-#[macro_use]
-extern crate quote;
-
+#[cfg(kernel)]
 mod helpers;
+#[cfg(kernel)]
 mod pin_data;
 #[cfg(kernel)]
 mod pinned_drop;
 #[cfg(kernel)]
 mod zeroable;
 
+#[cfg(not(kernel))]
+#[path = "syn_pin_data.rs"]
+mod pin_data;
 #[cfg(not(kernel))]
 #[path = "syn_pinned_drop.rs"]
 mod pinned_drop;
diff --git a/rust/pin-init/internal/src/syn_pin_data.rs b/rust/pin-init/internal/src/syn_pin_data.rs
new file mode 100644
index 000000000000..d95176389e17
--- /dev/null
+++ b/rust/pin-init/internal/src/syn_pin_data.rs
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{
+    parse_macro_input, parse_quote,
+    visit_mut::{visit_path_segment_mut, VisitMut},
+    Error, Field, ItemStruct, PathSegment, Result, Type, TypePath, WhereClause,
+};
+
+pub(crate) fn pin_data(
+    inner: proc_macro::TokenStream,
+    item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    do_impl(inner.into(), parse_macro_input!(item as ItemStruct))
+        .unwrap_or_else(|e| e.into_compile_error())
+        .into()
+}
+
+fn do_impl(args: TokenStream, mut struct_: ItemStruct) -> Result<TokenStream> {
+    // 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);
+
+    let the_pin_data = generate_the_pin_data(&struct_);
+    let unpin_impl = unpin_impl(&struct_);
+    let drop_impl = drop_impl(&struct_, args)?;
+
+    let mut errors = TokenStream::new();
+    for field in &mut struct_.fields {
+        if !is_pinned(field) && is_phantom_pinned(&field.ty) {
+            let message = format!("The field `{}` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute", field.ident.as_ref().unwrap() );
+            errors.extend(quote!(::core::compile_error!(#message);));
+        }
+        field.attrs.retain(|a| !a.path().is_ident("pin"));
+    }
+    Ok(quote! {
+        #struct_
+        #errors
+        const _: () = {
+            #the_pin_data
+            #unpin_impl
+            #drop_impl
+        };
+    })
+}
+
+struct SelfReplacer(PathSegment);
+
+impl VisitMut for SelfReplacer {
+    fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) {
+        if seg.ident == "Self" {
+            *seg = self.0.clone();
+        } else {
+            visit_path_segment_mut(self, seg);
+        }
+    }
+
+    fn visit_item_mut(&mut self, _: &mut syn::Item) {
+        // Do not descend into items, since items reset/change what `Self` refers to.
+    }
+}
+
+fn is_pinned(field: &Field) -> bool {
+    field.attrs.iter().any(|a| a.path().is_ident("pin"))
+}
+
+fn is_phantom_pinned(ty: &Type) -> bool {
+    match ty {
+        Type::Path(TypePath { qself: None, path }) => {
+            // Cannot possibly refer to `PhantomPinned`.
+            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 generate_the_pin_data(
+    ItemStruct {
+        vis,
+        ident,
+        generics,
+        fields,
+        ..
+    }: &ItemStruct,
+) -> TokenStream {
+    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
+
+    // For every field, we create a 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 must be initialized via `Init`.
+    let pinned_field_accessors = fields
+        .iter()
+        .filter(|f| is_pinned(f))
+        .map(|Field { vis, ident, ty, .. }| {
+            quote! {
+                #vis unsafe fn #ident<E>(
+                    self,
+                    slot: *mut #ty,
+                    init: impl ::pin_init::PinInit<#ty, E>,
+                ) -> ::core::result::Result<(), E> {
+                    unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
+                }
+            }
+        })
+        .collect::<TokenStream>();
+    let not_pinned_field_accessors = fields
+        .iter()
+        .filter(|f| !is_pinned(f))
+        .map(|Field { vis, ident, ty, .. }| {
+            quote! {
+                #vis unsafe fn #ident<E>(
+                    self,
+                    slot: *mut #ty,
+                    init: impl ::pin_init::Init<#ty, E>,
+                ) -> ::core::result::Result<(), E> {
+                    unsafe { ::pin_init::Init::__init(init, slot) }
+                }
+            }
+        })
+        .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.
+        #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_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
+        {
+            #pinned_field_accessors
+            #not_pinned_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 }
+            }
+        }
+
+        unsafe impl #impl_generics
+            ::pin_init::__internal::PinData for __ThePinData #ty_generics
+            #whr
+        {
+            type Datee = #ident #ty_generics;
+        }
+    }
+}
+
+fn unpin_impl(
+    ItemStruct {
+        ident,
+        generics,
+        fields,
+        ..
+    }: &ItemStruct,
+) -> TokenStream {
+    let generics_with_pinlt = {
+        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_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 drop_impl(
+    ItemStruct {
+        ident, generics, ..
+    }: &ItemStruct,
+    args: TokenStream,
+) -> Result<TokenStream> {
+    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
+    let has_pinned_drop = match syn::parse2::<Option<syn::Ident>>(args.clone()) {
+        Ok(None) => false,
+        Ok(Some(ident)) if ident == "PinnedDrop" => true,
+        _ => {
+            return Err(Error::new_spanned(
+                args,
+                "Expected nothing or `PinnedDrop` as arguments to `#[pin_data]`.",
+            ))
+        }
+    };
+    // We need to disallow normal `Drop` implementation, the exact behavior depends on whether
+    // `PinnedDrop` was specified in `args`.
+    Ok(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);
+                }
+            }
+        }
+    } 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
+            {}
+        }
+    })
+}
-- 
2.48.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC PATCH 4/6] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro
  2025-03-04 22:56 [RFC PATCH 0/6] add `syn` versions of pin-init macros Benno Lossin
                   ` (2 preceding siblings ...)
  2025-03-04 22:57 ` [RFC PATCH 3/6] rust: pin-init: internal: add syn version of the `pin_data` " Benno Lossin
@ 2025-03-04 22:57 ` Benno Lossin
  2025-03-04 22:57 ` [RFC PATCH 5/6] rust: pin-init: allow doctests to refer to the pin-init crate Benno Lossin
  2025-03-04 22:57 ` [RFC PATCH 6/6] rust: pin-init: internal: add syn version of `[try_][pin_]init!` macros Benno Lossin
  5 siblings, 0 replies; 7+ messages in thread
From: Benno Lossin @ 2025-03-04 22:57 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

`#[pin_data]` uses some auxiliary traits to ensure that a user does not
implement `Drop` for the annotated struct, as that is unsound and can
lead to UB. However, if the struct that is annotated is `!Sized`, the
current bounds do not work, because `Sized` is an implicit bound for
generics.

This is *not* a soundness hole of pin-init, as it currently is
impossible to construct an unsized value using pin-init.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
---
 rust/pin-init/internal/src/syn_pin_data.rs | 6 +++---
 rust/pin-init/src/macros.rs                | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/rust/pin-init/internal/src/syn_pin_data.rs b/rust/pin-init/internal/src/syn_pin_data.rs
index d95176389e17..30cd4bc35fd0 100644
--- a/rust/pin-init/internal/src/syn_pin_data.rs
+++ b/rust/pin-init/internal/src/syn_pin_data.rs
@@ -293,7 +293,7 @@ fn drop(&mut self) {
             // if it also implements `Drop`
             trait MustNotImplDrop {}
             #[expect(drop_bounds)]
-            impl<T: ::core::ops::Drop> MustNotImplDrop for T {}
+            impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
             impl #impl_generics MustNotImplDrop for #ident #ty_generics
                 #whr
             {}
@@ -302,8 +302,8 @@ impl #impl_generics MustNotImplDrop for #ident #ty_generics
             // `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<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
+                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
             impl #impl_generics
                 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
                 #whr
diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs
index 361623324d5c..03e2588423cc 100644
--- a/rust/pin-init/src/macros.rs
+++ b/rust/pin-init/src/macros.rs
@@ -931,7 +931,7 @@ impl<'__pin, $($impl_generics)*> ::core::marker::Unpin for $name<$($ty_generics)
         // if it also implements `Drop`
         trait MustNotImplDrop {}
         #[expect(drop_bounds)]
-        impl<T: ::core::ops::Drop> MustNotImplDrop for T {}
+        impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
         impl<$($impl_generics)*> MustNotImplDrop for $name<$($ty_generics)*>
         where $($whr)* {}
         // We also take care to prevent users from writing a useless `PinnedDrop` implementation.
@@ -939,7 +939,7 @@ impl<$($impl_generics)*> MustNotImplDrop for $name<$($ty_generics)*>
         // `PinnedDrop` as the parameter to `#[pin_data]`.
         #[expect(non_camel_case_types)]
         trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
-        impl<T: $crate::PinnedDrop>
+        impl<T: $crate::PinnedDrop + ?::core::marker::Sized>
             UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
         impl<$($impl_generics)*>
             UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for $name<$($ty_generics)*>
-- 
2.48.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC PATCH 5/6] rust: pin-init: allow doctests to refer to the pin-init crate
  2025-03-04 22:56 [RFC PATCH 0/6] add `syn` versions of pin-init macros Benno Lossin
                   ` (3 preceding siblings ...)
  2025-03-04 22:57 ` [RFC PATCH 4/6] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Benno Lossin
@ 2025-03-04 22:57 ` Benno Lossin
  2025-03-04 22:57 ` [RFC PATCH 6/6] rust: pin-init: internal: add syn version of `[try_][pin_]init!` macros Benno Lossin
  5 siblings, 0 replies; 7+ messages in thread
From: Benno Lossin @ 2025-03-04 22:57 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: rust-for-linux, linux-kernel

The next commit will name the pin-init crate from proc macros via
`::pin_init`. For this to work within tests of the pin-init crate
itself, it needs to be able to refer to itself via that name. Thus add
the required code for the name to be available.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
---
 rust/pin-init/src/lib.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index cb015b118283..356fc959bcb9 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -282,6 +282,9 @@
 #[cfg(any(feature = "std", feature = "alloc"))]
 pub use alloc::InPlaceInit;
 
+#[allow(unused_extern_crates)]
+extern crate self as pin_init;
+
 /// Used to specify the pinning information of the fields of a struct.
 ///
 /// This is somewhat similar in purpose as
-- 
2.48.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC PATCH 6/6] rust: pin-init: internal: add syn version of `[try_][pin_]init!` macros
  2025-03-04 22:56 [RFC PATCH 0/6] add `syn` versions of pin-init macros Benno Lossin
                   ` (4 preceding siblings ...)
  2025-03-04 22:57 ` [RFC PATCH 5/6] rust: pin-init: allow doctests to refer to the pin-init crate Benno Lossin
@ 2025-03-04 22:57 ` Benno Lossin
  5 siblings, 0 replies; 7+ messages in thread
From: Benno Lossin @ 2025-03-04 22:57 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich
  Cc: linux-kernel, rust-for-linux

Implement the `[try_][pin_]init!` derive macro using syn to simplify
parsing by not going through an additional declarative macro.
This not only simplifies the code by a lot, increasing maintainability
and making it easier to implement new features. But also improves the
user experience by improving the error messages one gets when giving
incorrect inputs to the macro.
For example, placing a `,` after `..Zeroable::zeroed()` is not allowed:

    use pin_init::*;

    #[derive(Zeroable)]
    struct Foo {
        a: usize,
        b: usize,
    }

    fn main() {
        let _ = init!(Foo {
            a: 0,
            ..Zeroable::zeroed(),
        });
    }

The declarative macro produces this error:

    error: no rules expected `,`
       |
    11 |       let _ = init!(Foo {
       |  _____________^
    12 | |         a: 0,
    13 | |         ..Zeroable::zeroed(),
    14 | |     });
       | |______^ no rules expected this token in macro call
       |
    note: while trying to match `)`
      --> src/macros.rs
       |
       |         @munch_fields($(..Zeroable::zeroed())? $(,)?),
       |                                                     ^
       = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)

    error: no rules expected `,`
       |
    11 |       let _ = init!(Foo {
       |  _____________^
    12 | |         a: 0,
    13 | |         ..Zeroable::zeroed(),
    14 | |     });
       | |______^ no rules expected this token in macro call
       |
    note: while trying to match `)`
      --> src/macros.rs
       |
       |         @munch_fields(..Zeroable::zeroed() $(,)?),
       |                                                 ^
       = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)

The syn version reduces this error to the much more manageable:

    error: unexpected token, expected `}`
       |
    12 |         ..Zeroable::zeroed(),
       |                             ^

This reimplementation is benefiting the most from syn, as can be seen in
this example. It declares a struct with a single generic, but then
supplies two type arguments in the initializer:

    use pin_init::*;

    struct Foo<T> {
        value: T,
    }
    fn main() {
        let _ = init!(Foo::<(), ()> {
            value <- (),
        });
    }

The declarative version emits the following unreadable mess of an error
(shortened for brevity of the commit message):

    error: struct literal body without path
      |
    7 |       let _ = init!(Foo::<(), ()> {
      |  _____________^
    8 | |         value <- (),
    9 | |     });
      | |______^
      |
      = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
    help: you might have forgotten to add the struct literal inside the block
     --> src/macros.rs
      |
      ~                 ::core::ptr::write($slot, $t { SomeStruct {
      |9                    $($acc)*
      ~                 } });
      |

<...40 lines skipped...>

    error[E0061]: this function takes 2 arguments but 3 arguments were supplied
      |
    7 |       let _ = init!(Foo::<(), ()> {
      |  _____________^
    8 | |         value <- (),
    9 | |     });
      | |______^ unexpected argument #3
      |
    note: function defined here
     --> $RUST/core/src/ptr/mod.rs
      |
      | pub const unsafe fn write<T>(dst: *mut T, src: T) {
      |                     ^^^^^
      = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)

This error delightfully reduces to the simple and clear message:

    error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied
      |
    7 |     let _ = init!(Foo::<(), ()> {
      |                   ^^^     ---- help: remove the unnecessary generic argument
      |                   |
      |                   expected 1 generic argument
      |
    note: struct defined here, with 1 generic parameter: `T`
     --> tests/ui/compile-fail/init/wrong_generics2.rs:3:8
      |
    3 | struct Foo<T> {
      |        ^^^ -

The syn version is only enabled in the user-space version and disabled
in the kernel until syn becomes available there.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
---
 rust/pin-init/internal/src/init.rs | 404 +++++++++++++++++++++++++++++
 rust/pin-init/internal/src/lib.rs  |  78 ++++++
 rust/pin-init/src/__internal.rs    |  62 +++++
 rust/pin-init/src/lib.rs           |  66 +----
 4 files changed, 548 insertions(+), 62 deletions(-)
 create mode 100644 rust/pin-init/internal/src/init.rs

diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
new file mode 100644
index 000000000000..4d325cd3c006
--- /dev/null
+++ b/rust/pin-init/internal/src/init.rs
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned};
+use syn::{
+    braced,
+    parse::{Parse, ParseStream},
+    parse_quote,
+    punctuated::Punctuated,
+    spanned::Spanned,
+    token, Error, Expr, ExprCall, ExprPath, Path, Result, Token, Type,
+};
+
+pub(crate) struct InPlaceInitializer {
+    this: Option<This>,
+    path: Path,
+    brace_token: token::Brace,
+    fields: Punctuated<FieldInitializer, Token![,]>,
+    rest: Option<(Token![..], Expr)>,
+    error: Option<(Token![?], Type)>,
+}
+
+struct This {
+    _and_token: Token![&],
+    ident: Ident,
+    _in_token: Token![in],
+}
+
+enum FieldInitializer {
+    Value {
+        ident: Ident,
+        value: Option<(Token![:], Expr)>,
+    },
+    Init {
+        ident: Ident,
+        _larrow: Token![<-],
+        value: Expr,
+    },
+}
+
+impl FieldInitializer {
+    fn ident(&self) -> &Ident {
+        match self {
+            FieldInitializer::Value { ident, .. } | FieldInitializer::Init { ident, .. } => ident,
+        }
+    }
+}
+
+enum InitKind {
+    Normal,
+    Zeroing,
+}
+
+pub(crate) fn init(
+    InPlaceInitializer {
+        this,
+        path,
+        fields,
+        rest,
+        mut error,
+        brace_token,
+        ..
+    }: InPlaceInitializer,
+    default_error: bool,
+    pin: bool,
+) -> Result<TokenStream> {
+    if default_error {
+        error.get_or_insert((
+            Default::default(),
+            parse_quote!(::core::convert::Infallible),
+        ));
+    }
+    let Some((_, error)) = error else {
+        return Err(Error::new(
+            brace_token.span.close(),
+            "expected `? <type>` after `}`",
+        ));
+    };
+    let use_data = pin;
+    let (has_data_trait, data_trait, get_data, from_closure) = match pin {
+        true => (
+            format_ident!("HasPinData"),
+            format_ident!("PinData"),
+            format_ident!("__pin_data"),
+            format_ident!("pin_init_from_closure"),
+        ),
+        false => (
+            format_ident!("HasInitData"),
+            format_ident!("InitData"),
+            format_ident!("__init_data"),
+            format_ident!("init_from_closure"),
+        ),
+    };
+
+    let init_kind = get_init_kind(rest)?;
+    let zeroable_check = match init_kind {
+        InitKind::Normal => quote! {},
+        InitKind::Zeroing => quote! {
+            // The user specified `..Zeroable::zeroed()` at the end of the list of fields.
+            // Therefore we check if the struct implements `Zeroable` and then zero the memory.
+            // This allows us to also remove the check that all fields are present (since we
+            // already set the memory to zero and that is a valid bit pattern).
+            fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
+            where T: ::pin_init::Zeroable
+            {}
+            // Ensure that the struct is indeed `Zeroable`.
+            assert_zeroable(slot);
+            // SAFETY: The type implements `Zeroable` by the check above.
+            unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
+        },
+    };
+
+    let this = match this {
+        None => quote!(),
+        Some(This { ident, .. }) => quote! {
+            // Create the `this` so it can be referenced by the user inside of the
+            // expressions creating the individual fields.
+            let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
+        },
+    };
+    let data = format_ident!("data", span = Span::mixed_site());
+    let init_fields = init_fields(&fields, use_data, &data);
+    let field_check = make_field_check(&fields, init_kind, &path);
+    Ok(quote! {{
+        // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
+        // type and shadow it later when we insert the arbitrary user code. That way there will be
+        // no possibility of returning without `unsafe`.
+        struct __InitOk;
+        // Get the data about fields from the supplied type.
+        let #data = unsafe {
+            use ::pin_init::__internal::#has_data_trait;
+            // Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit
+            // generics (which need to be present with that syntax).
+            #path::#get_data()
+        };
+        // Ensure that `#data` really is of type `#data` and help with type inference:
+        let init = ::pin_init::__internal::#data_trait::make_closure::<_, __InitOk, #error>(
+            #data,
+            move |slot| {
+                {
+                    // Shadow the structure so it cannot be used to return early.
+                    struct __InitOk;
+
+                    #zeroable_check
+
+                    #this
+
+                    #init_fields
+
+                    #field_check
+                }
+                Ok(__InitOk)
+            }
+        );
+        let init = move |slot| -> ::core::result::Result<(), #error> {
+            init(slot).map(|__InitOk| ())
+        };
+        let init = unsafe { ::pin_init::#from_closure::<_, #error>(init) };
+        init
+    }})
+}
+
+fn get_init_kind(rest: Option<(Token![..], Expr)>) -> Result<InitKind> {
+    let Some((dotdot, expr)) = rest else {
+        return Ok(InitKind::Normal);
+    };
+    let error = Error::new_spanned(
+        quote!(#dotdot #expr),
+        "Expected one of the following:\n- Nothing.\n- `..Zeroable::zeroed()`.",
+    );
+    let Expr::Call(ExprCall {
+        func, args, attrs, ..
+    }) = expr
+    else {
+        return Err(error);
+    };
+    if !args.is_empty() || !attrs.is_empty() {
+        return Err(error);
+    }
+    match *func {
+        Expr::Path(ExprPath {
+            attrs,
+            qself: None,
+            path:
+                Path {
+                    leading_colon: None,
+                    segments,
+                },
+        }) if attrs.is_empty()
+            && segments.len() == 2
+            && segments[0].ident == "Zeroable"
+            && segments[0].arguments.is_none()
+            && segments[1].ident == "zeroed"
+            && segments[1].arguments.is_none() =>
+        {
+            Ok(InitKind::Zeroing)
+        }
+        _ => Err(error),
+    }
+}
+
+/// Generate the code that initializes the fields of the struct using the initializers in `field`.
+fn init_fields(
+    fields: &Punctuated<FieldInitializer, Token![,]>,
+    use_data: bool,
+    data: &Ident,
+) -> TokenStream {
+    let mut guards = vec![];
+    let mut res = TokenStream::new();
+    for field in fields {
+        let ident = field.ident();
+        let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
+        guards.push(guard.clone());
+        let init = match field {
+            FieldInitializer::Value { ident, value } => {
+                let mut value_ident = ident.clone();
+                let value = value.as_ref().map(|value| &value.1).map(|value| {
+                    // Setting the span of `value_ident` to `value`'s span improves error messages
+                    // when the type of `value` is wrong.
+                    value_ident.set_span(value.span());
+                    quote!(let #value_ident = #value;)
+                });
+                // Again span for better diagnostics
+                let write = quote_spanned! {ident.span()=>
+                    ::core::ptr::write
+                };
+                quote! {
+                    {
+                        #value
+                        // Initialize the field.
+                        //
+                        // SAFETY: The memory at `slot` is uninitialized.
+                        unsafe { #write(::core::ptr::addr_of_mut!((*slot).#ident), #value_ident) };
+                    }
+                }
+            }
+            FieldInitializer::Init { ident, value, .. } => {
+                if use_data {
+                    quote! {
+                        let init = #value;
+                        // Call the initializer.
+                        //
+                        // SAFETY: `slot` is valid, because we are inside of an initializer closure,
+                        // we return when an error/panic occurs.
+                        // We also use the `#data` to require the correct trait (`Init` or `PinInit`)
+                        // for `#ident`.
+                        unsafe { #data.#ident(::core::ptr::addr_of_mut!((*slot).#ident), init)? };
+                    }
+                } else {
+                    quote! {
+                        let init = #value;
+                        // Call the initializer.
+                        //
+                        // SAFETY: `slot` is valid, because we are inside of an initializer closure,
+                        // we return when an error/panic occurs.
+                        unsafe {
+                            ::pin_init::Init::__init(
+                                init,
+                                ::core::ptr::addr_of_mut!((*slot).#ident),
+                            )?
+                        };
+                    }
+                }
+            }
+        };
+        res.extend(init);
+        res.extend(quote! {
+            // Create the drop guard:
+            //
+            // We rely on macro hygiene to make it impossible for users to access this local
+            // variable.
+            // SAFETY: We forget the guard later when initialization has succeeded.
+            let #guard = unsafe {
+                ::pin_init::__internal::DropGuard::new(
+                    ::core::ptr::addr_of_mut!((*slot).#ident)
+                )
+            };
+        });
+    }
+    quote! {
+        #res
+        // If execution reaches this point, all fields have been initialized. Therefore we can now
+        // dismiss the guards by forgetting them.
+        #(::core::mem::forget(#guards);)*
+    }
+}
+
+/// Generate the check for ensuring that every field has been initialized.
+fn make_field_check(
+    fields: &Punctuated<FieldInitializer, Token![,]>,
+    init_kind: InitKind,
+    path: &Path,
+) -> TokenStream {
+    let fields = fields.iter().map(|f| f.ident());
+    match init_kind {
+        InitKind::Normal => quote! {
+            // We use unreachable code to ensure that all fields have been mentioned exactly once,
+            // this struct initializer will still be type-checked and complain with a very natural
+            // error message if a field is forgotten/mentioned more than once.
+            #[allow(unreachable_code, clippy::diverging_sub_expression)]
+            // SAFETY: this code is never executed.
+            let _ = || unsafe {
+                ::core::ptr::write(slot, #path {
+                    #(
+                        #fields: ::core::panic!(),
+                    )*
+                })
+            };
+        },
+        InitKind::Zeroing => quote! {
+            // We use unreachable code to ensure that all fields have been mentioned at most once.
+            // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
+            // be zeroed. This struct initializer will still be type-checked and complain with a
+            // very natural error message if a field is mentioned more than once, or doesn't exist.
+            #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
+            // SAFETY: this code is never executed.
+            let _ = || unsafe {
+                let mut zeroed = ::core::mem::zeroed();
+                ::core::ptr::write(slot, zeroed);
+                zeroed = ::core::mem::zeroed();
+                ::core::ptr::write(slot, #path {
+                    #(
+                        #fields: ::core::panic!(),
+                    )*
+                    ..zeroed
+                })
+            };
+        },
+    }
+}
+
+impl Parse for InPlaceInitializer {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let content;
+        Ok(Self {
+            this: input.peek(Token![&]).then(|| input.parse()).transpose()?,
+            path: input.parse()?,
+            brace_token: braced!(content in input),
+            fields: {
+                let mut fields = Punctuated::new();
+                loop {
+                    let lookahead = content.lookahead1();
+                    if content.is_empty() || lookahead.peek(Token![..]) {
+                        break;
+                    } else if lookahead.peek(syn::Ident) {
+                        fields.push_value(content.parse()?);
+                        let lookahead = content.lookahead1();
+                        if lookahead.peek(Token![,]) {
+                            fields.push_punct(content.parse()?);
+                        } else if content.is_empty() {
+                            break;
+                        } else {
+                            return Err(lookahead.error());
+                        }
+                    } else {
+                        return Err(lookahead.error());
+                    }
+                }
+                fields
+            },
+            rest: content
+                .peek(Token![..])
+                .then(|| -> Result<_> { Ok((content.parse()?, content.parse()?)) })
+                .transpose()?,
+            error: input
+                .peek(Token![?])
+                .then(|| -> Result<_> { Ok((input.parse()?, input.parse()?)) })
+                .transpose()?,
+        })
+    }
+}
+
+impl Parse for This {
+    fn parse(input: ParseStream) -> Result<Self> {
+        Ok(Self {
+            _and_token: input.parse()?,
+            ident: input.parse()?,
+            _in_token: input.parse()?,
+        })
+    }
+}
+
+impl Parse for FieldInitializer {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let ident = input.parse()?;
+        let lookahead = input.lookahead1();
+        Ok(if lookahead.peek(Token![<-]) {
+            Self::Init {
+                ident,
+                _larrow: input.parse()?,
+                value: input.parse()?,
+            }
+        } else if lookahead.peek(Token![:]) {
+            Self::Value {
+                ident,
+                value: Some((input.parse()?, input.parse()?)),
+            }
+        } else if lookahead.peek(Token![,]) || input.is_empty() {
+            Self::Value { ident, value: None }
+        } else {
+            return Err(lookahead.error());
+        })
+    }
+}
diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 9ef6178e410f..bf524161022a 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -32,6 +32,8 @@
 #[cfg(kernel)]
 mod zeroable;
 
+#[cfg(not(kernel))]
+mod init;
 #[cfg(not(kernel))]
 #[path = "syn_pin_data.rs"]
 mod pin_data;
@@ -56,3 +58,79 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
 pub fn derive_zeroable(input: TokenStream) -> TokenStream {
     zeroable::derive(input.into()).into()
 }
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn pin_init(input: TokenStream) -> TokenStream {
+    quote!(::pin_init::__internal_pin_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn pin_init(input: TokenStream) -> TokenStream {
+    use syn::parse_macro_input;
+    init::init(
+        parse_macro_input!(input as init::InPlaceInitializer),
+        true,
+        true,
+    )
+    .unwrap_or_else(|e| e.into_compile_error())
+    .into()
+}
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn init(input: TokenStream) -> TokenStream {
+    quote!(::pin_init::__internal_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn init(input: TokenStream) -> TokenStream {
+    use syn::parse_macro_input;
+    init::init(
+        parse_macro_input!(input as init::InPlaceInitializer),
+        true,
+        false,
+    )
+    .unwrap_or_else(|e| e.into_compile_error())
+    .into()
+}
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn try_pin_init(input: TokenStream) -> TokenStream {
+    quote!(::pin_init::__internal_try_pin_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn try_pin_init(input: TokenStream) -> TokenStream {
+    use syn::parse_macro_input;
+    init::init(
+        parse_macro_input!(input as init::InPlaceInitializer),
+        false,
+        true,
+    )
+    .unwrap_or_else(|e| e.into_compile_error())
+    .into()
+}
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn try_init(input: TokenStream) -> TokenStream {
+    quote!(::pin_init::__internal_try_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn try_init(input: TokenStream) -> TokenStream {
+    use syn::parse_macro_input;
+    init::init(
+        parse_macro_input!(input as init::InPlaceInitializer),
+        false,
+        false,
+    )
+    .unwrap_or_else(|e| e.into_compile_error())
+    .into()
+}
diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs
index 6b258502a9d7..741af6f2fdf6 100644
--- a/rust/pin-init/src/__internal.rs
+++ b/rust/pin-init/src/__internal.rs
@@ -287,3 +287,65 @@ unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> {
         Err(())
     }
 }
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_init {
+    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+        $($fields:tt)*
+    }) => {
+        $crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
+            $($fields)*
+        }? ::core::convert::Infallible)
+    };
+}
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_pin_init {
+    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+        $($fields:tt)*
+    }) => {
+        $crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
+            $($fields)*
+        }? ::core::convert::Infallible)
+    };
+}
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_try_init {
+    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+        $($fields:tt)*
+    }? $err:ty) => {
+        $crate::__init_internal!(
+            @this($($this)?),
+            @typ($t $(::<$($generics),*>)?),
+            @fields($($fields)*),
+            @error($err),
+            @data(InitData, /*no use_data*/),
+            @has_data(HasInitData, __init_data),
+            @construct_closure(init_from_closure),
+            @munch_fields($($fields)*),
+        )
+    };
+}
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_try_pin_init {
+    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+        $($fields:tt)*
+    }? $err:ty) => {
+        $crate::__init_internal!(
+            @this($($this)?),
+            @typ($t $(::<$($generics),*>)? ),
+            @fields($($fields)*),
+            @error($err),
+            @data(PinData, use_data),
+            @has_data(HasPinData, __pin_data),
+            @construct_closure(pin_init_from_closure),
+            @munch_fields($($fields)*),
+        )
+    };
+}
diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index 356fc959bcb9..a5d86aae2e59 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -717,18 +717,7 @@ macro_rules! stack_try_pin_init {
 /// ```
 ///
 /// [`NonNull<Self>`]: core::ptr::NonNull
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! pin_init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }) => {
-        $crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? ::core::convert::Infallible)
-    };
-}
+pub use pin_init_internal::pin_init;
 
 /// Construct an in-place, fallible pinned initializer for `struct`s.
 ///
@@ -768,25 +757,7 @@ macro_rules! pin_init {
 /// }
 /// # let _ = Box::pin_init(BigBuf::new());
 /// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! try_pin_init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }? $err:ty) => {
-        $crate::__init_internal!(
-            @this($($this)?),
-            @typ($t $(::<$($generics),*>)? ),
-            @fields($($fields)*),
-            @error($err),
-            @data(PinData, use_data),
-            @has_data(HasPinData, __pin_data),
-            @construct_closure(pin_init_from_closure),
-            @munch_fields($($fields)*),
-        )
-    }
-}
+pub use pin_init_internal::try_pin_init;
 
 /// Construct an in-place initializer for `struct`s.
 ///
@@ -824,18 +795,7 @@ macro_rules! try_pin_init {
 /// }
 /// # let _ = Box::init(BigBuf::new());
 /// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }) => {
-        $crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
-            $($fields)*
-        }? ::core::convert::Infallible)
-    }
-}
+pub use pin_init_internal::init;
 
 /// Construct an in-place fallible initializer for `struct`s.
 ///
@@ -873,25 +833,7 @@ macro_rules! init {
 /// }
 /// # let _ = Box::init(BigBuf::new());
 /// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! try_init {
-    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
-        $($fields:tt)*
-    }? $err:ty) => {
-        $crate::__init_internal!(
-            @this($($this)?),
-            @typ($t $(::<$($generics),*>)?),
-            @fields($($fields)*),
-            @error($err),
-            @data(InitData, /*no use_data*/),
-            @has_data(HasInitData, __init_data),
-            @construct_closure(init_from_closure),
-            @munch_fields($($fields)*),
-        )
-    }
-}
+pub use pin_init_internal::try_init;
 
 /// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is
 /// structurally pinned.
-- 
2.48.1



^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-03-04 22:57 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-04 22:56 [RFC PATCH 0/6] add `syn` versions of pin-init macros Benno Lossin
2025-03-04 22:56 ` [RFC PATCH 1/6] rust: pin-init: internal: add syn version of the `Zeroable` derive macro Benno Lossin
2025-03-04 22:57 ` [RFC PATCH 2/6] rust: pin-init: internal: add syn version of `pinned_drop` proc macro Benno Lossin
2025-03-04 22:57 ` [RFC PATCH 3/6] rust: pin-init: internal: add syn version of the `pin_data` " Benno Lossin
2025-03-04 22:57 ` [RFC PATCH 4/6] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro Benno Lossin
2025-03-04 22:57 ` [RFC PATCH 5/6] rust: pin-init: allow doctests to refer to the pin-init crate Benno Lossin
2025-03-04 22:57 ` [RFC PATCH 6/6] rust: pin-init: internal: add syn version of `[try_][pin_]init!` macros Benno Lossin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).