* [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 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