From c455f19bbe6104debd980bb15515faf716bd81b8 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:40 +0000 Subject: [PATCH 01/59] rust: xarray: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Acked-by: Tamir Duberstein Link: https://lore.kernel.org/r/20260105-define-rust-helper-v2-27-51da5f454a67@google.com Signed-off-by: Andreas Hindborg --- rust/helpers/xarray.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/helpers/xarray.c b/rust/helpers/xarray.c index 60b299f11451..08979b304341 100644 --- a/rust/helpers/xarray.c +++ b/rust/helpers/xarray.c @@ -2,27 +2,27 @@ #include -int rust_helper_xa_err(void *entry) +__rust_helper int rust_helper_xa_err(void *entry) { return xa_err(entry); } -void rust_helper_xa_init_flags(struct xarray *xa, gfp_t flags) +__rust_helper void rust_helper_xa_init_flags(struct xarray *xa, gfp_t flags) { return xa_init_flags(xa, flags); } -int rust_helper_xa_trylock(struct xarray *xa) +__rust_helper int rust_helper_xa_trylock(struct xarray *xa) { return xa_trylock(xa); } -void rust_helper_xa_lock(struct xarray *xa) +__rust_helper void rust_helper_xa_lock(struct xarray *xa) { return xa_lock(xa); } -void rust_helper_xa_unlock(struct xarray *xa) +__rust_helper void rust_helper_xa_unlock(struct xarray *xa) { return xa_unlock(xa); } From 61d62ab08f0e62f89929f3920c0b6521d849fd57 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:16 +0100 Subject: [PATCH 02/59] rust: pin-init: remove `try_` versions of the initializer macros The `try_[pin_]init!` versions of the initializer macros are superfluous. Instead of forcing the user to always write an error in `try_[pin_]init!` and not allowing one in `[pin_]init!`, combine them into `[pin_]init!` that defaults the error to `core::convert::Infallible`, but also allows to specify a custom one. Projects using pin-init still can provide their own defaulting initializers using the `try_` prefix by using the `#[default_error]` attribute added in a future patch. [ Adjust the definition of the kernel's version of the `try_` initializer macros - Benno] Reviewed-by: Gary Guo Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- rust/kernel/init.rs | 8 +- rust/pin-init/README.md | 2 +- rust/pin-init/examples/linked_list.rs | 19 ++-- rust/pin-init/examples/pthread_mutex.rs | 10 +- rust/pin-init/src/lib.rs | 118 ++++-------------------- 5 files changed, 35 insertions(+), 122 deletions(-) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 899b9a962762..917f7ef001fd 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -222,14 +222,14 @@ macro_rules! try_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $crate::error::Error) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - ::pin_init::try_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $err) }; @@ -282,14 +282,14 @@ macro_rules! try_pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $crate::error::Error) }; ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }? $err:ty) => { - ::pin_init::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { + ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? $err) }; diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md index 74bbb4e0a2f7..6cee6ab1eb57 100644 --- a/rust/pin-init/README.md +++ b/rust/pin-init/README.md @@ -135,7 +135,7 @@ struct DriverData { impl DriverData { fn new() -> impl PinInit { - try_pin_init!(Self { + pin_init!(Self { status <- CMutex::new(0), buffer: Box::init(pin_init::init_zeroed())?, }? Error) diff --git a/rust/pin-init/examples/linked_list.rs b/rust/pin-init/examples/linked_list.rs index f9e117c7dfe0..8445a5890cb7 100644 --- a/rust/pin-init/examples/linked_list.rs +++ b/rust/pin-init/examples/linked_list.rs @@ -6,7 +6,6 @@ use core::{ cell::Cell, - convert::Infallible, marker::PhantomPinned, pin::Pin, ptr::{self, NonNull}, @@ -31,31 +30,31 @@ pub struct ListHead { impl ListHead { #[inline] - pub fn new() -> impl PinInit { - try_pin_init!(&this in Self { + pub fn new() -> impl PinInit { + pin_init!(&this in Self { next: unsafe { Link::new_unchecked(this) }, prev: unsafe { Link::new_unchecked(this) }, pin: PhantomPinned, - }? Infallible) + }) } #[inline] #[allow(dead_code)] - pub fn insert_next(list: &ListHead) -> impl PinInit + '_ { - try_pin_init!(&this in Self { + pub fn insert_next(list: &ListHead) -> impl PinInit + '_ { + pin_init!(&this in Self { prev: list.next.prev().replace(unsafe { Link::new_unchecked(this)}), next: list.next.replace(unsafe { Link::new_unchecked(this)}), pin: PhantomPinned, - }? Infallible) + }) } #[inline] - pub fn insert_prev(list: &ListHead) -> impl PinInit + '_ { - try_pin_init!(&this in Self { + pub fn insert_prev(list: &ListHead) -> impl PinInit + '_ { + pin_init!(&this in Self { next: list.prev.next().replace(unsafe { Link::new_unchecked(this)}), prev: list.prev.replace(unsafe { Link::new_unchecked(this)}), pin: PhantomPinned, - }? Infallible) + }) } #[inline] diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs index 49b004c8c137..4e082ec7d5de 100644 --- a/rust/pin-init/examples/pthread_mutex.rs +++ b/rust/pin-init/examples/pthread_mutex.rs @@ -98,11 +98,11 @@ mod pthread_mtx { // SAFETY: mutex has been initialized unsafe { pin_init_from_closure(init) } } - try_pin_init!(Self { - data: UnsafeCell::new(data), - raw <- init_raw(), - pin: PhantomPinned, - }? Error) + pin_init!(Self { + data: UnsafeCell::new(data), + raw <- init_raw(), + pin: PhantomPinned, + }? Error) } #[allow(dead_code)] diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 8dc9dd5ac6fd..8673008f45d2 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -146,7 +146,7 @@ //! //! impl DriverData { //! fn new() -> impl PinInit { -//! try_pin_init!(Self { +//! pin_init!(Self { //! status <- CMutex::new(0), //! buffer: Box::init(pin_init::init_zeroed())?, //! }? Error) @@ -528,7 +528,7 @@ macro_rules! stack_pin_init { /// x: u32, /// } /// -/// stack_try_pin_init!(let foo: Foo = try_pin_init!(Foo { +/// stack_try_pin_init!(let foo: Foo = pin_init!(Foo { /// a <- CMutex::new(42), /// b: Box::try_new(Bar { /// x: 64, @@ -555,7 +555,7 @@ macro_rules! stack_pin_init { /// x: u32, /// } /// -/// stack_try_pin_init!(let foo: Foo =? try_pin_init!(Foo { +/// stack_try_pin_init!(let foo: Foo =? pin_init!(Foo { /// a <- CMutex::new(42), /// b: Box::try_new(Bar { /// x: 64, @@ -584,10 +584,10 @@ macro_rules! stack_try_pin_init { }; } -/// Construct an in-place, pinned initializer for `struct`s. +/// Construct an in-place, fallible pinned initializer for `struct`s. /// -/// This macro defaults the error to [`Infallible`]. If you need a different error, then use -/// [`try_pin_init!`]. +/// The error type defaults to [`Infallible`]; if you need a different one, write `? Error` at the +/// end, after the struct initializer. /// /// The syntax is almost identical to that of a normal `struct` initializer: /// @@ -783,54 +783,10 @@ macro_rules! pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - $crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { + $crate::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? ::core::convert::Infallible) }; -} - -/// Construct an in-place, fallible pinned initializer for `struct`s. -/// -/// If the initialization can complete without error (or [`Infallible`]), then use [`pin_init!`]. -/// -/// You can use the `?` operator or use `return Err(err)` inside the initializer to stop -/// initialization and return the error. -/// -/// IMPORTANT: if you have `unsafe` code inside of the initializer you have to ensure that when -/// initialization fails, the memory can be safely deallocated without any further modifications. -/// -/// The syntax is identical to [`pin_init!`] with the following exception: you must append `? $type` -/// after the `struct` initializer to specify the error type you want to use. -/// -/// # Examples -/// -/// ```rust -/// # #![feature(allocator_api)] -/// # #[path = "../examples/error.rs"] mod error; use error::Error; -/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, init_zeroed}; -/// -/// #[pin_data] -/// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, -/// small: [u8; 1024 * 1024], -/// ptr: *mut u8, -/// } -/// -/// impl BigBuf { -/// fn new() -> impl PinInit { -/// try_pin_init!(Self { -/// big: Box::init(init_zeroed())?, -/// small: [0; 1024 * 1024], -/// ptr: core::ptr::null_mut(), -/// }? Error) -/// } -/// } -/// # 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) => { @@ -847,10 +803,10 @@ macro_rules! try_pin_init { } } -/// Construct an in-place initializer for `struct`s. +/// Construct an in-place, fallible initializer for `struct`s. /// -/// This macro defaults the error to [`Infallible`]. If you need a different error, then use -/// [`try_init!`]. +/// This macro defaults the error to [`Infallible`]; if you need a different one, write `? Error` +/// at the end, after the struct initializer. /// /// The syntax is identical to [`pin_init!`] and its safety caveats also apply: /// - `unsafe` code must guarantee either full initialization or return an error and allow @@ -890,52 +846,10 @@ macro_rules! init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { $($fields:tt)* }) => { - $crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? { + $crate::init!($(&$this in)? $t $(::<$($generics),*>)? { $($fields)* }? ::core::convert::Infallible) - } -} - -/// Construct an in-place fallible initializer for `struct`s. -/// -/// If the initialization can complete without error (or [`Infallible`]), then use -/// [`init!`]. -/// -/// The syntax is identical to [`try_pin_init!`]. You need to specify a custom error -/// via `? $type` after the `struct` initializer. -/// The safety caveats from [`try_pin_init!`] also apply: -/// - `unsafe` code must guarantee either full initialization or return an error and allow -/// deallocation of the memory. -/// - the fields are initialized in the order given in the initializer. -/// - no references to fields are allowed to be created inside of the initializer. -/// -/// # Examples -/// -/// ```rust -/// # #![feature(allocator_api)] -/// # use core::alloc::AllocError; -/// # use pin_init::InPlaceInit; -/// use pin_init::{try_init, Init, init_zeroed}; -/// -/// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, -/// small: [u8; 1024 * 1024], -/// } -/// -/// impl BigBuf { -/// fn new() -> impl Init { -/// try_init!(Self { -/// big: Box::init(init_zeroed())?, -/// small: [0; 1024 * 1024], -/// }? AllocError) -/// } -/// } -/// # 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) => { @@ -1410,14 +1324,14 @@ where /// fn init_foo() -> impl PinInit { /// pin_init_scope(|| { /// let bar = lookup_bar()?; -/// Ok(try_pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error)) +/// Ok(pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error)) /// }) /// } /// ``` /// /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the /// initializer itself will fail with that error. If it returned `Ok`, then it will run the -/// initializer returned by the [`try_pin_init!`] invocation. +/// initializer returned by the [`pin_init!`] invocation. pub fn pin_init_scope(make_init: F) -> impl PinInit where F: FnOnce() -> Result, @@ -1453,14 +1367,14 @@ where /// fn init_foo() -> impl Init { /// init_scope(|| { /// let bar = lookup_bar()?; -/// Ok(try_init!(Foo { a: bar.a.into(), b: bar.b }? Error)) +/// Ok(init!(Foo { a: bar.a.into(), b: bar.b }? Error)) /// }) /// } /// ``` /// /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the /// initializer itself will fail with that error. If it returned `Ok`, then it will run the -/// initializer returned by the [`try_init!`] invocation. +/// initializer returned by the [`init!`] invocation. pub fn init_scope(make_init: F) -> impl Init where F: FnOnce() -> Result, From 901f1d73d2c68017065212d75ffbfbffa119921e Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:17 +0100 Subject: [PATCH 03/59] rust: pin-init: allow the crate to refer to itself as `pin-init` in doc tests The `syn` approach requires use of `::pin_init::...` instead of the `$crate::...` construct available to declarative macros. To be able to use the `pin_init` crate from itself (which includes doc tests), we have to declare it as such. Reviewed-by: Gary Guo Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- rust/pin-init/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 8673008f45d2..0e707f00061f 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -290,6 +290,11 @@ use core::{ ptr::{self, NonNull}, }; +// This is used by doc-tests -- the proc-macros expand to `::pin_init::...` and without this the +// doc-tests wouldn't have an extern crate named `pin_init`. +#[allow(unused_extern_crates)] +extern crate self as pin_init; + #[doc(hidden)] pub mod __internal; #[doc(hidden)] From 514e4ed2c9da9112fcd378b1bd9b9d120edf7ca9 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:18 +0100 Subject: [PATCH 04/59] rust: pin-init: add `syn` dependency and remove `proc-macro[2]` and `quote` workarounds `syn` makes parsing Rust from proc-macros a lot simpler. `pin-init` has not used `syn` up until now, because the we did not support it. That changed in commit 54e3eae85562 ("Merge patch series "`syn` support""), so we can finally utilize the added ergonomics of parsing proc-macro input with `syn`. Previously we only had the `proc-macro` library available, whereas the user-space version also used `proc-macro2` and `quote`. Now both are available, so remove the workarounds. Due to these changes, clippy emits warnings about unnecessary `.to_string()` as `proc-macro2` provides an additional `PartialEq` impl on `Ident`, so the warnings are fixed. [ Adjusted wording from upstream version and added build system changes for the kernel - Benno ] Co-developed-by: Gary Guo Signed-off-by: Gary Guo Reviewed-by: Tamir Duberstein Tested-by: Andreas Hindborg Signed-off-by: Benno Lossin --- rust/Makefile | 16 ++++++++++------ rust/pin-init/internal/src/helpers.rs | 7 ++----- rust/pin-init/internal/src/lib.rs | 16 ---------------- rust/pin-init/internal/src/pin_data.rs | 18 ++++++------------ rust/pin-init/internal/src/pinned_drop.rs | 10 ++++------ rust/pin-init/internal/src/zeroable.rs | 6 ++---- scripts/generate_rust_analyzer.py | 2 +- 7 files changed, 25 insertions(+), 50 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index 5d357dce1704..82bda6ccfe39 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -212,9 +212,10 @@ rustdoc-ffi: $(src)/ffi.rs rustdoc-core FORCE rustdoc-pin_init_internal: private rustdoc_host = yes rustdoc-pin_init_internal: private rustc_target_flags = --cfg kernel \ - --extern proc_macro --crate-type proc-macro + --extern proc_macro --extern proc_macro2 --extern quote --extern syn \ + --crate-type proc-macro rustdoc-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \ - rustdoc-clean FORCE + rustdoc-clean rustdoc-proc_macro2 rustdoc-quote rustdoc-syn FORCE +$(call if_changed,rustdoc) rustdoc-pin_init: private rustdoc_host = yes @@ -273,9 +274,10 @@ rusttestlib-macros: $(src)/macros/lib.rs \ +$(call if_changed,rustc_test_library) rusttestlib-pin_init_internal: private rustc_target_flags = --cfg kernel \ - --extern proc_macro + --extern proc_macro --extern proc_macro2 --extern quote --extern syn rusttestlib-pin_init_internal: private rustc_test_library_proc = yes -rusttestlib-pin_init_internal: $(src)/pin-init/internal/src/lib.rs FORCE +rusttestlib-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \ + rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE +$(call if_changed,rustc_test_library) rusttestlib-pin_init: private rustc_target_flags = --extern pin_init_internal \ @@ -547,8 +549,10 @@ $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \ $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE +$(call if_changed_dep,rustc_procmacro) -$(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel -$(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs FORCE +$(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel \ + --extern proc_macro2 --extern quote --extern syn +$(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs \ + $(obj)/libproc_macro2.rlib $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE +$(call if_changed_dep,rustc_procmacro) quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@ diff --git a/rust/pin-init/internal/src/helpers.rs b/rust/pin-init/internal/src/helpers.rs index 236f989a50f2..90f85eaa4123 100644 --- a/rust/pin-init/internal/src/helpers.rs +++ b/rust/pin-init/internal/src/helpers.rs @@ -1,9 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; - -use proc_macro::{TokenStream, TokenTree}; +use proc_macro2::{TokenStream, TokenTree}; /// Parsed generics. /// @@ -101,7 +98,7 @@ pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec) { 1 => { // Here depending on the token, it might be a generic variable name. match tt.clone() { - TokenTree::Ident(i) if at_start && i.to_string() == "const" => { + TokenTree::Ident(i) if at_start && i == "const" => { let Some(name) = toks.next() else { // Parsing error. break; diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index 297b0129a5bf..4c4dc639ce82 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -7,27 +7,11 @@ //! `pin-init` proc macros. #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] -// Allow `.into()` to convert -// - `proc_macro2::TokenStream` into `proc_macro::TokenStream` in the user-space version. -// - `proc_macro::TokenStream` into `proc_macro::TokenStream` in the kernel version. -// Clippy warns on this conversion, but it's required by the user-space version. -// -// Remove once we have `proc_macro2` in the kernel. -#![allow(clippy::useless_conversion)] // Documentation is done in the pin-init crate instead. #![allow(missing_docs)] use proc_macro::TokenStream; -#[cfg(kernel)] -#[path = "../../../macros/quote.rs"] -#[macro_use] -#[cfg_attr(not(kernel), rustfmt::skip)] -mod quote; -#[cfg(not(kernel))] -#[macro_use] -extern crate quote; - mod helpers; mod pin_data; mod pinned_drop; diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 87d4a7eb1d35..86a53b37cc66 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1,10 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; - use crate::helpers::{parse_generics, Generics}; -use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree}; +use proc_macro2::{Group, Punct, Spacing, TokenStream, TokenTree}; +use quote::quote; pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { // This proc-macro only does some pre-parsing and then delegates the actual parsing to @@ -28,7 +26,7 @@ pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { // The name of the struct with ty_generics. let struct_name = rest .iter() - .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i.to_string() == "struct")) + .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i == "struct")) .nth(1) .and_then(|tt| match tt { TokenTree::Ident(_) => { @@ -65,7 +63,7 @@ pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { .into_iter() .flat_map(|tt| { // We ignore top level `struct` tokens, since they would emit a compile error. - if matches!(&tt, TokenTree::Ident(i) if i.to_string() == "struct") { + if matches!(&tt, TokenTree::Ident(i) if i == "struct") { vec![tt] } else { replace_self_and_deny_type_defs(&struct_name, tt, &mut errs) @@ -98,11 +96,7 @@ fn replace_self_and_deny_type_defs( ) -> Vec { match tt { TokenTree::Ident(ref i) - if i.to_string() == "enum" - || i.to_string() == "trait" - || i.to_string() == "struct" - || i.to_string() == "union" - || i.to_string() == "impl" => + if i == "enum" || i == "trait" || i == "struct" || i == "union" || i == "impl" => { errs.extend( format!( @@ -119,7 +113,7 @@ fn replace_self_and_deny_type_defs( ); vec![tt] } - TokenTree::Ident(i) if i.to_string() == "Self" => struct_name.clone(), + TokenTree::Ident(i) if i == "Self" => struct_name.clone(), TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt], TokenTree::Group(g) => vec![TokenTree::Group(Group::new( g.delimiter(), diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/internal/src/pinned_drop.rs index c4ca7a70b726..cf8cd1c42984 100644 --- a/rust/pin-init/internal/src/pinned_drop.rs +++ b/rust/pin-init/internal/src/pinned_drop.rs @@ -1,15 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; - -use proc_macro::{TokenStream, TokenTree}; +use proc_macro2::{TokenStream, TokenTree}; +use quote::quote; pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream { let mut toks = input.into_iter().collect::>(); assert!(!toks.is_empty()); // Ensure that we have an `impl` item. - assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() == "impl")); + assert!(matches!(&toks[0], TokenTree::Ident(i) if i == "impl")); // Ensure that we are implementing `PinnedDrop`. let mut nesting: usize = 0; let mut pinned_drop_idx = None; @@ -27,7 +25,7 @@ pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream if i >= 1 && nesting == 0 { // Found the end of the generics, this should be `PinnedDrop`. assert!( - matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"), + matches!(tt, TokenTree::Ident(i) if i == "PinnedDrop"), "expected 'PinnedDrop', found: '{tt:?}'" ); pinned_drop_idx = Some(i); diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs index e0ed3998445c..d8a5ef3883f4 100644 --- a/rust/pin-init/internal/src/zeroable.rs +++ b/rust/pin-init/internal/src/zeroable.rs @@ -1,10 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; - use crate::helpers::{parse_generics, Generics}; -use proc_macro::{TokenStream, TokenTree}; +use proc_macro2::{TokenStream, TokenTree}; +use quote::quote; pub(crate) fn parse_zeroable_derive_input( input: TokenStream, diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py index 147d0cc94068..d31d93888658 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -123,7 +123,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit append_crate( "pin_init_internal", srctree / "rust" / "pin-init" / "internal" / "src" / "lib.rs", - [], + ["std", "proc_macro", "proc_macro2", "quote", "syn"], cfg=["kernel"], is_proc_macro=True, ) From 26bd9402389eaebed086755afb03453dcae6617a Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:19 +0100 Subject: [PATCH 05/59] rust: pin-init: internal: add utility API for syn error handling The API is similar to diagnostics handling in rustc and uses a `ErrorGuaranteed` value to signify that an error has been emitted. It supports both fatal errors (which abort the macro expansion immediately by returning `Err(ErrorGuaranteed)`) and non-fatal ones at generation time. These errors are appended to the token stream after generation has finished normally. This allows giving good errors while still expanding most of the code as expected to avoid the user encountering additional errors (for example missing definitions). Suggested-by: Gary Guo Tested-by: Andreas Hindborg Reviewed-by: Gary Guo [ remove duplicate word in commit message - Benno ] Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/diagnostics.rs | 32 +++++++++++++++++++++++ rust/pin-init/internal/src/lib.rs | 1 + 2 files changed, 33 insertions(+) create mode 100644 rust/pin-init/internal/src/diagnostics.rs diff --git a/rust/pin-init/internal/src/diagnostics.rs b/rust/pin-init/internal/src/diagnostics.rs new file mode 100644 index 000000000000..555876c01bab --- /dev/null +++ b/rust/pin-init/internal/src/diagnostics.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::fmt::Display; + +use proc_macro2::TokenStream; +use syn::{spanned::Spanned, Error}; + +pub(crate) struct DiagCtxt(TokenStream); +pub(crate) struct ErrorGuaranteed(()); + +impl DiagCtxt { + #[expect(dead_code)] + pub(crate) fn error(&mut self, span: impl Spanned, msg: impl Display) -> ErrorGuaranteed { + let error = Error::new(span.span(), msg); + self.0.extend(error.into_compile_error()); + ErrorGuaranteed(()) + } + + #[expect(dead_code)] + pub(crate) fn with( + fun: impl FnOnce(&mut DiagCtxt) -> Result, + ) -> TokenStream { + let mut dcx = Self(TokenStream::new()); + match fun(&mut dcx) { + Ok(mut stream) => { + stream.extend(dcx.0); + stream + } + Err(ErrorGuaranteed(())) => dcx.0, + } + } +} diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index 4c4dc639ce82..0e1a4724549d 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -12,6 +12,7 @@ use proc_macro::TokenStream; +mod diagnostics; mod helpers; mod pin_data; mod pinned_drop; From 50426bde1577d17e61362bd199d487dbeb159110 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:20 +0100 Subject: [PATCH 06/59] rust: pin-init: rewrite `derive(Zeroable)` and `derive(MaybeZeroable)` using `syn` Rewrite the two derive macros for `Zeroable` using `syn`. One positive side effect of this change is that tuple structs are now supported by them. Additionally, syntax errors and the error emitted when trying to use one of the derive macros on an `enum` are improved. Otherwise no functional changes intended. For example: #[derive(Zeroable)] enum Num { A(u32), B(i32), } Produced this error before this commit: error: no rules expected keyword `enum` --> tests/ui/compile-fail/zeroable/enum.rs:5:1 | 5 | enum Num { | ^^^^ no rules expected this token in macro call | note: while trying to match keyword `struct` --> src/macros.rs | | $vis:vis struct $name:ident | ^^^^^^ Now the error is: error: cannot derive `Zeroable` for an enum --> tests/ui/compile-fail/zeroable/enum.rs:5:1 | 5 | enum Num { | ^^^^ error: cannot derive `Zeroable` for an enum Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/diagnostics.rs | 2 - rust/pin-init/internal/src/lib.rs | 9 +- rust/pin-init/internal/src/zeroable.rs | 155 ++++++++++------------ rust/pin-init/src/macros.rs | 124 ----------------- 4 files changed, 74 insertions(+), 216 deletions(-) diff --git a/rust/pin-init/internal/src/diagnostics.rs b/rust/pin-init/internal/src/diagnostics.rs index 555876c01bab..3bdb477c2f2b 100644 --- a/rust/pin-init/internal/src/diagnostics.rs +++ b/rust/pin-init/internal/src/diagnostics.rs @@ -9,14 +9,12 @@ pub(crate) struct DiagCtxt(TokenStream); pub(crate) struct ErrorGuaranteed(()); impl DiagCtxt { - #[expect(dead_code)] pub(crate) fn error(&mut self, span: impl Spanned, msg: impl Display) -> ErrorGuaranteed { let error = Error::new(span.span(), msg); self.0.extend(error.into_compile_error()); ErrorGuaranteed(()) } - #[expect(dead_code)] pub(crate) fn with( fun: impl FnOnce(&mut DiagCtxt) -> Result, ) -> TokenStream { diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index 0e1a4724549d..4cc9b7b0cda1 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -11,6 +11,9 @@ #![allow(missing_docs)] use proc_macro::TokenStream; +use syn::parse_macro_input; + +use crate::diagnostics::DiagCtxt; mod diagnostics; mod helpers; @@ -30,10 +33,12 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_derive(Zeroable)] pub fn derive_zeroable(input: TokenStream) -> TokenStream { - zeroable::derive(input.into()).into() + let input = parse_macro_input!(input); + DiagCtxt::with(|dcx| zeroable::derive(input, dcx)).into() } #[proc_macro_derive(MaybeZeroable)] pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream { - zeroable::maybe_derive(input.into()).into() + let input = parse_macro_input!(input); + DiagCtxt::with(|dcx| zeroable::maybe_derive(input, dcx)).into() } diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs index d8a5ef3883f4..05683319b0f7 100644 --- a/rust/pin-init/internal/src/zeroable.rs +++ b/rust/pin-init/internal/src/zeroable.rs @@ -1,99 +1,78 @@ // SPDX-License-Identifier: GPL-2.0 -use crate::helpers::{parse_generics, Generics}; -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::TokenStream; use quote::quote; +use syn::{parse_quote, Data, DeriveInput, Field, Fields}; -pub(crate) fn parse_zeroable_derive_input( - input: TokenStream, -) -> ( - Vec, - Vec, - Vec, - Option, -) { - let ( - Generics { - impl_generics, - decl_generics: _, - ty_generics, - }, - mut rest, - ) = parse_generics(input); - // This should be the body of the struct `{...}`. - let last = rest.pop(); - // Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`. - let mut new_impl_generics = Vec::with_capacity(impl_generics.len()); - // Are we inside of a generic where we want to add `Zeroable`? - let mut in_generic = !impl_generics.is_empty(); - // Have we already inserted `Zeroable`? - let mut inserted = false; - // Level of `<>` nestings. - let mut nested = 0; - for tt in impl_generics { - match &tt { - // If we find a `,`, then we have finished a generic/constant/lifetime parameter. - TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => { - if in_generic && !inserted { - new_impl_generics.extend(quote! { : ::pin_init::Zeroable }); - } - in_generic = true; - inserted = false; - new_impl_generics.push(tt); - } - // If we find `'`, then we are entering a lifetime. - TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => { - in_generic = false; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => { - new_impl_generics.push(tt); - if in_generic { - new_impl_generics.extend(quote! { ::pin_init::Zeroable + }); - inserted = true; - } - } - TokenTree::Punct(p) if p.as_char() == '<' => { - nested += 1; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if p.as_char() == '>' => { - assert!(nested > 0); - nested -= 1; - new_impl_generics.push(tt); - } - _ => new_impl_generics.push(tt), +use crate::{diagnostics::ErrorGuaranteed, DiagCtxt}; + +pub(crate) fn derive( + input: DeriveInput, + dcx: &mut DiagCtxt, +) -> Result { + let fields = match input.data { + Data::Struct(data_struct) => data_struct.fields, + Data::Union(data_union) => Fields::Named(data_union.fields), + Data::Enum(data_enum) => { + return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum")); } + }; + let name = input.ident; + let mut generics = input.generics; + for param in generics.type_params_mut() { + param.bounds.insert(0, parse_quote!(::pin_init::Zeroable)); } - assert_eq!(nested, 0); - if in_generic && !inserted { - new_impl_generics.extend(quote! { : ::pin_init::Zeroable }); - } - (rest, new_impl_generics, ty_generics, last) + let (impl_gen, ty_gen, whr) = generics.split_for_impl(); + let field_type = fields.iter().map(|field| &field.ty); + Ok(quote! { + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen + #whr + {} + const _: () = { + fn assert_zeroable() {} + fn ensure_zeroable #impl_gen () + #whr + { + #( + assert_zeroable::<#field_type>(); + )* + } + }; + }) } -pub(crate) fn derive(input: TokenStream) -> TokenStream { - let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); - quote! { - ::pin_init::__derive_zeroable!( - parse_input: - @sig(#(#rest)*), - @impl_generics(#(#new_impl_generics)*), - @ty_generics(#(#ty_generics)*), - @body(#last), - ); +pub(crate) fn maybe_derive( + input: DeriveInput, + dcx: &mut DiagCtxt, +) -> Result { + let fields = match input.data { + Data::Struct(data_struct) => data_struct.fields, + Data::Union(data_union) => Fields::Named(data_union.fields), + Data::Enum(data_enum) => { + return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum")); + } + }; + let name = input.ident; + let mut generics = input.generics; + for param in generics.type_params_mut() { + param.bounds.insert(0, parse_quote!(::pin_init::Zeroable)); } -} - -pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream { - let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); - quote! { - ::pin_init::__maybe_derive_zeroable!( - parse_input: - @sig(#(#rest)*), - @impl_generics(#(#new_impl_generics)*), - @ty_generics(#(#ty_generics)*), - @body(#last), - ); + for Field { ty, .. } in fields { + generics + .make_where_clause() + .predicates + // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` + // feature . + .push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable)); } + let (impl_gen, ty_gen, whr) = generics.split_for_impl(); + Ok(quote! { + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen + #whr + {} + }) } diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index 682c61a587a0..53ed5ce860fc 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -1551,127 +1551,3 @@ macro_rules! __init_internal { ); }; } - -#[doc(hidden)] -#[macro_export] -macro_rules! __derive_zeroable { - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis struct $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $($($whr)*)? - {} - const _: () = { - fn assert_zeroable() {} - fn ensure_zeroable<$($impl_generics)*>() - where $($($whr)*)? - { - $(assert_zeroable::<$field_ty>();)* - } - }; - }; - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis union $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $($($whr)*)? - {} - const _: () = { - fn assert_zeroable() {} - fn ensure_zeroable<$($impl_generics)*>() - where $($($whr)*)? - { - $(assert_zeroable::<$field_ty>();)* - } - }; - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __maybe_derive_zeroable { - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis struct $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $( - // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` - // feature . - $field_ty: for<'__dummy> $crate::Zeroable, - )* - $($($whr)*)? - {} - }; - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis union $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $( - // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` - // feature . - $field_ty: for<'__dummy> $crate::Zeroable, - )* - $($($whr)*)? - {} - }; -} From a92f5fd29257d3bb4c62b81aebca0774e5f5c856 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:21 +0100 Subject: [PATCH 07/59] rust: pin-init: rewrite the `#[pinned_drop]` attribute macro using `syn` Rewrite the attribute macro for implementing `PinnedDrop` using `syn`. Otherwise no functional changes intended aside from improved error messages on syntactic and semantical errors. For example: When missing the `drop` function in the implementation, the old error was: error: no rules expected `)` --> tests/ui/compile-fail/pinned_drop/no_fn.rs:6:1 | 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) And the new one is: error[E0046]: not all trait items implemented, missing: `drop` --> tests/ui/compile-fail/pinned_drop/no_fn.rs:7:1 | 7 | impl PinnedDrop for Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation | = help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }` Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/lib.rs | 4 +- rust/pin-init/internal/src/pinned_drop.rs | 86 +++++++++++++---------- rust/pin-init/src/macros.rs | 28 -------- 3 files changed, 52 insertions(+), 66 deletions(-) diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index 4cc9b7b0cda1..a75b99b58189 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -28,7 +28,9 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { - pinned_drop::pinned_drop(args.into(), input.into()).into() + let args = parse_macro_input!(args); + let input = parse_macro_input!(input); + DiagCtxt::with(|dcx| pinned_drop::pinned_drop(args, input, dcx)).into() } #[proc_macro_derive(Zeroable)] diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/internal/src/pinned_drop.rs index cf8cd1c42984..a20ac314ca82 100644 --- a/rust/pin-init/internal/src/pinned_drop.rs +++ b/rust/pin-init/internal/src/pinned_drop.rs @@ -1,49 +1,61 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::TokenStream; use quote::quote; +use syn::{parse::Nothing, parse_quote, spanned::Spanned, ImplItem, ItemImpl, Token}; -pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream { - let mut toks = input.into_iter().collect::>(); - assert!(!toks.is_empty()); - // Ensure that we have an `impl` item. - assert!(matches!(&toks[0], TokenTree::Ident(i) if i == "impl")); - // Ensure that we are implementing `PinnedDrop`. - let mut nesting: usize = 0; - let mut pinned_drop_idx = None; - for (i, tt) in toks.iter().enumerate() { - match tt { - TokenTree::Punct(p) if p.as_char() == '<' => { - nesting += 1; +use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; + +pub(crate) fn pinned_drop( + _args: Nothing, + mut input: ItemImpl, + dcx: &mut DiagCtxt, +) -> Result { + if let Some(unsafety) = input.unsafety { + dcx.error(unsafety, "implementing `PinnedDrop` is safe"); + } + input.unsafety = Some(Token![unsafe](input.impl_token.span)); + match &mut input.trait_ { + Some((not, path, _for)) => { + if let Some(not) = not { + dcx.error(not, "cannot implement `!PinnedDrop`"); } - TokenTree::Punct(p) if p.as_char() == '>' => { - nesting = nesting.checked_sub(1).unwrap(); - continue; + for (seg, expected) in path + .segments + .iter() + .rev() + .zip(["PinnedDrop", "pin_init", ""]) + { + if expected.is_empty() || seg.ident != expected { + dcx.error(seg, "bad import path for `PinnedDrop`"); + } + if !seg.arguments.is_none() { + dcx.error(&seg.arguments, "unexpected arguments for `PinnedDrop` path"); + } } - _ => {} + *path = parse_quote!(::pin_init::PinnedDrop); } - if i >= 1 && nesting == 0 { - // Found the end of the generics, this should be `PinnedDrop`. - assert!( - matches!(tt, TokenTree::Ident(i) if i == "PinnedDrop"), - "expected 'PinnedDrop', found: '{tt:?}'" + None => { + let span = input + .impl_token + .span + .join(input.self_ty.span()) + .unwrap_or(input.impl_token.span); + dcx.error( + span, + "expected `impl ... PinnedDrop for ...`, got inherent impl", ); - pinned_drop_idx = Some(i); - break; } } - let idx = pinned_drop_idx - .unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`.")); - // Fully qualify the `PinnedDrop`, as to avoid any tampering. - toks.splice(idx..idx, quote!(::pin_init::)); - // Take the `{}` body and call the declarative macro. - if let Some(TokenTree::Group(last)) = toks.pop() { - let last = last.stream(); - quote!(::pin_init::__pinned_drop! { - @impl_sig(#(#toks)*), - @impl_body(#last), - }) - } else { - TokenStream::from_iter(toks) + for item in &mut input.items { + if let ImplItem::Fn(fn_item) = item { + if fn_item.sig.ident == "drop" { + fn_item + .sig + .inputs + .push(parse_quote!(_: ::pin_init::__internal::OnlyCallFromDrop)); + } + } } + Ok(quote!(#input)) } diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index 53ed5ce860fc..b80c95612fd6 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -503,34 +503,6 @@ pub use ::macros::paste; #[cfg(not(kernel))] pub use ::paste::paste; -/// Creates a `unsafe impl<...> PinnedDrop for $type` block. -/// -/// See [`PinnedDrop`] for more information. -/// -/// [`PinnedDrop`]: crate::PinnedDrop -#[doc(hidden)] -#[macro_export] -macro_rules! __pinned_drop { - ( - @impl_sig($($impl_sig:tt)*), - @impl_body( - $(#[$($attr:tt)*])* - fn drop($($sig:tt)*) { - $($inner:tt)* - } - ), - ) => { - // SAFETY: TODO. - unsafe $($impl_sig)* { - // Inherit all attributes and the type/ident tokens for the signature. - $(#[$($attr)*])* - fn drop($($sig)*, _: $crate::__internal::OnlyCallFromDrop) { - $($inner)* - } - } - } -} - /// This macro first parses the struct definition such that it separates pinned and not pinned /// fields. Afterwards it declares the struct and implement the `PinData` trait safely. #[doc(hidden)] From 560f6d13c33f9f06ca34c14dc7c0a045d949c4a0 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:22 +0100 Subject: [PATCH 08/59] rust: pin-init: rewrite `#[pin_data]` using `syn` Rewrite the attribute macro `#[pin_data]` using `syn`. No functional changes intended aside from improved error messages on syntactic and semantical errors. For example if one forgets a comma at the end of a field: #[pin_data] struct Foo { a: Box b: Box } The declarative macro reports the following errors: error: expected `,`, or `}`, found `b` --> tests/ui/compile-fail/pin_data/missing_comma.rs:5:16 | 5 | a: Box | ^ help: try adding a comma: `,` error: recursion limit reached while expanding `$crate::__pin_data!` --> tests/ui/compile-fail/pin_data/missing_comma.rs:3:1 | 3 | #[pin_data] | ^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`) = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) The new `syn` version reports: error: expected `,`, or `}`, found `b` --> tests/ui/compile-fail/pin_data/missing_comma.rs:5:16 | 5 | a: Box | ^ help: try adding a comma: `,` error: expected `,` --> tests/ui/compile-fail/pin_data/missing_comma.rs:6:5 | 6 | b: Box | ^ Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/helpers.rs | 149 ------ rust/pin-init/internal/src/lib.rs | 7 +- rust/pin-init/internal/src/pin_data.rs | 621 ++++++++++++++++++++----- rust/pin-init/src/macros.rs | 574 ----------------------- 4 files changed, 508 insertions(+), 843 deletions(-) delete mode 100644 rust/pin-init/internal/src/helpers.rs diff --git a/rust/pin-init/internal/src/helpers.rs b/rust/pin-init/internal/src/helpers.rs deleted file mode 100644 index 90f85eaa4123..000000000000 --- a/rust/pin-init/internal/src/helpers.rs +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT - -use proc_macro2::{TokenStream, TokenTree}; - -/// Parsed generics. -/// -/// See the field documentation for an explanation what each of the fields represents. -/// -/// # Examples -/// -/// ```rust,ignore -/// # let input = todo!(); -/// let (Generics { decl_generics, impl_generics, ty_generics }, rest) = parse_generics(input); -/// quote! { -/// struct Foo<$($decl_generics)*> { -/// // ... -/// } -/// -/// impl<$impl_generics> Foo<$ty_generics> { -/// fn foo() { -/// // ... -/// } -/// } -/// } -/// ``` -pub(crate) struct Generics { - /// The generics with bounds and default values (e.g. `T: Clone, const N: usize = 0`). - /// - /// Use this on type definitions e.g. `struct Foo<$decl_generics> ...` (or `union`/`enum`). - pub(crate) decl_generics: Vec, - /// The generics with bounds (e.g. `T: Clone, const N: usize`). - /// - /// Use this on `impl` blocks e.g. `impl<$impl_generics> Trait for ...`. - pub(crate) impl_generics: Vec, - /// The generics without bounds and without default values (e.g. `T, N`). - /// - /// Use this when you use the type that is declared with these generics e.g. - /// `Foo<$ty_generics>`. - pub(crate) ty_generics: Vec, -} - -/// Parses the given `TokenStream` into `Generics` and the rest. -/// -/// The generics are not present in the rest, but a where clause might remain. -pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec) { - // The generics with bounds and default values. - let mut decl_generics = vec![]; - // `impl_generics`, the declared generics with their bounds. - let mut impl_generics = vec![]; - // Only the names of the generics, without any bounds. - let mut ty_generics = vec![]; - // Tokens not related to the generics e.g. the `where` token and definition. - let mut rest = vec![]; - // The current level of `<`. - let mut nesting = 0; - let mut toks = input.into_iter(); - // If we are at the beginning of a generic parameter. - let mut at_start = true; - let mut skip_until_comma = false; - while let Some(tt) = toks.next() { - if nesting == 1 && matches!(&tt, TokenTree::Punct(p) if p.as_char() == '>') { - // Found the end of the generics. - break; - } else if nesting >= 1 { - decl_generics.push(tt.clone()); - } - match tt.clone() { - TokenTree::Punct(p) if p.as_char() == '<' => { - if nesting >= 1 && !skip_until_comma { - // This is inside of the generics and part of some bound. - impl_generics.push(tt); - } - nesting += 1; - } - TokenTree::Punct(p) if p.as_char() == '>' => { - // This is a parsing error, so we just end it here. - if nesting == 0 { - break; - } else { - nesting -= 1; - if nesting >= 1 && !skip_until_comma { - // We are still inside of the generics and part of some bound. - impl_generics.push(tt); - } - } - } - TokenTree::Punct(p) if skip_until_comma && p.as_char() == ',' => { - if nesting == 1 { - impl_generics.push(tt.clone()); - impl_generics.push(tt); - skip_until_comma = false; - } - } - _ if !skip_until_comma => { - match nesting { - // If we haven't entered the generics yet, we still want to keep these tokens. - 0 => rest.push(tt), - 1 => { - // Here depending on the token, it might be a generic variable name. - match tt.clone() { - TokenTree::Ident(i) if at_start && i == "const" => { - let Some(name) = toks.next() else { - // Parsing error. - break; - }; - impl_generics.push(tt); - impl_generics.push(name.clone()); - ty_generics.push(name.clone()); - decl_generics.push(name); - at_start = false; - } - TokenTree::Ident(_) if at_start => { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start = false; - } - TokenTree::Punct(p) if p.as_char() == ',' => { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start = true; - } - // Lifetimes begin with `'`. - TokenTree::Punct(p) if p.as_char() == '\'' && at_start => { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - } - // Generics can have default values, we skip these. - TokenTree::Punct(p) if p.as_char() == '=' => { - skip_until_comma = true; - } - _ => impl_generics.push(tt), - } - } - _ => impl_generics.push(tt), - } - } - _ => {} - } - } - rest.extend(toks); - ( - Generics { - impl_generics, - decl_generics, - ty_generics, - }, - rest, - ) -} diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index a75b99b58189..56dc306e04a9 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -16,14 +16,15 @@ use syn::parse_macro_input; use crate::diagnostics::DiagCtxt; mod diagnostics; -mod helpers; mod pin_data; mod pinned_drop; mod zeroable; #[proc_macro_attribute] -pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { - pin_data::pin_data(inner.into(), item.into()).into() +pub fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args); + let input = parse_macro_input!(input); + DiagCtxt::with(|dcx| pin_data::pin_data(args, input, dcx)).into() } #[proc_macro_attribute] diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 86a53b37cc66..11ea3f8d8a1b 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1,126 +1,513 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use crate::helpers::{parse_generics, Generics}; -use proc_macro2::{Group, Punct, Spacing, TokenStream, TokenTree}; -use quote::quote; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parse::{End, Nothing, Parse}, + parse_quote, parse_quote_spanned, + spanned::Spanned, + visit_mut::VisitMut, + Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, +}; -pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { - // This proc-macro only does some pre-parsing and then delegates the actual parsing to - // `pin_init::__pin_data!`. +use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; - let ( - Generics { - impl_generics, - decl_generics, - ty_generics, - }, - rest, - ) = parse_generics(input); - // The struct definition might contain the `Self` type. Since `__pin_data!` will define a new - // type with the same generics and bounds, this poses a problem, since `Self` will refer to the - // new type as opposed to this struct definition. Therefore we have to replace `Self` with the - // concrete name. - - // Errors that occur when replacing `Self` with `struct_name`. - let mut errs = TokenStream::new(); - // The name of the struct with ty_generics. - let struct_name = rest - .iter() - .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i == "struct")) - .nth(1) - .and_then(|tt| match tt { - TokenTree::Ident(_) => { - let tt = tt.clone(); - let mut res = vec![tt]; - if !ty_generics.is_empty() { - // We add this, so it is maximally compatible with e.g. `Self::CONST` which - // will be replaced by `StructName::<$generics>::CONST`. - res.push(TokenTree::Punct(Punct::new(':', Spacing::Joint))); - res.push(TokenTree::Punct(Punct::new(':', Spacing::Alone))); - res.push(TokenTree::Punct(Punct::new('<', Spacing::Alone))); - res.extend(ty_generics.iter().cloned()); - res.push(TokenTree::Punct(Punct::new('>', Spacing::Alone))); - } - Some(res) - } - _ => None, - }) - .unwrap_or_else(|| { - // If we did not find the name of the struct then we will use `Self` as the replacement - // and add a compile error to ensure it does not compile. - errs.extend( - "::core::compile_error!(\"Could not locate type name.\");" - .parse::() - .unwrap(), - ); - "Self".parse::().unwrap().into_iter().collect() - }); - let impl_generics = impl_generics - .into_iter() - .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)) - .collect::>(); - let mut rest = rest - .into_iter() - .flat_map(|tt| { - // We ignore top level `struct` tokens, since they would emit a compile error. - if matches!(&tt, TokenTree::Ident(i) if i == "struct") { - vec![tt] - } else { - replace_self_and_deny_type_defs(&struct_name, tt, &mut errs) - } - }) - .collect::>(); - // This should be the body of the struct `{...}`. - let last = rest.pop(); - let mut quoted = quote!(::pin_init::__pin_data! { - parse_input: - @args(#args), - @sig(#(#rest)*), - @impl_generics(#(#impl_generics)*), - @ty_generics(#(#ty_generics)*), - @decl_generics(#(#decl_generics)*), - @body(#last), - }); - quoted.extend(errs); - quoted +pub(crate) mod kw { + syn::custom_keyword!(PinnedDrop); } -/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `struct` `union` and `impl` -/// keywords. -/// -/// The error is appended to `errs` to allow normal parsing to continue. -fn replace_self_and_deny_type_defs( - struct_name: &Vec, - tt: TokenTree, - errs: &mut TokenStream, -) -> Vec { - match tt { - TokenTree::Ident(ref i) - if i == "enum" || i == "trait" || i == "struct" || i == "union" || i == "impl" => - { - errs.extend( - format!( - "::core::compile_error!(\"Cannot use `{i}` inside of struct definition with \ - `#[pin_data]`.\");" - ) - .parse::() - .unwrap() - .into_iter() - .map(|mut tok| { - tok.set_span(tt.span()); - tok - }), - ); - vec![tt] +pub(crate) enum Args { + Nothing(Nothing), + #[allow(dead_code)] + PinnedDrop(kw::PinnedDrop), +} + +impl Parse for Args { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let lh = input.lookahead1(); + if lh.peek(End) { + input.parse().map(Self::Nothing) + } else if lh.peek(kw::PinnedDrop) { + input.parse().map(Self::PinnedDrop) + } else { + Err(lh.error()) } - TokenTree::Ident(i) if i == "Self" => struct_name.clone(), - TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt], - TokenTree::Group(g) => vec![TokenTree::Group(Group::new( - g.delimiter(), - g.stream() - .into_iter() - .flat_map(|tt| replace_self_and_deny_type_defs(struct_name, tt, errs)) - .collect(), - ))], } } + +pub(crate) fn pin_data( + args: Args, + input: Item, + dcx: &mut DiagCtxt, +) -> Result { + let mut struct_ = match input { + Item::Struct(struct_) => struct_, + Item::Enum(enum_) => { + return Err(dcx.error( + enum_.enum_token, + "`#[pin_data]` only supports structs for now", + )); + } + Item::Union(union) => { + return Err(dcx.error( + union.union_token, + "`#[pin_data]` only supports structs for now", + )); + } + rest => { + return Err(dcx.error( + rest, + "`#[pin_data]` can only be applied to struct, enum and union definitions", + )); + } + }; + + // The generics might contain the `Self` type. Since this macro will define a new type with the + // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed + // to this struct definition. Therefore we have to replace `Self` with the concrete name. + let mut replacer = { + let name = &struct_.ident; + let (_, ty_generics, _) = struct_.generics.split_for_impl(); + SelfReplacer(parse_quote!(#name #ty_generics)) + }; + replacer.visit_generics_mut(&mut struct_.generics); + replacer.visit_fields_mut(&mut struct_.fields); + + let fields: Vec<(bool, &Field)> = struct_ + .fields + .iter_mut() + .map(|field| { + let len = field.attrs.len(); + field.attrs.retain(|a| !a.path().is_ident("pin")); + (len != field.attrs.len(), &*field) + }) + .collect(); + + for (pinned, field) in &fields { + if !pinned && is_phantom_pinned(&field.ty) { + dcx.error( + field, + format!( + "The field `{}` of type `PhantomPinned` only has an effect \ + if it has the `#[pin]` attribute", + field.ident.as_ref().unwrap(), + ), + ); + } + } + + let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields); + let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args); + let projections = + generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields); + let the_pin_data = + generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields); + + Ok(quote! { + #struct_ + #projections + // We put the rest into this const item, because it then will not be accessible to anything + // outside. + const _: () = { + #the_pin_data + #unpin_impl + #drop_impl + }; + }) +} + +fn is_phantom_pinned(ty: &Type) -> bool { + match ty { + Type::Path(TypePath { qself: None, path }) => { + // Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user). + if path.segments.len() > 3 { + return false; + } + // If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or + // `::std::marker::PhantomPinned`. + if path.leading_colon.is_some() && path.segments.len() != 3 { + return false; + } + let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]]; + for (actual, expected) in path.segments.iter().rev().zip(expected) { + if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) { + return false; + } + } + true + } + _ => false, + } +} + +fn generate_unpin_impl( + ident: &Ident, + generics: &Generics, + fields: &[(bool, &Field)], +) -> TokenStream { + let (_, ty_generics, _) = generics.split_for_impl(); + let mut generics_with_pin_lt = generics.clone(); + generics_with_pin_lt.params.insert(0, parse_quote!('__pin)); + generics_with_pin_lt.make_where_clause(); + let ( + impl_generics_with_pin_lt, + ty_generics_with_pin_lt, + Some(WhereClause { + where_token, + predicates, + }), + ) = generics_with_pin_lt.split_for_impl() + else { + unreachable!() + }; + let pinned_fields = fields.iter().filter_map(|(b, f)| b.then_some(f)); + quote! { + // This struct will be used for the unpin analysis. It is needed, because only structurally + // pinned fields are relevant whether the struct should implement `Unpin`. + #[allow(dead_code)] // The fields below are never used. + struct __Unpin #generics_with_pin_lt + #where_token + #predicates + { + __phantom_pin: ::core::marker::PhantomData &'__pin ()>, + __phantom: ::core::marker::PhantomData< + fn(#ident #ty_generics) -> #ident #ty_generics + >, + #(#pinned_fields),* + } + + #[doc(hidden)] + impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics + #where_token + __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin, + #predicates + {} + } +} + +fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream { + let (impl_generics, ty_generics, whr) = generics.split_for_impl(); + let has_pinned_drop = matches!(args, Args::PinnedDrop(_)); + // We need to disallow normal `Drop` implementation, the exact behavior depends on whether + // `PinnedDrop` was specified in `args`. + if has_pinned_drop { + // When `PinnedDrop` was specified we just implement `Drop` and delegate. + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics + #whr + { + fn drop(&mut self) { + // SAFETY: Since this is a destructor, `self` will not move after this function + // terminates, since it is inaccessible. + let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) }; + // SAFETY: Since this is a drop function, we can create this token to call the + // pinned destructor of this type. + let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() }; + ::pin_init::PinnedDrop::drop(pinned, token); + } + } + } + } 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 MustNotImplDrop for T {} + impl #impl_generics MustNotImplDrop for #ident #ty_generics + #whr + {} + // We also take care to prevent users from writing a useless `PinnedDrop` implementation. + // They might implement `PinnedDrop` correctly for the struct, but forget to give + // `PinnedDrop` as the parameter to `#[pin_data]`. + #[expect(non_camel_case_types)] + trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} + impl + UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} + impl #impl_generics + UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics + #whr + {} + } + } +} + +fn generate_projections( + vis: &Visibility, + ident: &Ident, + generics: &Generics, + fields: &[(bool, &Field)], +) -> TokenStream { + let (impl_generics, ty_generics, _) = generics.split_for_impl(); + let mut generics_with_pin_lt = generics.clone(); + generics_with_pin_lt.params.insert(0, parse_quote!('__pin)); + let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl(); + let projection = format_ident!("{ident}Projection"); + let this = format_ident!("this"); + + let (fields_decl, fields_proj) = collect_tuple(fields.iter().map( + |( + pinned, + Field { + vis, + ident, + ty, + attrs, + .. + }, + )| { + let mut attrs = attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let mut no_doc_attrs = attrs.clone(); + no_doc_attrs.retain(|a| !a.path().is_ident("doc")); + let ident = ident + .as_ref() + .expect("only structs with named fields are supported"); + if *pinned { + ( + quote!( + #(#attrs)* + #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, + ), + quote!( + #(#no_doc_attrs)* + // SAFETY: this field is structurally pinned. + #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) }, + ), + ) + } else { + ( + quote!( + #(#attrs)* + #vis #ident: &'__pin mut #ty, + ), + quote!( + #(#no_doc_attrs)* + #ident: &mut #this.#ident, + ), + ) + } + }, + )); + let structurally_pinned_fields_docs = fields + .iter() + .filter_map(|(pinned, field)| pinned.then_some(field)) + .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap())); + let not_structurally_pinned_fields_docs = fields + .iter() + .filter_map(|(pinned, field)| (!pinned).then_some(field)) + .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap())); + let docs = format!(" Pin-projections of [`{ident}`]"); + quote! { + #[doc = #docs] + #[allow(dead_code)] + #[doc(hidden)] + #vis struct #projection #generics_with_pin_lt { + #(#fields_decl)* + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, + } + + impl #impl_generics #ident #ty_generics + #whr + { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + #(#[doc = #structurally_pinned_fields_docs])* + /// + /// These fields are **not** structurally pinned: + #(#[doc = #not_structurally_pinned_fields_docs])* + #[inline] + #vis fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> #projection #ty_generics_with_pin_lt { + // SAFETY: we only give access to `&mut` for fields not structurally pinned. + let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + #projection { + #(#fields_proj)* + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + } + } +} + +fn generate_the_pin_data( + vis: &Visibility, + ident: &Ident, + generics: &Generics, + fields: &[(bool, &Field)], +) -> TokenStream { + let (impl_generics, ty_generics, whr) = generics.split_for_impl(); + + // For every field, we create an initializing projection function according to its projection + // type. If a field is structurally pinned, then it must be initialized via `PinInit`, if it is + // not structurally pinned, then it can be initialized via `Init`. + // + // The functions are `unsafe` to prevent accidentally calling them. + fn handle_field( + Field { + vis, + ident, + ty, + attrs, + .. + }: &Field, + struct_ident: &Ident, + pinned: bool, + ) -> TokenStream { + let mut attrs = attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let ident = ident + .as_ref() + .expect("only structs with named fields are supported"); + let project_ident = format_ident!("__project_{ident}"); + let (init_ty, init_fn, project_ty, project_body, pin_safety) = if pinned { + ( + quote!(PinInit), + quote!(__pinned_init), + quote!(::core::pin::Pin<&'__slot mut #ty>), + // SAFETY: this field is structurally pinned. + quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }), + quote!( + /// - `slot` will not move until it is dropped, i.e. it will be pinned. + ), + ) + } else { + ( + quote!(Init), + quote!(__init), + quote!(&'__slot mut #ty), + quote!(slot), + quote!(), + ) + }; + let slot_safety = format!( + " `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.", + ); + quote! { + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. + #pin_safety + #(#attrs)* + #vis unsafe fn #ident( + self, + slot: *mut #ty, + init: impl ::pin_init::#init_ty<#ty, E>, + ) -> ::core::result::Result<(), E> { + // SAFETY: this function has the same safety requirements as the __init function + // called below. + unsafe { ::pin_init::#init_ty::#init_fn(init, slot) } + } + + /// # Safety + /// + #[doc = #slot_safety] + #(#attrs)* + #vis unsafe fn #project_ident<'__slot>( + self, + slot: &'__slot mut #ty, + ) -> #project_ty { + #project_body + } + } + } + + let field_accessors = fields + .iter() + .map(|(pinned, field)| handle_field(field, ident, *pinned)) + .collect::(); + quote! { + // We declare this struct which will host all of the projection function for our type. It + // will be invariant over all generic parameters which are inherited from the struct. + #[doc(hidden)] + #vis struct __ThePinData #generics + #whr + { + __phantom: ::core::marker::PhantomData< + fn(#ident #ty_generics) -> #ident #ty_generics + >, + } + + impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics + #whr + { + fn clone(&self) -> Self { *self } + } + + impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics + #whr + {} + + #[allow(dead_code)] // Some functions might never be used and private. + #[expect(clippy::missing_safety_doc)] + impl #impl_generics __ThePinData #ty_generics + #whr + { + #field_accessors + } + + // SAFETY: We have added the correct projection functions above to `__ThePinData` and + // we also use the least restrictive generics possible. + unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #ident #ty_generics + #whr + { + type PinData = __ThePinData #ty_generics; + + unsafe fn __pin_data() -> Self::PinData { + __ThePinData { __phantom: ::core::marker::PhantomData } + } + } + + // SAFETY: TODO + unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics + #whr + { + type Datee = #ident #ty_generics; + } + } +} + +struct SelfReplacer(PathSegment); + +impl VisitMut for SelfReplacer { + fn visit_path_mut(&mut self, i: &mut syn::Path) { + if i.is_ident("Self") { + let span = i.span(); + let seg = &self.0; + *i = parse_quote_spanned!(span=> #seg); + } else { + syn::visit_mut::visit_path_mut(self, i); + } + } + + fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) { + if seg.ident == "Self" { + let span = seg.span(); + let this = &self.0; + *seg = parse_quote_spanned!(span=> #this); + } else { + syn::visit_mut::visit_path_segment_mut(self, seg); + } + } + + fn visit_item_mut(&mut self, _: &mut Item) { + // Do not descend into items, since items reset/change what `Self` refers to. + } +} + +// replace with `.collect()` once MSRV is above 1.79 +fn collect_tuple(iter: impl Iterator) -> (Vec, Vec) { + let mut res_a = vec![]; + let mut res_b = vec![]; + for (a, b) in iter { + res_a.push(a); + res_b.push(b); + } + (res_a, res_b) +} diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index b80c95612fd6..eea8adc5c7ad 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -503,580 +503,6 @@ pub use ::macros::paste; #[cfg(not(kernel))] pub use ::paste::paste; -/// This macro first parses the struct definition such that it separates pinned and not pinned -/// fields. Afterwards it declares the struct and implement the `PinData` trait safely. -#[doc(hidden)] -#[macro_export] -macro_rules! __pin_data { - // Proc-macro entry point, this is supplied by the proc-macro pre-parsing. - (parse_input: - @args($($pinned_drop:ident)?), - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis struct $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @body({ $($fields:tt)* }), - ) => { - // We now use token munching to iterate through all of the fields. While doing this we - // identify fields marked with `#[pin]`, these fields are the 'pinned fields'. The user - // wants these to be structurally pinned. The rest of the fields are the - // 'not pinned fields'. Additionally we collect all fields, since we need them in the right - // order to declare the struct. - // - // In this call we also put some explaining comments for the parameters. - $crate::__pin_data!(find_pinned_fields: - // Attributes on the struct itself, these will just be propagated to be put onto the - // struct definition. - @struct_attrs($(#[$($struct_attr)*])*), - // The visibility of the struct. - @vis($vis), - // The name of the struct. - @name($name), - // The 'impl generics', the generics that will need to be specified on the struct inside - // of an `impl<$ty_generics>` block. - @impl_generics($($impl_generics)*), - // The 'ty generics', the generics that will need to be specified on the impl blocks. - @ty_generics($($ty_generics)*), - // The 'decl generics', the generics that need to be specified on the struct - // definition. - @decl_generics($($decl_generics)*), - // The where clause of any impl block and the declaration. - @where($($($whr)*)?), - // The remaining fields tokens that need to be processed. - // We add a `,` at the end to ensure correct parsing. - @fields_munch($($fields)* ,), - // The pinned fields. - @pinned(), - // The not pinned fields. - @not_pinned(), - // All fields. - @fields(), - // The accumulator containing all attributes already parsed. - @accum(), - // Contains `yes` or `` to indicate if `#[pin]` was found on the current field. - @is_pinned(), - // The proc-macro argument, this should be `PinnedDrop` or ``. - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We found a PhantomPinned field, this should generally be pinned! - @fields_munch($field:ident : $($($(::)?core::)?marker::)?PhantomPinned, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is not pinned. - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) => { - ::core::compile_error!(concat!( - "The field `", - stringify!($field), - "` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute.", - )); - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)* $($accum)* $field: ::core::marker::PhantomPinned,), - @not_pinned($($not_pinned)*), - @fields($($fields)* $($accum)* $field: ::core::marker::PhantomPinned,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration. - @fields_munch($field:ident : $type:ty, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is pinned. - @is_pinned(yes), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)* $($accum)* $field: $type,), - @not_pinned($($not_pinned)*), - @fields($($fields)* $($accum)* $field: $type,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration. - @fields_munch($field:ident : $type:ty, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is not pinned. - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)* $($accum)* $field: $type,), - @fields($($fields)* $($accum)* $field: $type,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We found the `#[pin]` attr. - @fields_munch(#[pin] $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - // We do not include `#[pin]` in the list of attributes, since it is not actually an - // attribute that is defined somewhere. - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)*), - // Set this to `yes`. - @is_pinned(yes), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration with visibility, for simplicity we only munch the - // visibility and put it into `$accum`. - @fields_munch($fvis:vis $field:ident $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($field $($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)* $fvis), - @is_pinned($($is_pinned)?), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // Some other attribute, just put it into `$accum`. - @fields_munch(#[$($attr:tt)*] $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)* #[$($attr)*]), - @is_pinned($($is_pinned)?), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the end of the fields, plus an optional additional comma, since we added one - // before and the user is also allowed to put a trailing comma. - @fields_munch($(,)?), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) => { - // Declare the struct with all fields in the correct order. - $($struct_attrs)* - $vis struct $name <$($decl_generics)*> - where $($whr)* - { - $($fields)* - } - - $crate::__pin_data!(make_pin_projections: - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - ); - - // We put the rest into this const item, because it then will not be accessible to anything - // outside. - const _: () = { - // 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<$($impl_generics)*> - where $($whr)* - { - __phantom: ::core::marker::PhantomData< - fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*> - >, - } - - impl<$($impl_generics)*> ::core::clone::Clone for __ThePinData<$($ty_generics)*> - where $($whr)* - { - fn clone(&self) -> Self { *self } - } - - impl<$($impl_generics)*> ::core::marker::Copy for __ThePinData<$($ty_generics)*> - where $($whr)* - {} - - // Make all projection functions. - $crate::__pin_data!(make_pin_data: - @pin_data(__ThePinData), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @where($($whr)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - ); - - // SAFETY: We have added the correct projection functions above to `__ThePinData` and - // we also use the least restrictive generics possible. - unsafe impl<$($impl_generics)*> - $crate::__internal::HasPinData for $name<$($ty_generics)*> - where $($whr)* - { - type PinData = __ThePinData<$($ty_generics)*>; - - unsafe fn __pin_data() -> Self::PinData { - __ThePinData { __phantom: ::core::marker::PhantomData } - } - } - - // SAFETY: TODO. - unsafe impl<$($impl_generics)*> - $crate::__internal::PinData for __ThePinData<$($ty_generics)*> - where $($whr)* - { - type Datee = $name<$($ty_generics)*>; - } - - // This struct will be used for the unpin analysis. Since only structurally pinned - // fields are relevant whether the struct should implement `Unpin`. - #[allow(dead_code)] - struct __Unpin <'__pin, $($impl_generics)*> - where $($whr)* - { - __phantom_pin: ::core::marker::PhantomData &'__pin ()>, - __phantom: ::core::marker::PhantomData< - fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*> - >, - // Only the pinned fields. - $($pinned)* - } - - #[doc(hidden)] - impl<'__pin, $($impl_generics)*> ::core::marker::Unpin for $name<$($ty_generics)*> - where - __Unpin<'__pin, $($ty_generics)*>: ::core::marker::Unpin, - $($whr)* - {} - - // We need to disallow normal `Drop` implementation, the exact behavior depends on - // whether `PinnedDrop` was specified as the parameter. - $crate::__pin_data!(drop_prevention: - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @where($($whr)*), - @pinned_drop($($pinned_drop)?), - ); - }; - }; - // When no `PinnedDrop` was specified, then we have to prevent implementing drop. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(), - ) => { - // 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 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. - // They might implement `PinnedDrop` correctly for the struct, but forget to give - // `PinnedDrop` as the parameter to `#[pin_data]`. - #[expect(non_camel_case_types)] - trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} - impl - UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} - impl<$($impl_generics)*> - UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for $name<$($ty_generics)*> - where $($whr)* {} - }; - // When `PinnedDrop` was specified we just implement `Drop` and delegate. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(PinnedDrop), - ) => { - impl<$($impl_generics)*> ::core::ops::Drop for $name<$($ty_generics)*> - where $($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 { $crate::__internal::OnlyCallFromDrop::new() }; - $crate::PinnedDrop::drop(pinned, token); - } - } - }; - // If some other parameter was specified, we emit a readable error. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop($($rest:tt)*), - ) => { - compile_error!( - "Wrong parameters to `#[pin_data]`, expected nothing or `PinnedDrop`, got '{}'.", - stringify!($($rest)*), - ); - }; - (make_pin_projections: - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?), - @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?), - ) => { - $crate::macros::paste! { - #[doc(hidden)] - $vis struct [< $name Projection >] <'__pin, $($decl_generics)*> { - $($(#[$($p_attr)*])* $pvis $p_field : ::core::pin::Pin<&'__pin mut $p_type>,)* - $($(#[$($attr)*])* $fvis $field : &'__pin mut $type,)* - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, - } - - impl<$($impl_generics)*> $name<$($ty_generics)*> - where $($whr)* - { - /// Pin-projects all fields of `Self`. - /// - /// These fields are structurally pinned: - $(#[doc = ::core::concat!(" - `", ::core::stringify!($p_field), "`")])* - /// - /// These fields are **not** structurally pinned: - $(#[doc = ::core::concat!(" - `", ::core::stringify!($field), "`")])* - #[inline] - $vis fn project<'__pin>( - self: ::core::pin::Pin<&'__pin mut Self>, - ) -> [< $name Projection >] <'__pin, $($ty_generics)*> { - // SAFETY: we only give access to `&mut` for fields not structurally pinned. - let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; - [< $name Projection >] { - $( - // SAFETY: `$p_field` is structurally pinned. - $(#[$($p_attr)*])* - $p_field : unsafe { ::core::pin::Pin::new_unchecked(&mut this.$p_field) }, - )* - $( - $(#[$($attr)*])* - $field : &mut this.$field, - )* - ___pin_phantom_data: ::core::marker::PhantomData, - } - } - } - } - }; - (make_pin_data: - @pin_data($pin_data:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?), - @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?), - ) => { - $crate::macros::paste! { - // For every field, we create a projection function according to its projection type. If a - // field is structurally pinned, then it must be initialized via `PinInit`, if it is not - // structurally pinned, then it can be initialized via `Init`. - // - // The functions are `unsafe` to prevent accidentally calling them. - #[allow(dead_code)] - #[expect(clippy::missing_safety_doc)] - impl<$($impl_generics)*> $pin_data<$($ty_generics)*> - where $($whr)* - { - $( - $(#[$($p_attr)*])* - $pvis unsafe fn $p_field( - self, - slot: *mut $p_type, - init: impl $crate::PinInit<$p_type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::PinInit::__pinned_init(init, slot) } - } - - $(#[$($p_attr)*])* - $pvis unsafe fn [<__project_ $p_field>]<'__slot>( - self, - slot: &'__slot mut $p_type, - ) -> ::core::pin::Pin<&'__slot mut $p_type> { - ::core::pin::Pin::new_unchecked(slot) - } - )* - $( - $(#[$($attr)*])* - $fvis unsafe fn $field( - self, - slot: *mut $type, - init: impl $crate::Init<$type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::Init::__init(init, slot) } - } - - $(#[$($attr)*])* - $fvis unsafe fn [<__project_ $field>]<'__slot>( - self, - slot: &'__slot mut $type, - ) -> &'__slot mut $type { - slot - } - )* - } - } - }; -} - /// The internal init macro. Do not call manually! /// /// This is called by the `{try_}{pin_}init!` macros with various inputs. From dae5466c4aa5b43a6cda4282bf9ff8e6b42ece0e Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:23 +0100 Subject: [PATCH 09/59] rust: pin-init: add `?Sized` bounds to traits in `#[pin_data]` macro The `#[pin_data]` macro 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 struct using pin-init. Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/pin_data.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 11ea3f8d8a1b..7d871236b49c 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -215,7 +215,7 @@ fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenSt // if it also implements `Drop` trait MustNotImplDrop {} #[expect(drop_bounds)] - impl MustNotImplDrop for T {} + impl MustNotImplDrop for T {} impl #impl_generics MustNotImplDrop for #ident #ty_generics #whr {} @@ -224,7 +224,7 @@ fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenSt // `PinnedDrop` as the parameter to `#[pin_data]`. #[expect(non_camel_case_types)] trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} - impl + impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} impl #impl_generics UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics From 4883830e9784bdf6223fe0e5f1ea36d4a4ab4fef Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:24 +0100 Subject: [PATCH 10/59] rust: pin-init: rewrite the initializer macros using `syn` Rewrite the initializer macros `[pin_]init!` using `syn`. No functional changes intended aside from improved error messages on syntactic and semantical errors. For example if one forgets to use `<-` with an initializer (and instead uses `:`): impl Bar { fn new() -> impl PinInit { ... } } impl Foo { fn new() -> impl PinInit { pin_init!(Self { bar: Bar::new() }) } } Then the declarative macro would report: error[E0308]: mismatched types --> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:9 | 14 | fn new() -> impl PinInit { | ------------------ the found opaque type ... 21 | pin_init!(Self { bar: Bar::new() }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | expected `Bar`, found opaque type | arguments to this function are incorrect | = note: expected struct `Bar` found opaque type `impl pin_init::PinInit` note: function defined here --> $RUST/core/src/ptr/mod.rs | | pub const unsafe fn write(dst: *mut T, src: T) { | ^^^^^ = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info) And the new error is: error[E0308]: mismatched types --> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:31 | 14 | fn new() -> impl PinInit { | ------------------ the found opaque type ... 21 | pin_init!(Self { bar: Bar::new() }) | --- ^^^^^^^^^^ expected `Bar`, found opaque type | | | arguments to this function are incorrect | = note: expected struct `Bar` found opaque type `impl pin_init::PinInit` note: function defined here --> $RUST/core/src/ptr/mod.rs | | pub const unsafe fn write(dst: *mut T, src: T) { | ^^^^^ Importantly, this error gives much more accurate span locations, pointing to the offending field, rather than the entire macro invocation. Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/init.rs | 445 ++++++++++++++ rust/pin-init/internal/src/lib.rs | 13 + rust/pin-init/src/lib.rs | 56 +- rust/pin-init/src/macros.rs | 951 ----------------------------- 4 files changed, 460 insertions(+), 1005 deletions(-) create mode 100644 rust/pin-init/internal/src/init.rs delete mode 100644 rust/pin-init/src/macros.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..b0eb66224341 --- /dev/null +++ b/rust/pin-init/internal/src/init.rs @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned}; +use syn::{ + braced, + parse::{End, Parse}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned, + token, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type, +}; + +use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; + +pub(crate) struct Initializer { + this: Option, + path: Path, + brace_token: token::Brace, + fields: Punctuated, + rest: Option<(Token![..], Expr)>, + error: Option<(Token![?], Type)>, +} + +struct This { + _and_token: Token![&], + ident: Ident, + _in_token: Token![in], +} + +enum InitializerField { + Value { + ident: Ident, + value: Option<(Token![:], Expr)>, + }, + Init { + ident: Ident, + _left_arrow_token: Token![<-], + value: Expr, + }, + Code { + _underscore_token: Token![_], + _colon_token: Token![:], + block: Block, + }, +} + +impl InitializerField { + fn ident(&self) -> Option<&Ident> { + match self { + Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident), + Self::Code { .. } => None, + } + } +} + +pub(crate) fn expand( + Initializer { + this, + path, + brace_token, + fields, + rest, + error, + }: Initializer, + default_error: Option<&'static str>, + pinned: bool, + dcx: &mut DiagCtxt, +) -> Result { + let error = error.map_or_else( + || { + if let Some(default_error) = default_error { + syn::parse_str(default_error).unwrap() + } else { + dcx.error(brace_token.span.close(), "expected `? ` after `}`"); + parse_quote!(::core::convert::Infallible) + } + }, + |(_, err)| err, + ); + let slot = format_ident!("slot"); + let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned { + ( + format_ident!("HasPinData"), + format_ident!("PinData"), + format_ident!("__pin_data"), + format_ident!("pin_init_from_closure"), + ) + } else { + ( + format_ident!("HasInitData"), + format_ident!("InitData"), + format_ident!("__init_data"), + format_ident!("init_from_closure"), + ) + }; + let init_kind = get_init_kind(rest, dcx); + 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(_: *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) }; + }, + }; + // `mixed_site` ensures that the data is not accessible to the user-controlled code. + let data = Ident::new("__data", Span::mixed_site()); + let init_fields = init_fields(&fields, pinned, &data, &slot); + 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. + // SAFETY: TODO + 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| ()) + }; + // SAFETY: TODO + let init = unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }; + init + }}) +} + +enum InitKind { + Normal, + Zeroing, +} + +fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKind { + let Some((dotdot, expr)) = rest else { + return InitKind::Normal; + }; + match &expr { + Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => 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 == "init_zeroed" + && segments[1].arguments.is_none() => + { + return InitKind::Zeroing; + } + _ => {} + }, + _ => {} + } + dcx.error( + dotdot.span().join(expr.span()).unwrap_or(expr.span()), + "expected nothing or `..Zeroable::init_zeroed()`.", + ); + InitKind::Normal +} + +/// Generate the code that initializes the fields of the struct using the initializers in `field`. +fn init_fields( + fields: &Punctuated, + pinned: bool, + data: &Ident, + slot: &Ident, +) -> TokenStream { + let mut guards = vec![]; + let mut res = TokenStream::new(); + for field in fields { + let init = match field { + InitializerField::Value { ident, value } => { + let mut value_ident = ident.clone(); + let value_prep = 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); + let accessor = if pinned { + let project_ident = format_ident!("__project_{ident}"); + quote! { + // SAFETY: TODO + unsafe { #data.#project_ident(&mut (*#slot).#ident) } + } + } else { + quote! { + // SAFETY: TODO + unsafe { &mut (*#slot).#ident } + } + }; + quote! { + { + #value_prep + // SAFETY: TODO + unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) }; + } + #[allow(unused_variables)] + let #ident = #accessor; + } + } + InitializerField::Init { ident, value, .. } => { + // Again span for better diagnostics + let init = format_ident!("init", span = value.span()); + if pinned { + let project_ident = format_ident!("__project_{ident}"); + quote! { + { + let #init = #value; + // SAFETY: + // - `slot` is valid, because we are inside of an initializer closure, we + // return when an error/panic occurs. + // - We also use `#data` to require the correct trait (`Init` or `PinInit`) + // for `#ident`. + unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? }; + } + // SAFETY: TODO + #[allow(unused_variables)] + let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) }; + } + } else { + quote! { + { + let #init = #value; + // 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), + )? + }; + } + // SAFETY: TODO + #[allow(unused_variables)] + let #ident = unsafe { &mut (*#slot).#ident }; + } + } + } + InitializerField::Code { block: value, .. } => quote!(#[allow(unused_braces)] #value), + }; + res.extend(init); + if let Some(ident) = field.ident() { + // `mixed_site` ensures that the guard is not accessible to the user-controlled code. + let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); + guards.push(guard.clone()); + 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, + init_kind: InitKind, + path: &Path, +) -> TokenStream { + let fields = fields.iter().filter_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(); + // We have to use type inference here to make zeroed have the correct type. This + // does not get executed, so it has no effect. + ::core::ptr::write(slot, zeroed); + zeroed = ::core::mem::zeroed(); + ::core::ptr::write(slot, #path { + #( + #fields: ::core::panic!(), + )* + ..zeroed + }) + }; + }, + } +} + +impl Parse for Initializer { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let this = input.peek(Token![&]).then(|| input.parse()).transpose()?; + let path = input.parse()?; + let content; + let brace_token = braced!(content in input); + let mut fields = Punctuated::new(); + loop { + let lh = content.lookahead1(); + if lh.peek(End) || lh.peek(Token![..]) { + break; + } else if lh.peek(Ident) || lh.peek(Token![_]) { + fields.push_value(content.parse()?); + let lh = content.lookahead1(); + if lh.peek(End) { + break; + } else if lh.peek(Token![,]) { + fields.push_punct(content.parse()?); + } else { + return Err(lh.error()); + } + } else { + return Err(lh.error()); + } + } + let rest = content + .peek(Token![..]) + .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?))) + .transpose()?; + let error = input + .peek(Token![?]) + .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?))) + .transpose()?; + Ok(Self { + this, + path, + brace_token, + fields, + rest, + error, + }) + } +} + +impl Parse for This { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + Ok(Self { + _and_token: input.parse()?, + ident: input.parse()?, + _in_token: input.parse()?, + }) + } +} + +impl Parse for InitializerField { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let lh = input.lookahead1(); + if lh.peek(Token![_]) { + Ok(Self::Code { + _underscore_token: input.parse()?, + _colon_token: input.parse()?, + block: input.parse()?, + }) + } else if lh.peek(Ident) { + let ident = input.parse()?; + let lh = input.lookahead1(); + if lh.peek(Token![<-]) { + Ok(Self::Init { + ident, + _left_arrow_token: input.parse()?, + value: input.parse()?, + }) + } else if lh.peek(Token![:]) { + Ok(Self::Value { + ident, + value: Some((input.parse()?, input.parse()?)), + }) + } else if lh.peek(Token![,]) || lh.peek(End) { + Ok(Self::Value { ident, value: None }) + } else { + Err(lh.error()) + } + } else { + Err(lh.error()) + } + } +} diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index 56dc306e04a9..08372c8f65f0 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -16,6 +16,7 @@ use syn::parse_macro_input; use crate::diagnostics::DiagCtxt; mod diagnostics; +mod init; mod pin_data; mod pinned_drop; mod zeroable; @@ -45,3 +46,15 @@ pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input); DiagCtxt::with(|dcx| zeroable::maybe_derive(input, dcx)).into() } +#[proc_macro] +pub fn init(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input); + DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infallible"), false, dcx)) + .into() +} + +#[proc_macro] +pub fn pin_init(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input); + DiagCtxt::with(|dcx| init::expand(input, Some("::core::convert::Infallible"), true, dcx)).into() +} diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 0e707f00061f..780b4fead22d 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -297,8 +297,6 @@ extern crate self as pin_init; #[doc(hidden)] pub mod __internal; -#[doc(hidden)] -pub mod macros; #[cfg(any(feature = "std", feature = "alloc"))] mod alloc; @@ -781,32 +779,7 @@ macro_rules! stack_try_pin_init { /// ``` /// /// [`NonNull`]: 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::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? ::core::convert::Infallible) - }; - ($(&$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::pin_init; /// Construct an in-place, fallible initializer for `struct`s. /// @@ -844,32 +817,7 @@ macro_rules! 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::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? ::core::convert::Infallible) - }; - ($(&$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::init; /// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is /// structurally pinned. diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs deleted file mode 100644 index eea8adc5c7ad..000000000000 --- a/rust/pin-init/src/macros.rs +++ /dev/null @@ -1,951 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT - -//! This module provides the macros that actually implement the proc-macros `pin_data` and -//! `pinned_drop`. It also contains `__init_internal`, the implementation of the -//! `{try_}{pin_}init!` macros. -//! -//! These macros should never be called directly, since they expect their input to be -//! in a certain format which is internal. If used incorrectly, these macros can lead to UB even in -//! safe code! Use the public facing macros instead. -//! -//! This architecture has been chosen because the kernel does not yet have access to `syn` which -//! would make matters a lot easier for implementing these as proc-macros. -//! -//! Since this library and the kernel implementation should diverge as little as possible, the same -//! approach has been taken here. -//! -//! # Macro expansion example -//! -//! This section is intended for readers trying to understand the macros in this module and the -//! `[try_][pin_]init!` macros from `lib.rs`. -//! -//! We will look at the following example: -//! -//! ```rust,ignore -//! #[pin_data] -//! #[repr(C)] -//! struct Bar { -//! #[pin] -//! t: T, -//! pub x: usize, -//! } -//! -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! pin_init!(Self { t, x: 0 }) -//! } -//! } -//! -//! #[pin_data(PinnedDrop)] -//! struct Foo { -//! a: usize, -//! #[pin] -//! b: Bar, -//! } -//! -//! #[pinned_drop] -//! impl PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! -//! let a = 42; -//! let initializer = pin_init!(Foo { -//! a, -//! b <- Bar::new(36), -//! }); -//! ``` -//! -//! This example includes the most common and important features of the pin-init API. -//! -//! Below you can find individual section about the different macro invocations. Here are some -//! general things we need to take into account when designing macros: -//! - use global paths, similarly to file paths, these start with the separator: `::core::panic!()` -//! this ensures that the correct item is used, since users could define their own `mod core {}` -//! and then their own `panic!` inside to execute arbitrary code inside of our macro. -//! - macro `unsafe` hygiene: we need to ensure that we do not expand arbitrary, user-supplied -//! expressions inside of an `unsafe` block in the macro, because this would allow users to do -//! `unsafe` operations without an associated `unsafe` block. -//! -//! ## `#[pin_data]` on `Bar` -//! -//! This macro is used to specify which fields are structurally pinned and which fields are not. It -//! is placed on the struct definition and allows `#[pin]` to be placed on the fields. -//! -//! Here is the definition of `Bar` from our example: -//! -//! ```rust,ignore -//! #[pin_data] -//! #[repr(C)] -//! struct Bar { -//! #[pin] -//! t: T, -//! pub x: usize, -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! // Firstly the normal definition of the struct, attributes are preserved: -//! #[repr(C)] -//! struct Bar { -//! t: T, -//! pub x: usize, -//! } -//! // Then an anonymous constant is defined, this is because we do not want any code to access the -//! // types that we define inside: -//! const _: () = { -//! // We define the pin-data carrying struct, it is a ZST and needs to have the same generics, -//! // since we need to implement access functions for each field and thus need to know its -//! // type. -//! struct __ThePinData { -//! __phantom: ::core::marker::PhantomData) -> Bar>, -//! } -//! // We implement `Copy` for the pin-data struct, since all functions it defines will take -//! // `self` by value. -//! impl ::core::clone::Clone for __ThePinData { -//! fn clone(&self) -> Self { -//! *self -//! } -//! } -//! impl ::core::marker::Copy for __ThePinData {} -//! // For every field of `Bar`, the pin-data struct will define a function with the same name -//! // and accessor (`pub` or `pub(crate)` etc.). This function will take a pointer to the -//! // field (`slot`) and a `PinInit` or `Init` depending on the projection kind of the field -//! // (if pinning is structural for the field, then `PinInit` otherwise `Init`). -//! #[allow(dead_code)] -//! impl __ThePinData { -//! unsafe fn t( -//! self, -//! slot: *mut T, -//! // Since `t` is `#[pin]`, this is `PinInit`. -//! init: impl ::pin_init::PinInit, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } -//! } -//! pub unsafe fn x( -//! self, -//! slot: *mut usize, -//! // Since `x` is not `#[pin]`, this is `Init`. -//! init: impl ::pin_init::Init, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::Init::__init(init, slot) } -//! } -//! } -//! // Implement the internal `HasPinData` trait that associates `Bar` with the pin-data struct -//! // that we constructed above. -//! unsafe impl ::pin_init::__internal::HasPinData for Bar { -//! type PinData = __ThePinData; -//! unsafe fn __pin_data() -> Self::PinData { -//! __ThePinData { -//! __phantom: ::core::marker::PhantomData, -//! } -//! } -//! } -//! // Implement the internal `PinData` trait that marks the pin-data struct as a pin-data -//! // struct. This is important to ensure that no user can implement a rogue `__pin_data` -//! // function without using `unsafe`. -//! unsafe impl ::pin_init::__internal::PinData for __ThePinData { -//! type Datee = Bar; -//! } -//! // Now we only want to implement `Unpin` for `Bar` when every structurally pinned field is -//! // `Unpin`. In other words, whether `Bar` is `Unpin` only depends on structurally pinned -//! // fields (those marked with `#[pin]`). These fields will be listed in this struct, in our -//! // case no such fields exist, hence this is almost empty. The two phantomdata fields exist -//! // for two reasons: -//! // - `__phantom`: every generic must be used, since we cannot really know which generics -//! // are used, we declare all and then use everything here once. -//! // - `__phantom_pin`: uses the `'__pin` lifetime and ensures that this struct is invariant -//! // over it. The lifetime is needed to work around the limitation that trait bounds must -//! // not be trivial, e.g. the user has a `#[pin] PhantomPinned` field -- this is -//! // unconditionally `!Unpin` and results in an error. The lifetime tricks the compiler -//! // into accepting these bounds regardless. -//! #[allow(dead_code)] -//! struct __Unpin<'__pin, T> { -//! __phantom_pin: ::core::marker::PhantomData &'__pin ()>, -//! __phantom: ::core::marker::PhantomData) -> Bar>, -//! // Our only `#[pin]` field is `t`. -//! t: T, -//! } -//! #[doc(hidden)] -//! impl<'__pin, T> ::core::marker::Unpin for Bar -//! where -//! __Unpin<'__pin, T>: ::core::marker::Unpin, -//! {} -//! // Now we need to ensure that `Bar` does not implement `Drop`, since that would give users -//! // access to `&mut self` inside of `drop` even if the struct was pinned. This could lead to -//! // UB with only safe code, so we disallow this by giving a trait implementation error using -//! // a direct impl and a blanket implementation. -//! trait MustNotImplDrop {} -//! // Normally `Drop` bounds do not have the correct semantics, but for this purpose they do -//! // (normally people want to know if a type has any kind of drop glue at all, here we want -//! // to know if it has any kind of custom drop glue, which is exactly what this bound does). -//! #[expect(drop_bounds)] -//! impl MustNotImplDrop for T {} -//! impl MustNotImplDrop for Bar {} -//! // Here comes a convenience check, if one implemented `PinnedDrop`, but forgot to add it to -//! // `#[pin_data]`, then this will error with the same mechanic as above, this is not needed -//! // for safety, but a good sanity check, since no normal code calls `PinnedDrop::drop`. -//! #[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 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for Bar {} -//! }; -//! ``` -//! -//! ## `pin_init!` in `impl Bar` -//! -//! This macro creates an pin-initializer for the given struct. It requires that the struct is -//! annotated by `#[pin_data]`. -//! -//! Here is the impl on `Bar` defining the new function: -//! -//! ```rust,ignore -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! pin_init!(Self { t, x: 0 }) -//! } -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! { -//! // 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. -//! // - the function is unsafe, hence the unsafe block -//! // - we `use` the `HasPinData` trait in the block, it is only available in that -//! // scope. -//! let data = unsafe { -//! use ::pin_init::__internal::HasPinData; -//! Self::__pin_data() -//! }; -//! // Ensure that `data` really is of type `PinData` and help with type inference: -//! let init = ::pin_init::__internal::PinData::make_closure::< -//! _, -//! __InitOk, -//! ::core::convert::Infallible, -//! >(data, move |slot| { -//! { -//! // Shadow the structure so it cannot be used to return early. If a user -//! // tries to write `return Ok(__InitOk)`, then they get a type error, -//! // since that will refer to this struct instead of the one defined -//! // above. -//! struct __InitOk; -//! // This is the expansion of `t,`, which is syntactic sugar for `t: t,`. -//! { -//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).t), t) }; -//! } -//! // Since initialization could fail later (not in this case, since the -//! // error type is `Infallible`) we will need to drop this field if there -//! // is an error later. This `DropGuard` will drop the field when it gets -//! // dropped and has not yet been forgotten. -//! let __t_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).t)) -//! }; -//! // Expansion of `x: 0,`: -//! // Since this can be an arbitrary expression we cannot place it inside -//! // of the `unsafe` block, so we bind it here. -//! { -//! let x = 0; -//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).x), x) }; -//! } -//! // We again create a `DropGuard`. -//! let __x_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).x)) -//! }; -//! // Since initialization has successfully completed, we can now forget -//! // the guards. This is not `mem::forget`, since we only have -//! // `&DropGuard`. -//! ::core::mem::forget(__x_guard); -//! ::core::mem::forget(__t_guard); -//! // Here we use the type checker to ensure that every field has been -//! // initialized exactly once, since this is `if false` it will never get -//! // executed, but still type-checked. -//! // Additionally we abuse `slot` to automatically infer the correct type -//! // for the struct. This is also another check that every field is -//! // accessible from this scope. -//! #[allow(unreachable_code, clippy::diverging_sub_expression)] -//! let _ = || { -//! unsafe { -//! ::core::ptr::write( -//! slot, -//! Self { -//! // We only care about typecheck finding every field -//! // here, the expression does not matter, just conjure -//! // one using `panic!()`: -//! t: ::core::panic!(), -//! x: ::core::panic!(), -//! }, -//! ); -//! }; -//! }; -//! } -//! // We leave the scope above and gain access to the previously shadowed -//! // `__InitOk` that we need to return. -//! Ok(__InitOk) -//! }); -//! // Change the return type from `__InitOk` to `()`. -//! let init = move | -//! slot, -//! | -> ::core::result::Result<(), ::core::convert::Infallible> { -//! init(slot).map(|__InitOk| ()) -//! }; -//! // Construct the initializer. -//! let init = unsafe { -//! ::pin_init::pin_init_from_closure::< -//! _, -//! ::core::convert::Infallible, -//! >(init) -//! }; -//! init -//! } -//! } -//! } -//! ``` -//! -//! ## `#[pin_data]` on `Foo` -//! -//! Since we already took a look at `#[pin_data]` on `Bar`, this section will only explain the -//! differences/new things in the expansion of the `Foo` definition: -//! -//! ```rust,ignore -//! #[pin_data(PinnedDrop)] -//! struct Foo { -//! a: usize, -//! #[pin] -//! b: Bar, -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! struct Foo { -//! a: usize, -//! b: Bar, -//! } -//! const _: () = { -//! struct __ThePinData { -//! __phantom: ::core::marker::PhantomData Foo>, -//! } -//! impl ::core::clone::Clone for __ThePinData { -//! fn clone(&self) -> Self { -//! *self -//! } -//! } -//! impl ::core::marker::Copy for __ThePinData {} -//! #[allow(dead_code)] -//! impl __ThePinData { -//! unsafe fn b( -//! self, -//! slot: *mut Bar, -//! init: impl ::pin_init::PinInit, E>, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } -//! } -//! unsafe fn a( -//! self, -//! slot: *mut usize, -//! init: impl ::pin_init::Init, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::Init::__init(init, slot) } -//! } -//! } -//! unsafe impl ::pin_init::__internal::HasPinData for Foo { -//! type PinData = __ThePinData; -//! unsafe fn __pin_data() -> Self::PinData { -//! __ThePinData { -//! __phantom: ::core::marker::PhantomData, -//! } -//! } -//! } -//! unsafe impl ::pin_init::__internal::PinData for __ThePinData { -//! type Datee = Foo; -//! } -//! #[allow(dead_code)] -//! struct __Unpin<'__pin> { -//! __phantom_pin: ::core::marker::PhantomData &'__pin ()>, -//! __phantom: ::core::marker::PhantomData Foo>, -//! b: Bar, -//! } -//! #[doc(hidden)] -//! impl<'__pin> ::core::marker::Unpin for Foo -//! where -//! __Unpin<'__pin>: ::core::marker::Unpin, -//! {} -//! // Since we specified `PinnedDrop` as the argument to `#[pin_data]`, we expect `Foo` to -//! // implement `PinnedDrop`. Thus we do not need to prevent `Drop` implementations like -//! // before, instead we implement `Drop` here and delegate to `PinnedDrop`. -//! impl ::core::ops::Drop for Foo { -//! fn drop(&mut self) { -//! // Since we are getting dropped, no one else has a reference to `self` and thus we -//! // can assume that we never move. -//! let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) }; -//! // Create the unsafe token that proves that we are inside of a destructor, this -//! // type is only allowed to be created in a destructor. -//! let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() }; -//! ::pin_init::PinnedDrop::drop(pinned, token); -//! } -//! } -//! }; -//! ``` -//! -//! ## `#[pinned_drop]` on `impl PinnedDrop for Foo` -//! -//! This macro is used to implement the `PinnedDrop` trait, since that trait is `unsafe` and has an -//! extra parameter that should not be used at all. The macro hides that parameter. -//! -//! Here is the `PinnedDrop` impl for `Foo`: -//! -//! ```rust,ignore -//! #[pinned_drop] -//! impl PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! // `unsafe`, full path and the token parameter are added, everything else stays the same. -//! unsafe impl ::pin_init::PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>, _: ::pin_init::__internal::OnlyCallFromDrop) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! ``` -//! -//! ## `pin_init!` on `Foo` -//! -//! Since we already took a look at `pin_init!` on `Bar`, this section will only show the expansion -//! of `pin_init!` on `Foo`: -//! -//! ```rust,ignore -//! let a = 42; -//! let initializer = pin_init!(Foo { -//! a, -//! b <- Bar::new(36), -//! }); -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! let a = 42; -//! let initializer = { -//! struct __InitOk; -//! let data = unsafe { -//! use ::pin_init::__internal::HasPinData; -//! Foo::__pin_data() -//! }; -//! let init = ::pin_init::__internal::PinData::make_closure::< -//! _, -//! __InitOk, -//! ::core::convert::Infallible, -//! >(data, move |slot| { -//! { -//! struct __InitOk; -//! { -//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).a), a) }; -//! } -//! let __a_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).a)) -//! }; -//! let init = Bar::new(36); -//! unsafe { data.b(::core::addr_of_mut!((*slot).b), b)? }; -//! let __b_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).b)) -//! }; -//! ::core::mem::forget(__b_guard); -//! ::core::mem::forget(__a_guard); -//! #[allow(unreachable_code, clippy::diverging_sub_expression)] -//! let _ = || { -//! unsafe { -//! ::core::ptr::write( -//! slot, -//! Foo { -//! a: ::core::panic!(), -//! b: ::core::panic!(), -//! }, -//! ); -//! }; -//! }; -//! } -//! Ok(__InitOk) -//! }); -//! let init = move | -//! slot, -//! | -> ::core::result::Result<(), ::core::convert::Infallible> { -//! init(slot).map(|__InitOk| ()) -//! }; -//! let init = unsafe { -//! ::pin_init::pin_init_from_closure::<_, ::core::convert::Infallible>(init) -//! }; -//! init -//! }; -//! ``` - -#[cfg(kernel)] -pub use ::macros::paste; -#[cfg(not(kernel))] -pub use ::paste::paste; - -/// The internal init macro. Do not call manually! -/// -/// This is called by the `{try_}{pin_}init!` macros with various inputs. -/// -/// This macro has multiple internal call configurations, these are always the very first ident: -/// - nothing: this is the base case and called by the `{try_}{pin_}init!` macros. -/// - `with_update_parsed`: when the `..Zeroable::init_zeroed()` syntax has been handled. -/// - `init_slot`: recursively creates the code that initializes all fields in `slot`. -/// - `make_initializer`: recursively create the struct initializer that guarantees that every -/// field has been initialized exactly once. -#[doc(hidden)] -#[macro_export] -macro_rules! __init_internal { - ( - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @munch_fields(), - ) => { - $crate::__init_internal!(with_update_parsed: - @this($($this)?), - @typ($t), - @fields($($fields)*), - @error($err), - @data($data, $($use_data)?), - @has_data($has_data, $get_data), - @construct_closure($construct_closure), - @init_zeroed(), // Nothing means default behavior. - ) - }; - ( - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @munch_fields(..Zeroable::init_zeroed()), - ) => { - $crate::__init_internal!(with_update_parsed: - @this($($this)?), - @typ($t), - @fields($($fields)*), - @error($err), - @data($data, $($use_data)?), - @has_data($has_data, $get_data), - @construct_closure($construct_closure), - @init_zeroed(()), // `()` means zero all fields not mentioned. - ) - }; - ( - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @munch_fields($ignore:tt $($rest:tt)*), - ) => { - $crate::__init_internal!( - @this($($this)?), - @typ($t), - @fields($($fields)*), - @error($err), - @data($data, $($use_data)?), - @has_data($has_data, $get_data), - @construct_closure($construct_closure), - @munch_fields($($rest)*), - ) - }; - (with_update_parsed: - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @init_zeroed($($init_zeroed:expr)?), - ) => {{ - // 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. - // - // SAFETY: TODO. - let data = unsafe { - use $crate::__internal::$has_data; - // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal - // information that is associated to already parsed fragments, so a path fragment - // cannot be used in this position. Doing the retokenization results in valid rust - // code. - $crate::macros::paste!($t::$get_data()) - }; - // Ensure that `data` really is of type `$data` and help with type inference: - let init = $crate::__internal::$data::make_closure::<_, __InitOk, $err>( - data, - move |slot| { - { - // Shadow the structure so it cannot be used to return early. - struct __InitOk; - // If `$init_zeroed` is present we should zero the slot now and not emit an - // error when fields are missing (since they will be zeroed). We also have to - // check that the type actually implements `Zeroable`. - $({ - fn assert_zeroable(_: *mut T) {} - // 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) }; - $init_zeroed // This will be `()` if set. - })? - // Create the `this` so it can be referenced by the user inside of the - // expressions creating the individual fields. - $(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)? - // Initialize every field. - $crate::__init_internal!(init_slot($($use_data)?): - @data(data), - @slot(slot), - @guards(), - @munch_fields($($fields)*,), - ); - // 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)] - let _ = || { - $crate::__init_internal!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); - }; - } - Ok(__InitOk) - } - ); - let init = move |slot| -> ::core::result::Result<(), $err> { - init(slot).map(|__InitOk| ()) - }; - // SAFETY: TODO. - let init = unsafe { $crate::$construct_closure::<_, $err>(init) }; - init - }}; - (init_slot($($use_data:ident)?): - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - @munch_fields($(..Zeroable::init_zeroed())? $(,)?), - ) => { - // Endpoint of munching, no fields are left. If execution reaches this point, all fields - // have been initialized. Therefore we can now dismiss the guards by forgetting them. - $(::core::mem::forget($guards);)* - }; - (init_slot($($use_data:ident)?): - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // arbitrary code block - @munch_fields(_: { $($code:tt)* }, $($rest:tt)*), - ) => { - { $($code)* } - $crate::__init_internal!(init_slot($($use_data)?): - @data($data), - @slot($slot), - @guards($($guards,)*), - @munch_fields($($rest)*), - ); - }; - (init_slot($use_data:ident): // `use_data` is present, so we use the `data` to init fields. - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // In-place initialization syntax. - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - let init = $val; - // 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 `$field`. - unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? }; - // SAFETY: - // - the project function does the correct field projection, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - #[allow(unused_variables)] - let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) }); - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot($use_data): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (init_slot(): // No `use_data`, so we use `Init::__init` directly. - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // In-place initialization syntax. - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - let init = $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? }; - - // SAFETY: - // - the field is not structurally pinned, since the line above must compile, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - #[allow(unused_variables)] - let $field = unsafe { &mut (*$slot).$field }; - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot(): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (init_slot(): // No `use_data`, so all fields are not structurally pinned - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // Init by-value. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - { - $(let $field = $val;)? - // Initialize the field. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; - } - - #[allow(unused_variables)] - // SAFETY: - // - the field is not structurally pinned, since no `use_data` was required to create this - // initializer, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - let $field = unsafe { &mut (*$slot).$field }; - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot(): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (init_slot($use_data:ident): - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // Init by-value. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - { - $(let $field = $val;)? - // Initialize the field. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; - } - // SAFETY: - // - the project function does the correct field projection, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - #[allow(unused_variables)] - let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) }); - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot($use_data): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields(_: { $($code:tt)* }, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - // code blocks are ignored for the initializer check - $crate::__init_internal!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)*), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields(..Zeroable::init_zeroed() $(,)?), - @acc($($acc:tt)*), - ) => { - // Endpoint, nothing more to munch, create the initializer. Since the users specified - // `..Zeroable::init_zeroed()`, the slot will already have been zeroed and all field that have - // not been overwritten are thus zero and initialized. We still check that all fields are - // actually accessible by using the struct update syntax ourselves. - // We are inside of a closure that is never executed and thus we can abuse `slot` to - // get the correct type inference here: - #[allow(unused_assignments)] - unsafe { - let mut zeroed = ::core::mem::zeroed(); - // We have to use type inference here to make zeroed have the correct type. This does - // not get executed, so it has no effect. - ::core::ptr::write($slot, zeroed); - zeroed = ::core::mem::zeroed(); - // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal - // information that is associated to already parsed fragments, so a path fragment - // cannot be used in this position. Doing the retokenization results in valid rust - // code. - $crate::macros::paste!( - ::core::ptr::write($slot, $t { - $($acc)* - ..zeroed - }); - ); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields($(,)?), - @acc($($acc:tt)*), - ) => { - // Endpoint, nothing more to munch, create the initializer. - // Since we are in the closure that is never called, this will never get executed. - // We abuse `slot` to get the correct type inference here: - // - // SAFETY: TODO. - unsafe { - // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal - // information that is associated to already parsed fragments, so a path fragment - // cannot be used in this position. Doing the retokenization results in valid rust - // code. - $crate::macros::paste!( - ::core::ptr::write($slot, $t { - $($acc)* - }); - ); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::__init_internal!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::__init_internal!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; -} From aeabc92eb2d8c27578274a7ec3d0d00558fedfc2 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:25 +0100 Subject: [PATCH 11/59] rust: pin-init: add `#[default_error()]` attribute to initializer macros The `#[default_error()]` attribute can be used to supply a default type as the error used for the `[pin_]init!` macros. This way one can easily define custom `try_[pin_]init!` variants that default to your project specific error type. Just write the following declarative macro: macro_rules! try_init { ($($args:tt)*) => { ::pin_init::init!( #[default_error(YourCustomErrorType)] $($args)* ) } } Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/init.rs | 44 ++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index b0eb66224341..21cddcd9e9f8 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -8,12 +8,13 @@ use syn::{ parse_quote, punctuated::Punctuated, spanned::Spanned, - token, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type, + token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type, }; use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; pub(crate) struct Initializer { + attrs: Vec, this: Option, path: Path, brace_token: token::Brace, @@ -54,8 +55,17 @@ impl InitializerField { } } +enum InitializerAttribute { + DefaultError(DefaultErrorAttribute), +} + +struct DefaultErrorAttribute { + ty: Box, +} + pub(crate) fn expand( Initializer { + attrs, this, path, brace_token, @@ -69,14 +79,23 @@ pub(crate) fn expand( ) -> Result { let error = error.map_or_else( || { - if let Some(default_error) = default_error { + if let Some(default_error) = attrs.iter().fold(None, |acc, attr| { + #[expect(irrefutable_let_patterns)] + if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr { + Some(ty.clone()) + } else { + acc + } + }) { + default_error + } else if let Some(default_error) = default_error { syn::parse_str(default_error).unwrap() } else { dcx.error(brace_token.span.close(), "expected `? ` after `}`"); parse_quote!(::core::convert::Infallible) } }, - |(_, err)| err, + |(_, err)| Box::new(err), ); let slot = format_ident!("slot"); let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned { @@ -358,6 +377,7 @@ fn make_field_check( impl Parse for Initializer { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; let this = input.peek(Token![&]).then(|| input.parse()).transpose()?; let path = input.parse()?; let content; @@ -389,7 +409,19 @@ impl Parse for Initializer { .peek(Token![?]) .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?))) .transpose()?; + let attrs = attrs + .into_iter() + .map(|a| { + if a.path().is_ident("default_error") { + a.parse_args::() + .map(InitializerAttribute::DefaultError) + } else { + Err(syn::Error::new_spanned(a, "unknown initializer attribute")) + } + }) + .collect::, _>>()?; Ok(Self { + attrs, this, path, brace_token, @@ -400,6 +432,12 @@ impl Parse for Initializer { } } +impl Parse for DefaultErrorAttribute { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + Ok(Self { ty: input.parse()? }) + } +} + impl Parse for This { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { Ok(Self { From d083a6214ca6d486ac58f84f8964c72028469342 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:26 +0100 Subject: [PATCH 12/59] rust: init: use `#[default_error(err)]` for the initializer macros Initializer macros should use this attribute instead of manually parsing the macro's input. This is because the syntax is now parsed using `syn`, which permits more complex constructs to be parsed. In addition, this ensures that the kernel's initializer marcos will have the exact same syntax as the ones from pin-init. Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/kernel/init.rs | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 917f7ef001fd..7a0d4559d7b5 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -219,20 +219,12 @@ pub trait InPlaceInit: Sized { /// [`Error`]: crate::error::Error #[macro_export] macro_rules! try_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) => { - ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $crate::error::Error) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) => { - ::pin_init::init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $err) - }; + ($($args:tt)*) => { + ::pin_init::init!( + #[default_error($crate::error::Error)] + $($args)* + ) + } } /// Construct an in-place, fallible pinned initializer for `struct`s. @@ -279,18 +271,10 @@ macro_rules! try_init { /// [`Error`]: crate::error::Error #[macro_export] macro_rules! try_pin_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) => { - ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $crate::error::Error) - }; - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) => { - ::pin_init::pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? $err) - }; + ($($args:tt)*) => { + ::pin_init::pin_init!( + #[default_error($crate::error::Error)] + $($args)* + ) + } } From d26732e57b06ef32dadfc32d5de9ac39262698cb Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:27 +0100 Subject: [PATCH 13/59] rust: pin-init: internal: init: add support for attributes on initializer fields Initializer fields ought to support the same attributes that are allowed in struct initializers on fields. For example, `cfg` or lint levels such as `expect`, `allow` etc. Add parsing support for these attributes using syn to initializer fields and adjust the macro expansion accordingly. Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/init.rs | 69 ++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 21cddcd9e9f8..e8b93b444f7e 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -29,7 +29,12 @@ struct This { _in_token: Token![in], } -enum InitializerField { +struct InitializerField { + attrs: Vec, + kind: InitializerKind, +} + +enum InitializerKind { Value { ident: Ident, value: Option<(Token![:], Expr)>, @@ -46,7 +51,7 @@ enum InitializerField { }, } -impl InitializerField { +impl InitializerKind { fn ident(&self) -> Option<&Ident> { match self { Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident), @@ -227,10 +232,16 @@ fn init_fields( slot: &Ident, ) -> TokenStream { let mut guards = vec![]; + let mut guard_attrs = vec![]; let mut res = TokenStream::new(); - for field in fields { - let init = match field { - InitializerField::Value { ident, value } => { + for InitializerField { attrs, kind } in fields { + let cfgs = { + let mut cfgs = attrs.clone(); + cfgs.retain(|attr| attr.path().is_ident("cfg")); + cfgs + }; + let init = match kind { + InitializerKind::Value { ident, value } => { let mut value_ident = ident.clone(); let value_prep = value.as_ref().map(|value| &value.1).map(|value| { // Setting the span of `value_ident` to `value`'s span improves error messages @@ -253,21 +264,24 @@ fn init_fields( } }; quote! { + #(#attrs)* { #value_prep // SAFETY: TODO unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) }; } + #(#cfgs)* #[allow(unused_variables)] let #ident = #accessor; } } - InitializerField::Init { ident, value, .. } => { + InitializerKind::Init { ident, value, .. } => { // Again span for better diagnostics let init = format_ident!("init", span = value.span()); if pinned { let project_ident = format_ident!("__project_{ident}"); quote! { + #(#attrs)* { let #init = #value; // SAFETY: @@ -277,12 +291,14 @@ fn init_fields( // for `#ident`. unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? }; } + #(#cfgs)* // SAFETY: TODO #[allow(unused_variables)] let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) }; } } else { quote! { + #(#attrs)* { let #init = #value; // SAFETY: `slot` is valid, because we are inside of an initializer @@ -294,20 +310,25 @@ fn init_fields( )? }; } + #(#cfgs)* // SAFETY: TODO #[allow(unused_variables)] let #ident = unsafe { &mut (*#slot).#ident }; } } } - InitializerField::Code { block: value, .. } => quote!(#[allow(unused_braces)] #value), + InitializerKind::Code { block: value, .. } => quote! { + #(#attrs)* + #[allow(unused_braces)] + #value + }, }; res.extend(init); - if let Some(ident) = field.ident() { + if let Some(ident) = kind.ident() { // `mixed_site` ensures that the guard is not accessible to the user-controlled code. let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - guards.push(guard.clone()); res.extend(quote! { + #(#cfgs)* // Create the drop guard: // // We rely on macro hygiene to make it impossible for users to access this local @@ -319,13 +340,18 @@ fn init_fields( ) }; }); + guards.push(guard); + guard_attrs.push(cfgs); } } 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);)* + #( + #(#guard_attrs)* + ::core::mem::forget(#guards); + )* } } @@ -335,7 +361,10 @@ fn make_field_check( init_kind: InitKind, path: &Path, ) -> TokenStream { - let fields = fields.iter().filter_map(|f| f.ident()); + let field_attrs = fields + .iter() + .filter_map(|f| f.kind.ident().map(|_| &f.attrs)); + let field_name = fields.iter().filter_map(|f| f.kind.ident()); match init_kind { InitKind::Normal => quote! { // We use unreachable code to ensure that all fields have been mentioned exactly once, @@ -346,7 +375,8 @@ fn make_field_check( let _ = || unsafe { ::core::ptr::write(slot, #path { #( - #fields: ::core::panic!(), + #(#field_attrs)* + #field_name: ::core::panic!(), )* }) }; @@ -366,7 +396,8 @@ fn make_field_check( zeroed = ::core::mem::zeroed(); ::core::ptr::write(slot, #path { #( - #fields: ::core::panic!(), + #(#field_attrs)* + #field_name: ::core::panic!(), )* ..zeroed }) @@ -387,7 +418,7 @@ impl Parse for Initializer { let lh = content.lookahead1(); if lh.peek(End) || lh.peek(Token![..]) { break; - } else if lh.peek(Ident) || lh.peek(Token![_]) { + } else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Token![#]) { fields.push_value(content.parse()?); let lh = content.lookahead1(); if lh.peek(End) { @@ -449,6 +480,16 @@ impl Parse for This { } impl Parse for InitializerField { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + Ok(Self { + attrs, + kind: input.parse()?, + }) + } +} + +impl Parse for InitializerKind { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let lh = input.lookahead1(); if lh.peek(Token![_]) { From ceca298c53f9300ea689207f9ae9a3da3b4b4c4f Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:28 +0100 Subject: [PATCH 14/59] rust: pin-init: internal: init: add escape hatch for referencing initialized fields The initializer macro emits mutable references for already initialized fields, which allows modifying or accessing them later in code blocks or when initializing other fields. This behavior results in compiler errors when combining with packed structs, since those do not permit creating references to misaligned fields. For example: #[repr(C, packed)] struct Foo { a: i8, b: i32, } fn main() { let _ = init!(Foo { a: -42, b: 42 }); } This will lead to an error like this: error[E0793]: reference to field of packed struct is unaligned --> tests/ui/compile-fail/init/packed_struct.rs:10:13 | 10 | let _ = init!(Foo { a: -42, b: 42 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this struct is 1-byte aligned, but the type of this field may require higher alignment = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) = note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) This was requested by Janne Grunau [1] and will most certainly be used by the kernel when we eventually end up with trying to initialize packed structs. Thus add an initializer attribute `#[disable_initialized_field_access]` that does what the name suggests: do not generate references to already initialized fields. There is space for future work: add yet another attribute which can be applied on fields of initializers that ask for said field to be made accessible. We can add that when the need arises. Requested-by: Janne Grunau Link: https://lore.kernel.org/all/20251206170214.GE1097212@robin.jannau.net [1] Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/init.rs | 75 +++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index e8b93b444f7e..ed2e1462e176 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -62,6 +62,7 @@ impl InitializerKind { enum InitializerAttribute { DefaultError(DefaultErrorAttribute), + DisableInitializedFieldAccess, } struct DefaultErrorAttribute { @@ -85,7 +86,6 @@ pub(crate) fn expand( let error = error.map_or_else( || { if let Some(default_error) = attrs.iter().fold(None, |acc, attr| { - #[expect(irrefutable_let_patterns)] if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr { Some(ty.clone()) } else { @@ -145,7 +145,15 @@ pub(crate) fn expand( }; // `mixed_site` ensures that the data is not accessible to the user-controlled code. let data = Ident::new("__data", Span::mixed_site()); - let init_fields = init_fields(&fields, pinned, &data, &slot); + let init_fields = init_fields( + &fields, + pinned, + !attrs + .iter() + .any(|attr| matches!(attr, InitializerAttribute::DisableInitializedFieldAccess)), + &data, + &slot, + ); 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 @@ -228,6 +236,7 @@ fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKi fn init_fields( fields: &Punctuated, pinned: bool, + generate_initialized_accessors: bool, data: &Ident, slot: &Ident, ) -> TokenStream { @@ -263,6 +272,13 @@ fn init_fields( unsafe { &mut (*#slot).#ident } } }; + let accessor = generate_initialized_accessors.then(|| { + quote! { + #(#cfgs)* + #[allow(unused_variables)] + let #ident = #accessor; + } + }); quote! { #(#attrs)* { @@ -270,37 +286,31 @@ fn init_fields( // SAFETY: TODO unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) }; } - #(#cfgs)* - #[allow(unused_variables)] - let #ident = #accessor; + #accessor } } InitializerKind::Init { ident, value, .. } => { // Again span for better diagnostics let init = format_ident!("init", span = value.span()); - if pinned { + let (value_init, accessor) = if pinned { let project_ident = format_ident!("__project_{ident}"); - quote! { - #(#attrs)* - { - let #init = #value; + ( + quote! { // SAFETY: // - `slot` is valid, because we are inside of an initializer closure, we // return when an error/panic occurs. // - We also use `#data` to require the correct trait (`Init` or `PinInit`) // for `#ident`. unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? }; - } - #(#cfgs)* - // SAFETY: TODO - #[allow(unused_variables)] - let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) }; - } + }, + quote! { + // SAFETY: TODO + unsafe { #data.#project_ident(&mut (*#slot).#ident) } + }, + ) } else { - quote! { - #(#attrs)* - { - let #init = #value; + ( + quote! { // SAFETY: `slot` is valid, because we are inside of an initializer // closure, we return when an error/panic occurs. unsafe { @@ -309,12 +319,27 @@ fn init_fields( ::core::ptr::addr_of_mut!((*#slot).#ident), )? }; - } + }, + quote! { + // SAFETY: TODO + unsafe { &mut (*#slot).#ident } + }, + ) + }; + let accessor = generate_initialized_accessors.then(|| { + quote! { #(#cfgs)* - // SAFETY: TODO #[allow(unused_variables)] - let #ident = unsafe { &mut (*#slot).#ident }; + let #ident = #accessor; } + }); + quote! { + #(#attrs)* + { + let #init = #value; + #value_init + } + #accessor } } InitializerKind::Code { block: value, .. } => quote! { @@ -446,6 +471,10 @@ impl Parse for Initializer { if a.path().is_ident("default_error") { a.parse_args::() .map(InitializerAttribute::DefaultError) + } else if a.path().is_ident("disable_initialized_field_access") { + a.meta + .require_path_only() + .map(|_| InitializerAttribute::DisableInitializedFieldAccess) } else { Err(syn::Error::new_spanned(a, "unknown initializer attribute")) } From 1f1cd6964bbc37f2cc82a0adc8a0acec34af1afb Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:29 +0100 Subject: [PATCH 15/59] rust: pin-init: internal: init: simplify Zeroable safety check The `Zeroable` type check uses a small dance with a raw pointer to aid type inference. It turns out that this is not necessary and type inference is powerful enough to resolve any ambiguity. Thus remove it. Suggested-by: Gary Guo Tested-by: Andreas Hindborg Reviewed-by: Gary Guo Signed-off-by: Benno Lossin --- rust/pin-init/internal/src/init.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index ed2e1462e176..42936f915a07 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -414,17 +414,12 @@ fn make_field_check( #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)] // SAFETY: this code is never executed. let _ = || unsafe { - let mut zeroed = ::core::mem::zeroed(); - // We have to use type inference here to make zeroed have the correct type. This - // does not get executed, so it has no effect. - ::core::ptr::write(slot, zeroed); - zeroed = ::core::mem::zeroed(); ::core::ptr::write(slot, #path { #( #(#field_attrs)* #field_name: ::core::panic!(), )* - ..zeroed + ..::core::mem::zeroed() }) }; }, From da9cfc4fcf535db9fb29cc05f41e28ef1e152fc1 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Fri, 16 Jan 2026 11:54:30 +0100 Subject: [PATCH 16/59] MAINTAINERS: add Gary Guo to pin-init Gary has been involved in pin-init since the very beginning. He created his own version before I even joined Rust-for-Linux, contributed ideas to my solution, and reviewed it. With the removal of the declarative macro mess, he is also going to be a maintainer of pin-init. Acked-by: Gary Guo Signed-off-by: Benno Lossin --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0d044a58cbfe..a0876a3c022c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22925,6 +22925,7 @@ F: rust/kernel/num/ RUST [PIN-INIT] M: Benno Lossin +M: Gary Guo L: rust-for-linux@vger.kernel.org S: Maintained W: https://rust-for-linux.com/pin-init From aeb5ecad5316f6af160993915163367290825b6b Mon Sep 17 00:00:00 2001 From: Oleksandr Babak Date: Thu, 8 Jan 2026 13:43:16 +0100 Subject: [PATCH 17/59] rust: pin-init: Implement `InPlaceWrite` for `&'static mut MaybeUninit` This feature allows users to use `&'static mut MaybeUninit` as a place to initialize the value. It mirrors an existing implemetation for `Box`, but enables users to use external allocation mechanisms such as `static_cell` [1]. Signed-off-by: Oleksandr Babak Link: https://crates.io/crates/static_cell [1] [ Added link to `static_cell` - Benno ] Signed-off-by: Benno Lossin --- rust/pin-init/src/lib.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 780b4fead22d..49945fc07f25 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1403,6 +1403,33 @@ pub trait InPlaceWrite { fn write_pin_init(self, init: impl PinInit) -> Result, E>; } +impl InPlaceWrite for &'static mut MaybeUninit { + type Initialized = &'static mut T; + + fn write_init(self, init: impl Init) -> Result { + let slot = self.as_mut_ptr(); + + // SAFETY: `slot` is a valid pointer to uninitialized memory. + unsafe { init.__init(slot)? }; + + // SAFETY: The above call initialized the memory. + unsafe { Ok(self.assume_init_mut()) } + } + + fn write_pin_init(self, init: impl PinInit) -> Result, E> { + let slot = self.as_mut_ptr(); + + // SAFETY: `slot` is a valid pointer to uninitialized memory. + // + // The `'static` borrow guarantees the data will not be + // moved/invalidated until it gets dropped (which is never). + unsafe { init.__pinned_init(slot)? }; + + // SAFETY: The above call initialized the memory. + Ok(Pin::static_mut(unsafe { self.assume_init_mut() })) + } +} + /// Trait facilitating pinned destruction. /// /// Use [`pinned_drop`] to implement this trait safely: From 9fa7153c31a3e5fe578b83d23bc9f185fde115da Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 13 Dec 2025 01:00:42 +0100 Subject: [PATCH 18/59] rust: conclude the Rust experiment The Rust support was merged in v6.1 into mainline in order to help determine whether Rust as a language was suitable for the kernel, i.e. worth the tradeoffs, technically, procedurally and socially. At the 2025 Linux Kernel Maintainers Summit, the experiment has just been deemed concluded [1]. Thus remove the section -- it was not fully true already anyway, since there are already uses of Rust in production out there, some well-known Linux distributions enable it and it is already in millions of devices via Android. Obviously, this does not mean that everything works for every kernel configuration, architecture, toolchain etc., or that there won't be new issues. There is still a ton of work to do in all areas, from the kernel to upstream Rust, GCC and other projects. And, in fact, certain combinations (such as the mixed GCC+LLVM builds and the upcoming GCC support) are still quite experimental but getting there. But the experiment is done, i.e. Rust is here to stay. I hope this signals commitment from the kernel to companies and other entities to invest more into it, e.g. into giving time to their kernel developers to train themselves in Rust. Thanks to the many kernel maintainers that gave the project their support and patience throughout these years, and to the many other developers, whether in the kernel or in other projects, that have made this possible. I had a long list of 173 names in the credits of the original pull that merged the support into the kernel [2], and now such a list would be way longer, so I will not even try to compose one, but again, thanks a lot, everybody. Link: https://lwn.net/Articles/1050174/ [1] Link: https://git.kernel.org/linus/8aebac82933f [2] Reviewed-by: Gary Guo Acked-by: Greg Kroah-Hartman Acked-by: Benno Lossin Acked-by: Andreas Hindborg Acked-by: Danilo Krummrich Reviewed-by: Boqun Feng Link: https://patch.msgid.link/20251213000042.23072-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- Documentation/process/programming-language.rst | 2 +- Documentation/rust/index.rst | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Documentation/process/programming-language.rst b/Documentation/process/programming-language.rst index bc56dee6d0bc..f39d1d3dd9ce 100644 --- a/Documentation/process/programming-language.rst +++ b/Documentation/process/programming-language.rst @@ -34,7 +34,7 @@ Please refer to ``include/linux/compiler_attributes.h`` for more information. Rust ---- -The kernel has experimental support for the Rust programming language +The kernel has support for the Rust programming language [rust-language]_ under ``CONFIG_RUST``. It is compiled with ``rustc`` [rustc]_ under ``--edition=2021`` [rust-editions]_. Editions are a way to introduce small changes to the language that are not backwards compatible. diff --git a/Documentation/rust/index.rst b/Documentation/rust/index.rst index ec62001c7d8c..e61524959dbc 100644 --- a/Documentation/rust/index.rst +++ b/Documentation/rust/index.rst @@ -7,24 +7,6 @@ Documentation related to Rust within the kernel. To start using Rust in the kernel, please read the quick-start.rst guide. -The Rust experiment -------------------- - -The Rust support was merged in v6.1 into mainline in order to help in -determining whether Rust as a language was suitable for the kernel, i.e. worth -the tradeoffs. - -Currently, the Rust support is primarily intended for kernel developers and -maintainers interested in the Rust support, so that they can start working on -abstractions and drivers, as well as helping the development of infrastructure -and tools. - -If you are an end user, please note that there are currently no in-tree -drivers/modules suitable or intended for production use, and that the Rust -support is still in development/experimental, especially for certain kernel -configurations. - - Code documentation ------------------ From 1b18b37a2c30f6e6698205a06de393f7e626f5d2 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 8 Dec 2025 11:46:59 +0900 Subject: [PATCH 19/59] rust: build_assert: add instructions for use with function arguments `build_assert` relies on the compiler to optimize out its error path, lest build fails with the dreaded error: ERROR: modpost: "rust_build_error" [path/to/module.ko] undefined! It has been observed that very trivial code performing I/O accesses (sometimes even using an immediate value) would seemingly randomly fail with this error whenever `CLIPPY=1` was set. The same behavior was also observed until different, very similar conditions [1][2]. The cause appears to be that the failing function is eventually using `build_assert` with its argument, but is only annotated with `#[inline]`. This gives the compiler freedom to not inline the function, which it notably did when Clippy was active, triggering the error. The fix is to annotate functions passing their argument to `build_assert` with `#[inline(always)]`, telling the compiler to be as aggressive as possible with their inlining. This is also the correct behavior as inlining is mandatory for correct behavior in these cases. Add a paragraph instructing to annotate such functions with `#[inline(always)]` in `build_assert`'s documentation, and split its example to illustrate. Reviewed-by: Daniel Almeida Signed-off-by: Alexandre Courbot Link: https://patch.msgid.link/20251208-io-build-assert-v3-1-98aded02c1ea@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/build_assert.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/kernel/build_assert.rs b/rust/kernel/build_assert.rs index 6331b15d7c4d..f8124dbc663f 100644 --- a/rust/kernel/build_assert.rs +++ b/rust/kernel/build_assert.rs @@ -61,8 +61,13 @@ macro_rules! build_error { /// build_assert!(N > 1); // Build-time check /// assert!(N > 1); // Run-time check /// } +/// ``` /// -/// #[inline] +/// When a condition depends on a function argument, the function must be annotated with +/// `#[inline(always)]`. Without this attribute, the compiler may choose to not inline the +/// function, preventing it from optimizing out the error path. +/// ``` +/// #[inline(always)] /// fn bar(n: usize) { /// // `static_assert!(n > 1);` is not allowed /// build_assert!(n > 1); // Build-time check From 84b1b49eccb79ec2e4aaa45116fffb2ac61b876c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 16 Dec 2025 17:24:49 +0900 Subject: [PATCH 20/59] rust: ptr: replace unneeded use of `build_assert` Since `ALIGN` is a const parameter, this assertion can be done in const context using the `assert!` macro. Suggested-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20251216-ptr_assert-v1-1-d8b2d5c5741d@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/ptr.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs index e3893ed04049..5b6a382637fe 100644 --- a/rust/kernel/ptr.rs +++ b/rust/kernel/ptr.rs @@ -5,8 +5,6 @@ use core::mem::align_of; use core::num::NonZero; -use crate::build_assert; - /// Type representing an alignment, which is always a power of two. /// /// It is used to validate that a given value is a valid alignment, and to perform masking and @@ -40,10 +38,12 @@ impl Alignment { /// ``` #[inline(always)] pub const fn new() -> Self { - build_assert!( - ALIGN.is_power_of_two(), - "Provided alignment is not a power of two." - ); + const { + assert!( + ALIGN.is_power_of_two(), + "Provided alignment is not a power of two." + ); + } // INVARIANT: `align` is a power of two. // SAFETY: `align` is a power of two, and thus non-zero. From eeaad2f021def7efdaa441b104550615ca328a48 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:14:32 +0100 Subject: [PATCH 21/59] rust: i2c: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Acked-by: Igor Korotin Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-i2c-v1-1-df1c258d4615@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/i2c.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 491e6cc25cf4..792a71b15463 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -253,7 +253,7 @@ macro_rules! module_i2c_driver { /// # Example /// ///``` -/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of}; +/// # use kernel::{acpi, bindings, device::Core, i2c, of}; /// /// struct MyDriver; /// @@ -262,7 +262,7 @@ macro_rules! module_i2c_driver { /// MODULE_ACPI_TABLE, /// ::IdInfo, /// [ -/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// (acpi::DeviceId::new(c"LNUXBEEF"), ()) /// ] /// ); /// @@ -271,7 +271,7 @@ macro_rules! module_i2c_driver { /// MODULE_I2C_TABLE, /// ::IdInfo, /// [ -/// (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ()) +/// (i2c::DeviceId::new(c"rust_driver_i2c"), ()) /// ] /// ); /// @@ -280,7 +280,7 @@ macro_rules! module_i2c_driver { /// MODULE_OF_TABLE, /// ::IdInfo, /// [ -/// (of::DeviceId::new(c_str!("test,device")), ()) +/// (of::DeviceId::new(c"test,device"), ()) /// ] /// ); /// From 32d61c516f75c15b8c6420d8ef749d80615943df Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:14:33 +0100 Subject: [PATCH 22/59] samples: rust: i2c: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Signed-off-by: Tamir Duberstein Acked-by: Igor Korotin Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-i2c-v1-2-df1c258d4615@gmail.com Signed-off-by: Miguel Ojeda --- samples/rust/rust_driver_i2c.rs | 7 +++---- samples/rust/rust_i2c_client.rs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs index ecefeca3e22f..6be79f9e9fb5 100644 --- a/samples/rust/rust_driver_i2c.rs +++ b/samples/rust/rust_driver_i2c.rs @@ -4,7 +4,6 @@ use kernel::{ acpi, - c_str, device::Core, i2c, of, @@ -17,21 +16,21 @@ kernel::acpi_device_table! { ACPI_TABLE, MODULE_ACPI_TABLE, ::IdInfo, - [(acpi::DeviceId::new(c_str!("LNUXBEEF")), 0)] + [(acpi::DeviceId::new(c"LNUXBEEF"), 0)] } kernel::i2c_device_table! { I2C_TABLE, MODULE_I2C_TABLE, ::IdInfo, - [(i2c::DeviceId::new(c_str!("rust_driver_i2c")), 0)] + [(i2c::DeviceId::new(c"rust_driver_i2c"), 0)] } kernel::of_device_table! { OF_TABLE, MODULE_OF_TABLE, ::IdInfo, - [(of::DeviceId::new(c_str!("test,rust_driver_i2c")), 0)] + [(of::DeviceId::new(c"test,rust_driver_i2c"), 0)] } impl i2c::Driver for SampleDriver { diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs index f67938396dce..8d2c12e535b0 100644 --- a/samples/rust/rust_i2c_client.rs +++ b/samples/rust/rust_i2c_client.rs @@ -69,7 +69,6 @@ use kernel::{ acpi, - c_str, device, devres::Devres, i2c, @@ -90,20 +89,20 @@ kernel::of_device_table!( OF_TABLE, MODULE_OF_TABLE, ::IdInfo, - [(of::DeviceId::new(c_str!("test,rust-device")), ())] + [(of::DeviceId::new(c"test,rust-device"), ())] ); kernel::acpi_device_table!( ACPI_TABLE, MODULE_ACPI_TABLE, ::IdInfo, - [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())] + [(acpi::DeviceId::new(c"LNUXBEEF"), ())] ); const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30; const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0; const BOARD_INFO: i2c::I2cBoardInfo = - i2c::I2cBoardInfo::new(c_str!("rust_driver_i2c"), SAMPLE_I2C_CLIENT_ADDR); + i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR); impl platform::Driver for SampleDriver { type IdInfo = (); From 6c37b6841a92714eba4a7b7f823aea801da4e09f Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:28:27 +0100 Subject: [PATCH 23/59] rust: kunit: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Reviewed-by: David Gow Link: https://patch.msgid.link/20251222-cstr-kunit-v1-1-39d999672f35@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/kunit.rs | 11 ++++------- rust/macros/kunit.rs | 10 +++++----- scripts/rustdoc_test_gen.rs | 4 ++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 79436509dd73..21aef6c97325 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -9,9 +9,6 @@ use crate::fmt; use crate::prelude::*; -#[cfg(CONFIG_PRINTK)] -use crate::c_str; - /// Prints a KUnit error-level message. /// /// Public but hidden since it should only be used from KUnit generated code. @@ -22,7 +19,7 @@ pub fn err(args: fmt::Arguments<'_>) { #[cfg(CONFIG_PRINTK)] unsafe { bindings::_printk( - c_str!("\x013%pA").as_char_ptr(), + c"\x013%pA".as_char_ptr(), core::ptr::from_ref(&args).cast::(), ); } @@ -38,7 +35,7 @@ pub fn info(args: fmt::Arguments<'_>) { #[cfg(CONFIG_PRINTK)] unsafe { bindings::_printk( - c_str!("\x016%pA").as_char_ptr(), + c"\x016%pA".as_char_ptr(), core::ptr::from_ref(&args).cast::(), ); } @@ -60,7 +57,7 @@ macro_rules! kunit_assert { break 'out; } - static FILE: &'static $crate::str::CStr = $crate::c_str!($file); + static FILE: &'static $crate::str::CStr = $file; static LINE: i32 = ::core::line!() as i32 - $diff; static CONDITION: &'static $crate::str::CStr = $crate::c_str!(stringify!($condition)); @@ -253,7 +250,7 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { /// } /// /// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [ -/// kernel::kunit::kunit_case(kernel::c_str!("name"), test_fn), +/// kernel::kunit::kunit_case(c"name", test_fn), /// kernel::kunit::kunit_case_null(), /// ]; /// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES); diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs index b395bb053695..3d7724b35c0f 100644 --- a/rust/macros/kunit.rs +++ b/rust/macros/kunit.rs @@ -102,8 +102,8 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { // unsafe extern "C" fn kunit_rust_wrapper_bar(_test: *mut ::kernel::bindings::kunit) { bar(); } // // static mut TEST_CASES: [::kernel::bindings::kunit_case; 3] = [ - // ::kernel::kunit::kunit_case(::kernel::c_str!("foo"), kunit_rust_wrapper_foo), - // ::kernel::kunit::kunit_case(::kernel::c_str!("bar"), kunit_rust_wrapper_bar), + // ::kernel::kunit::kunit_case(c"foo", kunit_rust_wrapper_foo), + // ::kernel::kunit::kunit_case(c"bar", kunit_rust_wrapper_bar), // ::kernel::kunit::kunit_case_null(), // ]; // @@ -133,7 +133,7 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { writeln!(kunit_macros, "{kunit_wrapper}").unwrap(); writeln!( test_cases, - " ::kernel::kunit::kunit_case(::kernel::c_str!(\"{test}\"), {kunit_wrapper_fn_name})," + " ::kernel::kunit::kunit_case(c\"{test}\", {kunit_wrapper_fn_name})," ) .unwrap(); writeln!( @@ -143,7 +143,7 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { #[allow(unused)] macro_rules! assert {{ ($cond:expr $(,)?) => {{{{ - kernel::kunit_assert!("{test}", "{path}", 0, $cond); + kernel::kunit_assert!("{test}", c"{path}", 0, $cond); }}}} }} @@ -151,7 +151,7 @@ macro_rules! assert {{ #[allow(unused)] macro_rules! assert_eq {{ ($left:expr, $right:expr $(,)?) => {{{{ - kernel::kunit_assert_eq!("{test}", "{path}", 0, $left, $right); + kernel::kunit_assert_eq!("{test}", c"{path}", 0, $left, $right); }}}} }} "# diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index be0561049660..967064ebd391 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -174,7 +174,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ macro_rules! assert {{ ($cond:expr $(,)?) => {{{{ ::kernel::kunit_assert!( - "{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $cond + "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $cond ); }}}} }} @@ -184,7 +184,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ macro_rules! assert_eq {{ ($left:expr, $right:expr $(,)?) => {{{{ ::kernel::kunit_assert_eq!( - "{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right + "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right ); }}}} }} From 2ad6c5cdc89acfefb01b84afa5e55262c40d6fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Thu, 13 Nov 2025 17:45:47 +0300 Subject: [PATCH 24/59] rust: rbtree: reduce unsafe blocks on pointer derefs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactors parts of the get() and find_best_match() traversal logic to minimize the scope of unsafe blocks and avoid duplicating same safety comments. One of the removed comments was also misleading: // SAFETY: `node` is a non-null node... Ordering::Equal => return Some(unsafe { &(*this).value }), as `node` should have been `this`. No functional changes intended; this is purely a safety improvement that reduces the amount of unsafe blocks while keeping all invariants intact. [ Alice writes: "One consequence of creating a &_ to the bindings::rb_node struct means that we assert immutability for the entire struct and not just the rb_left/rb_right fields, but I have verified that this is ok." - Miguel ] Signed-off-by: Onur Özkan Reviewed-by: Charalampos Mitrodimas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20251113144547.502-1-work@onurozkan.dev [ Reworded title and replaced `cursor_lower_bound()` with `find_best_match()` in message. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/rbtree.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs index 4729eb56827a..ed3582d88e4e 100644 --- a/rust/kernel/rbtree.rs +++ b/rust/kernel/rbtree.rs @@ -414,14 +414,17 @@ where // SAFETY: By the type invariant of `Self`, all non-null `rb_node` pointers stored in `self` // point to the links field of `Node` objects. let this = unsafe { container_of!(node, Node, links) }; + // SAFETY: `this` is a non-null node so it is valid by the type invariants. - node = match key.cmp(unsafe { &(*this).key }) { - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - Ordering::Less => unsafe { (*node).rb_left }, - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - Ordering::Greater => unsafe { (*node).rb_right }, - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - Ordering::Equal => return Some(unsafe { &(*this).value }), + let this_ref = unsafe { &*this }; + + // SAFETY: `node` is a non-null node so it is valid by the type invariants. + let node_ref = unsafe { &*node }; + + node = match key.cmp(&this_ref.key) { + Ordering::Less => node_ref.rb_left, + Ordering::Greater => node_ref.rb_right, + Ordering::Equal => return Some(&this_ref.value), } } None @@ -498,10 +501,10 @@ where let this = unsafe { container_of!(node, Node, links) }; // SAFETY: `this` is a non-null node so it is valid by the type invariants. let this_key = unsafe { &(*this).key }; + // SAFETY: `node` is a non-null node so it is valid by the type invariants. - let left_child = unsafe { (*node).rb_left }; - // SAFETY: `node` is a non-null node so it is valid by the type invariants. - let right_child = unsafe { (*node).rb_right }; + let node_ref = unsafe { &*node }; + match key.cmp(this_key) { Ordering::Equal => { // SAFETY: `this` is a non-null node so it is valid by the type invariants. @@ -509,7 +512,7 @@ where break; } Ordering::Greater => { - node = right_child; + node = node_ref.rb_right; } Ordering::Less => { let is_better_match = match best_key { @@ -521,7 +524,7 @@ where // SAFETY: `this` is a non-null node so it is valid by the type invariants. best_links = Some(unsafe { NonNull::new_unchecked(&mut (*this).links) }); } - node = left_child; + node = node_ref.rb_left; } }; } From 4b2dbf44320fc2e4bfd6a2261f16f8fdbfcfbf36 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Thu, 15 Jan 2026 22:45:45 +0100 Subject: [PATCH 25/59] MAINTAINERS: add "RUST [RUST-ANALYZER]" entry This entry will handle the support for `rust-analyzer`, which at the moment is essentially about the `scripts/generate_rust_analyzer.py` script. It is one more subentry of the Rust subsystem. The new branch is already part of linux-next and will be used to send PRs to be merged into rust-next. Tamir and Jesung will take care of it. They have both been active around the `rust-analyzer` support for a while. Acked-by: Tamir Duberstein Acked-by: Jesung Yang Acked-by: Andreas Hindborg Link: https://patch.msgid.link/20260115214545.63262-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index da9dbc1a4019..2547f8f6f662 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22942,6 +22942,14 @@ F: rust/kernel/init.rs F: rust/pin-init/ K: \bpin-init\b|pin_init\b|PinInit +RUST [RUST-ANALYZER] +M: Tamir Duberstein +R: Jesung Yang +L: rust-for-linux@vger.kernel.org +S: Maintained +T: git https://github.com/Rust-for-Linux/linux.git rust-analyzer-next +F: scripts/generate_rust_analyzer.py + RXRPC SOCKETS (AF_RXRPC) M: David Howells M: Marc Dionne From 7f87c7a003125d5af5ec7abbbc0ac21b4a4661ae Mon Sep 17 00:00:00 2001 From: Peter Novak Date: Sun, 30 Nov 2025 22:12:33 +0100 Subject: [PATCH 26/59] rust: use consistent backtick formatting for NULL in docs Some doc comments use `NULL` while others use plain NULL. Make it consistent by adding backticks everywhere, matching the majority of existing usage. Signed-off-by: Peter Novak Acked-by: Stephen Boyd Acked-by: David Gow Reviewed-by: Alexandre Courbot Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20251130211233.367946-1-seimun018r@gmail.com [ Reworded slightly. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/clk.rs | 2 +- rust/kernel/debugfs/entry.rs | 2 +- rust/kernel/kunit.rs | 8 ++++---- rust/macros/lib.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs index c1cfaeaa36a2..db3464e2df32 100644 --- a/rust/kernel/clk.rs +++ b/rust/kernel/clk.rs @@ -94,7 +94,7 @@ mod common_clk { /// # Invariants /// /// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C - /// portion of the kernel or a NULL pointer. + /// portion of the kernel or a `NULL` pointer. /// /// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the /// allocation remains valid for the lifetime of the [`Clk`]. diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs index 706cb7f73d6c..a30bf8f29679 100644 --- a/rust/kernel/debugfs/entry.rs +++ b/rust/kernel/debugfs/entry.rs @@ -148,7 +148,7 @@ impl Entry<'_> { /// # Guarantees /// /// Due to the type invariant, the value returned from this function will always be an error - /// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as + /// code, `NULL`, or a live DebugFS directory. If it is live, it will remain live at least as /// long as this entry lives. pub(crate) fn as_ptr(&self) -> *mut bindings::dentry { self.entry diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 21aef6c97325..4ccc8fc4a800 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -190,7 +190,7 @@ pub fn is_test_result_ok(t: impl TestResult) -> bool { /// Represents an individual test case. /// -/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of valid test cases. +/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of valid test cases. /// Use [`kunit_case_null`] to generate such a delimiter. #[doc(hidden)] pub const fn kunit_case( @@ -212,9 +212,9 @@ pub const fn kunit_case( } } -/// Represents the NULL test case delimiter. +/// Represents the `NULL` test case delimiter. /// -/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of test cases. This +/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of test cases. This /// function returns such a delimiter. #[doc(hidden)] pub const fn kunit_case_null() -> kernel::bindings::kunit_case { @@ -237,7 +237,7 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { /// /// # Safety /// -/// `test_cases` must be a NULL terminated array of valid test cases, +/// `test_cases` must be a `NULL` terminated array of valid test cases, /// whose lifetime is at least that of the test suite (i.e., static). /// /// # Examples diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index b38002151871..f26775285dc5 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -154,7 +154,7 @@ pub fn module(ts: TokenStream) -> TokenStream { /// case the default implementation will never be executed. The reason for this /// is that the functions will be called through function pointers installed in /// C side vtables. When an optional method is not implemented on a `#[vtable]` -/// trait, a NULL entry is installed in the vtable. Thus the default +/// trait, a `NULL` entry is installed in the vtable. Thus the default /// implementation is never called. Since these traits are not designed to be /// used on the Rust side, it should not be possible to call the default /// implementation. This is done to ensure that we call the vtable methods From 209c70953aa3630eb25e93e95464306a41b16d12 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 15 Dec 2025 14:49:09 +0900 Subject: [PATCH 27/59] rust: transmute: implement FromBytes and AsBytes for inhabited ZSTs This is useful when using types that may or may not be empty in generic code relying on these traits. It is also safe because technically a no-op. Reviewed-by: Alistair Popple Reviewed-by: Gary Guo Signed-off-by: Alexandre Courbot Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20251215-transmute_unit-v4-1-477d71ec7c23@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/transmute.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs index be5dbf3829e2..5711580c9f9b 100644 --- a/rust/kernel/transmute.rs +++ b/rust/kernel/transmute.rs @@ -170,6 +170,10 @@ macro_rules! impl_frombytes { } impl_frombytes! { + // SAFETY: Inhabited ZSTs only have one possible bit pattern, and these two have no invariant. + (), + {} core::marker::PhantomData, + // SAFETY: All bit patterns are acceptable values of the types below. u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, @@ -230,6 +234,10 @@ macro_rules! impl_asbytes { } impl_asbytes! { + // SAFETY: Inhabited ZSTs only have one possible bit pattern, and these two have no invariant. + (), + {} core::marker::PhantomData, + // SAFETY: Instances of the following types have no uninitialized portions. u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, From 0bfea95c3c02a887c25260e8aaf31cc9567bc478 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 15 Dec 2025 14:49:10 +0900 Subject: [PATCH 28/59] gpu: nova-core: gsp: use () as message type for GspInitDone message `GspInitDone` has no payload whatsoever, so the unit type `()` is the correct way to represent its message content. We can use it now that `()` implements `FromBytes`. Reviewed-by: Alistair Popple Signed-off-by: Alexandre Courbot Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20251215-transmute_unit-v4-2-477d71ec7c23@nvidia.com Signed-off-by: Miguel Ojeda --- drivers/gpu/nova-core/gsp/commands.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 0425c65b5d6f..2050771f9b53 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -142,7 +142,7 @@ impl CommandToGsp for SetRegistry { } /// Message type for GSP initialization done notification. -struct GspInitDone {} +struct GspInitDone; // SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it // trivially has no uninitialized bytes. @@ -151,13 +151,13 @@ unsafe impl FromBytes for GspInitDone {} impl MessageFromGsp for GspInitDone { const FUNCTION: MsgFunction = MsgFunction::GspInitDone; type InitError = Infallible; - type Message = GspInitDone; + type Message = (); fn read( _msg: &Self::Message, _sbuffer: &mut SBufferIter>, ) -> Result { - Ok(GspInitDone {}) + Ok(GspInitDone) } } From 7caeac0b382ac3d48606e00d5683efda42204349 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:16 +0000 Subject: [PATCH 29/59] rust: bug: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260105-define-rust-helper-v2-3-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/bug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/helpers/bug.c b/rust/helpers/bug.c index a62c96f507d1..b51e60772578 100644 --- a/rust/helpers/bug.c +++ b/rust/helpers/bug.c @@ -2,12 +2,12 @@ #include -__noreturn void rust_helper_BUG(void) +__rust_helper __noreturn void rust_helper_BUG(void) { BUG(); } -bool rust_helper_WARN_ON(bool cond) +__rust_helper bool rust_helper_WARN_ON(bool cond) { return WARN_ON(cond); } From 227d1955bfc3cade00f11580a6cf35a1ee770d8c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:21 +0000 Subject: [PATCH 30/59] rust: err: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260105-define-rust-helper-v2-8-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/build_bug.c | 2 +- rust/helpers/err.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/helpers/build_bug.c b/rust/helpers/build_bug.c index 44e579488037..14dbc55bb539 100644 --- a/rust/helpers/build_bug.c +++ b/rust/helpers/build_bug.c @@ -2,7 +2,7 @@ #include -const char *rust_helper_errname(int err) +__rust_helper const char *rust_helper_errname(int err) { return errname(err); } diff --git a/rust/helpers/err.c b/rust/helpers/err.c index 544c7cb86632..2872158e3793 100644 --- a/rust/helpers/err.c +++ b/rust/helpers/err.c @@ -2,17 +2,17 @@ #include -__force void *rust_helper_ERR_PTR(long err) +__rust_helper __force void *rust_helper_ERR_PTR(long err) { return ERR_PTR(err); } -bool rust_helper_IS_ERR(__force const void *ptr) +__rust_helper bool rust_helper_IS_ERR(__force const void *ptr) { return IS_ERR(ptr); } -long rust_helper_PTR_ERR(__force const void *ptr) +__rust_helper long rust_helper_PTR_ERR(__force const void *ptr) { return PTR_ERR(ptr); } From fa6cbf1f5acc0ae0cf9f8e5c0ce486b8e36c86cf Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:23 +0000 Subject: [PATCH 31/59] rust: maple_tree: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Acked-by: Andrew Ballance Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Acked-by: Liam R. Howlett Link: https://patch.msgid.link/20260105-define-rust-helper-v2-10-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/maple_tree.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/helpers/maple_tree.c b/rust/helpers/maple_tree.c index 1dd9ac84a13f..5586486a76e0 100644 --- a/rust/helpers/maple_tree.c +++ b/rust/helpers/maple_tree.c @@ -2,7 +2,8 @@ #include -void rust_helper_mt_init_flags(struct maple_tree *mt, unsigned int flags) +__rust_helper void rust_helper_mt_init_flags(struct maple_tree *mt, + unsigned int flags) { mt_init_flags(mt, flags); } From 5092aeaf70266b6a8fcf6c8b59f0531e9f88647e Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:24 +0000 Subject: [PATCH 32/59] rust: mm: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Acked-by: Liam R. Howlett Reviewed-by: Lorenzo Stoakes Link: https://patch.msgid.link/20260105-define-rust-helper-v2-11-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/mm.c | 20 ++++++++++---------- rust/helpers/page.c | 9 +++++---- rust/helpers/vmalloc.c | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/rust/helpers/mm.c b/rust/helpers/mm.c index 81b510c96fd2..b5540997bd20 100644 --- a/rust/helpers/mm.c +++ b/rust/helpers/mm.c @@ -3,48 +3,48 @@ #include #include -void rust_helper_mmgrab(struct mm_struct *mm) +__rust_helper void rust_helper_mmgrab(struct mm_struct *mm) { mmgrab(mm); } -void rust_helper_mmdrop(struct mm_struct *mm) +__rust_helper void rust_helper_mmdrop(struct mm_struct *mm) { mmdrop(mm); } -void rust_helper_mmget(struct mm_struct *mm) +__rust_helper void rust_helper_mmget(struct mm_struct *mm) { mmget(mm); } -bool rust_helper_mmget_not_zero(struct mm_struct *mm) +__rust_helper bool rust_helper_mmget_not_zero(struct mm_struct *mm) { return mmget_not_zero(mm); } -void rust_helper_mmap_read_lock(struct mm_struct *mm) +__rust_helper void rust_helper_mmap_read_lock(struct mm_struct *mm) { mmap_read_lock(mm); } -bool rust_helper_mmap_read_trylock(struct mm_struct *mm) +__rust_helper bool rust_helper_mmap_read_trylock(struct mm_struct *mm) { return mmap_read_trylock(mm); } -void rust_helper_mmap_read_unlock(struct mm_struct *mm) +__rust_helper void rust_helper_mmap_read_unlock(struct mm_struct *mm) { mmap_read_unlock(mm); } -struct vm_area_struct *rust_helper_vma_lookup(struct mm_struct *mm, - unsigned long addr) +__rust_helper struct vm_area_struct * +rust_helper_vma_lookup(struct mm_struct *mm, unsigned long addr) { return vma_lookup(mm, addr); } -void rust_helper_vma_end_read(struct vm_area_struct *vma) +__rust_helper void rust_helper_vma_end_read(struct vm_area_struct *vma) { vma_end_read(vma); } diff --git a/rust/helpers/page.c b/rust/helpers/page.c index 7144de5a61db..f8463fbed2a2 100644 --- a/rust/helpers/page.c +++ b/rust/helpers/page.c @@ -4,23 +4,24 @@ #include #include -struct page *rust_helper_alloc_pages(gfp_t gfp_mask, unsigned int order) +__rust_helper struct page *rust_helper_alloc_pages(gfp_t gfp_mask, + unsigned int order) { return alloc_pages(gfp_mask, order); } -void *rust_helper_kmap_local_page(struct page *page) +__rust_helper void *rust_helper_kmap_local_page(struct page *page) { return kmap_local_page(page); } -void rust_helper_kunmap_local(const void *addr) +__rust_helper void rust_helper_kunmap_local(const void *addr) { kunmap_local(addr); } #ifndef NODE_NOT_IN_PAGE_FLAGS -int rust_helper_page_to_nid(const struct page *page) +__rust_helper int rust_helper_page_to_nid(const struct page *page) { return page_to_nid(page); } diff --git a/rust/helpers/vmalloc.c b/rust/helpers/vmalloc.c index 7d7f7336b3d2..326b030487a2 100644 --- a/rust/helpers/vmalloc.c +++ b/rust/helpers/vmalloc.c @@ -2,7 +2,7 @@ #include -void * __must_check __realloc_size(2) +__rust_helper void *__must_check __realloc_size(2) rust_helper_vrealloc_node_align(const void *p, size_t size, unsigned long align, gfp_t flags, int node) { From fffdb58732a979564b8834b2ce25b6562dbbcbb7 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:25 +0000 Subject: [PATCH 33/59] rust: of: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260105-define-rust-helper-v2-12-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/helpers/of.c b/rust/helpers/of.c index 86b51167c913..8f62ca69e8ba 100644 --- a/rust/helpers/of.c +++ b/rust/helpers/of.c @@ -2,7 +2,7 @@ #include -bool rust_helper_is_of_node(const struct fwnode_handle *fwnode) +__rust_helper bool rust_helper_is_of_node(const struct fwnode_handle *fwnode) { return is_of_node(fwnode); } From ac585bf970205eca30cb6f17b4db589c0dca2538 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:28 +0000 Subject: [PATCH 34/59] rust: rbtree: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260105-define-rust-helper-v2-15-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/rbtree.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rust/helpers/rbtree.c b/rust/helpers/rbtree.c index 2a0eabbb4160..a85defb22ff7 100644 --- a/rust/helpers/rbtree.c +++ b/rust/helpers/rbtree.c @@ -2,18 +2,19 @@ #include -void rust_helper_rb_link_node(struct rb_node *node, struct rb_node *parent, - struct rb_node **rb_link) +__rust_helper void rust_helper_rb_link_node(struct rb_node *node, + struct rb_node *parent, + struct rb_node **rb_link) { rb_link_node(node, parent, rb_link); } -struct rb_node *rust_helper_rb_first(const struct rb_root *root) +__rust_helper struct rb_node *rust_helper_rb_first(const struct rb_root *root) { return rb_first(root); } -struct rb_node *rust_helper_rb_last(const struct rb_root *root) +__rust_helper struct rb_node *rust_helper_rb_last(const struct rb_root *root) { return rb_last(root); } From 4890cd1d331704db824923696f3fef6b0e30e496 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:32 +0000 Subject: [PATCH 35/59] rust: slab: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260105-define-rust-helper-v2-19-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/slab.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/helpers/slab.c b/rust/helpers/slab.c index 7fac958907b0..9279f082467d 100644 --- a/rust/helpers/slab.c +++ b/rust/helpers/slab.c @@ -2,14 +2,14 @@ #include -void * __must_check __realloc_size(2) +__rust_helper void *__must_check __realloc_size(2) rust_helper_krealloc_node_align(const void *objp, size_t new_size, unsigned long align, gfp_t flags, int node) { return krealloc_node_align(objp, new_size, align, flags, node); } -void * __must_check __realloc_size(2) +__rust_helper void *__must_check __realloc_size(2) rust_helper_kvrealloc_node_align(const void *p, size_t size, unsigned long align, gfp_t flags, int node) { From 93ad1d734ed55b010fc6a36e7f69037b4c77b776 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:36 +0000 Subject: [PATCH 36/59] rust: uaccess: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260105-define-rust-helper-v2-23-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/uaccess.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c index 4629b2d15529..d9625b9ee046 100644 --- a/rust/helpers/uaccess.c +++ b/rust/helpers/uaccess.c @@ -2,24 +2,26 @@ #include -unsigned long rust_helper_copy_from_user(void *to, const void __user *from, - unsigned long n) +__rust_helper unsigned long +rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n) { return copy_from_user(to, from, n); } -unsigned long rust_helper_copy_to_user(void __user *to, const void *from, - unsigned long n) +__rust_helper unsigned long +rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) { return copy_to_user(to, from, n); } #ifdef INLINE_COPY_FROM_USER +__rust_helper unsigned long rust_helper__copy_from_user(void *to, const void __user *from, unsigned long n) { return _inline_copy_from_user(to, from, n); } +__rust_helper unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n) { return _inline_copy_to_user(to, from, n); From e741e19d7691c5e6f5c2bbff980d835dccb86054 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 5 Jan 2026 12:42:39 +0000 Subject: [PATCH 37/59] rust: workqueue: add __rust_helper to helpers This is needed to inline these helpers into Rust code. Reviewed-by: Boqun Feng Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Acked-by: Tejun Heo Link: https://patch.msgid.link/20260105-define-rust-helper-v2-26-51da5f454a67@google.com Signed-off-by: Miguel Ojeda --- rust/helpers/workqueue.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rust/helpers/workqueue.c b/rust/helpers/workqueue.c index b2b82753509b..ce1c3a5b2150 100644 --- a/rust/helpers/workqueue.c +++ b/rust/helpers/workqueue.c @@ -2,9 +2,11 @@ #include -void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, - bool onstack, const char *name, - struct lock_class_key *key) +__rust_helper void rust_helper_init_work_with_key(struct work_struct *work, + work_func_t func, + bool onstack, + const char *name, + struct lock_class_key *key) { __init_work(work, onstack); work->data = (atomic_long_t)WORK_DATA_INIT(); From fcbc35587778738145d4f18f95d44631a5c54845 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 23 Jan 2026 17:19:42 +0000 Subject: [PATCH 38/59] samples: rust: remove imports available via prelude These imports are already in scope by importing `kernel::prelude::*` and does not need to be imported separately. Signed-off-by: Gary Guo Reviewed-by: Alice Ryhl Acked-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260123172007.136873-4-gary@garyguo.net Signed-off-by: Miguel Ojeda --- samples/rust/rust_driver_auxiliary.rs | 1 - samples/rust/rust_misc_device.rs | 2 -- samples/rust/rust_print_main.rs | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 5761ea314f44..65492a703f30 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -16,7 +16,6 @@ use kernel::{ }; use core::any::TypeId; -use pin_init::PinInit; const MODULE_NAME: &CStr = ::NAME; const AUXILIARY_NAME: &CStr = c_str!("auxiliary"); diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs index d69bc33dbd99..3ef8da543abf 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -95,8 +95,6 @@ //! } //! ``` -use core::pin::Pin; - use kernel::{ c_str, device::Device, diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs index 4095c72afeab..682207c81fc2 100644 --- a/samples/rust/rust_print_main.rs +++ b/samples/rust/rust_print_main.rs @@ -101,7 +101,7 @@ impl Drop for RustPrint { } mod trace { - use kernel::ffi::c_int; + use kernel::prelude::*; kernel::declare_trace! { /// # Safety From 196906d4acf1048b4dbb0cb8dde81f0adb663f83 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 26 Jan 2026 09:46:53 -0500 Subject: [PATCH 39/59] MAINTAINERS: mailmap: update Tamir Duberstein's email address Update Tamir Duberstein's email address to `@kernel.org`. Signed-off-by: Tamir Duberstein Link: https://patch.msgid.link/20260126-tamird-kernel-org-v1-1-b390d329b0b9@kernel.org Signed-off-by: Miguel Ojeda --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index 4a8a160f28ed..e8a03e193854 100644 --- a/.mailmap +++ b/.mailmap @@ -797,6 +797,7 @@ Sven Eckelmann Sven Peter Szymon Wilczek Takashi YOSHII +Tamir Duberstein Tamizh Chelvam Raja Taniya Das Tanzir Hasan diff --git a/MAINTAINERS b/MAINTAINERS index 2547f8f6f662..17057b5d0168 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -28355,7 +28355,7 @@ F: lib/xarray.c F: tools/testing/radix-tree XARRAY API [RUST] -M: Tamir Duberstein +M: Tamir Duberstein M: Andreas Hindborg L: rust-for-linux@vger.kernel.org S: Supported From f637bafe1ff15fa356c1e0576c32f077b9e6e46a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:13 +0000 Subject: [PATCH 40/59] rust: macros: use `quote!` from vendored crate With `quote` crate now vendored in the kernel, we can remove our custom `quote!` macro implementation and just rely on that crate instead. The `quote` crate uses types from the `proc-macro2` library so we also update to use that, and perform conversion in the top-level lib.rs. Clippy complains about unnecessary `.to_string()` as `proc-macro2` provides additional `PartialEq` impl, so they are removed. Reviewed-by: Tamir Duberstein Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Acked-by: David Gow # for kunit Link: https://patch.msgid.link/20260112170919.1888584-3-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/concat_idents.rs | 2 +- rust/macros/export.rs | 4 +- rust/macros/fmt.rs | 4 +- rust/macros/helpers.rs | 4 +- rust/macros/kunit.rs | 5 +- rust/macros/lib.rs | 21 ++-- rust/macros/module.rs | 6 +- rust/macros/paste.rs | 2 +- rust/macros/quote.rs | 182 ----------------------------------- rust/macros/vtable.rs | 7 +- 10 files changed, 32 insertions(+), 205 deletions(-) delete mode 100644 rust/macros/quote.rs diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs index 7e4b450f3a50..12cb231c3d71 100644 --- a/rust/macros/concat_idents.rs +++ b/rust/macros/concat_idents.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro::{token_stream, Ident, TokenStream, TokenTree}; +use proc_macro2::{token_stream, Ident, TokenStream, TokenTree}; use crate::helpers::expect_punct; diff --git a/rust/macros/export.rs b/rust/macros/export.rs index a08f6337d5c8..92d9b3097192 100644 --- a/rust/macros/export.rs +++ b/rust/macros/export.rs @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +use proc_macro2::TokenStream; +use quote::quote; + use crate::helpers::function_name; -use proc_macro::TokenStream; /// Please see [`crate::export`] for documentation. pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream { diff --git a/rust/macros/fmt.rs b/rust/macros/fmt.rs index 2f4b9f6e2211..19f709262552 100644 --- a/rust/macros/fmt.rs +++ b/rust/macros/fmt.rs @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro::{Ident, TokenStream, TokenTree}; use std::collections::BTreeSet; +use proc_macro2::{Ident, TokenStream, TokenTree}; +use quote::quote_spanned; + /// Please see [`crate::fmt`] for documentation. pub(crate) fn fmt(input: TokenStream) -> TokenStream { let mut input = input.into_iter(); diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index 365d7eb499c0..13fafaba1226 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree}; +use proc_macro2::{token_stream, Group, Ident, TokenStream, TokenTree}; pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option { if let Some(TokenTree::Ident(ident)) = it.next() { @@ -86,7 +86,7 @@ pub(crate) fn function_name(input: TokenStream) -> Option { let mut input = input.into_iter(); while let Some(token) = input.next() { match token { - TokenTree::Ident(i) if i.to_string() == "fn" => { + TokenTree::Ident(i) if i == "fn" => { if let Some(TokenTree::Ident(i)) = input.next() { return Some(i); } diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs index 3d7724b35c0f..79fdad13b04a 100644 --- a/rust/macros/kunit.rs +++ b/rust/macros/kunit.rs @@ -4,10 +4,11 @@ //! //! Copyright (c) 2023 José Expósito -use proc_macro::{Delimiter, Group, TokenStream, TokenTree}; use std::collections::HashMap; use std::fmt::Write; +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; + pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { let attr = attr.to_string(); @@ -59,7 +60,7 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { } _ => (), }, - TokenTree::Ident(i) if i.to_string() == "fn" && attributes.contains_key("test") => { + TokenTree::Ident(i) if i == "fn" && attributes.contains_key("test") => { if let Some(TokenTree::Ident(test_name)) = body_it.next() { tests.push((test_name, attributes.remove("cfg").unwrap_or_default())) } diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index f26775285dc5..b884ea17391b 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -11,8 +11,6 @@ // to avoid depending on the full `proc_macro_span` on Rust >= 1.88.0. #![cfg_attr(not(CONFIG_RUSTC_HAS_SPAN_FILE), feature(proc_macro_span))] -#[macro_use] -mod quote; mod concat_idents; mod export; mod fmt; @@ -132,7 +130,7 @@ use proc_macro::TokenStream; /// the kernel module. #[proc_macro] pub fn module(ts: TokenStream) -> TokenStream { - module::module(ts) + module::module(ts.into()).into() } /// Declares or implements a vtable trait. @@ -207,7 +205,7 @@ pub fn module(ts: TokenStream) -> TokenStream { /// [`kernel::error::VTABLE_DEFAULT_ERROR`]: ../kernel/error/constant.VTABLE_DEFAULT_ERROR.html #[proc_macro_attribute] pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream { - vtable::vtable(attr, ts) + vtable::vtable(attr.into(), ts.into()).into() } /// Export a function so that C code can call it via a header file. @@ -230,7 +228,7 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream { /// automatically exported with `EXPORT_SYMBOL_GPL`. #[proc_macro_attribute] pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream { - export::export(attr, ts) + export::export(attr.into(), ts.into()).into() } /// Like [`core::format_args!`], but automatically wraps arguments in [`kernel::fmt::Adapter`]. @@ -248,7 +246,7 @@ pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream { /// [`pr_info!`]: ../kernel/macro.pr_info.html #[proc_macro] pub fn fmt(input: TokenStream) -> TokenStream { - fmt::fmt(input) + fmt::fmt(input.into()).into() } /// Concatenate two identifiers. @@ -306,7 +304,7 @@ pub fn fmt(input: TokenStream) -> TokenStream { /// ``` #[proc_macro] pub fn concat_idents(ts: TokenStream) -> TokenStream { - concat_idents::concat_idents(ts) + concat_idents::concat_idents(ts.into()).into() } /// Paste identifiers together. @@ -444,9 +442,12 @@ pub fn concat_idents(ts: TokenStream) -> TokenStream { /// [`paste`]: https://docs.rs/paste/ #[proc_macro] pub fn paste(input: TokenStream) -> TokenStream { - let mut tokens = input.into_iter().collect(); + let mut tokens = proc_macro2::TokenStream::from(input).into_iter().collect(); paste::expand(&mut tokens); - tokens.into_iter().collect() + tokens + .into_iter() + .collect::() + .into() } /// Registers a KUnit test suite and its test cases using a user-space like syntax. @@ -473,5 +474,5 @@ pub fn paste(input: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { - kunit::kunit_tests(attr, ts) + kunit::kunit_tests(attr.into(), ts.into()).into() } diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 80cb9b16f5aa..b855a2b586e1 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 -use crate::helpers::*; -use proc_macro::{token_stream, Delimiter, Literal, TokenStream, TokenTree}; use std::fmt::Write; +use proc_macro2::{token_stream, Delimiter, Literal, TokenStream, TokenTree}; + +use crate::helpers::*; + fn expect_string_array(it: &mut token_stream::IntoIter) -> Vec { let group = expect_group(it); assert_eq!(group.delimiter(), Delimiter::Bracket); diff --git a/rust/macros/paste.rs b/rust/macros/paste.rs index cce712d19855..2181e312a7d3 100644 --- a/rust/macros/paste.rs +++ b/rust/macros/paste.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree}; +use proc_macro2::{Delimiter, Group, Ident, Spacing, Span, TokenTree}; fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> { let mut tokens = tokens.iter(); diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs deleted file mode 100644 index ddfc21577539..000000000000 --- a/rust/macros/quote.rs +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT - -use proc_macro::{TokenStream, TokenTree}; - -pub(crate) trait ToTokens { - fn to_tokens(&self, tokens: &mut TokenStream); -} - -impl ToTokens for Option { - fn to_tokens(&self, tokens: &mut TokenStream) { - if let Some(v) = self { - v.to_tokens(tokens); - } - } -} - -impl ToTokens for proc_macro::Group { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend([TokenTree::from(self.clone())]); - } -} - -impl ToTokens for proc_macro::Ident { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend([TokenTree::from(self.clone())]); - } -} - -impl ToTokens for TokenTree { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend([self.clone()]); - } -} - -impl ToTokens for TokenStream { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(self.clone()); - } -} - -/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with -/// the given span. -/// -/// This is a similar to the -/// [`quote_spanned!`](https://docs.rs/quote/latest/quote/macro.quote_spanned.html) macro from the -/// `quote` crate but provides only just enough functionality needed by the current `macros` crate. -macro_rules! quote_spanned { - ($span:expr => $($tt:tt)*) => {{ - let mut tokens = ::proc_macro::TokenStream::new(); - { - #[allow(unused_variables)] - let span = $span; - quote_spanned!(@proc tokens span $($tt)*); - } - tokens - }}; - (@proc $v:ident $span:ident) => {}; - (@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => { - $crate::quote::ToTokens::to_tokens(&$id, &mut $v); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => { - for token in $id { - $crate::quote::ToTokens::to_tokens(&token, &mut $v); - } - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => { - #[allow(unused_mut)] - let mut tokens = ::proc_macro::TokenStream::new(); - quote_spanned!(@proc tokens $span $($inner)*); - $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new( - ::proc_macro::Delimiter::Parenthesis, - tokens, - ))]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident [ $($inner:tt)* ] $($tt:tt)*) => { - let mut tokens = ::proc_macro::TokenStream::new(); - quote_spanned!(@proc tokens $span $($inner)*); - $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new( - ::proc_macro::Delimiter::Bracket, - tokens, - ))]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => { - let mut tokens = ::proc_macro::TokenStream::new(); - quote_spanned!(@proc tokens $span $($inner)*); - $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new( - ::proc_macro::Delimiter::Brace, - tokens, - ))]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident :: $($tt:tt)*) => { - $v.extend([::proc_macro::Spacing::Joint, ::proc_macro::Spacing::Alone].map(|spacing| { - ::proc_macro::TokenTree::Punct(::proc_macro::Punct::new(':', spacing)) - })); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident : $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident , $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new(',', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident @ $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new('@', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident ! $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new('!', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident ; $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident + $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident = $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident # $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new('#', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident & $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Punct( - ::proc_macro::Punct::new('&', ::proc_macro::Spacing::Alone), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident _ $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Ident( - ::proc_macro::Ident::new("_", $span), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; - (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => { - $v.extend([::proc_macro::TokenTree::Ident( - ::proc_macro::Ident::new(stringify!($id), $span), - )]); - quote_spanned!(@proc $v $span $($tt)*); - }; -} - -/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with -/// mixed site span ([`Span::mixed_site()`]). -/// -/// This is a similar to the [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) macro -/// from the `quote` crate but provides only just enough functionality needed by the current -/// `macros` crate. -/// -/// [`Span::mixed_site()`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.mixed_site -macro_rules! quote { - ($($tt:tt)*) => { - quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*) - } -} diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs index ee06044fcd4f..a67d1cc81a2d 100644 --- a/rust/macros/vtable.rs +++ b/rust/macros/vtable.rs @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro::{Delimiter, Group, TokenStream, TokenTree}; use std::collections::HashSet; use std::fmt::Write; +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; + pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream { let mut tokens: Vec<_> = ts.into_iter().collect(); @@ -31,7 +32,7 @@ pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream { let mut consts = HashSet::new(); while let Some(token) = body_it.next() { match token { - TokenTree::Ident(ident) if ident.to_string() == "fn" => { + TokenTree::Ident(ident) if ident == "fn" => { let fn_name = match body_it.next() { Some(TokenTree::Ident(ident)) => ident.to_string(), // Possibly we've encountered a fn pointer type instead. @@ -39,7 +40,7 @@ pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream { }; functions.push(fn_name); } - TokenTree::Ident(ident) if ident.to_string() == "const" => { + TokenTree::Ident(ident) if ident == "const" => { let const_name = match body_it.next() { Some(TokenTree::Ident(ident)) => ident.to_string(), // Possibly we've encountered an inline const block instead. From 5f160950a5cdc36f222299905e09a72f67ebfcd4 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:14 +0000 Subject: [PATCH 41/59] rust: macros: convert `#[vtable]` macro to use `syn` `#[vtable]` is converted to use syn. This is more robust than the previous heuristic-based searching of defined methods and functions. When doing so, the trait and impl are split into two code paths as the types are distinct when parsed by `syn`. Reviewed-by: Tamir Duberstein Signed-off-by: Gary Guo Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260112170919.1888584-4-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/lib.rs | 9 ++- rust/macros/vtable.rs | 179 ++++++++++++++++++++++-------------------- 2 files changed, 103 insertions(+), 85 deletions(-) diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index b884ea17391b..0ecbb2e16da3 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -22,6 +22,8 @@ mod vtable; use proc_macro::TokenStream; +use syn::parse_macro_input; + /// Declares a kernel module. /// /// The `type` argument should be a type which implements the [`Module`] @@ -204,8 +206,11 @@ pub fn module(ts: TokenStream) -> TokenStream { /// /// [`kernel::error::VTABLE_DEFAULT_ERROR`]: ../kernel/error/constant.VTABLE_DEFAULT_ERROR.html #[proc_macro_attribute] -pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream { - vtable::vtable(attr.into(), ts.into()).into() +pub fn vtable(attr: TokenStream, input: TokenStream) -> TokenStream { + parse_macro_input!(attr as syn::parse::Nothing); + vtable::vtable(parse_macro_input!(input)) + .unwrap_or_else(|e| e.into_compile_error()) + .into() } /// Export a function so that C code can call it via a header file. diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs index a67d1cc81a2d..72ae0a1816a0 100644 --- a/rust/macros/vtable.rs +++ b/rust/macros/vtable.rs @@ -1,97 +1,110 @@ // SPDX-License-Identifier: GPL-2.0 -use std::collections::HashSet; -use std::fmt::Write; +use std::{ + collections::HashSet, + iter::Extend, // +}; -use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; +use proc_macro2::{ + Ident, + TokenStream, // +}; +use quote::ToTokens; +use syn::{ + parse_quote, + Error, + ImplItem, + Item, + ItemImpl, + ItemTrait, + Result, + TraitItem, // +}; -pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream { - let mut tokens: Vec<_> = ts.into_iter().collect(); +fn handle_trait(mut item: ItemTrait) -> Result { + let mut gen_items = Vec::new(); + let mut gen_consts = HashSet::new(); - // Scan for the `trait` or `impl` keyword. - let is_trait = tokens - .iter() - .find_map(|token| match token { - TokenTree::Ident(ident) => match ident.to_string().as_str() { - "trait" => Some(true), - "impl" => Some(false), - _ => None, - }, - _ => None, - }) - .expect("#[vtable] attribute should only be applied to trait or impl block"); + gen_items.push(parse_quote! { + /// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable) + /// attribute when implementing this trait. + const USE_VTABLE_ATTR: (); + }); - // Retrieve the main body. The main body should be the last token tree. - let body = match tokens.pop() { - Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group, - _ => panic!("cannot locate main body of trait or impl block"), - }; - - let mut body_it = body.stream().into_iter(); - let mut functions = Vec::new(); - let mut consts = HashSet::new(); - while let Some(token) = body_it.next() { - match token { - TokenTree::Ident(ident) if ident == "fn" => { - let fn_name = match body_it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - // Possibly we've encountered a fn pointer type instead. - _ => continue, - }; - functions.push(fn_name); - } - TokenTree::Ident(ident) if ident == "const" => { - let const_name = match body_it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - // Possibly we've encountered an inline const block instead. - _ => continue, - }; - consts.insert(const_name); - } - _ => (), - } - } - - let mut const_items; - if is_trait { - const_items = " - /// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable) - /// attribute when implementing this trait. - const USE_VTABLE_ATTR: (); - " - .to_owned(); - - for f in functions { - let gen_const_name = format!("HAS_{}", f.to_uppercase()); - // Skip if it's declared already -- this allows user override. - if consts.contains(&gen_const_name) { + for item in &item.items { + if let TraitItem::Fn(fn_item) = item { + let name = &fn_item.sig.ident; + let gen_const_name = Ident::new( + &format!("HAS_{}", name.to_string().to_uppercase()), + name.span(), + ); + // Skip if it's declared already -- this can happen if `#[cfg]` is used to selectively + // define functions. + // FIXME: `#[cfg]` should be copied and propagated to the generated consts. + if gen_consts.contains(&gen_const_name) { continue; } + // We don't know on the implementation-site whether a method is required or provided // so we have to generate a const for all methods. - write!( - const_items, - "/// Indicates if the `{f}` method is overridden by the implementor. - const {gen_const_name}: bool = false;", - ) - .unwrap(); - consts.insert(gen_const_name); - } - } else { - const_items = "const USE_VTABLE_ATTR: () = ();".to_owned(); - - for f in functions { - let gen_const_name = format!("HAS_{}", f.to_uppercase()); - if consts.contains(&gen_const_name) { - continue; - } - write!(const_items, "const {gen_const_name}: bool = true;").unwrap(); + let comment = + format!("Indicates if the `{name}` method is overridden by the implementor."); + gen_items.push(parse_quote! { + #[doc = #comment] + const #gen_const_name: bool = false; + }); + gen_consts.insert(gen_const_name); } } - let new_body = vec![const_items.parse().unwrap(), body.stream()] - .into_iter() - .collect(); - tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, new_body))); - tokens.into_iter().collect() + item.items.extend(gen_items); + Ok(item) +} + +fn handle_impl(mut item: ItemImpl) -> Result { + let mut gen_items = Vec::new(); + let mut defined_consts = HashSet::new(); + + // Iterate over all user-defined constants to gather any possible explicit overrides. + for item in &item.items { + if let ImplItem::Const(const_item) = item { + defined_consts.insert(const_item.ident.clone()); + } + } + + gen_items.push(parse_quote! { + const USE_VTABLE_ATTR: () = (); + }); + + for item in &item.items { + if let ImplItem::Fn(fn_item) = item { + let name = &fn_item.sig.ident; + let gen_const_name = Ident::new( + &format!("HAS_{}", name.to_string().to_uppercase()), + name.span(), + ); + // Skip if it's declared already -- this allows user override. + if defined_consts.contains(&gen_const_name) { + continue; + } + gen_items.push(parse_quote! { + const #gen_const_name: bool = true; + }); + defined_consts.insert(gen_const_name); + } + } + + item.items.extend(gen_items); + Ok(item) +} + +pub(crate) fn vtable(input: Item) -> Result { + match input { + Item::Trait(item) => Ok(handle_trait(item)?.into_token_stream()), + Item::Impl(item) => Ok(handle_impl(item)?.into_token_stream()), + _ => Err(Error::new_spanned( + input, + "`#[vtable]` attribute should only be applied to trait or impl block", + ))?, + } } From c578ad703ae9a219aa4bdd13343cf5254541c5e3 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:15 +0000 Subject: [PATCH 42/59] rust: macros: use `syn` to parse `module!` macro With `syn` being available in the kernel, use it to parse the complex custom `module!` macro to replace existing helpers. Only parsing is changed in this commit, the code generation is untouched. This has the benefit of better error message when the macro is used incorrectly, as it can point to a concrete span on what's going wrong. For example, if a field is specified twice, previously it reads: error: proc macro panicked --> samples/rust/rust_minimal.rs:7:1 | 7 | / module! { 8 | | type: RustMinimal, 9 | | name: "rust_minimal", 10 | | author: "Rust for Linux Contributors", 11 | | description: "Rust minimal sample", 12 | | license: "GPL", 13 | | license: "GPL", 14 | | } | |_^ | = help: message: Duplicated key "license". Keys can only be specified once. now it reads: error: duplicated key "license". Keys can only be specified once. --> samples/rust/rust_minimal.rs:13:5 | 13 | license: "GPL", | ^^^^^^^ Reviewed-by: Tamir Duberstein Signed-off-by: Gary Guo Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260112170919.1888584-5-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/helpers.rs | 113 ++++-------- rust/macros/lib.rs | 6 +- rust/macros/module.rs | 393 +++++++++++++++++++++++++---------------- 3 files changed, 279 insertions(+), 233 deletions(-) diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index 13fafaba1226..fa66ef6eb0f3 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -1,53 +1,21 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro2::{token_stream, Group, Ident, TokenStream, TokenTree}; - -pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option { - if let Some(TokenTree::Ident(ident)) = it.next() { - Some(ident.to_string()) - } else { - None - } -} - -pub(crate) fn try_sign(it: &mut token_stream::IntoIter) -> Option { - let peek = it.clone().next(); - match peek { - Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => { - let _ = it.next(); - Some(punct.as_char()) - } - _ => None, - } -} - -pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option { - if let Some(TokenTree::Literal(literal)) = it.next() { - Some(literal.to_string()) - } else { - None - } -} - -pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option { - try_literal(it).and_then(|string| { - if string.starts_with('\"') && string.ends_with('\"') { - let content = &string[1..string.len() - 1]; - if content.contains('\\') { - panic!("Escape sequences in string literals not yet handled"); - } - Some(content.to_string()) - } else if string.starts_with("r\"") { - panic!("Raw string literals are not yet handled"); - } else { - None - } - }) -} - -pub(crate) fn expect_ident(it: &mut token_stream::IntoIter) -> String { - try_ident(it).expect("Expected Ident") -} +use proc_macro2::{ + token_stream, + Ident, + TokenStream, + TokenTree, // +}; +use quote::ToTokens; +use syn::{ + parse::{ + Parse, + ParseStream, // + }, + Error, + LitStr, + Result, // +}; pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char { if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") { @@ -57,27 +25,28 @@ pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char { } } -pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String { - try_string(it).expect("Expected string") -} +/// A string literal that is required to have ASCII value only. +pub(crate) struct AsciiLitStr(LitStr); -pub(crate) fn expect_string_ascii(it: &mut token_stream::IntoIter) -> String { - let string = try_string(it).expect("Expected string"); - assert!(string.is_ascii(), "Expected ASCII string"); - string -} - -pub(crate) fn expect_group(it: &mut token_stream::IntoIter) -> Group { - if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") { - group - } else { - panic!("Expected Group"); +impl Parse for AsciiLitStr { + fn parse(input: ParseStream<'_>) -> Result { + let s: LitStr = input.parse()?; + if !s.value().is_ascii() { + return Err(Error::new_spanned(s, "expected ASCII-only string literal")); + } + Ok(Self(s)) } } -pub(crate) fn expect_end(it: &mut token_stream::IntoIter) { - if it.next().is_some() { - panic!("Expected end"); +impl ToTokens for AsciiLitStr { + fn to_tokens(&self, ts: &mut TokenStream) { + self.0.to_tokens(ts); + } +} + +impl AsciiLitStr { + pub(crate) fn value(&self) -> String { + self.0.value() } } @@ -114,17 +83,3 @@ pub(crate) fn file() -> String { proc_macro::Span::call_site().file() } } - -/// Parse a token stream of the form `expected_name: "value",` and return the -/// string in the position of "value". -/// -/// # Panics -/// -/// - On parse error. -pub(crate) fn expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String { - assert_eq!(expect_ident(it), expected_name); - assert_eq!(expect_punct(it), ':'); - let string = expect_string(it); - assert_eq!(expect_punct(it), ','); - string -} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 0ecbb2e16da3..9ce250cbf7ea 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -131,8 +131,10 @@ use syn::parse_macro_input; /// - `firmware`: array of ASCII string literals of the firmware files of /// the kernel module. #[proc_macro] -pub fn module(ts: TokenStream) -> TokenStream { - module::module(ts.into()).into() +pub fn module(input: TokenStream) -> TokenStream { + module::module(parse_macro_input!(input)) + .unwrap_or_else(|e| e.into_compile_error()) + .into() } /// Declares or implements a vtable trait. diff --git a/rust/macros/module.rs b/rust/macros/module.rs index b855a2b586e1..9fdc9ed1faaf 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -2,28 +2,30 @@ use std::fmt::Write; -use proc_macro2::{token_stream, Delimiter, Literal, TokenStream, TokenTree}; +use proc_macro2::{ + Literal, + TokenStream, // +}; +use quote::ToTokens; +use syn::{ + braced, + bracketed, + ext::IdentExt, + parse::{ + Parse, + ParseStream, // + }, + punctuated::Punctuated, + Error, + Expr, + Ident, + LitStr, + Result, + Token, // +}; use crate::helpers::*; -fn expect_string_array(it: &mut token_stream::IntoIter) -> Vec { - let group = expect_group(it); - assert_eq!(group.delimiter(), Delimiter::Bracket); - let mut values = Vec::new(); - let mut it = group.stream().into_iter(); - - while let Some(val) = try_string(&mut it) { - assert!(val.is_ascii(), "Expected ASCII string"); - values.push(val); - match it.next() { - Some(TokenTree::Punct(punct)) => assert_eq!(punct.as_char(), ','), - None => break, - _ => panic!("Expected ',' or end of array"), - } - } - values -} - struct ModInfoBuilder<'a> { module: &'a str, counter: usize, @@ -113,31 +115,35 @@ impl<'a> ModInfoBuilder<'a> { }; for param in params { - let ops = param_ops_path(¶m.ptype); + let param_name_str = param.name.to_string(); + let param_type_str = param.ptype.to_string(); + let param_default = param.default.to_token_stream().to_string(); + + let ops = param_ops_path(¶m_type_str); // Note: The spelling of these fields is dictated by the user space // tool `modinfo`. - self.emit_param("parmtype", ¶m.name, ¶m.ptype); - self.emit_param("parm", ¶m.name, ¶m.description); + self.emit_param("parmtype", ¶m_name_str, ¶m_type_str); + self.emit_param("parm", ¶m_name_str, ¶m.description.value()); write!( self.param_buffer, " - pub(crate) static {param_name}: - ::kernel::module_param::ModuleParamAccess<{param_type}> = + pub(crate) static {param_name_str}: + ::kernel::module_param::ModuleParamAccess<{param_type_str}> = ::kernel::module_param::ModuleParamAccess::new({param_default}); const _: () = {{ #[link_section = \"__param\"] #[used] - static __{module_name}_{param_name}_struct: + static __{module_name}_{param_name_str}_struct: ::kernel::module_param::KernelParam = ::kernel::module_param::KernelParam::new( ::kernel::bindings::kernel_param {{ name: if ::core::cfg!(MODULE) {{ - ::kernel::c_str!(\"{param_name}\").to_bytes_with_nul() + ::kernel::c_str!(\"{param_name_str}\").to_bytes_with_nul() }} else {{ - ::kernel::c_str!(\"{module_name}.{param_name}\") + ::kernel::c_str!(\"{module_name}.{param_name_str}\") .to_bytes_with_nul() }}.as_ptr(), // SAFETY: `__this_module` is constructed by the kernel at load @@ -154,16 +160,13 @@ impl<'a> ModInfoBuilder<'a> { level: -1, flags: 0, __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{ - arg: {param_name}.as_void_ptr() + arg: {param_name_str}.as_void_ptr() }}, }} ); }}; ", - module_name = info.name, - param_type = param.ptype, - param_default = param.default, - param_name = param.name, + module_name = info.name.value(), ops = ops, ) .unwrap(); @@ -187,127 +190,78 @@ fn param_ops_path(param_type: &str) -> &'static str { } } -fn expect_param_default(param_it: &mut token_stream::IntoIter) -> String { - assert_eq!(expect_ident(param_it), "default"); - assert_eq!(expect_punct(param_it), ':'); - let sign = try_sign(param_it); - let default = try_literal(param_it).expect("Expected default param value"); - assert_eq!(expect_punct(param_it), ','); - let mut value = sign.map(String::from).unwrap_or_default(); - value.push_str(&default); - value -} +/// Parse fields that are required to use a specific order. +/// +/// As fields must follow a specific order, we *could* just parse fields one by one by peeking. +/// However the error message generated when implementing that way is not very friendly. +/// +/// So instead we parse fields in an arbitrary order, but only enforce the ordering after parsing, +/// and if the wrong order is used, the proper order is communicated to the user with error message. +/// +/// Usage looks like this: +/// ```ignore +/// parse_ordered_fields! { +/// from input; +/// +/// // This will extract "foo: " into a variable named "foo". +/// // The variable will have type `Option<_>`. +/// foo => , +/// +/// // If you need the variable name to be different than the key name. +/// // This extracts "baz: " into a variable named "bar". +/// // You might want this if "baz" is a keyword. +/// baz as bar => , +/// +/// // You can mark a key as required, and the variable will no longer be `Option`. +/// // foobar will be of type `Expr` instead of `Option`. +/// foobar [required] => input.parse::()?, +/// } +/// ``` +macro_rules! parse_ordered_fields { + (@gen + [$input:expr] + [$([$name:ident; $key:ident; $parser:expr])*] + [$([$req_name:ident; $req_key:ident])*] + ) => { + $(let mut $name = None;)* -#[derive(Debug, Default)] -struct ModuleInfo { - type_: String, - license: String, - name: String, - authors: Option>, - description: Option, - alias: Option>, - firmware: Option>, - imports_ns: Option>, - params: Option>, -} + const EXPECTED_KEYS: &[&str] = &[$(stringify!($key),)*]; + const REQUIRED_KEYS: &[&str] = &[$(stringify!($req_key),)*]; -#[derive(Debug)] -struct Parameter { - name: String, - ptype: String, - default: String, - description: String, -} - -fn expect_params(it: &mut token_stream::IntoIter) -> Vec { - let params = expect_group(it); - assert_eq!(params.delimiter(), Delimiter::Brace); - let mut it = params.stream().into_iter(); - let mut parsed = Vec::new(); - - loop { - let param_name = match it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - Some(_) => panic!("Expected Ident or end"), - None => break, - }; - - assert_eq!(expect_punct(&mut it), ':'); - let param_type = expect_ident(&mut it); - let group = expect_group(&mut it); - assert_eq!(group.delimiter(), Delimiter::Brace); - assert_eq!(expect_punct(&mut it), ','); - - let mut param_it = group.stream().into_iter(); - let param_default = expect_param_default(&mut param_it); - let param_description = expect_string_field(&mut param_it, "description"); - expect_end(&mut param_it); - - parsed.push(Parameter { - name: param_name, - ptype: param_type, - default: param_default, - description: param_description, - }) - } - - parsed -} - -impl ModuleInfo { - fn parse(it: &mut token_stream::IntoIter) -> Self { - let mut info = ModuleInfo::default(); - - const EXPECTED_KEYS: &[&str] = &[ - "type", - "name", - "authors", - "description", - "license", - "alias", - "firmware", - "imports_ns", - "params", - ]; - const REQUIRED_KEYS: &[&str] = &["type", "name", "license"]; + let span = $input.span(); let mut seen_keys = Vec::new(); - loop { - let key = match it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - Some(_) => panic!("Expected Ident or end"), - None => break, - }; + while !$input.is_empty() { + let key = $input.call(Ident::parse_any)?; if seen_keys.contains(&key) { - panic!("Duplicated key \"{key}\". Keys can only be specified once."); + Err(Error::new_spanned( + &key, + format!(r#"duplicated key "{key}". Keys can only be specified once."#), + ))? } - assert_eq!(expect_punct(it), ':'); + $input.parse::()?; - match key.as_str() { - "type" => info.type_ = expect_ident(it), - "name" => info.name = expect_string_ascii(it), - "authors" => info.authors = Some(expect_string_array(it)), - "description" => info.description = Some(expect_string(it)), - "license" => info.license = expect_string_ascii(it), - "alias" => info.alias = Some(expect_string_array(it)), - "firmware" => info.firmware = Some(expect_string_array(it)), - "imports_ns" => info.imports_ns = Some(expect_string_array(it)), - "params" => info.params = Some(expect_params(it)), - _ => panic!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."), + match &*key.to_string() { + $( + stringify!($key) => $name = Some($parser), + )* + _ => { + Err(Error::new_spanned( + &key, + format!(r#"unknown key "{key}". Valid keys are: {EXPECTED_KEYS:?}."#), + ))? + } } - assert_eq!(expect_punct(it), ','); - + $input.parse::()?; seen_keys.push(key); } - expect_end(it); - for key in REQUIRED_KEYS { if !seen_keys.iter().any(|e| e == key) { - panic!("Missing required key \"{key}\"."); + Err(Error::new(span, format!(r#"missing required key "{key}""#)))? } } @@ -319,43 +273,178 @@ impl ModuleInfo { } if seen_keys != ordered_keys { - panic!("Keys are not ordered as expected. Order them like: {ordered_keys:?}."); + Err(Error::new( + span, + format!(r#"keys are not ordered as expected. Order them like: {ordered_keys:?}."#), + ))? } - info + $(let $req_name = $req_name.expect("required field");)* + }; + + // Handle required fields. + (@gen + [$input:expr] [$($tok:tt)*] [$($req:tt)*] + $key:ident as $name:ident [required] => $parser:expr, + $($rest:tt)* + ) => { + parse_ordered_fields!( + @gen [$input] [$($tok)* [$name; $key; $parser]] [$($req)* [$name; $key]] $($rest)* + ) + }; + (@gen + [$input:expr] [$($tok:tt)*] [$($req:tt)*] + $name:ident [required] => $parser:expr, + $($rest:tt)* + ) => { + parse_ordered_fields!( + @gen [$input] [$($tok)* [$name; $name; $parser]] [$($req)* [$name; $name]] $($rest)* + ) + }; + + // Handle optional fields. + (@gen + [$input:expr] [$($tok:tt)*] [$($req:tt)*] + $key:ident as $name:ident => $parser:expr, + $($rest:tt)* + ) => { + parse_ordered_fields!( + @gen [$input] [$($tok)* [$name; $key; $parser]] [$($req)*] $($rest)* + ) + }; + (@gen + [$input:expr] [$($tok:tt)*] [$($req:tt)*] + $name:ident => $parser:expr, + $($rest:tt)* + ) => { + parse_ordered_fields!( + @gen [$input] [$($tok)* [$name; $name; $parser]] [$($req)*] $($rest)* + ) + }; + + (from $input:expr; $($tok:tt)*) => { + parse_ordered_fields!(@gen [$input] [] [] $($tok)*) } } -pub(crate) fn module(ts: TokenStream) -> TokenStream { - let mut it = ts.into_iter(); +struct Parameter { + name: Ident, + ptype: Ident, + default: Expr, + description: LitStr, +} - let info = ModuleInfo::parse(&mut it); +impl Parse for Parameter { + fn parse(input: ParseStream<'_>) -> Result { + let name = input.parse()?; + input.parse::()?; + let ptype = input.parse()?; + let fields; + braced!(fields in input); + + parse_ordered_fields! { + from fields; + default [required] => fields.parse()?, + description [required] => fields.parse()?, + } + + Ok(Self { + name, + ptype, + default, + description, + }) + } +} + +pub(crate) struct ModuleInfo { + type_: Ident, + license: AsciiLitStr, + name: AsciiLitStr, + authors: Option>, + description: Option, + alias: Option>, + firmware: Option>, + imports_ns: Option>, + params: Option>, +} + +impl Parse for ModuleInfo { + fn parse(input: ParseStream<'_>) -> Result { + parse_ordered_fields!( + from input; + type as type_ [required] => input.parse()?, + name [required] => input.parse()?, + authors => { + let list; + bracketed!(list in input); + Punctuated::parse_terminated(&list)? + }, + description => input.parse()?, + license [required] => input.parse()?, + alias => { + let list; + bracketed!(list in input); + Punctuated::parse_terminated(&list)? + }, + firmware => { + let list; + bracketed!(list in input); + Punctuated::parse_terminated(&list)? + }, + imports_ns => { + let list; + bracketed!(list in input); + Punctuated::parse_terminated(&list)? + }, + params => { + let list; + braced!(list in input); + Punctuated::parse_terminated(&list)? + }, + ); + + Ok(ModuleInfo { + type_, + license, + name, + authors, + description, + alias, + firmware, + imports_ns, + params, + }) + } +} + +pub(crate) fn module(info: ModuleInfo) -> Result { // Rust does not allow hyphens in identifiers, use underscore instead. - let ident = info.name.replace('-', "_"); + let ident = info.name.value().replace('-', "_"); let mut modinfo = ModInfoBuilder::new(ident.as_ref()); if let Some(authors) = &info.authors { for author in authors { - modinfo.emit("author", author); + modinfo.emit("author", &author.value()); } } if let Some(description) = &info.description { - modinfo.emit("description", description); + modinfo.emit("description", &description.value()); } - modinfo.emit("license", &info.license); + modinfo.emit("license", &info.license.value()); if let Some(aliases) = &info.alias { for alias in aliases { - modinfo.emit("alias", alias); + modinfo.emit("alias", &alias.value()); } } if let Some(firmware) = &info.firmware { for fw in firmware { - modinfo.emit("firmware", fw); + modinfo.emit("firmware", &fw.value()); } } if let Some(imports) = &info.imports_ns { for ns in imports { - modinfo.emit("import_ns", ns); + modinfo.emit("import_ns", &ns.value()); } } @@ -366,7 +455,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { modinfo.emit_params(&info); - format!( + Ok(format!( " /// The module name. /// @@ -536,12 +625,12 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { }} ", type_ = info.type_, - name = info.name, + name = info.name.value(), ident = ident, modinfo = modinfo.buffer, params = modinfo.param_buffer, initcall_section = ".initcall6.init" ) .parse() - .expect("Error parsing formatted string into token stream.") + .expect("Error parsing formatted string into token stream.")) } From 5f7045772037b33365fddb541b671ded6a6bded6 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:16 +0000 Subject: [PATCH 43/59] rust: macros: use `quote!` for `module!` macro This has no behavioural change, but is good for maintainability. With `quote!`, we're no longer using string templates, so we don't need to quote " and {} inside the template anymore. Further more, editors can now highlight the code template. This also improves the robustness as it eliminates the need for string quoting and escaping. Co-developed-by: Benno Lossin Signed-off-by: Benno Lossin Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260112170919.1888584-6-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/module.rs | 507 ++++++++++++++++++++++-------------------- 1 file changed, 268 insertions(+), 239 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 9fdc9ed1faaf..43ada49525c9 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -1,12 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 -use std::fmt::Write; +use std::ffi::CString; use proc_macro2::{ Literal, TokenStream, // }; -use quote::ToTokens; +use quote::{ + format_ident, + quote, // +}; use syn::{ braced, bracketed, @@ -15,11 +18,13 @@ use syn::{ Parse, ParseStream, // }, + parse_quote, punctuated::Punctuated, Error, Expr, Ident, LitStr, + Path, Result, Token, // }; @@ -29,8 +34,8 @@ use crate::helpers::*; struct ModInfoBuilder<'a> { module: &'a str, counter: usize, - buffer: String, - param_buffer: String, + ts: TokenStream, + param_ts: TokenStream, } impl<'a> ModInfoBuilder<'a> { @@ -38,8 +43,8 @@ impl<'a> ModInfoBuilder<'a> { ModInfoBuilder { module, counter: 0, - buffer: String::new(), - param_buffer: String::new(), + ts: TokenStream::new(), + param_ts: TokenStream::new(), } } @@ -56,33 +61,32 @@ impl<'a> ModInfoBuilder<'a> { // Loadable modules' modinfo strings go as-is. format!("{field}={content}\0") }; - - let buffer = if param { - &mut self.param_buffer + let length = string.len(); + let string = Literal::byte_string(string.as_bytes()); + let cfg = if builtin { + quote!(#[cfg(not(MODULE))]) } else { - &mut self.buffer + quote!(#[cfg(MODULE)]) }; - write!( - buffer, - " - {cfg} - #[doc(hidden)] - #[cfg_attr(not(target_os = \"macos\"), link_section = \".modinfo\")] - #[used(compiler)] - pub static __{module}_{counter}: [u8; {length}] = *{string}; - ", - cfg = if builtin { - "#[cfg(not(MODULE))]" - } else { - "#[cfg(MODULE)]" - }, + let counter = format_ident!( + "__{module}_{counter}", module = self.module.to_uppercase(), - counter = self.counter, - length = string.len(), - string = Literal::byte_string(string.as_bytes()), - ) - .unwrap(); + counter = self.counter + ); + let item = quote! { + #cfg + #[doc(hidden)] + #[cfg_attr(not(target_os = "macos"), link_section = ".modinfo")] + #[used(compiler)] + pub static #counter: [u8; #length] = *#string; + }; + + if param { + self.param_ts.extend(item); + } else { + self.ts.extend(item); + } self.counter += 1; } @@ -117,7 +121,6 @@ impl<'a> ModInfoBuilder<'a> { for param in params { let param_name_str = param.name.to_string(); let param_type_str = param.ptype.to_string(); - let param_default = param.default.to_token_stream().to_string(); let ops = param_ops_path(¶m_type_str); @@ -126,66 +129,74 @@ impl<'a> ModInfoBuilder<'a> { self.emit_param("parmtype", ¶m_name_str, ¶m_type_str); self.emit_param("parm", ¶m_name_str, ¶m.description.value()); - write!( - self.param_buffer, - " - pub(crate) static {param_name_str}: - ::kernel::module_param::ModuleParamAccess<{param_type_str}> = - ::kernel::module_param::ModuleParamAccess::new({param_default}); + let static_name = format_ident!("__{}_{}_struct", self.module, param.name); + let param_name_cstr = + CString::new(param_name_str).expect("name contains NUL-terminator"); + let param_name_cstr_with_module = + CString::new(format!("{}.{}", self.module, param.name)) + .expect("name contains NUL-terminator"); - const _: () = {{ - #[link_section = \"__param\"] - #[used] - static __{module_name}_{param_name_str}_struct: + let param_name = ¶m.name; + let param_type = ¶m.ptype; + let param_default = ¶m.default; + + self.param_ts.extend(quote! { + #[allow(non_upper_case_globals)] + pub(crate) static #param_name: + ::kernel::module_param::ModuleParamAccess<#param_type> = + ::kernel::module_param::ModuleParamAccess::new(#param_default); + + const _: () = { + #[allow(non_upper_case_globals)] + #[link_section = "__param"] + #[used(compiler)] + static #static_name: ::kernel::module_param::KernelParam = ::kernel::module_param::KernelParam::new( - ::kernel::bindings::kernel_param {{ - name: if ::core::cfg!(MODULE) {{ - ::kernel::c_str!(\"{param_name_str}\").to_bytes_with_nul() - }} else {{ - ::kernel::c_str!(\"{module_name}.{param_name_str}\") - .to_bytes_with_nul() - }}.as_ptr(), + ::kernel::bindings::kernel_param { + name: kernel::str::as_char_ptr_in_const_context( + if ::core::cfg!(MODULE) { + #param_name_cstr + } else { + #param_name_cstr_with_module + } + ), // SAFETY: `__this_module` is constructed by the kernel at load // time and will not be freed until the module is unloaded. #[cfg(MODULE)] - mod_: unsafe {{ + mod_: unsafe { core::ptr::from_ref(&::kernel::bindings::__this_module) .cast_mut() - }}, + }, #[cfg(not(MODULE))] mod_: ::core::ptr::null_mut(), - ops: core::ptr::from_ref(&{ops}), + ops: core::ptr::from_ref(&#ops), perm: 0, // Will not appear in sysfs level: -1, flags: 0, - __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{ - arg: {param_name_str}.as_void_ptr() - }}, - }} + __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 { + arg: #param_name.as_void_ptr() + }, + } ); - }}; - ", - module_name = info.name.value(), - ops = ops, - ) - .unwrap(); + }; + }); } } } -fn param_ops_path(param_type: &str) -> &'static str { +fn param_ops_path(param_type: &str) -> Path { match param_type { - "i8" => "::kernel::module_param::PARAM_OPS_I8", - "u8" => "::kernel::module_param::PARAM_OPS_U8", - "i16" => "::kernel::module_param::PARAM_OPS_I16", - "u16" => "::kernel::module_param::PARAM_OPS_U16", - "i32" => "::kernel::module_param::PARAM_OPS_I32", - "u32" => "::kernel::module_param::PARAM_OPS_U32", - "i64" => "::kernel::module_param::PARAM_OPS_I64", - "u64" => "::kernel::module_param::PARAM_OPS_U64", - "isize" => "::kernel::module_param::PARAM_OPS_ISIZE", - "usize" => "::kernel::module_param::PARAM_OPS_USIZE", + "i8" => parse_quote!(::kernel::module_param::PARAM_OPS_I8), + "u8" => parse_quote!(::kernel::module_param::PARAM_OPS_U8), + "i16" => parse_quote!(::kernel::module_param::PARAM_OPS_I16), + "u16" => parse_quote!(::kernel::module_param::PARAM_OPS_U16), + "i32" => parse_quote!(::kernel::module_param::PARAM_OPS_I32), + "u32" => parse_quote!(::kernel::module_param::PARAM_OPS_U32), + "i64" => parse_quote!(::kernel::module_param::PARAM_OPS_I64), + "u64" => parse_quote!(::kernel::module_param::PARAM_OPS_U64), + "isize" => parse_quote!(::kernel::module_param::PARAM_OPS_ISIZE), + "usize" => parse_quote!(::kernel::module_param::PARAM_OPS_USIZE), t => panic!("Unsupported parameter type {}", t), } } @@ -420,29 +431,41 @@ impl Parse for ModuleInfo { } pub(crate) fn module(info: ModuleInfo) -> Result { + let ModuleInfo { + type_, + license, + name, + authors, + description, + alias, + firmware, + imports_ns, + params: _, + } = &info; + // Rust does not allow hyphens in identifiers, use underscore instead. - let ident = info.name.value().replace('-', "_"); + let ident = name.value().replace('-', "_"); let mut modinfo = ModInfoBuilder::new(ident.as_ref()); - if let Some(authors) = &info.authors { + if let Some(authors) = authors { for author in authors { modinfo.emit("author", &author.value()); } } - if let Some(description) = &info.description { + if let Some(description) = description { modinfo.emit("description", &description.value()); } - modinfo.emit("license", &info.license.value()); - if let Some(aliases) = &info.alias { + modinfo.emit("license", &license.value()); + if let Some(aliases) = alias { for alias in aliases { modinfo.emit("alias", &alias.value()); } } - if let Some(firmware) = &info.firmware { + if let Some(firmware) = firmware { for fw in firmware { modinfo.emit("firmware", &fw.value()); } } - if let Some(imports) = &info.imports_ns { + if let Some(imports) = imports_ns { for ns in imports { modinfo.emit("import_ns", &ns.value()); } @@ -455,182 +478,188 @@ pub(crate) fn module(info: ModuleInfo) -> Result { modinfo.emit_params(&info); - Ok(format!( - " - /// The module name. - /// - /// Used by the printing macros, e.g. [`info!`]. - const __LOG_PREFIX: &[u8] = b\"{name}\\0\"; + let modinfo_ts = modinfo.ts; + let params_ts = modinfo.param_ts; - // SAFETY: `__this_module` is constructed by the kernel at load time and will not be - // freed until the module is unloaded. - #[cfg(MODULE)] - static THIS_MODULE: ::kernel::ThisModule = unsafe {{ - extern \"C\" {{ - static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>; - }} + let ident_init = format_ident!("__{ident}_init"); + let ident_exit = format_ident!("__{ident}_exit"); + let ident_initcall = format_ident!("__{ident}_initcall"); + let initcall_section = ".initcall6.init"; - ::kernel::ThisModule::from_ptr(__this_module.get()) - }}; - #[cfg(not(MODULE))] - static THIS_MODULE: ::kernel::ThisModule = unsafe {{ - ::kernel::ThisModule::from_ptr(::core::ptr::null_mut()) - }}; + let global_asm = format!( + r#".section "{initcall_section}", "a" + __{ident}_initcall: + .long __{ident}_init - . + .previous + "# + ); - /// The `LocalModule` type is the type of the module created by `module!`, - /// `module_pci_driver!`, `module_platform_driver!`, etc. - type LocalModule = {type_}; + let name_cstr = CString::new(name.value()).expect("name contains NUL-terminator"); - impl ::kernel::ModuleMetadata for {type_} {{ - const NAME: &'static ::kernel::str::CStr = c\"{name}\"; - }} + Ok(quote! { + /// The module name. + /// + /// Used by the printing macros, e.g. [`info!`]. + const __LOG_PREFIX: &[u8] = #name_cstr.to_bytes_with_nul(); - // Double nested modules, since then nobody can access the public items inside. - mod __module_init {{ - mod __module_init {{ - use super::super::{type_}; - use pin_init::PinInit; + // SAFETY: `__this_module` is constructed by the kernel at load time and will not be + // freed until the module is unloaded. + #[cfg(MODULE)] + static THIS_MODULE: ::kernel::ThisModule = unsafe { + extern "C" { + static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>; + }; - /// The \"Rust loadable module\" mark. - // - // This may be best done another way later on, e.g. as a new modinfo - // key or a new section. For the moment, keep it simple. - #[cfg(MODULE)] - #[doc(hidden)] - #[used(compiler)] - static __IS_RUST_MODULE: () = (); + ::kernel::ThisModule::from_ptr(__this_module.get()) + }; - static mut __MOD: ::core::mem::MaybeUninit<{type_}> = - ::core::mem::MaybeUninit::uninit(); + #[cfg(not(MODULE))] + static THIS_MODULE: ::kernel::ThisModule = unsafe { + ::kernel::ThisModule::from_ptr(::core::ptr::null_mut()) + }; - // Loadable modules need to export the `{{init,cleanup}}_module` identifiers. - /// # Safety - /// - /// This function must not be called after module initialization, because it may be - /// freed after that completes. - #[cfg(MODULE)] - #[doc(hidden)] - #[no_mangle] - #[link_section = \".init.text\"] - pub unsafe extern \"C\" fn init_module() -> ::kernel::ffi::c_int {{ - // SAFETY: This function is inaccessible to the outside due to the double - // module wrapping it. It is called exactly once by the C side via its - // unique name. - unsafe {{ __init() }} - }} + /// The `LocalModule` type is the type of the module created by `module!`, + /// `module_pci_driver!`, `module_platform_driver!`, etc. + type LocalModule = #type_; - #[cfg(MODULE)] - #[doc(hidden)] - #[used(compiler)] - #[link_section = \".init.data\"] - static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module; + impl ::kernel::ModuleMetadata for #type_ { + const NAME: &'static ::kernel::str::CStr = #name_cstr; + } - #[cfg(MODULE)] - #[doc(hidden)] - #[no_mangle] - #[link_section = \".exit.text\"] - pub extern \"C\" fn cleanup_module() {{ - // SAFETY: - // - This function is inaccessible to the outside due to the double - // module wrapping it. It is called exactly once by the C side via its - // unique name, - // - furthermore it is only called after `init_module` has returned `0` - // (which delegates to `__init`). - unsafe {{ __exit() }} - }} + // Double nested modules, since then nobody can access the public items inside. + mod __module_init { + mod __module_init { + use super::super::#type_; + use pin_init::PinInit; - #[cfg(MODULE)] - #[doc(hidden)] - #[used(compiler)] - #[link_section = \".exit.data\"] - static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module; + /// The "Rust loadable module" mark. + // + // This may be best done another way later on, e.g. as a new modinfo + // key or a new section. For the moment, keep it simple. + #[cfg(MODULE)] + #[doc(hidden)] + #[used(compiler)] + static __IS_RUST_MODULE: () = (); - // Built-in modules are initialized through an initcall pointer - // and the identifiers need to be unique. - #[cfg(not(MODULE))] - #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] - #[doc(hidden)] - #[link_section = \"{initcall_section}\"] - #[used(compiler)] - pub static __{ident}_initcall: extern \"C\" fn() -> - ::kernel::ffi::c_int = __{ident}_init; + static mut __MOD: ::core::mem::MaybeUninit<#type_> = + ::core::mem::MaybeUninit::uninit(); - #[cfg(not(MODULE))] - #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] - ::core::arch::global_asm!( - r#\".section \"{initcall_section}\", \"a\" - __{ident}_initcall: - .long __{ident}_init - . - .previous - \"# - ); + // Loadable modules need to export the `{init,cleanup}_module` identifiers. + /// # Safety + /// + /// This function must not be called after module initialization, because it may be + /// freed after that completes. + #[cfg(MODULE)] + #[doc(hidden)] + #[no_mangle] + #[link_section = ".init.text"] + pub unsafe extern "C" fn init_module() -> ::kernel::ffi::c_int { + // SAFETY: This function is inaccessible to the outside due to the double + // module wrapping it. It is called exactly once by the C side via its + // unique name. + unsafe { __init() } + } - #[cfg(not(MODULE))] - #[doc(hidden)] - #[no_mangle] - pub extern \"C\" fn __{ident}_init() -> ::kernel::ffi::c_int {{ - // SAFETY: This function is inaccessible to the outside due to the double - // module wrapping it. It is called exactly once by the C side via its - // placement above in the initcall section. - unsafe {{ __init() }} - }} + #[cfg(MODULE)] + #[doc(hidden)] + #[used(compiler)] + #[link_section = ".init.data"] + static __UNIQUE_ID___addressable_init_module: unsafe extern "C" fn() -> i32 = + init_module; - #[cfg(not(MODULE))] - #[doc(hidden)] - #[no_mangle] - pub extern \"C\" fn __{ident}_exit() {{ - // SAFETY: - // - This function is inaccessible to the outside due to the double - // module wrapping it. It is called exactly once by the C side via its - // unique name, - // - furthermore it is only called after `__{ident}_init` has - // returned `0` (which delegates to `__init`). - unsafe {{ __exit() }} - }} + #[cfg(MODULE)] + #[doc(hidden)] + #[no_mangle] + #[link_section = ".exit.text"] + pub extern "C" fn cleanup_module() { + // SAFETY: + // - This function is inaccessible to the outside due to the double + // module wrapping it. It is called exactly once by the C side via its + // unique name, + // - furthermore it is only called after `init_module` has returned `0` + // (which delegates to `__init`). + unsafe { __exit() } + } - /// # Safety - /// - /// This function must only be called once. - unsafe fn __init() -> ::kernel::ffi::c_int {{ - let initer = - <{type_} as ::kernel::InPlaceModule>::init(&super::super::THIS_MODULE); - // SAFETY: No data race, since `__MOD` can only be accessed by this module - // and there only `__init` and `__exit` access it. These functions are only - // called once and `__exit` cannot be called before or during `__init`. - match unsafe {{ initer.__pinned_init(__MOD.as_mut_ptr()) }} {{ - Ok(m) => 0, - Err(e) => e.to_errno(), - }} - }} + #[cfg(MODULE)] + #[doc(hidden)] + #[used(compiler)] + #[link_section = ".exit.data"] + static __UNIQUE_ID___addressable_cleanup_module: extern "C" fn() = cleanup_module; - /// # Safety - /// - /// This function must - /// - only be called once, - /// - be called after `__init` has been called and returned `0`. - unsafe fn __exit() {{ - // SAFETY: No data race, since `__MOD` can only be accessed by this module - // and there only `__init` and `__exit` access it. These functions are only - // called once and `__init` was already called. - unsafe {{ - // Invokes `drop()` on `__MOD`, which should be used for cleanup. - __MOD.assume_init_drop(); - }} - }} - {modinfo} - }} - }} - mod module_parameters {{ - {params} - }} - ", - type_ = info.type_, - name = info.name.value(), - ident = ident, - modinfo = modinfo.buffer, - params = modinfo.param_buffer, - initcall_section = ".initcall6.init" - ) - .parse() - .expect("Error parsing formatted string into token stream.")) + // Built-in modules are initialized through an initcall pointer + // and the identifiers need to be unique. + #[cfg(not(MODULE))] + #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] + #[doc(hidden)] + #[link_section = #initcall_section] + #[used(compiler)] + pub static #ident_initcall: extern "C" fn() -> + ::kernel::ffi::c_int = #ident_init; + + #[cfg(not(MODULE))] + #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] + ::core::arch::global_asm!(#global_asm); + + #[cfg(not(MODULE))] + #[doc(hidden)] + #[no_mangle] + pub extern "C" fn #ident_init() -> ::kernel::ffi::c_int { + // SAFETY: This function is inaccessible to the outside due to the double + // module wrapping it. It is called exactly once by the C side via its + // placement above in the initcall section. + unsafe { __init() } + } + + #[cfg(not(MODULE))] + #[doc(hidden)] + #[no_mangle] + pub extern "C" fn #ident_exit() { + // SAFETY: + // - This function is inaccessible to the outside due to the double + // module wrapping it. It is called exactly once by the C side via its + // unique name, + // - furthermore it is only called after `#ident_init` has + // returned `0` (which delegates to `__init`). + unsafe { __exit() } + } + + /// # Safety + /// + /// This function must only be called once. + unsafe fn __init() -> ::kernel::ffi::c_int { + let initer = + <#type_ as ::kernel::InPlaceModule>::init(&super::super::THIS_MODULE); + // SAFETY: No data race, since `__MOD` can only be accessed by this module + // and there only `__init` and `__exit` access it. These functions are only + // called once and `__exit` cannot be called before or during `__init`. + match unsafe { initer.__pinned_init(__MOD.as_mut_ptr()) } { + Ok(m) => 0, + Err(e) => e.to_errno(), + } + } + + /// # Safety + /// + /// This function must + /// - only be called once, + /// - be called after `__init` has been called and returned `0`. + unsafe fn __exit() { + // SAFETY: No data race, since `__MOD` can only be accessed by this module + // and there only `__init` and `__exit` access it. These functions are only + // called once and `__init` was already called. + unsafe { + // Invokes `drop()` on `__MOD`, which should be used for cleanup. + __MOD.assume_init_drop(); + } + } + + #modinfo_ts + } + } + + mod module_parameters { + #params_ts + } + }) } From 8db9164b7694612f6b72c56e865b60c0e67d944d Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:17 +0000 Subject: [PATCH 44/59] rust: macros: convert `#[export]` to use `syn` This eliminates the custom `function_name` helper. Reviewed-by: Tamir Duberstein Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260112170919.1888584-7-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/export.rs | 24 +++++++++--------------- rust/macros/helpers.rs | 18 ------------------ rust/macros/lib.rs | 5 +++-- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/rust/macros/export.rs b/rust/macros/export.rs index 92d9b3097192..6d53521f62fc 100644 --- a/rust/macros/export.rs +++ b/rust/macros/export.rs @@ -3,19 +3,14 @@ use proc_macro2::TokenStream; use quote::quote; -use crate::helpers::function_name; - /// Please see [`crate::export`] for documentation. -pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream { - let Some(name) = function_name(ts.clone()) else { - return "::core::compile_error!(\"The #[export] attribute must be used on a function.\");" - .parse::() - .unwrap(); - }; +pub(crate) fn export(f: syn::ItemFn) -> TokenStream { + let name = &f.sig.ident; - // This verifies that the function has the same signature as the declaration generated by - // bindgen. It makes use of the fact that all branches of an if/else must have the same type. - let signature_check = quote!( + quote! { + // This verifies that the function has the same signature as the declaration generated by + // bindgen. It makes use of the fact that all branches of an if/else must have the same + // type. const _: () = { if true { ::kernel::bindings::#name @@ -23,9 +18,8 @@ pub(crate) fn export(_attr: TokenStream, ts: TokenStream) -> TokenStream { #name }; }; - ); - let no_mangle = quote!(#[no_mangle]); - - TokenStream::from_iter([signature_check, no_mangle, ts]) + #[no_mangle] + #f + } } diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index fa66ef6eb0f3..754c827cc21e 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -2,7 +2,6 @@ use proc_macro2::{ token_stream, - Ident, TokenStream, TokenTree, // }; @@ -50,23 +49,6 @@ impl AsciiLitStr { } } -/// Given a function declaration, finds the name of the function. -pub(crate) fn function_name(input: TokenStream) -> Option { - let mut input = input.into_iter(); - while let Some(token) = input.next() { - match token { - TokenTree::Ident(i) if i == "fn" => { - if let Some(TokenTree::Ident(i)) = input.next() { - return Some(i); - } - return None; - } - _ => continue, - } - } - None -} - pub(crate) fn file() -> String { #[cfg(not(CONFIG_RUSTC_HAS_SPAN_FILE))] { diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 9ce250cbf7ea..027ddaa2d710 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -234,8 +234,9 @@ pub fn vtable(attr: TokenStream, input: TokenStream) -> TokenStream { /// This macro is *not* the same as the C macros `EXPORT_SYMBOL_*`. All Rust symbols are currently /// automatically exported with `EXPORT_SYMBOL_GPL`. #[proc_macro_attribute] -pub fn export(attr: TokenStream, ts: TokenStream) -> TokenStream { - export::export(attr.into(), ts.into()).into() +pub fn export(attr: TokenStream, input: TokenStream) -> TokenStream { + parse_macro_input!(attr as syn::parse::Nothing); + export::export(parse_macro_input!(input)).into() } /// Like [`core::format_args!`], but automatically wraps arguments in [`kernel::fmt::Adapter`]. From a863b21b1e665d8fed0022c0175ffbdc5d94c06c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:18 +0000 Subject: [PATCH 45/59] rust: macros: convert `concat_idents!` to use `syn` This eliminates the need for `expect_punct` helper. Reviewed-by: Tamir Duberstein Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260112170919.1888584-8-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/concat_idents.rs | 39 ++++++++++++++++++++++++------------ rust/macros/helpers.rs | 14 +------------ rust/macros/lib.rs | 4 ++-- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs index 12cb231c3d71..47b6add378d2 100644 --- a/rust/macros/concat_idents.rs +++ b/rust/macros/concat_idents.rs @@ -1,23 +1,36 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro2::{token_stream, Ident, TokenStream, TokenTree}; +use proc_macro2::{ + Ident, + TokenStream, + TokenTree, // +}; +use syn::{ + parse::{ + Parse, + ParseStream, // + }, + Result, + Token, // +}; -use crate::helpers::expect_punct; +pub(crate) struct Input { + a: Ident, + _comma: Token![,], + b: Ident, +} -fn expect_ident(it: &mut token_stream::IntoIter) -> Ident { - if let Some(TokenTree::Ident(ident)) = it.next() { - ident - } else { - panic!("Expected Ident") +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + a: input.parse()?, + _comma: input.parse()?, + b: input.parse()?, + }) } } -pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream { - let mut it = ts.into_iter(); - let a = expect_ident(&mut it); - assert_eq!(expect_punct(&mut it), ','); - let b = expect_ident(&mut it); - assert!(it.next().is_none(), "only two idents can be concatenated"); +pub(crate) fn concat_idents(Input { a, b, .. }: Input) -> TokenStream { let res = Ident::new(&format!("{a}{b}"), b.span()); TokenStream::from_iter([TokenTree::Ident(res)]) } diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index 754c827cc21e..adfa60d8f42d 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -1,10 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use proc_macro2::{ - token_stream, - TokenStream, - TokenTree, // -}; +use proc_macro2::TokenStream; use quote::ToTokens; use syn::{ parse::{ @@ -16,14 +12,6 @@ use syn::{ Result, // }; -pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char { - if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") { - punct.as_char() - } else { - panic!("Expected Punct"); - } -} - /// A string literal that is required to have ASCII value only. pub(crate) struct AsciiLitStr(LitStr); diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 027ddaa2d710..b45e974f992e 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -311,8 +311,8 @@ pub fn fmt(input: TokenStream) -> TokenStream { /// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK); /// ``` #[proc_macro] -pub fn concat_idents(ts: TokenStream) -> TokenStream { - concat_idents::concat_idents(ts.into()).into() +pub fn concat_idents(input: TokenStream) -> TokenStream { + concat_idents::concat_idents(parse_macro_input!(input)).into() } /// Paste identifiers together. From c3b416e19eb38a6b8d9a30bc7df1361ac3244464 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:19 +0000 Subject: [PATCH 46/59] rust: macros: convert `#[kunit_tests]` macro to use `syn` Make use of `syn` to parse the module structurally and thus improve the robustness of parsing. String interpolation is avoided by generating tokens directly using `quote!`. Reviewed-by: Tamir Duberstein Signed-off-by: Gary Guo Reviewed-by: Benno Lossin Reviewed-by: David Gow Link: https://patch.msgid.link/20260112170919.1888584-9-gary@kernel.org [ Pass C string to match commit 6c37b6841a92 ("rust: kunit: replace `kernel::c_str!` with C-Strings"). - Miguel ] Signed-off-by: Miguel Ojeda --- rust/macros/kunit.rs | 268 +++++++++++++++++++------------------------ rust/macros/lib.rs | 6 +- 2 files changed, 120 insertions(+), 154 deletions(-) diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs index 79fdad13b04a..7539d93201bc 100644 --- a/rust/macros/kunit.rs +++ b/rust/macros/kunit.rs @@ -4,81 +4,50 @@ //! //! Copyright (c) 2023 José Expósito -use std::collections::HashMap; -use std::fmt::Write; +use std::ffi::CString; -use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; +use proc_macro2::TokenStream; +use quote::{ + format_ident, + quote, + ToTokens, // +}; +use syn::{ + parse_quote, + Error, + Ident, + Item, + ItemMod, + LitCStr, + Result, // +}; -pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { - let attr = attr.to_string(); - - if attr.is_empty() { - panic!("Missing test name in `#[kunit_tests(test_name)]` macro") +pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result { + if test_suite.to_string().len() > 255 { + return Err(Error::new_spanned( + test_suite, + "test suite names cannot exceed the maximum length of 255 bytes", + )); } - if attr.len() > 255 { - panic!("The test suite name `{attr}` exceeds the maximum length of 255 bytes") - } - - let mut tokens: Vec<_> = ts.into_iter().collect(); - - // Scan for the `mod` keyword. - tokens - .iter() - .find_map(|token| match token { - TokenTree::Ident(ident) => match ident.to_string().as_str() { - "mod" => Some(true), - _ => None, - }, - _ => None, - }) - .expect("`#[kunit_tests(test_name)]` attribute should only be applied to modules"); - - // Retrieve the main body. The main body should be the last token tree. - let body = match tokens.pop() { - Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group, - _ => panic!("Cannot locate main body of module"), + // We cannot handle modules that defer to another file (e.g. `mod foo;`). + let Some((module_brace, module_items)) = module.content.take() else { + Err(Error::new_spanned( + module, + "`#[kunit_tests(test_name)]` attribute should only be applied to inline modules", + ))? }; - // Get the functions set as tests. Search for `[test]` -> `fn`. - let mut body_it = body.stream().into_iter(); - let mut tests = Vec::new(); - let mut attributes: HashMap = HashMap::new(); - while let Some(token) = body_it.next() { - match token { - TokenTree::Punct(ref p) if p.as_char() == '#' => match body_it.next() { - Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => { - if let Some(TokenTree::Ident(name)) = g.stream().into_iter().next() { - // Collect attributes because we need to find which are tests. We also - // need to copy `cfg` attributes so tests can be conditionally enabled. - attributes - .entry(name.to_string()) - .or_default() - .extend([token, TokenTree::Group(g)]); - } - continue; - } - _ => (), - }, - TokenTree::Ident(i) if i == "fn" && attributes.contains_key("test") => { - if let Some(TokenTree::Ident(test_name)) = body_it.next() { - tests.push((test_name, attributes.remove("cfg").unwrap_or_default())) - } - } + // Make the entire module gated behind `CONFIG_KUNIT`. + module + .attrs + .insert(0, parse_quote!(#[cfg(CONFIG_KUNIT="y")])); - _ => (), - } - attributes.clear(); - } - - // Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration. - let config_kunit = "#[cfg(CONFIG_KUNIT=\"y\")]".to_owned().parse().unwrap(); - tokens.insert( - 0, - TokenTree::Group(Group::new(Delimiter::None, config_kunit)), - ); + let mut processed_items = Vec::new(); + let mut test_cases = Vec::new(); // Generate the test KUnit test suite and a test case for each `#[test]`. + // // The code generated for the following test module: // // ``` @@ -110,98 +79,93 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { // // ::kernel::kunit_unsafe_test_suite!(kunit_test_suit_name, TEST_CASES); // ``` - let mut kunit_macros = "".to_owned(); - let mut test_cases = "".to_owned(); - let mut assert_macros = "".to_owned(); - let path = crate::helpers::file(); - let num_tests = tests.len(); - for (test, cfg_attr) in tests { - let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}"); - // Append any `cfg` attributes the user might have written on their tests so we don't - // attempt to call them when they are `cfg`'d out. An extra `use` is used here to reduce - // the length of the assert message. - let kunit_wrapper = format!( - r#"unsafe extern "C" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit) - {{ + // + // Non-function items (e.g. imports) are preserved. + for item in module_items { + let Item::Fn(mut f) = item else { + processed_items.push(item); + continue; + }; + + // TODO: Replace below with `extract_if` when MSRV is bumped above 1.85. + let before_len = f.attrs.len(); + f.attrs.retain(|attr| !attr.path().is_ident("test")); + if f.attrs.len() == before_len { + processed_items.push(Item::Fn(f)); + continue; + } + + let test = f.sig.ident.clone(); + + // Retrieve `#[cfg]` applied on the function which needs to be present on derived items too. + let cfg_attrs: Vec<_> = f + .attrs + .iter() + .filter(|attr| attr.path().is_ident("cfg")) + .cloned() + .collect(); + + // Before the test, override usual `assert!` and `assert_eq!` macros with ones that call + // KUnit instead. + let test_str = test.to_string(); + let path = CString::new(crate::helpers::file()).expect("file path cannot contain NUL"); + processed_items.push(parse_quote! { + #[allow(unused)] + macro_rules! assert { + ($cond:expr $(,)?) => {{ + kernel::kunit_assert!(#test_str, #path, 0, $cond); + }} + } + }); + processed_items.push(parse_quote! { + #[allow(unused)] + macro_rules! assert_eq { + ($left:expr, $right:expr $(,)?) => {{ + kernel::kunit_assert_eq!(#test_str, #path, 0, $left, $right); + }} + } + }); + + // Add back the test item. + processed_items.push(Item::Fn(f)); + + let kunit_wrapper_fn_name = format_ident!("kunit_rust_wrapper_{test}"); + let test_cstr = LitCStr::new( + &CString::new(test_str.as_str()).expect("identifier cannot contain NUL"), + test.span(), + ); + processed_items.push(parse_quote! { + unsafe extern "C" fn #kunit_wrapper_fn_name(_test: *mut ::kernel::bindings::kunit) { (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED; - {cfg_attr} {{ + + // Append any `cfg` attributes the user might have written on their tests so we + // don't attempt to call them when they are `cfg`'d out. An extra `use` is used + // here to reduce the length of the assert message. + #(#cfg_attrs)* + { (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS; use ::kernel::kunit::is_test_result_ok; - assert!(is_test_result_ok({test}())); - }} - }}"#, - ); - writeln!(kunit_macros, "{kunit_wrapper}").unwrap(); - writeln!( - test_cases, - " ::kernel::kunit::kunit_case(c\"{test}\", {kunit_wrapper_fn_name})," - ) - .unwrap(); - writeln!( - assert_macros, - r#" -/// Overrides the usual [`assert!`] macro with one that calls KUnit instead. -#[allow(unused)] -macro_rules! assert {{ - ($cond:expr $(,)?) => {{{{ - kernel::kunit_assert!("{test}", c"{path}", 0, $cond); - }}}} -}} - -/// Overrides the usual [`assert_eq!`] macro with one that calls KUnit instead. -#[allow(unused)] -macro_rules! assert_eq {{ - ($left:expr, $right:expr $(,)?) => {{{{ - kernel::kunit_assert_eq!("{test}", c"{path}", 0, $left, $right); - }}}} -}} - "# - ) - .unwrap(); - } - - writeln!(kunit_macros).unwrap(); - writeln!( - kunit_macros, - "static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];", - num_tests + 1 - ) - .unwrap(); - - writeln!( - kunit_macros, - "::kernel::kunit_unsafe_test_suite!({attr}, TEST_CASES);" - ) - .unwrap(); - - // Remove the `#[test]` macros. - // We do this at a token level, in order to preserve span information. - let mut new_body = vec![]; - let mut body_it = body.stream().into_iter(); - - while let Some(token) = body_it.next() { - match token { - TokenTree::Punct(ref c) if c.as_char() == '#' => match body_it.next() { - Some(TokenTree::Group(group)) if group.to_string() == "[test]" => (), - Some(next) => { - new_body.extend([token, next]); + assert!(is_test_result_ok(#test())); } - _ => { - new_body.push(token); - } - }, - _ => { - new_body.push(token); } - } + }); + + test_cases.push(quote!( + ::kernel::kunit::kunit_case(#test_cstr, #kunit_wrapper_fn_name) + )); } - let mut final_body = TokenStream::new(); - final_body.extend::(assert_macros.parse().unwrap()); - final_body.extend(new_body); - final_body.extend::(kunit_macros.parse().unwrap()); + let num_tests_plus_1 = test_cases.len() + 1; + processed_items.push(parse_quote! { + static mut TEST_CASES: [::kernel::bindings::kunit_case; #num_tests_plus_1] = [ + #(#test_cases,)* + ::kernel::kunit::kunit_case_null(), + ]; + }); + processed_items.push(parse_quote! { + ::kernel::kunit_unsafe_test_suite!(#test_suite, TEST_CASES); + }); - tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, final_body))); - - tokens.into_iter().collect() + module.content = Some((module_brace, processed_items)); + Ok(module.to_token_stream()) } diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index b45e974f992e..85b7938c08e5 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -481,6 +481,8 @@ pub fn paste(input: TokenStream) -> TokenStream { /// } /// ``` #[proc_macro_attribute] -pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { - kunit::kunit_tests(attr.into(), ts.into()).into() +pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream { + kunit::kunit_tests(parse_macro_input!(attr), parse_macro_input!(input)) + .unwrap_or_else(|e| e.into_compile_error()) + .into() } From 3d5731a6be6a43d8c90d766e1404502c44545241 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:20 +0000 Subject: [PATCH 47/59] rust: macros: allow arbitrary types to be used in `module!` macro Previously this only accepts an identifier, but now with `syn` it is easy to make it accepts any type. Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260112170919.1888584-10-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/module.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 43ada49525c9..9e0242d42d51 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -26,7 +26,8 @@ use syn::{ LitStr, Path, Result, - Token, // + Token, + Type, // }; use crate::helpers::*; @@ -370,7 +371,7 @@ impl Parse for Parameter { } pub(crate) struct ModuleInfo { - type_: Ident, + type_: Type, license: AsciiLitStr, name: AsciiLitStr, authors: Option>, @@ -529,7 +530,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { // Double nested modules, since then nobody can access the public items inside. mod __module_init { mod __module_init { - use super::super::#type_; use pin_init::PinInit; /// The "Rust loadable module" mark. @@ -541,7 +541,7 @@ pub(crate) fn module(info: ModuleInfo) -> Result { #[used(compiler)] static __IS_RUST_MODULE: () = (); - static mut __MOD: ::core::mem::MaybeUninit<#type_> = + static mut __MOD: ::core::mem::MaybeUninit = ::core::mem::MaybeUninit::uninit(); // Loadable modules need to export the `{init,cleanup}_module` identifiers. @@ -628,8 +628,9 @@ pub(crate) fn module(info: ModuleInfo) -> Result { /// /// This function must only be called once. unsafe fn __init() -> ::kernel::ffi::c_int { - let initer = - <#type_ as ::kernel::InPlaceModule>::init(&super::super::THIS_MODULE); + let initer = ::init( + &super::super::THIS_MODULE + ); // SAFETY: No data race, since `__MOD` can only be accessed by this module // and there only `__init` and `__exit` access it. These functions are only // called once and `__exit` cannot be called before or during `__init`. From d421fa4f73f59c512b30338a3453b62ed8fd9122 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:21 +0000 Subject: [PATCH 48/59] rust: macros: rearrange `#[doc(hidden)]` in `module!` macro This `#[doc(hidden)]` can just be applied on a module to hide anything within. Reviewed-by: Tamir Duberstein Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260112170919.1888584-11-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/module.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 9e0242d42d51..e16298e520c7 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -77,7 +77,6 @@ impl<'a> ModInfoBuilder<'a> { ); let item = quote! { #cfg - #[doc(hidden)] #[cfg_attr(not(target_os = "macos"), link_section = ".modinfo")] #[used(compiler)] pub static #counter: [u8; #length] = *#string; @@ -528,6 +527,7 @@ pub(crate) fn module(info: ModuleInfo) -> Result { } // Double nested modules, since then nobody can access the public items inside. + #[doc(hidden)] mod __module_init { mod __module_init { use pin_init::PinInit; @@ -537,7 +537,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { // This may be best done another way later on, e.g. as a new modinfo // key or a new section. For the moment, keep it simple. #[cfg(MODULE)] - #[doc(hidden)] #[used(compiler)] static __IS_RUST_MODULE: () = (); @@ -550,7 +549,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { /// This function must not be called after module initialization, because it may be /// freed after that completes. #[cfg(MODULE)] - #[doc(hidden)] #[no_mangle] #[link_section = ".init.text"] pub unsafe extern "C" fn init_module() -> ::kernel::ffi::c_int { @@ -561,14 +559,12 @@ pub(crate) fn module(info: ModuleInfo) -> Result { } #[cfg(MODULE)] - #[doc(hidden)] #[used(compiler)] #[link_section = ".init.data"] static __UNIQUE_ID___addressable_init_module: unsafe extern "C" fn() -> i32 = init_module; #[cfg(MODULE)] - #[doc(hidden)] #[no_mangle] #[link_section = ".exit.text"] pub extern "C" fn cleanup_module() { @@ -582,7 +578,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { } #[cfg(MODULE)] - #[doc(hidden)] #[used(compiler)] #[link_section = ".exit.data"] static __UNIQUE_ID___addressable_cleanup_module: extern "C" fn() = cleanup_module; @@ -591,7 +586,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { // and the identifiers need to be unique. #[cfg(not(MODULE))] #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] - #[doc(hidden)] #[link_section = #initcall_section] #[used(compiler)] pub static #ident_initcall: extern "C" fn() -> @@ -602,7 +596,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { ::core::arch::global_asm!(#global_asm); #[cfg(not(MODULE))] - #[doc(hidden)] #[no_mangle] pub extern "C" fn #ident_init() -> ::kernel::ffi::c_int { // SAFETY: This function is inaccessible to the outside due to the double @@ -612,7 +605,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { } #[cfg(not(MODULE))] - #[doc(hidden)] #[no_mangle] pub extern "C" fn #ident_exit() { // SAFETY: From be97f3c82021239476ce32cddde32948c597753e Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 12 Jan 2026 17:07:22 +0000 Subject: [PATCH 49/59] rust: kunit: use `pin_init::zeroed` instead of custom null value The last null element can be created (constly) using `pin_init::zeroed`, so prefer to use it instead of adding a custom way of building it. Reviewed-by: Tamir Duberstein Reviewed-by: Benno Lossin Signed-off-by: Gary Guo Reviewed-by: David Gow Link: https://patch.msgid.link/20260112170919.1888584-12-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/kunit.rs | 26 +------------------------- rust/macros/kunit.rs | 4 ++-- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 4ccc8fc4a800..f93f24a60bdd 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -189,9 +189,6 @@ pub fn is_test_result_ok(t: impl TestResult) -> bool { } /// Represents an individual test case. -/// -/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of valid test cases. -/// Use [`kunit_case_null`] to generate such a delimiter. #[doc(hidden)] pub const fn kunit_case( name: &'static kernel::str::CStr, @@ -212,27 +209,6 @@ pub const fn kunit_case( } } -/// Represents the `NULL` test case delimiter. -/// -/// The [`kunit_unsafe_test_suite!`] macro expects a `NULL`-terminated list of test cases. This -/// function returns such a delimiter. -#[doc(hidden)] -pub const fn kunit_case_null() -> kernel::bindings::kunit_case { - kernel::bindings::kunit_case { - run_case: None, - name: core::ptr::null_mut(), - generate_params: None, - attr: kernel::bindings::kunit_attributes { - speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, - }, - status: kernel::bindings::kunit_status_KUNIT_SUCCESS, - module_name: core::ptr::null_mut(), - log: core::ptr::null_mut(), - param_init: None, - param_exit: None, - } -} - /// Registers a KUnit test suite. /// /// # Safety @@ -251,7 +227,7 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { /// /// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [ /// kernel::kunit::kunit_case(c"name", test_fn), -/// kernel::kunit::kunit_case_null(), +/// pin_init::zeroed(), /// ]; /// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES); /// ``` diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs index 7539d93201bc..6be880d634e2 100644 --- a/rust/macros/kunit.rs +++ b/rust/macros/kunit.rs @@ -74,7 +74,7 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result Result Date: Tue, 13 Jan 2026 17:05:21 +0000 Subject: [PATCH 50/59] rust: macros: support `#[cfg]` properly in `#[vtable]` macro. Currently, we generate `HAS_` constants as long as the definition exists in the source, regardless if it is cfg-ed out or not. Currently, uses of `#[cfg]` present in both trait and impl, so it is not a problem; however if only the impl side uses `#[cfg]` then `HAS_` constants will incorrectly be true while it shouldnt't. With `syn` support, we can now implement `#[cfg]` handling properly by propagating the `#[cfg]` attributes to the constants. Signed-off-by: Gary Guo Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260113170529.2240744-1-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/macros/helpers.rs | 6 ++++++ rust/macros/vtable.rs | 13 ++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index adfa60d8f42d..37ef6a6f2c85 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -7,6 +7,7 @@ use syn::{ Parse, ParseStream, // }, + Attribute, Error, LitStr, Result, // @@ -53,3 +54,8 @@ pub(crate) fn file() -> String { proc_macro::Span::call_site().file() } } + +/// Obtain all `#[cfg]` attributes. +pub(crate) fn gather_cfg_attrs(attr: &[Attribute]) -> impl Iterator + '_ { + attr.iter().filter(|a| a.path().is_ident("cfg")) +} diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs index 72ae0a1816a0..c6510b0c4ea1 100644 --- a/rust/macros/vtable.rs +++ b/rust/macros/vtable.rs @@ -23,7 +23,6 @@ use syn::{ fn handle_trait(mut item: ItemTrait) -> Result { let mut gen_items = Vec::new(); - let mut gen_consts = HashSet::new(); gen_items.push(parse_quote! { /// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable) @@ -38,22 +37,17 @@ fn handle_trait(mut item: ItemTrait) -> Result { &format!("HAS_{}", name.to_string().to_uppercase()), name.span(), ); - // Skip if it's declared already -- this can happen if `#[cfg]` is used to selectively - // define functions. - // FIXME: `#[cfg]` should be copied and propagated to the generated consts. - if gen_consts.contains(&gen_const_name) { - continue; - } // We don't know on the implementation-site whether a method is required or provided // so we have to generate a const for all methods. + let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs); let comment = format!("Indicates if the `{name}` method is overridden by the implementor."); gen_items.push(parse_quote! { + #(#cfg_attrs)* #[doc = #comment] const #gen_const_name: bool = false; }); - gen_consts.insert(gen_const_name); } } @@ -87,10 +81,11 @@ fn handle_impl(mut item: ItemImpl) -> Result { if defined_consts.contains(&gen_const_name) { continue; } + let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs); gen_items.push(parse_quote! { + #(#cfg_attrs)* const #gen_const_name: bool = true; }); - defined_consts.insert(gen_const_name); } } From 1e37f6f949b521935781b761414fe4ca0e9128c1 Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Wed, 28 Jan 2026 16:45:18 -0800 Subject: [PATCH 51/59] MAINTAINERS: Update my email address to @kernel.org I had issues using my gmail account multiple times this week, hence switch to @kernel.org. Signed-off-by: Boqun Feng Reviewed-by: Paul E. McKenney Acked-by: Gary Guo Acked-by: Kunwu Chan Link: https://patch.msgid.link/20260129004520.62697-1-boqun@kernel.org [ Sorted alphabetically in mailmap. Removed Cc tag list. - Miguel ] Signed-off-by: Miguel Ojeda --- .mailmap | 1 + MAINTAINERS | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.mailmap b/.mailmap index e8a03e193854..3e325d56b9f4 100644 --- a/.mailmap +++ b/.mailmap @@ -150,6 +150,7 @@ Bjorn Andersson Björn Steinbrink Björn Töpel Björn Töpel +Boqun Feng Boris Brezillon Boris Brezillon Boris Brezillon diff --git a/MAINTAINERS b/MAINTAINERS index f6bc65de83c7..2c0bdd08b74c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4109,7 +4109,7 @@ F: drivers/input/touchscreen/atmel_mxt_ts.c ATOMIC INFRASTRUCTURE M: Will Deacon M: Peter Zijlstra -M: Boqun Feng +M: Boqun Feng R: Mark Rutland R: Gary Guo L: linux-kernel@vger.kernel.org @@ -4500,7 +4500,7 @@ F: lib/sbitmap.c BLOCK LAYER DEVICE DRIVER API [RUST] M: Andreas Hindborg -R: Boqun Feng +R: Boqun Feng L: linux-block@vger.kernel.org L: rust-for-linux@vger.kernel.org S: Supported @@ -11264,7 +11264,7 @@ F: tools/testing/selftests/timers/ DELAY, SLEEP, TIMEKEEPING, TIMERS [RUST] M: Andreas Hindborg -R: Boqun Feng +R: Boqun Feng R: FUJITA Tomonori R: Frederic Weisbecker R: Lyude Paul @@ -14559,7 +14559,7 @@ M: Alan Stern M: Andrea Parri M: Will Deacon M: Peter Zijlstra -M: Boqun Feng +M: Boqun Feng M: Nicholas Piggin M: David Howells M: Jade Alglave @@ -14718,7 +14718,7 @@ LOCKING PRIMITIVES M: Peter Zijlstra M: Ingo Molnar M: Will Deacon -M: Boqun Feng (LOCKDEP & RUST) +M: Boqun Feng (LOCKDEP & RUST) R: Waiman Long L: linux-kernel@vger.kernel.org S: Maintained @@ -21912,7 +21912,7 @@ M: Frederic Weisbecker (kernel/rcu/tree_nocb.h) M: Neeraj Upadhyay (kernel/rcu/tasks.h) M: Joel Fernandes M: Josh Triplett -M: Boqun Feng +M: Boqun Feng M: Uladzislau Rezki R: Steven Rostedt R: Mathieu Desnoyers @@ -22362,7 +22362,7 @@ RESTARTABLE SEQUENCES SUPPORT M: Mathieu Desnoyers M: Peter Zijlstra M: "Paul E. McKenney" -M: Boqun Feng +M: Boqun Feng L: linux-kernel@vger.kernel.org S: Supported F: include/trace/events/rseq.h @@ -22885,7 +22885,7 @@ F: tools/verification/ RUST M: Miguel Ojeda -R: Boqun Feng +R: Boqun Feng R: Gary Guo R: Björn Roy Baron R: Benno Lossin From 844590b439870c921fa0e11d412c300564391a18 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:16:59 +0100 Subject: [PATCH 52/59] rust: clk: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Acked-by: Stephen Boyd Acked-by: Viresh Kumar Signed-off-by: Tamir Duberstein Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20251222-cstr-clk-v1-1-ef0687717aa1@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/clk.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs index db3464e2df32..4059aff34d09 100644 --- a/rust/kernel/clk.rs +++ b/rust/kernel/clk.rs @@ -104,13 +104,12 @@ mod common_clk { /// The following example demonstrates how to obtain and configure a clock for a device. /// /// ``` - /// use kernel::c_str; /// use kernel::clk::{Clk, Hertz}; /// use kernel::device::Device; /// use kernel::error::Result; /// /// fn configure_clk(dev: &Device) -> Result { - /// let clk = Clk::get(dev, Some(c_str!("apb_clk")))?; + /// let clk = Clk::get(dev, Some(c"apb_clk"))?; /// /// clk.prepare_enable()?; /// @@ -272,13 +271,12 @@ mod common_clk { /// device. The code functions correctly whether or not the clock is available. /// /// ``` - /// use kernel::c_str; /// use kernel::clk::{OptionalClk, Hertz}; /// use kernel::device::Device; /// use kernel::error::Result; /// /// fn configure_clk(dev: &Device) -> Result { - /// let clk = OptionalClk::get(dev, Some(c_str!("apb_clk")))?; + /// let clk = OptionalClk::get(dev, Some(c"apb_clk"))?; /// /// clk.prepare_enable()?; /// From 9b89cea1119386480151a6ca2bfbab54ce569ba7 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 23 Jan 2026 17:19:40 +0000 Subject: [PATCH 53/59] gpu: nova-core: remove imports available via prelude These imports are already in scope by importing `kernel::prelude::*` and does not need to be imported separately. Signed-off-by: Gary Guo Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260123172007.136873-2-gary@garyguo.net Signed-off-by: Miguel Ojeda --- drivers/gpu/nova-core/firmware/fwsec.rs | 1 - drivers/gpu/nova-core/firmware/gsp.rs | 12 +++++------- drivers/gpu/nova-core/firmware/riscv.rs | 2 -- drivers/gpu/nova-core/gsp/sequencer.rs | 8 +------- drivers/gpu/nova-core/sbuffer.rs | 5 +---- 5 files changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs index b28e34d279f4..dd2d1f0f24ed 100644 --- a/drivers/gpu/nova-core/firmware/fwsec.rs +++ b/drivers/gpu/nova-core/firmware/fwsec.rs @@ -12,7 +12,6 @@ use core::{ marker::PhantomData, - mem::size_of, ops::Deref, // }; diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 0549805282ab..dc09e59cf4f5 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -use core::mem::size_of_val; - use kernel::{ device, dma::{ @@ -34,11 +32,11 @@ use crate::{ /// that scheme before nova-core becomes stable, which means this module will eventually be /// removed. mod elf { - use core::mem::size_of; - - use kernel::bindings; - use kernel::str::CStr; - use kernel::transmute::FromBytes; + use kernel::{ + bindings, + prelude::*, + transmute::FromBytes, // + }; /// Newtype to provide a [`FromBytes`] implementation. #[repr(transparent)] diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs index 28dfef63657a..4bdd89bd0757 100644 --- a/drivers/gpu/nova-core/firmware/riscv.rs +++ b/drivers/gpu/nova-core/firmware/riscv.rs @@ -3,8 +3,6 @@ //! Support for firmware binaries designed to run on a RISC-V core. Such firmwares files have a //! dedicated header. -use core::mem::size_of; - use kernel::{ device, firmware::Firmware, diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index 2d0369c49092..870f2da3327b 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -2,13 +2,7 @@ //! GSP Sequencer implementation for Pre-hopper GSP boot sequence. -use core::{ - array, - mem::{ - size_of, - size_of_val, // - }, -}; +use core::array; use kernel::{ device, diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs index 64758b7fae56..3a41d224c77a 100644 --- a/drivers/gpu/nova-core/sbuffer.rs +++ b/drivers/gpu/nova-core/sbuffer.rs @@ -2,10 +2,7 @@ use core::ops::Deref; -use kernel::{ - alloc::KVec, - prelude::*, // -}; +use kernel::prelude::*; /// A buffer abstraction for discontiguous byte slices. /// From abbe94591e8864af3df5ba36b61fe8615b8ff990 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 27 Jan 2026 08:55:50 -0500 Subject: [PATCH 54/59] rust: kbuild: deduplicate pin-init flags Extract `pin_init{,_internal}-{cfgs,flags}` to reduce duplication. [ The new variables will be used later on to easily pass them to the `scripts/generate_rust_analyzer.py` script. - Miguel ] Acked-by: Benno Lossin Signed-off-by: Tamir Duberstein Link: https://patch.msgid.link/20260127-rust-analyzer-pin-init-duplication-v3-1-118c48c35e88@kernel.org [ Rebased. Moved new variables above. Reworded title. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/Makefile | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index 82bda6ccfe39..5c0155b83454 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -117,6 +117,23 @@ syn-flags := \ --extern quote \ $(call cfgs-to-flags,$(syn-cfgs)) +pin_init_internal-cfgs := \ + kernel + +pin_init_internal-flags := \ + --extern proc_macro2 \ + --extern quote \ + --extern syn \ + $(call cfgs-to-flags,$(pin_init_internal-cfgs)) + +pin_init-cfgs := \ + kernel + +pin_init-flags := \ + --extern pin_init_internal \ + --extern macros \ + $(call cfgs-to-flags,$(pin_init-cfgs)) + # `rustdoc` did not save the target modifiers, thus workaround for # the time being (https://github.com/rust-lang/rust/issues/144521). rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18) @@ -211,16 +228,15 @@ rustdoc-ffi: $(src)/ffi.rs rustdoc-core FORCE +$(call if_changed,rustdoc) rustdoc-pin_init_internal: private rustdoc_host = yes -rustdoc-pin_init_internal: private rustc_target_flags = --cfg kernel \ - --extern proc_macro --extern proc_macro2 --extern quote --extern syn \ - --crate-type proc-macro +rustdoc-pin_init_internal: private rustc_target_flags = $(pin_init_internal-flags) \ + --extern proc_macro --crate-type proc-macro rustdoc-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \ rustdoc-clean rustdoc-proc_macro2 rustdoc-quote rustdoc-syn FORCE +$(call if_changed,rustdoc) rustdoc-pin_init: private rustdoc_host = yes -rustdoc-pin_init: private rustc_target_flags = --extern pin_init_internal \ - --extern macros --extern alloc --cfg kernel --cfg feature=\"alloc\" +rustdoc-pin_init: private rustc_target_flags = $(pin_init-flags) \ + --extern alloc --cfg feature=\"alloc\" rustdoc-pin_init: $(src)/pin-init/src/lib.rs rustdoc-pin_init_internal \ rustdoc-macros FORCE +$(call if_changed,rustdoc) @@ -273,15 +289,14 @@ rusttestlib-macros: $(src)/macros/lib.rs \ rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE +$(call if_changed,rustc_test_library) -rusttestlib-pin_init_internal: private rustc_target_flags = --cfg kernel \ - --extern proc_macro --extern proc_macro2 --extern quote --extern syn +rusttestlib-pin_init_internal: private rustc_target_flags = $(pin_init_internal-flags) \ + --extern proc_macro rusttestlib-pin_init_internal: private rustc_test_library_proc = yes rusttestlib-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \ rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE +$(call if_changed,rustc_test_library) -rusttestlib-pin_init: private rustc_target_flags = --extern pin_init_internal \ - --extern macros --cfg kernel +rusttestlib-pin_init: private rustc_target_flags = $(pin_init-flags) rusttestlib-pin_init: $(src)/pin-init/src/lib.rs rusttestlib-macros \ rusttestlib-pin_init_internal $(obj)/$(libpin_init_internal_name) FORCE +$(call if_changed,rustc_test_library) @@ -549,8 +564,7 @@ $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \ $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE +$(call if_changed_dep,rustc_procmacro) -$(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel \ - --extern proc_macro2 --extern quote --extern syn +$(obj)/$(libpin_init_internal_name): private rustc_target_flags = $(pin_init_internal-flags) $(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs \ $(obj)/libproc_macro2.rlib $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE +$(call if_changed_dep,rustc_procmacro) @@ -646,8 +660,7 @@ $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE +$(call if_changed_rule,rustc_library) $(obj)/pin_init.o: private skip_gendwarfksyms = 1 -$(obj)/pin_init.o: private rustc_target_flags = --extern pin_init_internal \ - --extern macros --cfg kernel +$(obj)/pin_init.o: private rustc_target_flags = $(pin_init-flags) $(obj)/pin_init.o: $(src)/pin-init/src/lib.rs $(obj)/compiler_builtins.o \ $(obj)/$(libpin_init_internal_name) $(obj)/$(libmacros_name) FORCE +$(call if_changed_rule,rustc_library) From e600425708148e952adce496ac9e9def79264c8a Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 17 Nov 2025 09:24:51 +0900 Subject: [PATCH 55/59] rust: print: Add support for calling a function exactly once Add the Rust equivalent of the kernel's `DO_ONCE_LITE` macro. While it would be possible to implement the feature entirely as a Rust macro, the functionality that can be implemented as regular functions has been extracted and implemented as the `OnceLite` struct for better code maintainability. Signed-off-by: FUJITA Tomonori Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Link: https://patch.msgid.link/20251117002452.4068692-2-fujita.tomonori@gmail.com [ Added prefix to title. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/print.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index 2d743d78d220..af32054be5a2 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -11,6 +11,11 @@ use crate::{ fmt, prelude::*, str::RawFormatter, + sync::atomic::{ + Atomic, + AtomicType, + Relaxed, // + }, }; // Called from `vsprintf` with format specifier `%pA`. @@ -423,3 +428,81 @@ macro_rules! pr_cont ( $crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*) ) ); + +/// A lightweight `call_once` primitive. +/// +/// This structure provides the Rust equivalent of the kernel's `DO_ONCE_LITE` macro. +/// While it would be possible to implement the feature entirely as a Rust macro, +/// the functionality that can be implemented as regular functions has been +/// extracted and implemented as the `OnceLite` struct for better code maintainability. +pub struct OnceLite(Atomic); + +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(i32)] +enum State { + Incomplete = 0, + Complete = 1, +} + +// SAFETY: `State` and `i32` has the same size and alignment, and it's round-trip +// transmutable to `i32`. +unsafe impl AtomicType for State { + type Repr = i32; +} + +impl OnceLite { + /// Creates a new [`OnceLite`] in the incomplete state. + #[inline(always)] + #[allow(clippy::new_without_default)] + pub const fn new() -> Self { + OnceLite(Atomic::new(State::Incomplete)) + } + + /// Calls the provided function exactly once. + /// + /// There is no other synchronization between two `call_once()`s + /// except that only one will execute `f`, in other words, callers + /// should not use a failed `call_once()` as a proof that another + /// `call_once()` has already finished and the effect is observable + /// to this thread. + pub fn call_once(&self, f: F) -> bool + where + F: FnOnce(), + { + // Avoid expensive cmpxchg if already completed. + // ORDERING: `Relaxed` is used here since no synchronization is required. + let old = self.0.load(Relaxed); + if old == State::Complete { + return false; + } + + // ORDERING: `Relaxed` is used here since no synchronization is required. + let old = self.0.xchg(State::Complete, Relaxed); + if old == State::Complete { + return false; + } + + f(); + true + } +} + +/// Run the given function exactly once. +/// +/// This is equivalent to the kernel's `DO_ONCE_LITE` macro. +/// +/// # Examples +/// +/// ``` +/// kernel::do_once_lite! { +/// kernel::pr_info!("This will be printed only once\n"); +/// }; +/// ``` +#[macro_export] +macro_rules! do_once_lite { + { $($e:tt)* } => {{ + #[link_section = ".data..once"] + static ONCE: $crate::print::OnceLite = $crate::print::OnceLite::new(); + ONCE.call_once(|| { $($e)* }); + }}; +} From 966f79ce6f6b3d138b69cacc3cdcbb5001141b4d Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 8 Jan 2026 10:33:50 +0900 Subject: [PATCH 56/59] rust: bug: Support DEBUG_BUGVERBOSE_DETAILED option Make warn_on() support DEBUG_BUGVERBOSE_DETAILED option, which was introduced by the commit aec58b48517c ("bugs/core: Extend __WARN_FLAGS() with the 'cond_str' parameter"). When the option is enabled, WARN splats now show the evaluated warn_on() condition alongside the file path, e.g.: ------------[ cut here ]------------ WARNING: [val == 1] linux/samples/rust/rust_minimal.rs:27 at _RNvXCsk7t4azzUqHP_12rust_minimalNtB2_11RustMinimalNtCs8pcx3n4 Modules linked in: rust_minimal(+) Signed-off-by: FUJITA Tomonori Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260108013350.2880613-1-fujita.tomonori@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/bug.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs index 36aef43e5ebe..ed943960f851 100644 --- a/rust/kernel/bug.rs +++ b/rust/kernel/bug.rs @@ -11,9 +11,9 @@ #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(CONFIG_DEBUG_BUGVERBOSE)] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; - const _FILE: &[u8] = file!().as_bytes(); + const _FILE: &[u8] = $file.as_bytes(); // Plus one for null-terminator. static FILE: [u8; _FILE.len() + 1] = { let mut bytes = [0; _FILE.len() + 1]; @@ -50,7 +50,7 @@ macro_rules! warn_flags { #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(not(CONFIG_DEBUG_BUGVERBOSE))] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; // SAFETY: @@ -75,7 +75,7 @@ macro_rules! warn_flags { #[doc(hidden)] #[cfg(all(CONFIG_BUG, CONFIG_UML))] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { // SAFETY: It is always safe to call `warn_slowpath_fmt()` // with a valid null-terminated string. unsafe { @@ -93,7 +93,7 @@ macro_rules! warn_flags { #[doc(hidden)] #[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))] macro_rules! warn_flags { - ($flags:expr) => { + ($file:expr, $flags:expr) => { // SAFETY: It is always safe to call `WARN_ON()`. unsafe { $crate::bindings::WARN_ON(true) } }; @@ -103,7 +103,7 @@ macro_rules! warn_flags { #[doc(hidden)] #[cfg(not(CONFIG_BUG))] macro_rules! warn_flags { - ($flags:expr) => {}; + ($file:expr, $flags:expr) => {}; } #[doc(hidden)] @@ -116,10 +116,16 @@ pub const fn bugflag_taint(value: u32) -> u32 { macro_rules! warn_on { ($cond:expr) => {{ let cond = $cond; + + #[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)] + const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!()); + #[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))] + const _COND_STR: &str = file!(); + if cond { const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN); - $crate::warn_flags!(WARN_ON_FLAGS); + $crate::warn_flags!(_COND_STR, WARN_ON_FLAGS); } cond }}; From 46c40f938f5f15e0a3ecfdd0feba485f8feaff92 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 17 Nov 2025 09:24:52 +0900 Subject: [PATCH 57/59] rust: print: Add pr_*_once macros Add Rust version of pr_[emerg|alert|crit|err|warn|notice|info]_once macros, which print a message only once. Reviewed-by: Alice Ryhl Signed-off-by: FUJITA Tomonori Link: https://patch.msgid.link/20251117002452.4068692-3-fujita.tomonori@gmail.com [ Added prefix to title. Fixed typo. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/print.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index af32054be5a2..6fd84389a858 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -506,3 +506,73 @@ macro_rules! do_once_lite { ONCE.call_once(|| { $($e)* }); }}; } + +/// Prints an emergency-level message (level 0) only once. +/// +/// Equivalent to the kernel's `pr_emerg_once` macro. +#[macro_export] +macro_rules! pr_emerg_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_emerg!($($arg)*) } + ) +); + +/// Prints an alert-level message (level 1) only once. +/// +/// Equivalent to the kernel's `pr_alert_once` macro. +#[macro_export] +macro_rules! pr_alert_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_alert!($($arg)*) } + ) +); + +/// Prints a critical-level message (level 2) only once. +/// +/// Equivalent to the kernel's `pr_crit_once` macro. +#[macro_export] +macro_rules! pr_crit_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_crit!($($arg)*) } + ) +); + +/// Prints an error-level message (level 3) only once. +/// +/// Equivalent to the kernel's `pr_err_once` macro. +#[macro_export] +macro_rules! pr_err_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_err!($($arg)*) } + ) +); + +/// Prints a warning-level message (level 4) only once. +/// +/// Equivalent to the kernel's `pr_warn_once` macro. +#[macro_export] +macro_rules! pr_warn_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_warn!($($arg)*) } + ) +); + +/// Prints a notice-level message (level 5) only once. +/// +/// Equivalent to the kernel's `pr_notice_once` macro. +#[macro_export] +macro_rules! pr_notice_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_notice!($($arg)*) } + ) +); + +/// Prints an info-level message (level 6) only once. +/// +/// Equivalent to the kernel's `pr_info_once` macro. +#[macro_export] +macro_rules! pr_info_once ( + ($($arg:tt)*) => ( + $crate::do_once_lite! { $crate::pr_info!($($arg)*) } + ) +); From 0e62e4f3e56cf6c44926db2ee82ff29b4a28ac03 Mon Sep 17 00:00:00 2001 From: Filipe Xavier Date: Sat, 17 Jan 2026 07:41:25 -0300 Subject: [PATCH 58/59] rust: add `impl_flags!` macro for defining common bitflag operations We have seen a proliferation of `mod_whatever::foo::Flags` being defined with essentially the same implementation for `BitAnd`, `BitOr`, `.contains()` etc. This macro aims to bring a solution for this, allowing to generate these methods for user-defined structs. With some use cases in KMS and upcoming GPU drivers. Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/We.20really.20need.20a.20common.20.60Flags.60.20type Suggested-by: Daniel Almeida Suggested-by: Lyude Paul Reviewed-by: Daniel Almeida Reviewed-by: Lyude Paul Tested-by: Andreas Hindborg Reviewed-by: Andreas Hindborg Signed-off-by: Filipe Xavier Link: https://patch.msgid.link/20260117-feat-add-bitmask-macro-v9-1-45ea1f00f846@gmail.com [ Implemented missing `BitXorAssign<$flag> for $flags`. Sorted `impl`s. Removed prelude addition for now -- I asked the team and they also felt it wasn't needed. We can always add it later on if needed. Fixed intra-doc link (by removing the sentence since it was superfluous anyway). Simplified `empty()` title. Reworded commit slightly. Added docs to enum variants in example to avoid 'missing_docs' lint when used in actual code. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/impl_flags.rs | 272 ++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 2 files changed, 274 insertions(+) create mode 100644 rust/kernel/impl_flags.rs diff --git a/rust/kernel/impl_flags.rs b/rust/kernel/impl_flags.rs new file mode 100644 index 000000000000..e2bd7639da12 --- /dev/null +++ b/rust/kernel/impl_flags.rs @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Bitflag type generator. + +/// Common helper for declaring bitflag and bitmask types. +/// +/// This macro takes as input: +/// - A struct declaration representing a bitmask type +/// (e.g., `pub struct Permissions(u32)`). +/// - An enumeration declaration representing individual bit flags +/// (e.g., `pub enum Permission { ... }`). +/// +/// And generates: +/// - The struct and enum types with appropriate `#[repr]` attributes. +/// - Implementations of common bitflag operators +/// ([`::core::ops::BitOr`], [`::core::ops::BitAnd`], etc.). +/// - Utility methods such as `.contains()` to check flags. +/// +/// # Examples +/// +/// ``` +/// use kernel::impl_flags; +/// +/// impl_flags!( +/// /// Represents multiple permissions. +/// #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] +/// pub struct Permissions(u32); +/// +/// /// Represents a single permission. +/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// pub enum Permission { +/// /// Read permission. +/// Read = 1 << 0, +/// +/// /// Write permission. +/// Write = 1 << 1, +/// +/// /// Execute permission. +/// Execute = 1 << 2, +/// } +/// ); +/// +/// // Combine multiple permissions using the bitwise OR (`|`) operator. +/// let mut read_write: Permissions = Permission::Read | Permission::Write; +/// assert!(read_write.contains(Permission::Read)); +/// assert!(read_write.contains(Permission::Write)); +/// assert!(!read_write.contains(Permission::Execute)); +/// assert!(read_write.contains_any(Permission::Read | Permission::Execute)); +/// assert!(read_write.contains_all(Permission::Read | Permission::Write)); +/// +/// // Using the bitwise OR assignment (`|=`) operator. +/// read_write |= Permission::Execute; +/// assert!(read_write.contains(Permission::Execute)); +/// +/// // Masking a permission with the bitwise AND (`&`) operator. +/// let read_only: Permissions = read_write & Permission::Read; +/// assert!(read_only.contains(Permission::Read)); +/// assert!(!read_only.contains(Permission::Write)); +/// +/// // Toggling permissions with the bitwise XOR (`^`) operator. +/// let toggled: Permissions = read_only ^ Permission::Read; +/// assert!(!toggled.contains(Permission::Read)); +/// +/// // Inverting permissions with the bitwise NOT (`!`) operator. +/// let negated = !read_only; +/// assert!(negated.contains(Permission::Write)); +/// assert!(!negated.contains(Permission::Read)); +/// ``` +#[macro_export] +macro_rules! impl_flags { + ( + $(#[$outer_flags:meta])* + $vis_flags:vis struct $flags:ident($ty:ty); + + $(#[$outer_flag:meta])* + $vis_flag:vis enum $flag:ident { + $( + $(#[$inner_flag:meta])* + $name:ident = $value:expr + ),+ $( , )? + } + ) => { + $(#[$outer_flags])* + #[repr(transparent)] + $vis_flags struct $flags($ty); + + $(#[$outer_flag])* + #[repr($ty)] + $vis_flag enum $flag { + $( + $(#[$inner_flag])* + $name = $value + ),+ + } + + impl ::core::convert::From<$flag> for $flags { + #[inline] + fn from(value: $flag) -> Self { + Self(value as $ty) + } + } + + impl ::core::convert::From<$flags> for $ty { + #[inline] + fn from(value: $flags) -> Self { + value.0 + } + } + + impl ::core::ops::BitOr for $flags { + type Output = Self; + #[inline] + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl ::core::ops::BitOrAssign for $flags { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } + } + + impl ::core::ops::BitOr<$flag> for $flags { + type Output = Self; + #[inline] + fn bitor(self, rhs: $flag) -> Self::Output { + self | Self::from(rhs) + } + } + + impl ::core::ops::BitOrAssign<$flag> for $flags { + #[inline] + fn bitor_assign(&mut self, rhs: $flag) { + *self = *self | rhs; + } + } + + impl ::core::ops::BitAnd for $flags { + type Output = Self; + #[inline] + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } + } + + impl ::core::ops::BitAndAssign for $flags { + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + *self = *self & rhs; + } + } + + impl ::core::ops::BitAnd<$flag> for $flags { + type Output = Self; + #[inline] + fn bitand(self, rhs: $flag) -> Self::Output { + self & Self::from(rhs) + } + } + + impl ::core::ops::BitAndAssign<$flag> for $flags { + #[inline] + fn bitand_assign(&mut self, rhs: $flag) { + *self = *self & rhs; + } + } + + impl ::core::ops::BitXor for $flags { + type Output = Self; + #[inline] + fn bitxor(self, rhs: Self) -> Self::Output { + Self((self.0 ^ rhs.0) & Self::all_bits()) + } + } + + impl ::core::ops::BitXorAssign for $flags { + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + *self = *self ^ rhs; + } + } + + impl ::core::ops::BitXor<$flag> for $flags { + type Output = Self; + #[inline] + fn bitxor(self, rhs: $flag) -> Self::Output { + self ^ Self::from(rhs) + } + } + + impl ::core::ops::BitXorAssign<$flag> for $flags { + #[inline] + fn bitxor_assign(&mut self, rhs: $flag) { + *self = *self ^ rhs; + } + } + + impl ::core::ops::Not for $flags { + type Output = Self; + #[inline] + fn not(self) -> Self::Output { + Self((!self.0) & Self::all_bits()) + } + } + + impl ::core::ops::BitOr for $flag { + type Output = $flags; + #[inline] + fn bitor(self, rhs: Self) -> Self::Output { + $flags(self as $ty | rhs as $ty) + } + } + + impl ::core::ops::BitAnd for $flag { + type Output = $flags; + #[inline] + fn bitand(self, rhs: Self) -> Self::Output { + $flags(self as $ty & rhs as $ty) + } + } + + impl ::core::ops::BitXor for $flag { + type Output = $flags; + #[inline] + fn bitxor(self, rhs: Self) -> Self::Output { + $flags((self as $ty ^ rhs as $ty) & $flags::all_bits()) + } + } + + impl ::core::ops::Not for $flag { + type Output = $flags; + #[inline] + fn not(self) -> Self::Output { + $flags((!(self as $ty)) & $flags::all_bits()) + } + } + + impl $flags { + /// Returns an empty instance where no flags are set. + #[inline] + pub const fn empty() -> Self { + Self(0) + } + + /// Returns a mask containing all valid flag bits. + #[inline] + pub const fn all_bits() -> $ty { + 0 $( | $value )+ + } + + /// Checks if a specific flag is set. + #[inline] + pub fn contains(self, flag: $flag) -> bool { + (self.0 & flag as $ty) == flag as $ty + } + + /// Checks if at least one of the provided flags is set. + #[inline] + pub fn contains_any(self, flags: $flags) -> bool { + (self.0 & flags.0) != 0 + } + + /// Checks if all of the provided flags are set. + #[inline] + pub fn contains_all(self, flags: $flags) -> bool { + (self.0 & flags.0) == flags.0 + } + } + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f812cf120042..996affce2c9e 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -100,6 +100,8 @@ pub mod fs; #[cfg(CONFIG_I2C = "y")] pub mod i2c; pub mod id_pool; +#[doc(hidden)] +pub mod impl_flags; pub mod init; pub mod io; pub mod ioctl; From b8d687c7eeb52d0353ac27c4f71594a2e6aa365f Mon Sep 17 00:00:00 2001 From: Ritvik Gupta Date: Wed, 8 Oct 2025 03:20:28 +0530 Subject: [PATCH 59/59] rust: safety: introduce `unsafe_precondition_assert!` macro Introduce a new `safety` module containing `unsafe_precondition_assert!` macro. It is a wrapper around `debug_assert!`, intended for validating preconditions of unsafe function. When `CONFIG_RUST_DEBUG_ASSERTIONS` flag is enabled, this macro performs runtime checks to ensure that the preconditions for unsafe function hold. Otherwise, the macro is a no-op. Suggested-by: Miguel Ojeda Link: https://github.com/Rust-for-Linux/linux/issues/1162 Link: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60unsafe_precondition_assert.60.20macro/with/528457452 Signed-off-by: Ritvik Gupta Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20251007215034.213779-1-ritvikfoss@gmail.com [ Added trailing periods, intra-doc link, "a" in "is a no-op" and `()` to function reference. Removed plural in assertion message and title of macro. Reworded slightly. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/lib.rs | 1 + rust/kernel/safety.rs | 53 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 rust/kernel/safety.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 996affce2c9e..696f62f85eb5 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -135,6 +135,7 @@ pub mod pwm; pub mod rbtree; pub mod regulator; pub mod revocable; +pub mod safety; pub mod scatterlist; pub mod security; pub mod seq_file; diff --git a/rust/kernel/safety.rs b/rust/kernel/safety.rs new file mode 100644 index 000000000000..c1c6bd0fa2cc --- /dev/null +++ b/rust/kernel/safety.rs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Safety related APIs. + +/// Checks that a precondition of an unsafe function is followed. +/// +/// The check is enabled at runtime if debug assertions (`CONFIG_RUST_DEBUG_ASSERTIONS`) +/// are enabled. Otherwise, this macro is a no-op. +/// +/// # Examples +/// +/// ```no_run +/// use kernel::unsafe_precondition_assert; +/// +/// struct RawBuffer { +/// data: [T; N], +/// } +/// +/// impl RawBuffer { +/// /// # Safety +/// /// +/// /// The caller must ensure that `index` is less than `N`. +/// unsafe fn set_unchecked(&mut self, index: usize, value: T) { +/// unsafe_precondition_assert!( +/// index < N, +/// "RawBuffer::set_unchecked() requires index ({index}) < N ({N})" +/// ); +/// +/// // SAFETY: By the safety requirements of this function, `index` is valid. +/// unsafe { +/// *self.data.get_unchecked_mut(index) = value; +/// } +/// } +/// } +/// ``` +/// +/// # Panics +/// +/// Panics if the expression is evaluated to [`false`] at runtime. +#[macro_export] +macro_rules! unsafe_precondition_assert { + ($cond:expr $(,)?) => { + $crate::unsafe_precondition_assert!(@inner $cond, ::core::stringify!($cond)) + }; + + ($cond:expr, $($arg:tt)+) => { + $crate::unsafe_precondition_assert!(@inner $cond, $crate::prelude::fmt!($($arg)+)) + }; + + (@inner $cond:expr, $msg:expr) => { + ::core::debug_assert!($cond, "unsafe precondition violated: {}", $msg) + }; +}