Re: [PATCH 1/2] rust: num: casts: replace const type narrowing methods with a macro

From: Alexandre Courbot

Date: Wed Aug 26 2026 - 09:32:27 EST


On Wed Aug 26, 2026 at 8:16 PM JST, Alexandre Courbot wrote:
> On Tue Aug 25, 2026 at 4:18 PM JST, Eliot Courtney wrote:
>> On Tue Aug 25, 2026 at 11:44 AM JST, Alexandre Courbot wrote:
>>> The casts module features a series of const converters (e.g.
>>> `u32_into_u16`) that narrow the type of a const expression provided that
>>> its value can be proven to fit into the destination type at
>>> compile-time.
>>>
>>> These functions are numerous (9 of them), generated by a macro and thus
>>> not easily discoverable, and cumbersome to use as they require a
>>> turbofish and const expression between `{` and `}` braces.
>>>
>>> Replace them all by a single `const_as!` macro that expands to a const
>>> block verifying the lossless nature of the conversion at compile-time.
>>> This turns e.g.:
>>>
>>> const DMA_LEN: u32 = casts::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
>>>
>>> into
>>>
>>> const DMA_LEN: u32 = casts::const_as!(MEM_BLOCK_ALIGNMENT => u32);
>>>
>>> This makes things easier to read and understand, while shifting the
>>> burden of checking the conversion's validity from reviewers (via a CAST
>>> comment) to the compiler.
>>>
>>> Signed-off-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
>>> ---
>>> rust/kernel/num/casts.rs | 129 +++++++++++++++++++++++++++++------------------
>>> 1 file changed, 79 insertions(+), 50 deletions(-)
>>>
>>> diff --git a/rust/kernel/num/casts.rs b/rust/kernel/num/casts.rs
>>> index 7e6c7dec747d..a4a18a6f2ba8 100644
>>> --- a/rust/kernel/num/casts.rs
>>> +++ b/rust/kernel/num/casts.rs
>>> @@ -20,10 +20,8 @@
>>> //! - Two extension traits, [`FromSafeCast`] and [`IntoSafeCast`], providing conversion methods
>>> //! similar to [`From`] and [`Into`] for conversions that are safe to perform in the kernel, but
>>> //! not supported by the standard library.
>>> -//! - Another series of const functions (e.g. [`u64_into_u8`]) supporting the conversion of a const
>>> -//! value from a larger type into a smaller one, provided the value fits into the destination
>>> -//! type. This is useful if a constant is defined as a larger type, but needs to be used as a
>>> -//! smaller one.
>>> +//! - A [`const_as!`] macro, losslessly casting a constant expression between any two integer
>>> +//! types, with conversions that would alter the value reported as build errors.
>>> //! - An [`arch`] sub-module, defining more conversion functions that are only guaranteed to be
>>> //! lossless for a given pointer size. These can only be used in code that is specific to a
>>> //! given pointer size.
>>
>> Can we add guidance somewhere in this file on when to use const_as! vs
>> when to use the u8_as_usize etc ones, when both could work? e.g. use
>> const_as! if you can, otherwise use the function version, or, use the
>> function version if it's sufficient (types alone are enough to prove)
>> otherwise use the macro.
>
> Yes, that's a very good idea and the use we are currently doing in Nova
> is also not consistent. Basically I think that the priority should be,
> in order of preference:
>
> - stdlib's `From`,
> - `FromSafeCast`/`IntoSafeCast` (for non-const contexts)
> - `const_as!` (for constant expressions including narrowing)
> - `*_as_*` (for const fns with a runtime value)
>
>>
>> [...]
>>> +#[macro_export]
>>> +#[doc(hidden)]
>>> +macro_rules! const_as {
>>> + ($v:expr => $into:ty) => {
>>> + const {
>>> + #[allow(unused_comparisons, unused_assignments, clippy::as_underscore)]
>>> + {
>>> + let v = $v;
>>> + let r = v as $into;
>>> + // Pin `back` to `v`'s type so `as _` casts back to the source type.
>>> + let mut back = v;
>>> + back = r as _;
>>>
>>> - N as $into
>>> + ::core::assert!(
>>> + back == v && (v < 0) == (r < 0),
>>> + "value does not fit into the target type"
>>> + );
>>
>> What about giving some text on what doesn't fit where? e.g.
>> ::core::concat!("`", ::core::stringify!($v), "` does not fit into `", ::core::stringify!($into), "`")
>
> Will do.

... actually, that's probably not needed. Compiler diagnostics already
print the faulting line verbatim, so the expression is fully visible.
Stringifying it would just duplicate information that sits on the very
next line.