[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <c7a1a9a872f206deb65b7e84a8ecc88c528ed50e.1754228164.git.y.j3ms.n@gmail.com>
Date: Sun, 3 Aug 2025 14:20:54 +0000
From: Jesung Yang <y.j3ms.n@...il.com>
To: Miguel Ojeda <ojeda@...nel.org>,
Alex Gaynor <alex.gaynor@...il.com>,
Boqun Feng <boqun.feng@...il.com>,
Gary Guo <gary@...yguo.net>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>,
Benno Lossin <lossin@...nel.org>,
Andreas Hindborg <a.hindborg@...nel.org>,
Alice Ryhl <aliceryhl@...gle.com>,
Trevor Gross <tmgross@...ch.edu>,
Danilo Krummrich <dakr@...nel.org>,
Alexandre Courbot <acourbot@...dia.com>
Cc: linux-kernel@...r.kernel.org,
rust-for-linux@...r.kernel.org,
nouveau@...ts.freedesktop.org,
Jesung Yang <y.j3ms.n@...il.com>
Subject: [PATCH 4/4] rust: macro: add derive macro for `Into`
Introduce a procedural macro `Into` to automatically implement the
`Into` trait for unit-only enums.
This reduces boilerplate in cases where enum variants need to be
interpreted as relevant numeric values. A concrete example can be
found in nova-core, where the `register!()` macro requires enum types
used within it to be convertible via `u32::from()` [1].
Note that the macro actually generates `From<E> for T` implementations,
where `E` is an enum identifier and `T` is an arbitrary integer type.
This automatically provides the corresponding `Into<T> for E`
implementations through the blanket implementation.
Link: https://lore.kernel.org/rust-for-linux/20250624132337.2242-1-dakr@kernel.org/ [1]
Signed-off-by: Jesung Yang <y.j3ms.n@...il.com>
---
rust/macros/convert.rs | 36 ++++++++++---
rust/macros/lib.rs | 115 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 145 insertions(+), 6 deletions(-)
diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs
index 0084bc4308c1..a6ef67ba27c7 100644
--- a/rust/macros/convert.rs
+++ b/rust/macros/convert.rs
@@ -3,6 +3,12 @@
use proc_macro::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree};
use std::iter::Peekable;
+#[derive(Debug)]
+enum DeriveTarget {
+ TryFrom,
+ Into,
+}
+
#[derive(Debug)]
struct TypeArgs {
helper: Vec<Ident>,
@@ -13,13 +19,20 @@ struct TypeArgs {
"u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
];
+pub(crate) fn derive_into(input: TokenStream) -> TokenStream {
+ derive(input, DeriveTarget::Into)
+}
+
pub(crate) fn derive_try_from(input: TokenStream) -> TokenStream {
- derive(input)
+ derive(input, DeriveTarget::TryFrom)
}
-fn derive(input: TokenStream) -> TokenStream {
- let derive_target = "TryFrom";
- let derive_helper = "try_from";
+fn derive(input: TokenStream, target: DeriveTarget) -> TokenStream {
+ type ImplFn = fn(&Ident, &Ident, &[Ident]) -> TokenStream;
+ let (derive_target, derive_helper, impl_trait) = match target {
+ DeriveTarget::TryFrom => ("TryFrom", "try_from", impl_try_from as ImplFn),
+ DeriveTarget::Into => ("Into", "into", impl_into as ImplFn),
+ };
let mut tokens = input.into_iter().peekable();
@@ -85,12 +98,12 @@ fn derive(input: TokenStream) -> TokenStream {
let ty = type_args
.repr
.unwrap_or_else(|| Ident::new("isize", Span::mixed_site()));
- impl_try_from(&ty, &enum_ident, &variants)
+ impl_trait(&ty, &enum_ident, &variants)
} else {
let impls = type_args
.helper
.iter()
- .map(|ty| impl_try_from(ty, &enum_ident, &variants));
+ .map(|ty| impl_trait(ty, &enum_ident, &variants));
quote! { #(#impls)* }
}
}
@@ -335,3 +348,14 @@ fn try_from(#param: #ty) -> Result<Self, Self::Error> {
}
}
}
+
+fn impl_into(ty: &Ident, enum_ident: &Ident, _: &[Ident]) -> TokenStream {
+ quote! {
+ #[automatically_derived]
+ impl ::core::convert::From<#enum_ident> for #ty {
+ fn from(value: #enum_ident) -> #ty {
+ value as #ty
+ }
+ }
+ }
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 569198f188f7..374c1bdb696a 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -427,6 +427,121 @@ pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
kunit::kunit_tests(attr, ts)
}
+/// A derive macro for providing an impl of the [`Into`] trait.
+///
+/// This macro automatically derives [`Into`] trait for a given enum by generating
+/// the relevant [`From`] implementation. Currently, it only supports [unit-only enum]s
+/// without generic parameters.
+///
+/// [unit-only enum]: https://doc.rust-lang.org/reference/items/enumerations.html#r-items.enum.unit-only
+///
+/// # Notes
+///
+/// Unlike its name suggests, the macro actually generates [`From`] implementations
+/// which automatically provide corresponding [`Into`] implementations.
+///
+/// The macro uses the `into` custom attribute or `repr` attribute to generate [`From`]
+/// implementations. `into` always takes precedence over `repr`.
+///
+/// # Caveats
+///
+/// Ensure that every integer type specified in `#[into(...)]` is large enough to cover
+/// all enum discriminants. Otherwise, the internal `as` casts may overflow.
+///
+/// # Examples
+///
+/// ## Without Attributes
+///
+/// Since [the default `Rust` representation uses `isize` for the discriminant type][repr-rs],
+/// the macro implements `From<Foo>` for `isize`:
+///
+/// [repr-rs]: https://doc.rust-lang.org/reference/items/enumerations.html#r-items.enum.discriminant.repr-rust
+///
+/// ```rust
+/// use kernel::macros::Into;
+/// use kernel::prelude::*;
+///
+/// #[derive(Debug, Default, Into)]
+/// enum Foo {
+/// #[default]
+/// A,
+/// B = 0x17,
+/// }
+///
+/// assert_eq!(0isize, Foo::A.into());
+/// assert_eq!(0x17isize, Foo::B.into());
+/// ```
+///
+/// ## With `#[repr(T)]`
+///
+/// The macro implements `From<Foo>` for `T`:
+///
+/// ```rust
+/// use kernel::macros::Into;
+/// use kernel::prelude::*;
+///
+/// #[derive(Debug, Default, Into)]
+/// #[repr(u8)]
+/// enum Foo {
+/// #[default]
+/// A,
+/// B = 0x17,
+/// }
+///
+/// assert_eq!(0u8, Foo::A.into());
+/// assert_eq!(0x17u8, Foo::B.into());
+/// ```
+///
+/// ## With `#[into(...)]`
+///
+/// The macro implements `From<Foo>` for each `T` specified in `#[into(...)]`,
+/// which always overrides `#[repr(...)]`:
+///
+/// ```rust
+/// use kernel::macros::Into;
+/// use kernel::prelude::*;
+///
+/// #[derive(Debug, Default, Into)]
+/// #[into(u8, u16)]
+/// #[repr(u8)]
+/// enum Foo {
+/// #[default]
+/// A,
+/// B = 0x17,
+/// }
+///
+/// assert_eq!(0u16, Foo::A.into());
+/// assert_eq!(0x17u16, Foo::B.into());
+/// ```
+///
+/// ## Unsupported Cases
+///
+/// The following examples do not compile:
+///
+/// ```compile_fail
+/// # use kernel::macros::Into;
+/// // Generic parameters are not allowed.
+/// #[derive(Into)]
+/// enum Foo<T> {
+/// A,
+/// }
+///
+/// // Tuple-like enums or struct-like enums are not allowed.
+/// #[derive(Into)]
+/// enum Bar {
+/// A(u8),
+/// B { inner: u8 },
+/// }
+///
+/// // Structs are not allowed.
+/// #[derive(Into)]
+/// struct Baz(u8);
+/// ```
+#[proc_macro_derive(Into, attributes(into))]
+pub fn derive_into(input: TokenStream) -> TokenStream {
+ convert::derive_into(input)
+}
+
/// A derive macro for generating an impl of the [`TryFrom`] trait.
///
/// This macro automatically derives [`TryFrom`] trait for a given enum. Currently,
--
2.39.5
Powered by blists - more mailing lists