[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CA+tqQ4+NY2zFJrr2omGqQcVG1N4cJBgYNO8qH=TFi5UG_vLfTA@mail.gmail.com>
Date: Mon, 3 Nov 2025 09:18:35 +0900
From: Jesung Yang <y.j3ms.n@...il.com>
To: Alexandre Courbot <acourbot@...dia.com>
Cc: Miguel Ojeda <miguel.ojeda.sandonis@...il.com>, 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>, linux-kernel@...r.kernel.org,
rust-for-linux@...r.kernel.org, nouveau@...ts.freedesktop.org
Subject: Re: [PATCH v2 0/5] rust: add `TryFrom` and `Into` derive macros
On Tue, Oct 14, 2025 at 3:35 PM Alexandre Courbot <acourbot@...dia.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
Powered by blists - more mailing lists