* [PATCH v2 1/5] rust: macros: allow conversion from `&T` to `TokenStream`
2025-08-15 5:32 [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Jesung Yang
@ 2025-08-15 5:32 ` Jesung Yang
2025-08-15 5:32 ` [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro Jesung Yang
` (5 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Jesung Yang @ 2025-08-15 5:32 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot
Cc: linux-kernel, rust-for-linux, nouveau, Jesung Yang
Implement the `ToTokens` trait for `&T` where `T` implements `ToTokens`.
This allows users to use the `quote!()` macro with references directly,
avoiding the need to clone values.
Tested-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Jesung Yang <y.j3ms.n@gmail.com>
---
rust/macros/quote.rs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
index 92cacc4067c9..8a89f0b1e785 100644
--- a/rust/macros/quote.rs
+++ b/rust/macros/quote.rs
@@ -7,6 +7,12 @@ pub(crate) trait ToTokens {
fn to_tokens(&self, tokens: &mut TokenStream);
}
+impl<T: ToTokens> ToTokens for &T {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ (*self).to_tokens(tokens);
+ }
+}
+
impl<T: ToTokens> ToTokens for Option<T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(v) = self {
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro
2025-08-15 5:32 [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Jesung Yang
2025-08-15 5:32 ` [PATCH v2 1/5] rust: macros: allow conversion from `&T` to `TokenStream` Jesung Yang
@ 2025-08-15 5:32 ` Jesung Yang
2025-08-28 6:39 ` Alice Ryhl
2025-09-30 4:58 ` Alexandre Courbot
2025-08-15 5:32 ` [PATCH v2 3/5] rust: macros: prefix variable `span` with underscore Jesung Yang
` (4 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Jesung Yang @ 2025-08-15 5:32 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot
Cc: linux-kernel, rust-for-linux, nouveau, Jesung Yang
Extend the `quote_spanned!()` macro to support additional punctuation
tokens: `->`, `<`, `>`, and `==`. This symbols are commonly needed when
dealing with functions, generic bounds, and equality comparisons.
Tested-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Jesung Yang <y.j3ms.n@gmail.com>
---
rust/macros/quote.rs | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
index 8a89f0b1e785..24764b04a07d 100644
--- a/rust/macros/quote.rs
+++ b/rust/macros/quote.rs
@@ -150,6 +150,36 @@ macro_rules! quote_spanned {
));
quote_spanned!(@proc $v $span $($tt)*);
};
+ (@proc $v:ident $span:ident -> $($tt:tt)*) => {
+ $v.push(::proc_macro::TokenTree::Punct(
+ ::proc_macro::Punct::new('-', ::proc_macro::Spacing::Joint)
+ ));
+ $v.push(::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.push(::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.push(::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.push(::proc_macro::TokenTree::Punct(
+ ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Joint)
+ ));
+ $v.push(::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.push(::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone)
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro
2025-08-15 5:32 ` [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro Jesung Yang
@ 2025-08-28 6:39 ` Alice Ryhl
2025-08-28 7:14 ` Benno Lossin
2025-09-30 4:58 ` Alexandre Courbot
1 sibling, 1 reply; 18+ messages in thread
From: Alice Ryhl @ 2025-08-28 6:39 UTC (permalink / raw)
To: Jesung Yang
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Alexandre Courbot, linux-kernel,
rust-for-linux, nouveau
On Fri, Aug 15, 2025 at 7:32 AM Jesung Yang <y.j3ms.n@gmail.com> wrote:
>
> Extend the `quote_spanned!()` macro to support additional punctuation
> tokens: `->`, `<`, `>`, and `==`. This symbols are commonly needed when
> dealing with functions, generic bounds, and equality comparisons.
>
> Tested-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Jesung Yang <y.j3ms.n@gmail.com>
> ---
> rust/macros/quote.rs | 30 ++++++++++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>
> diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
> index 8a89f0b1e785..24764b04a07d 100644
> --- a/rust/macros/quote.rs
> +++ b/rust/macros/quote.rs
> @@ -150,6 +150,36 @@ macro_rules! quote_spanned {
> ));
> quote_spanned!(@proc $v $span $($tt)*);
> };
> + (@proc $v:ident $span:ident -> $($tt:tt)*) => {
> + $v.push(::proc_macro::TokenTree::Punct(
> + ::proc_macro::Punct::new('-', ::proc_macro::Spacing::Joint)
> + ));
> + $v.push(::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.push(::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.push(::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.push(::proc_macro::TokenTree::Punct(
> + ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Joint)
> + ));
> + $v.push(::proc_macro::TokenTree::Punct(
> + ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone)
> + ));
> + quote_spanned!(@proc $v $span $($tt)*);
Not a blocker, but if the way to implement this one is to push =
twice, then I think the pattern should just be a single = and then you
push a = once. The pattern can match twice to handle ==.
Alice
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro
2025-08-28 6:39 ` Alice Ryhl
@ 2025-08-28 7:14 ` Benno Lossin
2025-08-28 13:05 ` Alice Ryhl
0 siblings, 1 reply; 18+ messages in thread
From: Benno Lossin @ 2025-08-28 7:14 UTC (permalink / raw)
To: Alice Ryhl, Jesung Yang
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross,
Danilo Krummrich, Alexandre Courbot, linux-kernel, rust-for-linux,
nouveau
On Thu Aug 28, 2025 at 8:39 AM CEST, Alice Ryhl wrote:
> On Fri, Aug 15, 2025 at 7:32 AM Jesung Yang <y.j3ms.n@gmail.com> wrote:
>> + (@proc $v:ident $span:ident == $($tt:tt)*) => {
>> + $v.push(::proc_macro::TokenTree::Punct(
>> + ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Joint)
>> + ));
>> + $v.push(::proc_macro::TokenTree::Punct(
>> + ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone)
>> + ));
>> + quote_spanned!(@proc $v $span $($tt)*);
>
> Not a blocker, but if the way to implement this one is to push =
> twice, then I think the pattern should just be a single = and then you
> push a = once. The pattern can match twice to handle ==.
You can't do that, since the first one needs the `Joint` spacing and the
second one the `Alone` one. `==` also is a single token in macro input,
so matching only on `=` doesn't work.
---
Cheers,
Benno
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro
2025-08-28 7:14 ` Benno Lossin
@ 2025-08-28 13:05 ` Alice Ryhl
0 siblings, 0 replies; 18+ messages in thread
From: Alice Ryhl @ 2025-08-28 13:05 UTC (permalink / raw)
To: Benno Lossin
Cc: Jesung Yang, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Trevor Gross,
Danilo Krummrich, Alexandre Courbot, linux-kernel, rust-for-linux,
nouveau
On Thu, Aug 28, 2025 at 9:14 AM Benno Lossin <lossin@kernel.org> wrote:
>
> On Thu Aug 28, 2025 at 8:39 AM CEST, Alice Ryhl wrote:
> > On Fri, Aug 15, 2025 at 7:32 AM Jesung Yang <y.j3ms.n@gmail.com> wrote:
> >> + (@proc $v:ident $span:ident == $($tt:tt)*) => {
> >> + $v.push(::proc_macro::TokenTree::Punct(
> >> + ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Joint)
> >> + ));
> >> + $v.push(::proc_macro::TokenTree::Punct(
> >> + ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone)
> >> + ));
> >> + quote_spanned!(@proc $v $span $($tt)*);
> >
> > Not a blocker, but if the way to implement this one is to push =
> > twice, then I think the pattern should just be a single = and then you
> > push a = once. The pattern can match twice to handle ==.
>
> You can't do that, since the first one needs the `Joint` spacing and the
> second one the `Alone` one. `==` also is a single token in macro input,
> so matching only on `=` doesn't work.
Ah, ok. LGTM then.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro
2025-08-15 5:32 ` [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro Jesung Yang
2025-08-28 6:39 ` Alice Ryhl
@ 2025-09-30 4:58 ` Alexandre Courbot
2025-09-30 7:17 ` Jesung Yang
1 sibling, 1 reply; 18+ messages in thread
From: Alexandre Courbot @ 2025-09-30 4:58 UTC (permalink / raw)
To: Jesung Yang, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot
Cc: linux-kernel, rust-for-linux, nouveau
Hi Jesung,
On Fri Aug 15, 2025 at 2:32 PM JST, Jesung Yang wrote:
> Extend the `quote_spanned!()` macro to support additional punctuation
> tokens: `->`, `<`, `>`, and `==`. This symbols are commonly needed when
> dealing with functions, generic bounds, and equality comparisons.
>
> Tested-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Jesung Yang <y.j3ms.n@gmail.com>
> ---
Note that this patch doesn't apply cleanly in `rust-next`, I've had to
add the following on top of it.
I suggest waiting for -rc1 to be released and using it as a base for a
new version - hopefully this will also give time for more feedback to
come.
diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
index 76a99f7e01c4..bb6970fd2a26 100644
--- a/rust/macros/quote.rs
+++ b/rust/macros/quote.rs
@@ -147,33 +147,33 @@ macro_rules! quote_spanned {
quote_spanned!(@proc $v $span $($tt)*);
};
(@proc $v:ident $span:ident -> $($tt:tt)*) => {
- $v.push(::proc_macro::TokenTree::Punct(
+ $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('-', ::proc_macro::Spacing::Joint)
- ));
- $v.push(::proc_macro::TokenTree::Punct(
+ )]);
+ $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.push(::proc_macro::TokenTree::Punct(
+ $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.push(::proc_macro::TokenTree::Punct(
+ $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.push(::proc_macro::TokenTree::Punct(
+ $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('=', ::proc_macro::Spacing::Joint)
- ));
- $v.push(::proc_macro::TokenTree::Punct(
+ )]);
+ $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)*) => {
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro
2025-09-30 4:58 ` Alexandre Courbot
@ 2025-09-30 7:17 ` Jesung Yang
0 siblings, 0 replies; 18+ messages in thread
From: Jesung Yang @ 2025-09-30 7:17 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, linux-kernel, rust-for-linux,
nouveau
Hi,
On Tue, Sep 30, 2025 at 1:58 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
[...]
> On Fri Aug 15, 2025 at 2:32 PM JST, Jesung Yang wrote:
> > Extend the `quote_spanned!()` macro to support additional punctuation
> > tokens: `->`, `<`, `>`, and `==`. This symbols are commonly needed when
> > dealing with functions, generic bounds, and equality comparisons.
> >
> > Tested-by: Alexandre Courbot <acourbot@nvidia.com>
> > Signed-off-by: Jesung Yang <y.j3ms.n@gmail.com>
> > ---
>
> Note that this patch doesn't apply cleanly in `rust-next`, I've had to
> add the following on top of it.
Thanks for pointing out the conflict. I see that the commit
9578c3906c7d ("rust: macros: reduce collections in `quote!` macro") was
added after my patch series.
> I suggest waiting for -rc1 to be released and using it as a base for a
> new version - hopefully this will also give time for more feedback to
> come.
Sure, happy to wait until -rc1 and perhaps we'll get more feedback by
then. Once it's out, I'll rebase on top of it and send v3.
Best Regards,
Jesung
>
> diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
> index 76a99f7e01c4..bb6970fd2a26 100644
> --- a/rust/macros/quote.rs
> +++ b/rust/macros/quote.rs
> @@ -147,33 +147,33 @@ macro_rules! quote_spanned {
> quote_spanned!(@proc $v $span $($tt)*);
> };
> (@proc $v:ident $span:ident -> $($tt:tt)*) => {
> - $v.push(::proc_macro::TokenTree::Punct(
> + $v.extend([::proc_macro::TokenTree::Punct(
> ::proc_macro::Punct::new('-', ::proc_macro::Spacing::Joint)
> - ));
> - $v.push(::proc_macro::TokenTree::Punct(
> + )]);
> + $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.push(::proc_macro::TokenTree::Punct(
> + $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.push(::proc_macro::TokenTree::Punct(
> + $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.push(::proc_macro::TokenTree::Punct(
> + $v.extend([::proc_macro::TokenTree::Punct(
> ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Joint)
> - ));
> - $v.push(::proc_macro::TokenTree::Punct(
> + )]);
> + $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)*) => {
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 3/5] rust: macros: prefix variable `span` with underscore
2025-08-15 5:32 [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Jesung Yang
2025-08-15 5:32 ` [PATCH v2 1/5] rust: macros: allow conversion from `&T` to `TokenStream` Jesung Yang
2025-08-15 5:32 ` [PATCH v2 2/5] rust: macros: extend custom `quote!()` macro Jesung Yang
@ 2025-08-15 5:32 ` Jesung Yang
2025-08-15 5:32 ` [PATCH v2 4/5] rust: macros: add derive macro for `TryFrom` Jesung Yang
` (3 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Jesung Yang @ 2025-08-15 5:32 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot
Cc: linux-kernel, rust-for-linux, nouveau, Jesung Yang
Prefix the variable `span` in `quote_spanned!()` macro with an
underscore to silence unused variable warnings.
The warning occurs when the macro is used without any uninterpolated
identifiers. For example:
// Triggers a warning: "unused variable: `span`"
quote! { #foo }
// This is fine
quote! { Some(#foo) }
There is no good reason to disallow such quoting patterns, so fix the
warning instead.
Tested-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Jesung Yang <y.j3ms.n@gmail.com>
---
rust/macros/quote.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rust/macros/quote.rs b/rust/macros/quote.rs
index 24764b04a07d..75367a93e0d3 100644
--- a/rust/macros/quote.rs
+++ b/rust/macros/quote.rs
@@ -57,8 +57,8 @@ macro_rules! quote_spanned {
#[allow(clippy::vec_init_then_push)]
{
tokens = ::std::vec::Vec::new();
- let span = $span;
- quote_spanned!(@proc tokens span $($tt)*);
+ let _span = $span;
+ quote_spanned!(@proc tokens _span $($tt)*);
}
::proc_macro::TokenStream::from_iter(tokens)
}};
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 4/5] rust: macros: add derive macro for `TryFrom`
2025-08-15 5:32 [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Jesung Yang
` (2 preceding siblings ...)
2025-08-15 5:32 ` [PATCH v2 3/5] rust: macros: prefix variable `span` with underscore Jesung Yang
@ 2025-08-15 5:32 ` Jesung Yang
2025-08-15 5:32 ` [PATCH v2 5/5] rust: macros: add derive macro for `Into` Jesung Yang
` (2 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Jesung Yang @ 2025-08-15 5:32 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot
Cc: linux-kernel, rust-for-linux, nouveau, Jesung Yang
Introduce a procedural macro `TryFrom` to automatically implement the
`TryFrom` trait for unit-only enums.
This reduces boilerplate in cases where numeric values need to be
interpreted as relevant enum variants. This situation often arise when
working with low-level data sources. A typical example is the `Chipset`
enum in nova-core, where the value read from a GPU register should be
mapped to a corresponding variant.
Since a pending RFC [1] proposes adding the `syn` crate [2] as a
dependency, keep the parsing logic minimal.
Tested-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/rust-for-linux/20250304225536.2033853-1-benno.lossin@proton.me [1]
Link: https://docs.rs/syn/latest/syn [2]
Signed-off-by: Jesung Yang <y.j3ms.n@gmail.com>
---
rust/macros/convert.rs | 337 +++++++++++++++++++++++++++++++++++++++++
rust/macros/lib.rs | 124 +++++++++++++++
2 files changed, 461 insertions(+)
create mode 100644 rust/macros/convert.rs
diff --git a/rust/macros/convert.rs b/rust/macros/convert.rs
new file mode 100644
index 000000000000..0084bc4308c1
--- /dev/null
+++ b/rust/macros/convert.rs
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use proc_macro::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree};
+use std::iter::Peekable;
+
+#[derive(Debug)]
+struct TypeArgs {
+ helper: Vec<Ident>,
+ repr: Option<Ident>,
+}
+
+const VALID_TYPES: [&str; 12] = [
+ "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
+];
+
+pub(crate) fn derive_try_from(input: TokenStream) -> TokenStream {
+ derive(input)
+}
+
+fn derive(input: TokenStream) -> TokenStream {
+ let derive_target = "TryFrom";
+ let derive_helper = "try_from";
+
+ let mut tokens = input.into_iter().peekable();
+
+ let type_args = match parse_types(&mut tokens, derive_helper) {
+ Ok(type_args) => type_args,
+ Err(errs) => return errs,
+ };
+
+ // Skip until the `enum` keyword, including the `enum` itself.
+ for tt in tokens.by_ref() {
+ if matches!(tt, TokenTree::Ident(ident) if ident.to_string() == "enum") {
+ break;
+ }
+ }
+
+ let Some(TokenTree::Ident(enum_ident)) = tokens.next() else {
+ return format!(
+ "::core::compile_error!(\"`#[derive({derive_target})]` can only \
+ be applied to an enum\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap();
+ };
+
+ let mut errs = TokenStream::new();
+
+ if matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '<') {
+ errs.extend(
+ format!(
+ "::core::compile_error!(\"`#[derive({derive_target})]` \
+ does not support enums with generic parameters\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap(),
+ );
+ }
+
+ let Some(variants_group) = tokens.find_map(|tt| match tt {
+ TokenTree::Group(g) if g.delimiter() == Delimiter::Brace => Some(g),
+ _ => None,
+ }) else {
+ unreachable!("Enums have its corresponding body")
+ };
+
+ let enum_body_tokens = variants_group.stream().into_iter().peekable();
+ let variants = match parse_enum_variants(enum_body_tokens, &enum_ident, derive_target) {
+ Ok(variants) => variants,
+ Err(new_errs) => {
+ errs.extend(new_errs);
+ return errs;
+ }
+ };
+
+ if !errs.is_empty() {
+ return errs;
+ }
+
+ if type_args.helper.is_empty() {
+ // Extract the representation passed by `#[repr(...)]` if present.
+ // If nothing is specified, the default is `Rust` representation,
+ // which uses `isize` for the discriminant type.
+ // See: https://doc.rust-lang.org/reference/items/enumerations.html#r-items.enum.discriminant.repr-rust
+ let ty = type_args
+ .repr
+ .unwrap_or_else(|| Ident::new("isize", Span::mixed_site()));
+ impl_try_from(&ty, &enum_ident, &variants)
+ } else {
+ let impls = type_args
+ .helper
+ .iter()
+ .map(|ty| impl_try_from(ty, &enum_ident, &variants));
+ quote! { #(#impls)* }
+ }
+}
+
+fn parse_types(
+ attrs: &mut Peekable<token_stream::IntoIter>,
+ helper: &str,
+) -> Result<TypeArgs, TokenStream> {
+ let mut helper_args = vec![];
+ let mut repr_arg = None;
+
+ // Scan only the attributes. As soon as we see a token that is
+ // not `#`, we know we have consumed all attributes.
+ while let Some(TokenTree::Punct(p)) = attrs.peek() {
+ if p.as_char() != '#' {
+ unreachable!("Outer attributes start with `#`");
+ }
+ attrs.next();
+
+ // The next token should be a `Group` delimited by brackets.
+ // (e.g., #[try_from(u8, u16)])
+ // ^^^^^^^^^^^^^^^^^^^
+ let Some(TokenTree::Group(attr_group)) = attrs.next() else {
+ unreachable!("Outer attributes are surrounded by `[` and `]`");
+ };
+
+ let mut inner = attr_group.stream().into_iter();
+
+ // Extract the attribute identifier.
+ // (e.g., #[try_from(u8, u16)])
+ // ^^^^^^^^
+ let attr_name = match inner.next() {
+ Some(TokenTree::Ident(ident)) => ident.to_string(),
+ _ => unreachable!("Attributes have identifiers"),
+ };
+
+ if attr_name == helper {
+ match parse_helper_args(inner, helper) {
+ Ok(v) => helper_args.extend_from_slice(&v),
+ Err(errs) => return Err(errs),
+ }
+ } else if attr_name == "repr" {
+ repr_arg = parse_repr_args(inner);
+ }
+ }
+
+ Ok(TypeArgs {
+ helper: helper_args,
+ repr: repr_arg,
+ })
+}
+
+fn parse_repr_args(mut tt_group: impl Iterator<Item = TokenTree>) -> Option<Ident> {
+ // The next token should be a `Group` delimited by parentheses.
+ // (e.g., #[repr(C, u8)])
+ // ^^^^^^^
+ let Some(TokenTree::Group(args_group)) = tt_group.next() else {
+ unreachable!("`repr` attribute has at least one argument")
+ };
+
+ for arg in args_group.stream() {
+ if let TokenTree::Ident(type_ident) = arg {
+ if VALID_TYPES.contains(&type_ident.to_string().as_str()) {
+ return Some(type_ident);
+ }
+ }
+ }
+
+ None
+}
+
+fn parse_helper_args(
+ mut tt_group: impl Iterator<Item = TokenTree>,
+ helper: &str,
+) -> Result<Vec<Ident>, TokenStream> {
+ let mut errs = TokenStream::new();
+ let mut args = vec![];
+
+ // The next token should be a `Group`.
+ // (e.g., #[try_from(u8, u16)])
+ // ^^^^^^^^^
+ let Some(TokenTree::Group(args_group)) = tt_group.next() else {
+ return Err(format!(
+ "::core::compile_error!(\"`{helper}` attribute expects at \
+ least one integer type argument (e.g., `#[{helper}(u8)]`)\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap());
+ };
+
+ let raw_args = args_group.stream();
+ if raw_args.is_empty() {
+ return Err(format!(
+ "::core::compile_error!(\"`{helper}` attribute expects at \
+ least one integer type argument (e.g., `#[{helper}(u8)]`)\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap());
+ }
+
+ // Iterate over the attribute argument tokens to collect valid integer
+ // type identifiers.
+ let mut raw_args = raw_args.into_iter();
+ while let Some(tt) = raw_args.next() {
+ let TokenTree::Ident(type_ident) = tt else {
+ errs.extend(
+ format!(
+ "::core::compile_error!(\"`{helper}` attribute expects \
+ comma-separated integer type arguments \
+ (e.g., `#[{helper}(u8, u16)]`)\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap(),
+ );
+ return Err(errs);
+ };
+
+ if VALID_TYPES.contains(&type_ident.to_string().as_str()) {
+ args.push(type_ident);
+ } else {
+ errs.extend(
+ format!(
+ "::core::compile_error!(\"`{type_ident}` in `{helper}` \
+ attribute is not an integer type\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap(),
+ );
+ }
+
+ match raw_args.next() {
+ Some(TokenTree::Punct(p)) if p.as_char() == ',' => continue,
+ None => break,
+ Some(_) => {
+ errs.extend(
+ format!(
+ "::core::compile_error!(\"`{helper}` attribute expects \
+ comma-separated integer type arguments \
+ (e.g., `#[{helper}(u8, u16)]`)\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap(),
+ );
+ return Err(errs);
+ }
+ }
+ }
+
+ if !errs.is_empty() {
+ return Err(errs);
+ }
+
+ Ok(args)
+}
+
+fn parse_enum_variants(
+ mut tokens: Peekable<token_stream::IntoIter>,
+ enum_ident: &Ident,
+ derive_target: &str,
+) -> Result<Vec<Ident>, TokenStream> {
+ let mut errs = TokenStream::new();
+
+ let mut variants = vec![];
+
+ if tokens.peek().is_none() {
+ errs.extend(
+ format!(
+ "::core::compile_error!(\"`#[derive({derive_target})]` \
+ does not support zero-variant enums\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap(),
+ );
+ }
+
+ while let Some(tt) = tokens.next() {
+ // Skip attributes like `#[...]` if present.
+ if matches!(&tt, TokenTree::Punct(p) if p.as_char() == '#') {
+ tokens.next();
+ continue;
+ }
+
+ let TokenTree::Ident(ident) = tt else {
+ unreachable!("Enum variants have its corresponding identifier");
+ };
+
+ // Reject tuple-like or struct-like variants.
+ if let Some(TokenTree::Group(g)) = tokens.peek() {
+ let variant_kind = match g.delimiter() {
+ Delimiter::Brace => "struct-like",
+ Delimiter::Parenthesis => "tuple-like",
+ _ => unreachable!("Invalid enum variant syntax"),
+ };
+ errs.extend(
+ format!(
+ "::core::compile_error!(\"`#[derive({derive_target})]` does not \
+ support {variant_kind} variant `{enum_ident}::{ident}`; \
+ only unit variants are allowed\");"
+ )
+ .parse::<TokenStream>()
+ .unwrap(),
+ );
+ }
+
+ // Skip through the comma.
+ for tt in tokens.by_ref() {
+ if matches!(tt, TokenTree::Punct(p) if p.as_char() == ',') {
+ break;
+ }
+ }
+
+ variants.push(ident);
+ }
+
+ if !errs.is_empty() {
+ return Err(errs);
+ }
+
+ Ok(variants)
+}
+
+fn impl_try_from(ty: &Ident, enum_ident: &Ident, variants: &[Ident]) -> TokenStream {
+ let param = Ident::new("value", Span::mixed_site());
+
+ let clauses = variants.iter().map(|variant| {
+ quote! {
+ if #param == Self::#variant as #ty {
+ ::core::result::Result::Ok(Self::#variant)
+ } else
+ }
+ });
+
+ quote! {
+ #[automatically_derived]
+ impl ::core::convert::TryFrom<#ty> for #enum_ident {
+ type Error = ::kernel::prelude::Error;
+ fn try_from(#param: #ty) -> Result<Self, Self::Error> {
+ #(#clauses)* {
+ ::core::result::Result::Err(::kernel::prelude::EINVAL)
+ }
+ }
+ }
+ }
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index fa847cf3a9b5..569198f188f7 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -14,6 +14,7 @@
#[macro_use]
mod quote;
mod concat_idents;
+mod convert;
mod export;
mod helpers;
mod kunit;
@@ -425,3 +426,126 @@ pub fn paste(input: TokenStream) -> TokenStream {
pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
kunit::kunit_tests(attr, ts)
}
+
+/// A derive macro for generating an impl of the [`TryFrom`] trait.
+///
+/// This macro automatically derives [`TryFrom`] trait for a given enum. 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
+///
+/// The macro generates [`TryFrom`] implementations that:
+/// - Convert numeric values to enum variants by matching discriminant values.
+/// - Return `Ok(VARIANT)` for valid matches.
+/// - Return `Err(EINVAL)` for invalid matches (where `EINVAL` is from
+/// [`kernel::error::code`]).
+///
+/// The macro uses the `try_from` custom attribute or `repr` attribute to generate
+/// corresponding [`TryFrom`] implementations. `try_from` always takes precedence
+/// over `repr`.
+///
+/// [`kernel::error::code`]: ../kernel/error/code/index.html
+///
+/// # Caveats
+///
+/// Ensure that every integer type specified in `#[try_from(...)]` 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 `TryFrom<isize>`:
+///
+/// [repr-rs]: https://doc.rust-lang.org/reference/items/enumerations.html#r-items.enum.discriminant.repr-rust
+///
+/// ```rust
+/// use kernel::macros::TryFrom;
+/// use kernel::prelude::*;
+///
+/// #[derive(Debug, Default, PartialEq, TryFrom)]
+/// enum Foo {
+/// #[default]
+/// A,
+/// B = 0x17,
+/// }
+///
+/// assert_eq!(Foo::try_from(0isize), Ok(Foo::A));
+/// assert_eq!(Foo::try_from(0x17isize), Ok(Foo::B));
+/// assert_eq!(Foo::try_from(0x19isize), Err(EINVAL));
+/// ```
+///
+/// ## With `#[repr(T)]`
+///
+/// The macro implements `TryFrom<T>`:
+///
+/// ```rust
+/// use kernel::macros::TryFrom;
+/// use kernel::prelude::*;
+///
+/// #[derive(Debug, Default, PartialEq, TryFrom)]
+/// #[repr(u8)]
+/// enum Foo {
+/// #[default]
+/// A,
+/// B = 0x17,
+/// }
+///
+/// assert_eq!(Foo::try_from(0u8), Ok(Foo::A));
+/// assert_eq!(Foo::try_from(0x17u8), Ok(Foo::B));
+/// assert_eq!(Foo::try_from(0x19u8), Err(EINVAL));
+/// ```
+///
+/// ## With `#[try_from(...)]`
+///
+/// The macro implements `TryFrom<T>` for each `T` specified in `#[try_from(...)]`,
+/// which always overrides `#[repr(...)]`:
+///
+/// ```rust
+/// use kernel::macros::TryFrom;
+/// use kernel::prelude::*;
+///
+/// #[derive(Debug, Default, PartialEq, TryFrom)]
+/// #[try_from(u8, u16)]
+/// #[repr(u8)]
+/// enum Foo {
+/// #[default]
+/// A,
+/// B = 0x17,
+/// }
+///
+/// assert_eq!(Foo::try_from(0u16), Ok(Foo::A));
+/// assert_eq!(Foo::try_from(0x17u16), Ok(Foo::B));
+/// assert_eq!(Foo::try_from(0x19u16), Err(EINVAL));
+/// ```
+///
+/// ## Unsupported Cases
+///
+/// The following examples do not compile:
+///
+/// ```compile_fail
+/// # use kernel::macros::TryFrom;
+/// // Generic parameters are not allowed.
+/// #[derive(TryFrom)]
+/// enum Foo<T> {
+/// A,
+/// }
+///
+/// // Tuple-like enums or struct-like enums are not allowed.
+/// #[derive(TryFrom)]
+/// enum Bar {
+/// A(u8),
+/// B { inner: u8 },
+/// }
+///
+/// // Structs are not allowed.
+/// #[derive(TryFrom)]
+/// struct Baz(u8);
+/// ```
+#[proc_macro_derive(TryFrom, attributes(try_from))]
+pub fn derive_try_from(input: TokenStream) -> TokenStream {
+ convert::derive_try_from(input)
+}
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 5/5] rust: macros: add derive macro for `Into`
2025-08-15 5:32 [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Jesung Yang
` (3 preceding siblings ...)
2025-08-15 5:32 ` [PATCH v2 4/5] rust: macros: add derive macro for `TryFrom` Jesung Yang
@ 2025-08-15 5:32 ` Jesung Yang
2025-08-29 8:38 ` [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Alice Ryhl
2025-09-30 20:12 ` Miguel Ojeda
6 siblings, 0 replies; 18+ messages in thread
From: Jesung Yang @ 2025-08-15 5:32 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot
Cc: linux-kernel, rust-for-linux, nouveau, Jesung Yang
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.
Tested-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/rust-for-linux/20250624132337.2242-1-dakr@kernel.org/ [1]
Signed-off-by: Jesung Yang <y.j3ms.n@gmail.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
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
2025-08-15 5:32 [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Jesung Yang
` (4 preceding siblings ...)
2025-08-15 5:32 ` [PATCH v2 5/5] rust: macros: add derive macro for `Into` Jesung Yang
@ 2025-08-29 8:38 ` Alice Ryhl
2025-09-30 20:12 ` Miguel Ojeda
6 siblings, 0 replies; 18+ messages in thread
From: Alice Ryhl @ 2025-08-29 8:38 UTC (permalink / raw)
To: Jesung Yang
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Alexandre Courbot, linux-kernel,
rust-for-linux, nouveau
On Fri, Aug 15, 2025 at 05:32:10AM +0000, Jesung Yang wrote:
> This patch series introduces derive macros for `TryFrom` and `Into`
> traits.
>
> A few enhancements were made to the custom `quote!()` macro to write
> the derive macro. These include support for additional punctuation
> tokens and a fix for an unused variable warning when quoting simple
> forms. Detailed information about these enhancements is provided in the
> relevant patches.
>
> This series builds on the previous work [1], where the `FromPrimitive`
> trait was considered too heavy for the current use cases. In response
> to the emerging need for functionality similar to `ToPrimitive`, this
> series also implements the `Into` derive macro.
>
> The original discussion can be found on Zulip [2].
>
> [1] https://lore.kernel.org/rust-for-linux/cover.1750689857.git.y.j3ms.n@gmail.com/
> [2] https://rust-for-linux.zulipchat.com/#narrow/channel/288089/topic/x/near/524335626
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
2025-08-15 5:32 [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Jesung Yang
` (5 preceding siblings ...)
2025-08-29 8:38 ` [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros Alice Ryhl
@ 2025-09-30 20:12 ` Miguel Ojeda
2025-10-10 10:07 ` Jesung Yang
6 siblings, 1 reply; 18+ messages in thread
From: Miguel Ojeda @ 2025-09-30 20:12 UTC (permalink / raw)
To: Jesung Yang
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot, linux-kernel,
rust-for-linux, nouveau
On Fri, Aug 15, 2025 at 7:32 AM Jesung Yang <y.j3ms.n@gmail.com> wrote:
>
> This patch series introduces derive macros for `TryFrom` and `Into`
> traits.
I took a quick look -- it is a nice series!
Some notes:
- My biggest concern is the overflow caveat, which is a fairly big
one if one, especially if one is dealing with runtime values.
Can we do better? Accessing the discriminant via `as` is available
in const context, and you already have every variant easily available,
so can we check that every variant fits in the relevant target types?
For instance, by creating some item-level const blocks
(`static_assert!`s) -- dummy example for an unsigned-to-unsigned case:
const _: () = { assert! (E::A as u128 <= u8::MAX as u128); };
const _: () = { assert! (E::B as u128 <= u8::MAX as u128); };
...
and so on. There may be better ways to do it -- I just quickly
brute forced it that unsigned case with what you already had for
handling variants:
variants.iter().map(|variant| {
quote! {
const _: () = { assert!(#enum_ident::#variant as u128
<= #ty::MAX as u128); };
}
});
Maybe this was already discussed elsewhere and there is a reason
not to do something like this, but it seems to me that we should try
to avoid that risk.
- On other news, I will post in the coming days the `syn` patches,
and my plan is to merge them for next cycle, so when those are out,
Benno thought you could give them a go (we can still merge this with
your current approach and then convert, but still, more `syn` users
and converting the existing macros would be nice :).
(By the way, the linked patches about converting the existing
macros to `syn` are an RFC in the sense that they cannot be applied,
but having `syn` itself is something we already agreed on a long time
ago.)
- Couple nits: typo arise -> arises, and I would do `repr-rust`
instead of `repr-rs` since that is the anchor in the reference that
you are linking.
Thanks a lot!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
2025-09-30 20:12 ` Miguel Ojeda
@ 2025-10-10 10:07 ` Jesung Yang
2025-10-10 13:04 ` Miguel Ojeda
0 siblings, 1 reply; 18+ messages in thread
From: Jesung Yang @ 2025-10-10 10:07 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot, linux-kernel,
rust-for-linux, nouveau
Hi,
On Wed, Oct 1, 2025 at 5:13 AM Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
[...]
> - My biggest concern is the overflow caveat, which is a fairly big
> one if one, especially if one is dealing with runtime values.
>
> Can we do better? Accessing the discriminant via `as` is available
> in const context, and you already have every variant easily available,
> so can we check that every variant fits in the relevant target types?
>
> For instance, by creating some item-level const blocks
> (`static_assert!`s) -- dummy example for an unsigned-to-unsigned case:
>
> const _: () = { assert! (E::A as u128 <= u8::MAX as u128); };
> const _: () = { assert! (E::B as u128 <= u8::MAX as u128); };
> ...
>
> and so on. There may be better ways to do it -- I just quickly
> brute forced it that unsigned case with what you already had for
> handling variants:
>
> variants.iter().map(|variant| {
> quote! {
> const _: () = { assert!(#enum_ident::#variant as u128
> <= #ty::MAX as u128); };
> }
> });
>
> Maybe this was already discussed elsewhere and there is a reason
> not to do something like this, but it seems to me that we should try
> to avoid that risk.
Thanks, I see your point, and I agree that compile-time checking for
potential overflows is a better and safer approach.
That said, it becomes a bit trickier when dealing with conversions
between signed and unsigned types, particularly when `u128` and `i128`
are involved. For example:
#[derive(TryFrom, Into)]
#[try_from(u128)]
#[into(u128)]
#[repr(i128)]
enum MyEnum {
A = 0xffffffffffffffff0, // larger than u64::MAX
B = -1
}
In this case, since there's no numeric type that can encompass both
`u128` and `i128`, I don't think we can express a compile-time
assertion like the one you suggested. While such edge cases involving
128-bit numeric types are unlikely in practice, the broader challenge
is that, in signed-to-unsigned conversions, I think it's difficult to
detect overflows using only the `repr` type, the target type, and the
discriminant value interpreted as the target type (please correct me if
I've misunderstood something here).
I'm considering an alternative approach: performing these checks while
parsing the macro inputs, to handle all combinations of `repr` and
target types (such as `u128` in the above example) in a unified way. I
believe this makes the behavior easier to reason about and better
covers edge cases like conversions between `i128` and `u128`. For
example:
const U128_ALLOWED: [&str; 9] =
["u8", "i8", "u16", "i16", "i32", "u32", "u64", "i64", "u128"];
const I128_ALLOWED: [&str; 9] =
["u8", "i8", "u16", "i16", "i32", "u32", "u64", "i64", "i128"];
...
// Use this function after parsing `#[repr(...)]`, `#[into(u128)]`
// and `#[try_from(...)]`.
fn check_overflow(
discriminant_repr: &str,
helper_inputs: Vec<&str>
) -> Vec<&str> {
let mut violations = Vec::new();
if discriminant_repr == "u128" {
for ty in helper_inputs.iter() {
if !U128_ALLOWED.contains(&ty) {
violations.push(ty);
}
}
} else if discriminant_repr == "i128" {
for ty in helper_inputs.iter() {
if !I128_ALLOWED.contains(&ty) {
violations.push(ty);
}
}
}
...
violations
}
This is a rough sketch, but it gives a consistent way to reject
obviously invalid combinations early during parsing. I'd appreciate
your thoughts -- does this approach seem reasonable to you as well?
> - On other news, I will post in the coming days the `syn` patches,
> and my plan is to merge them for next cycle, so when those are out,
> Benno thought you could give them a go (we can still merge this with
> your current approach and then convert, but still, more `syn` users
> and converting the existing macros would be nice :).
>
> (By the way, the linked patches about converting the existing
> macros to `syn` are an RFC in the sense that they cannot be applied,
> but having `syn` itself is something we already agreed on a long time
> ago.)
Sounds good -- I'd be happy to give `syn` a try. It should simplify
the parsing logic quite a bit, and I believe it'll also make things
easier for reviewers.
> - Couple nits: typo arise -> arises, and I would do `repr-rust`
> instead of `repr-rs` since that is the anchor in the reference that
> you are linking.
Thanks, I'll fix them in v3.
Best Regards,
Jesung
>
> Thanks a lot!
>
> Cheers,
> Miguel
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
2025-10-10 10:07 ` Jesung Yang
@ 2025-10-10 13:04 ` Miguel Ojeda
2025-10-11 14:53 ` Jesung Yang
0 siblings, 1 reply; 18+ messages in thread
From: Miguel Ojeda @ 2025-10-10 13:04 UTC (permalink / raw)
To: Jesung Yang
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot, linux-kernel,
rust-for-linux, nouveau
On Fri, Oct 10, 2025 at 12:08 PM Jesung Yang <y.j3ms.n@gmail.com> wrote:
>
> That said, it becomes a bit trickier when dealing with conversions
> between signed and unsigned types, particularly when `u128` and `i128`
> are involved. For example:
Yeah, it is why I said it was a dummy unsigned case -- I didn't mean
that single comparison would work for all cases.
But what case do you think we cannot assert? We can always take the
discriminant and reject whatever inputs (e.g. ranges) we decide, no?
And we know what type we are going into, so we can always decide what
the values to check will be, i.e. we could in principle even support
infallible conversions of the discriminant to other types like, say,
the bounded integers or powers of two.
Maybe the issue is in what you say at "the discriminant value
interpreted as the target type" -- I am not sure what you mean by
"interpreted", i.e. I would think of this as accepting only some bit
patterns, i.e. working with in the discriminant space, not the target
type one.
I may be missing something, but in any case, at the end of the day,
within a proc macro "everything" should be possible one way or another
-- even if we had to inspect manually the literals :) And it seems
worth to remove the pitfall.
If really needed, we can always drop support for certain combinations.
We already do, in the sense that we don't cover every single other
type out there, like the ones I mention above, e.g. `Alignment`. But,
just in case, I assume with that approach you mean skipping some
combinations early (the ones that cannot be checked) and then still
asserting the discriminants, right? Otherwise the caveat is still
there.
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
2025-10-10 13:04 ` Miguel Ojeda
@ 2025-10-11 14:53 ` Jesung Yang
2025-10-14 6:35 ` Alexandre Courbot
0 siblings, 1 reply; 18+ messages in thread
From: Jesung Yang @ 2025-10-11 14:53 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot, linux-kernel,
rust-for-linux, nouveau
Hi,
On Fri, Oct 10, 2025 at 10:04 PM Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
[...]
> But what case do you think we cannot assert? We can always take the
> discriminant and reject whatever inputs (e.g. ranges) we decide, no?
> And we know what type we are going into, so we can always decide what
> the values to check will be, i.e. we could in principle even support
> infallible conversions of the discriminant to other types like, say,
> the bounded integers or powers of two.
>
> Maybe the issue is in what you say at "the discriminant value
> interpreted as the target type" -- I am not sure what you mean by
> "interpreted", i.e. I would think of this as accepting only some bit
> patterns, i.e. working with in the discriminant space, not the target
> type one.
>
> I may be missing something, but in any case, at the end of the day,
> within a proc macro "everything" should be possible one way or another
> -- even if we had to inspect manually the literals :) And it seems
> worth to remove the pitfall.
>
> If really needed, we can always drop support for certain combinations.
> We already do, in the sense that we don't cover every single other
> type out there, like the ones I mention above, e.g. `Alignment`. But,
> just in case, I assume with that approach you mean skipping some
> combinations early (the ones that cannot be checked) and then still
> asserting the discriminants, right? Otherwise the caveat is still
> there.
Sorry about the confusion -- the rough sketch I shared earlier had
several mistakes.
My actual intention was to emit a compile-time error using
`compile_error!()` whenever a conversion could overflow. With this
approach, the caveat wouldn't exist, since proc macro users wouldn't be
able to generate `TryFrom` or `Into` (`From`) implementations that
could potentially cause overflow issues. For example:
// This emits a compile-time error because not all `i128` values
// can be converted to `u128`, even though 0 and 1 are valid `u128`
// values.
#[derive(TryFrom, Into)]
#[try_from(u128)]
#[into(u128)]
#[repr(i128)]
enum MyEnum {
A = 0,
B = 1,
}
To make this idea work as intended, I should have revised the earlier
sketch as follows:
- const U128_ALLOWED: [&str; 9] =
- ["u8", "i8", "u16", "i16", "i32", "u32", "u64", "i64", "u128"];
- const I128_ALLOWED: [&str; 9] =
- ["u8", "i8", "u16", "i16", "i32", "u32", "u64", "i64", "i128"];
+ // Allowed helper inputs when `repr(u128)` is used.
+ const U128_ALLOWED: [&str; 1] = ["u128"];
+ // Allowed helper inputs when `repr(i128)` is used.
+ const I128_ALLOWED: [&str; 1] = ["i128"];
The downside of this approach is that, as you can see, it is overly
restrictive for large target types such as `u128` and `i128`, because
the remaining numeric types cannot accommodate their full range. As a
result, the macros could reject some valid use cases, for example when
the actual discriminants can be converted without overflow, since the
check operates at the type level rather than on specific discriminants.
Considering this, and as you suggested, I think the right direction is
to introduce a compile-time check for each discriminant. I initially
thought it would be difficult, but after some exploration, it seems
doable.
Thanks a lot for your feedback, I really appreciate it!
Best Regards,
Jesung
> Thanks!
>
> Cheers,
> Miguel
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
2025-10-11 14:53 ` Jesung Yang
@ 2025-10-14 6:35 ` Alexandre Courbot
2025-11-03 0:18 ` Jesung Yang
0 siblings, 1 reply; 18+ messages in thread
From: Alexandre Courbot @ 2025-10-14 6:35 UTC (permalink / raw)
To: Jesung Yang, Miguel Ojeda
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Alexandre Courbot, linux-kernel,
rust-for-linux, nouveau
On Sat Oct 11, 2025 at 11:53 PM JST, Jesung Yang wrote:
> Hi,
>
> On Fri, Oct 10, 2025 at 10:04 PM Miguel Ojeda
> <miguel.ojeda.sandonis@gmail.com> wrote:
> [...]
>> But what case do you think we cannot assert? We can always take the
>> discriminant and reject whatever inputs (e.g. ranges) we decide, no?
>> And we know what type we are going into, so we can always decide what
>> the values to check will be, i.e. we could in principle even support
>> infallible conversions of the discriminant to other types like, say,
>> the bounded integers or powers of two.
>>
>> Maybe the issue is in what you say at "the discriminant value
>> interpreted as the target type" -- I am not sure what you mean by
>> "interpreted", i.e. I would think of this as accepting only some bit
>> patterns, i.e. working with in the discriminant space, not the target
>> type one.
>>
>> I may be missing something, but in any case, at the end of the day,
>> within a proc macro "everything" should be possible one way or another
>> -- even if we had to inspect manually the literals :) And it seems
>> worth to remove the pitfall.
>>
>> If really needed, we can always drop support for certain combinations.
>> We already do, in the sense that we don't cover every single other
>> type out there, like the ones I mention above, e.g. `Alignment`. But,
>> just in case, I assume with that approach you mean skipping some
>> combinations early (the ones that cannot be checked) and then still
>> asserting the discriminants, right? Otherwise the caveat is still
>> there.
>
> Sorry about the confusion -- the rough sketch I shared earlier had
> several mistakes.
>
> My actual intention was to emit a compile-time error using
> `compile_error!()` whenever a conversion could overflow. With this
> approach, the caveat wouldn't exist, since proc macro users wouldn't be
> able to generate `TryFrom` or `Into` (`From`) implementations that
> could potentially cause overflow issues. For example:
>
> // This emits a compile-time error because not all `i128` values
> // can be converted to `u128`, even though 0 and 1 are valid `u128`
> // values.
> #[derive(TryFrom, Into)]
> #[try_from(u128)]
> #[into(u128)]
> #[repr(i128)]
> enum MyEnum {
> A = 0,
> B = 1,
> }
>
> To make this idea work as intended, I should have revised the earlier
> sketch as follows:
>
> - const U128_ALLOWED: [&str; 9] =
> - ["u8", "i8", "u16", "i16", "i32", "u32", "u64", "i64", "u128"];
> - const I128_ALLOWED: [&str; 9] =
> - ["u8", "i8", "u16", "i16", "i32", "u32", "u64", "i64", "i128"];
> + // Allowed helper inputs when `repr(u128)` is used.
> + const U128_ALLOWED: [&str; 1] = ["u128"];
> + // Allowed helper inputs when `repr(i128)` is used.
> + const I128_ALLOWED: [&str; 1] = ["i128"];
>
> The downside of this approach is that, as you can see, it is overly
> restrictive for large target types such as `u128` and `i128`, because
> the remaining numeric types cannot accommodate their full range. As a
> result, the macros could reject some valid use cases, for example when
> the actual discriminants can be converted without overflow, since the
> check operates at the type level rather than on specific discriminants.
>
> Considering this, and as you suggested, I think the right direction is
> to introduce a compile-time check for each discriminant. I initially
> thought it would be difficult, but after some exploration, it seems
> doable.
>
> Thanks a lot for your feedback, I really appreciate it!
Note that if we adopt the bounded integer types [1], this problem might
get exacerbated.
The initial motivation for these macros was automatically generate the
conversions between register fields and their Rust enum. This was
working fine as long as the fields were represented using primitive
integers, but we will likely switch to more restrictive types where only
a given set of bits is valid. So it is highly desirable to support
conversion from/to these bounded types as well.
This might not be too difficult to shoehorn as there is an
`is_in_bounds!` macro (which we can turn into a const method if that's
more suitable) that your proc macro could leverage, but I can't help but
thing that maybe there is a better, more general solution than
special-casing this?
[1] https://lore.kernel.org/rust-for-linux/20251009-bounded_ints-v2-0-ff3d7fee3ffd@nvidia.com/
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
2025-10-14 6:35 ` Alexandre Courbot
@ 2025-11-03 0:18 ` Jesung Yang
0 siblings, 0 replies; 18+ messages in thread
From: Jesung Yang @ 2025-11-03 0:18 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, linux-kernel, rust-for-linux,
nouveau
On Tue, Oct 14, 2025 at 3:35 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
[...]
> This might not be too difficult to shoehorn as there is an
> `is_in_bounds!` macro (which we can turn into a const method if that's
> more suitable) that your proc macro could leverage, but I can't help but
> thing that maybe there is a better, more general solution than
> special-casing this?
>
> [1] https://lore.kernel.org/rust-for-linux/20251009-bounded_ints-v2-0-ff3d7fee3ffd@nvidia.com/
I've tried your `BitInt` series, and I think I can generalize (at
least the overflow check) even without the help of `fits_within!`.
Since I can retrieve the number of valid bits and the signedness of a
given integer type (including `BitInt`) while parsing the helper
attributes, I can use that information to check whether casting from
a value of type T to type U would overflow. Here's a sketch using
declarative macro syntax to demonstrate the idea:
```
// I'll write a function that returns equivalent `TokenStream`, which
// is suitable for procedural macros.
macro_rules! check_overflow {
($val:expr, $src_ty:ty, $dst_ty:ty, $dst_nbits:expr) => {{
let val: $src_ty = $val;
// For every integer type (including `BitInt`), its minimum
// value always fits in `i128`.
let dst_min =
(<$dst_ty>::MIN >> (<$dst_ty>::BITS - ($dst_nbits))) as i128;
// For every integer type (including `BitInt`), its maximum
// value always fits in `u128`.
let dst_max =
(<$dst_ty>::MAX >> (<$dst_ty>::BITS - ($dst_nbits))) as u128;
#[allow(unused_comparisons)]
let is_src_signed = <$src_ty>::MIN < 0;
#[allow(unused_comparisons)]
let is_dst_signed = dst_min < 0;
let fits = if is_src_signed && is_dst_signed {
// Casting from a signed value to `i128` does not
// overflow since `i128` is the largest signed
// primitive integer type.
(val as i128) >= dst_min && val <= dst_max
} else if !is_src_signed && !is_dst_signed {
// Casting from an unsigned value to `u128` does not
// overflow since `u128` is the largest unsigned
// primitive integer type.
(val as u128) <= dst_max
} else if is_src_signed && !is_dst_signed {
// Casting from a signed value greater than 0 to `u128`
// does not overflow since since `u128::MAX` is
// greater than `i128::MAX`.
val >= 0 && (val as u128) <= dst_max
} else {
// Casting from an unsigned value to `u128` does not
// overflow since `u128` is the largest unsigned
// primitive integer type.
(val as u128) <= dst_max
};
!fits
}};
// Yes, we can also support `bool`!
($val:expr, $src_ty:ty, bool) => {{
let val: $src_ty = $val;
let fits = val == 0 || val == 1;
!fits
}};
}
// For a `#[repr(i32)]` enum with `#[try_from(BitInt<u8, 4>)]` and
// `#[into(BitInt<u8, 4>)]`:
check_overflow!(Enum::A as i32, i32, u8, 4);
// For a `#[repr(i32)]` enum with `#[try_from(u8)]` and `#[into(u8)]`:
check_overflow!(Enum::A as i32, i32, u8, u8::BITS);
// For a `#[repr(i32)]` enum with `#[try_from(bool)]` and
// `#[into(bool)]`:
check_overflow!(Enum::A as i32, i32, bool);
```
It is somewhat similar to `fits_within!`, but it also cares signedness
differences between source and destination types.
It might help if `BitInt` exposed `MIN` and `MAX` associated constants
in terms of its backing type:
```
macro_rules! impl_max_min {
($($type:ty),+) => {
$(
impl<const NUM_BITS: u32> BitInt<$type, NUM_BITS> {
pub const MIN: $type =
<$type>::MIN >> (<$type>::BITS - NUM_BITS);
pub const MAX: $type =
<$type>::MAX >> (<$type>::BITS - NUM_BITS);
}
)+
};
}
impl_max_min!(u8, u16, u32, u64, i8, i16, i32, i64);
```
... but for consistency with existing primitive integer types, these
constants would ideally be of type `BitInt` instead of the backing
type, which unfortunately limits their usefulness in implementing
`check_overflow!`.
As a final side note: as you're already aware, breaking changes to
`BitInt` will also affect the `TryFrom` and `Into` derive macros, since
these macros depend on certain public APIs provided by `BitInt`. The
same applies to any future custom types that require support from these
macros. That said, I'll do my best to minimize the dependency wherever
possible.
Best Regards,
Jesung
^ permalink raw reply [flat|nested] 18+ messages in thread