From: "Benno Lossin" <lossin@kernel.org>
To: "Tamir Duberstein" <tamird@gmail.com>,
"Michal Rostecki" <vadorovsky@protonmail.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Brendan Higgins" <brendan.higgins@linux.dev>,
"David Gow" <davidgow@google.com>, "Rae Moar" <rmoar@google.com>,
"Danilo Krummrich" <dakr@kernel.org>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
"Maxime Ripard" <mripard@kernel.org>,
"Thomas Zimmermann" <tzimmermann@suse.de>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
"Luis Chamberlain" <mcgrof@kernel.org>,
"Russ Weight" <russ.weight@linux.dev>,
"FUJITA Tomonori" <fujita.tomonori@gmail.com>,
"Rob Herring" <robh@kernel.org>,
"Saravana Kannan" <saravanak@google.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Ingo Molnar" <mingo@redhat.com>, "Will Deacon" <will@kernel.org>,
"Waiman Long" <longman@redhat.com>,
"Nathan Chancellor" <nathan@kernel.org>,
"Nick Desaulniers" <nick.desaulniers+lkml@gmail.com>,
"Bill Wendling" <morbo@google.com>,
"Justin Stitt" <justinstitt@google.com>,
"Andrew Lunn" <andrew@lunn.ch>,
"Heiner Kallweit" <hkallweit1@gmail.com>,
"Russell King" <linux@armlinux.org.uk>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Arnd Bergmann" <arnd@arndb.de>, "Jens Axboe" <axboe@kernel.dk>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"Dave Ertman" <david.m.ertman@intel.com>,
"Ira Weiny" <ira.weiny@intel.com>,
"Leon Romanovsky" <leon@kernel.org>,
"Breno Leitao" <leitao@debian.org>,
"Viresh Kumar" <viresh.kumar@linaro.org>,
"Michael Turquette" <mturquette@baylibre.com>,
"Stephen Boyd" <sboyd@kernel.org>
Cc: <rust-for-linux@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<linux-kselftest@vger.kernel.org>, <kunit-dev@googlegroups.com>,
<dri-devel@lists.freedesktop.org>, <netdev@vger.kernel.org>,
<devicetree@vger.kernel.org>, <llvm@lists.linux.dev>,
<linux-pci@vger.kernel.org>, <nouveau@lists.freedesktop.org>,
<linux-block@vger.kernel.org>, <linux-pm@vger.kernel.org>,
<linux-clk@vger.kernel.org>
Subject: Re: [PATCH v13 2/5] rust: support formatting of foreign types
Date: Thu, 03 Jul 2025 11:32:05 +0200 [thread overview]
Message-ID: <DB2BDSN1JH51.14ZZPETJORBC6@kernel.org> (raw)
In-Reply-To: <20250701-cstr-core-v13-2-29f7d3eb97a6@gmail.com>
On Tue Jul 1, 2025 at 6:49 PM CEST, Tamir Duberstein wrote:
> Introduce a `fmt!` macro which wraps all arguments in
> `kernel::fmt::Adapter` and a `kernel::fmt::Display` trait. This enables
> formatting of foreign types (like `core::ffi::CStr`) that do not
> implement `core::fmt::Display` due to concerns around lossy conversions which
> do not apply in the kernel.
>
> Replace all direct calls to `format_args!` with `fmt!`.
>
> Replace all implementations of `core::fmt::Display` with implementations
> of `kernel::fmt::Display`.
>
> Suggested-by: Alice Ryhl <aliceryhl@google.com>
> Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Custom.20formatting/with/516476467
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Tamir Duberstein <tamird@gmail.com>
> ---
> drivers/block/rnull.rs | 2 +-
> drivers/gpu/nova-core/gpu.rs | 4 +-
> rust/kernel/block/mq.rs | 2 +-
> rust/kernel/device.rs | 2 +-
> rust/kernel/fmt.rs | 89 +++++++++++++++++++++++++++++++++++++++
> rust/kernel/kunit.rs | 6 +--
> rust/kernel/lib.rs | 1 +
> rust/kernel/prelude.rs | 3 +-
> rust/kernel/print.rs | 4 +-
> rust/kernel/seq_file.rs | 2 +-
> rust/kernel/str.rs | 22 ++++------
> rust/macros/fmt.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++
> rust/macros/lib.rs | 19 +++++++++
> rust/macros/quote.rs | 7 ++++
> scripts/rustdoc_test_gen.rs | 2 +-
> 15 files changed, 236 insertions(+), 28 deletions(-)
This would be a lot easier to review if he proc-macro and the call
replacement were different patches.
Also the `kernel/fmt.rs` file should be a different commit.
> diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs
> new file mode 100644
> index 000000000000..348d16987de6
> --- /dev/null
> +++ b/rust/kernel/fmt.rs
> @@ -0,0 +1,89 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Formatting utilities.
> +
> +use core::fmt;
I think we should pub export all types that we are still using from
`core::fmt`. For example `Result`, `Formatter`, `Debug` etc.
That way I can still use the same pattern of importing `fmt` and then
writing
impl fmt::Display for MyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {}
}
> +
> +/// Internal adapter used to route allow implementations of formatting traits for foreign types.
> +///
> +/// It is inserted automatically by the [`fmt!`] macro and is not meant to be used directly.
> +///
> +/// [`fmt!`]: crate::prelude::fmt!
> +#[doc(hidden)]
> +pub struct Adapter<T>(pub T);
> +
> +macro_rules! impl_fmt_adapter_forward {
> + ($($trait:ident),* $(,)?) => {
> + $(
> + impl<T: fmt::$trait> fmt::$trait for Adapter<T> {
> + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> + let Self(t) = self;
> + fmt::$trait::fmt(t, f)
> + }
> + }
> + )*
> + };
> +}
> +
> +impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, Pointer, LowerExp, UpperExp);
> +
> +/// A copy of [`fmt::Display`] that allows us to implement it for foreign types.
> +///
> +/// Types should implement this trait rather than [`fmt::Display`]. Together with the [`Adapter`]
> +/// type and [`fmt!`] macro, it allows for formatting foreign types (e.g. types from core) which do
> +/// not implement [`fmt::Display`] directly.
> +///
> +/// [`fmt!`]: crate::prelude::fmt!
> +pub trait Display {
> + /// Same as [`fmt::Display::fmt`].
> + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
> +}
> +
> +impl<T: ?Sized + Display> Display for &T {
> + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> + Display::fmt(*self, f)
> + }
> +}
> +
> +impl<T: ?Sized + Display> fmt::Display for Adapter<&T> {
> + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> + let Self(t) = self;
> + Display::fmt(t, f)
Why not `Display::fmt(&self.0, f)`?
> + }
> +}
> +
> +macro_rules! impl_display_forward {
> + ($(
> + $( { $($generics:tt)* } )? $ty:ty $( { where $($where:tt)* } )?
> + ),* $(,)?) => {
> + $(
> + impl$($($generics)*)? Display for $ty $(where $($where)*)? {
> + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> + fmt::Display::fmt(self, f)
> + }
> + }
> + )*
> + };
> +}
> +
> +impl_display_forward!(
> + bool,
> + char,
> + core::panic::PanicInfo<'_>,
> + fmt::Arguments<'_>,
> + i128,
> + i16,
> + i32,
> + i64,
> + i8,
> + isize,
> + str,
> + u128,
> + u16,
> + u32,
> + u64,
> + u8,
> + usize,
> + {<T: ?Sized>} crate::sync::Arc<T> {where crate::sync::Arc<T>: fmt::Display},
> + {<T: ?Sized>} crate::sync::UniqueArc<T> {where crate::sync::UniqueArc<T>: fmt::Display},
> +);
> diff --git a/rust/macros/fmt.rs b/rust/macros/fmt.rs
> new file mode 100644
> index 000000000000..edc37c220a89
> --- /dev/null
> +++ b/rust/macros/fmt.rs
> @@ -0,0 +1,99 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use proc_macro::{Ident, TokenStream, TokenTree};
> +use std::collections::BTreeSet;
> +
> +/// Please see [`crate::fmt`] for documentation.
> +pub(crate) fn fmt(input: TokenStream) -> TokenStream {
> + let mut input = input.into_iter();
> +
> + let first_opt = input.next();
> + let first_owned_str;
> + let mut names = BTreeSet::new();
> + let first_lit = {
> + let Some((mut first_str, first_lit)) = (match first_opt.as_ref() {
> + Some(TokenTree::Literal(first_lit)) => {
> + first_owned_str = first_lit.to_string();
> + Some(first_owned_str.as_str()).and_then(|first| {
> + let first = first.strip_prefix('"')?;
> + let first = first.strip_suffix('"')?;
> + Some((first, first_lit))
You're only using first_lit to get the span later, so why not just get
the span directly here?
> + })
> + }
> + _ => None,
> + }) else {
> + return first_opt.into_iter().chain(input).collect();
> + };
> + while let Some((_, rest)) = first_str.split_once('{') {
Let's put a comment above this loop mentioning [1] and saying that it
parses the identifiers from the format arguments.
[1]: https://doc.rust-lang.org/std/fmt/index.html#syntax
> + first_str = rest;
> + if let Some(rest) = first_str.strip_prefix('{') {
> + first_str = rest;
> + continue;
> + }
> + if let Some((name, rest)) = first_str.split_once('}') {
> + first_str = rest;
> + let name = name.split_once(':').map_or(name, |(name, _)| name);
> + if !name.is_empty() && !name.chars().all(|c| c.is_ascii_digit()) {
> + names.insert(name);
> + }
> + }
> + }
> + first_lit
> + };
> +
> + let first_span = first_lit.span();
> + let adapter = quote_spanned! {
> + first_span => ::kernel::fmt::Adapter
> + };
I think we should follow the formatting convention from the quote crate:
let adapter = quote_spanned!(first_span=> ::kernel::fmt::Adapter);
> +
> + let mut args = TokenStream::from_iter(first_opt);
> + {
> + let mut flush = |args: &mut TokenStream, current: &mut TokenStream| {
You don't need to pass `args` as a closure argument, since you always
call it with `&mut args`.
> + let current = std::mem::take(current);
> + if !current.is_empty() {
> + let (lhs, rhs) = (|| {
> + let mut current = current.into_iter();
> + let mut acc = TokenStream::new();
> + while let Some(tt) = current.next() {
> + // Split on `=` only once to handle cases like `a = b = c`.
> + if matches!(&tt, TokenTree::Punct(p) if p.as_char() == '=') {
> + names.remove(acc.to_string().as_str());
> + // Include the `=` itself to keep the handling below uniform.
> + acc.extend([tt]);
> + return (Some(acc), current.collect::<TokenStream>());
> + }
> + acc.extend([tt]);
> + }
> + (None, acc)
> + })();
> + args.extend(quote_spanned! {
> + first_span => #lhs #adapter(&#rhs)
> + });
> + }
> + };
> +
> + let mut current = TokenStream::new();
Define this before the closure, then you don't need to pass it as an
argument.
---
Cheers,
Benno
> + for tt in input {
> + match &tt {
> + TokenTree::Punct(p) if p.as_char() == ',' => {
> + flush(&mut args, &mut current);
> + &mut args
> + }
> + _ => &mut current,
> + }
> + .extend([tt]);
> + }
> + flush(&mut args, &mut current);
> + }
> +
> + for name in names {
> + let name = Ident::new(name, first_span);
> + args.extend(quote_spanned! {
> + first_span => , #name = #adapter(&#name)
> + });
> + }
> +
> + quote_spanned! {
> + first_span => ::core::format_args!(#args)
> + }
> +}
next prev parent reply other threads:[~2025-07-03 9:32 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-01 16:49 [PATCH v13 0/5] rust: replace kernel::str::CStr w/ core::ffi::CStr Tamir Duberstein
2025-07-01 16:49 ` [PATCH v13 1/5] rust: macros: reduce collections in `quote!` macro Tamir Duberstein
2025-07-01 16:49 ` [PATCH v13 2/5] rust: support formatting of foreign types Tamir Duberstein
2025-07-03 9:32 ` Benno Lossin [this message]
2025-07-03 13:55 ` Tamir Duberstein
2025-07-03 15:08 ` Benno Lossin
2025-07-03 18:55 ` Tamir Duberstein
2025-07-03 19:16 ` Tamir Duberstein
2025-07-03 20:36 ` Benno Lossin
2025-07-03 22:41 ` Tamir Duberstein
2025-07-03 23:23 ` Tamir Duberstein
2025-07-04 10:09 ` Benno Lossin
2025-07-04 11:58 ` Tamir Duberstein
2025-07-04 12:15 ` Miguel Ojeda
2025-07-04 19:38 ` Tamir Duberstein
2025-07-05 8:04 ` Andrew Lunn
2025-07-04 7:57 ` Andrew Lunn
2025-07-04 10:05 ` Benno Lossin
2025-07-03 21:28 ` Andrew Lunn
2025-07-03 21:38 ` Miguel Ojeda
2025-07-03 22:45 ` Tamir Duberstein
2025-07-04 7:46 ` Andrew Lunn
2025-07-04 8:40 ` Miguel Ojeda
2025-07-03 16:26 ` Miguel Ojeda
2025-07-03 18:57 ` Tamir Duberstein
2025-07-01 16:49 ` [PATCH v13 3/5] rust: replace `CStr` with `core::ffi::CStr` Tamir Duberstein
2025-07-04 13:00 ` Benno Lossin
2025-07-04 19:38 ` Tamir Duberstein
2025-07-01 16:49 ` [PATCH v13 4/5] rust: replace `kernel::c_str!` with C-Strings Tamir Duberstein
2025-07-04 13:01 ` Benno Lossin
2025-07-04 19:38 ` Tamir Duberstein
2025-07-01 16:49 ` [PATCH v13 5/5] rust: remove core::ffi::CStr reexport Tamir Duberstein
2025-07-01 17:07 ` [PATCH v13 0/5] rust: replace kernel::str::CStr w/ core::ffi::CStr Miguel Ojeda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DB2BDSN1JH51.14ZZPETJORBC6@kernel.org \
--to=lossin@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=airlied@gmail.com \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=andrew@lunn.ch \
--cc=arnd@arndb.de \
--cc=axboe@kernel.dk \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=brendan.higgins@linux.dev \
--cc=dakr@kernel.org \
--cc=davem@davemloft.net \
--cc=david.m.ertman@intel.com \
--cc=davidgow@google.com \
--cc=devicetree@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=edumazet@google.com \
--cc=fujita.tomonori@gmail.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=hkallweit1@gmail.com \
--cc=ira.weiny@intel.com \
--cc=justinstitt@google.com \
--cc=kuba@kernel.org \
--cc=kunit-dev@googlegroups.com \
--cc=kwilczynski@kernel.org \
--cc=leitao@debian.org \
--cc=leon@kernel.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=llvm@lists.linux.dev \
--cc=longman@redhat.com \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mcgrof@kernel.org \
--cc=mingo@redhat.com \
--cc=morbo@google.com \
--cc=mripard@kernel.org \
--cc=mturquette@baylibre.com \
--cc=nathan@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nick.desaulniers+lkml@gmail.com \
--cc=nouveau@lists.freedesktop.org \
--cc=ojeda@kernel.org \
--cc=pabeni@redhat.com \
--cc=peterz@infradead.org \
--cc=rafael@kernel.org \
--cc=rmoar@google.com \
--cc=robh@kernel.org \
--cc=russ.weight@linux.dev \
--cc=rust-for-linux@vger.kernel.org \
--cc=saravanak@google.com \
--cc=sboyd@kernel.org \
--cc=simona@ffwll.ch \
--cc=tamird@gmail.com \
--cc=tmgross@umich.edu \
--cc=tzimmermann@suse.de \
--cc=vadorovsky@protonmail.com \
--cc=viresh.kumar@linaro.org \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.