From: Alexandre Courbot <acourbot@nvidia.com>
To: "Yury Norov" <yury.norov@gmail.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Onur Özkan" <work@onurozkan.dev>
Cc: John Hubbard <jhubbard@nvidia.com>,
Alistair Popple <apopple@nvidia.com>,
Timur Tabi <ttabi@nvidia.com>,
Eliot Courtney <ecourtney@nvidia.com>,
Zhi Wang <zhiw@nvidia.com>,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
nova-gpu@lists.linux.dev,
Alexandre Courbot <acourbot@nvidia.com>
Subject: [PATCH 1/2] rust: num: casts: replace const type narrowing methods with a macro
Date: Tue, 25 Aug 2026 11:44:47 +0900 [thread overview]
Message-ID: <20260825-const_as-v1-1-1ce712225fe2@nvidia.com> (raw)
In-Reply-To: <20260825-const_as-v1-0-1ce712225fe2@nvidia.com>
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@nvidia.com>
---
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.
@@ -36,6 +34,9 @@
//! // Conversion from const context.
//! const USIZED_CONST: usize = casts::u8_as_usize(255u8);
//!
+//! // Build-time checked narrowing conversion of a constant expression.
+//! const NARROWED_CONST: u16 = casts::const_as!(0xf00u32 => u16);
+//!
//! // Non-const conversions.
//! let a = u64::from_safe_cast(4096usize);
//! let b: u64 = 4096usize.into_safe_cast();
@@ -182,57 +183,85 @@ fn into_safe_cast(self) -> T {
}
}
-/// Implements lossless conversion of a constant from a larger type into a smaller one.
-macro_rules! impl_const_into {
- ($from:ty => { $($into:ty),* }) => {
- $(
- $crate::macros::paste! {
- #[doc = ::core::concat!(
- "Performs a build-time safe conversion of a [`",
- ::core::stringify!($from),
- "`] constant value into a [`",
- ::core::stringify!($into),
- "`].")]
- ///
- /// This checks at compile-time that the conversion is lossless, and triggers a build
- /// error if it isn't.
- ///
- /// # Examples
- ///
- /// ```
- /// use kernel::num::casts;
- ///
- /// // Succeeds because the value of the source fits into the destination's type.
- #[doc = ::core::concat!(
- "assert_eq!(casts::",
- ::core::stringify!($from),
- "_into_",
- ::core::stringify!($into),
- "::<1",
- ::core::stringify!($from),
- ">(), 1",
- ::core::stringify!($into),
- ");")]
- /// ```
- #[inline]
- pub const fn [<$from _into_ $into>]<const N: $from>() -> $into {
- // Make sure that the target type is smaller than the source one.
- $crate::static_assert!($from::BITS >= $into::BITS);
- // CAST: we statically enforced above that `$from` is larger than `$into`, so the
- // `as` conversion will be lossless.
- $crate::const_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);
+/// Losslessly casts a constant expression into a target integer type, or fails the build.
+///
+/// This is a checked replacement for the `as` keyword on constant expressions: the conversion is
+/// evaluated at build time, and a build error is triggered if the source value does not fit into
+/// the destination type. Since the compiler verifies that the conversion is lossless, a `CAST`
+/// comment is not needed.
+///
+/// The argument is a constant expression.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::num::casts;
+///
+/// // Narrows the type of a constant in const context.
+/// const CAP_ID: u16 = casts::const_as!(0x0010u32 => u16);
+/// assert_eq!(CAP_ID, 0x0010u16);
+///
+/// // Widens the type of a constant, outside of const context.
+/// let v: u64 = casts::const_as!(42u16 => u64);
+/// assert_eq!(v, 42u64);
+///
+/// // Signed conversions work as well...
+/// assert_eq!(casts::const_as!(-42i32 => i16), -42i16);
+///
+/// // ...and so do cross-signedness conversions as long as the value fits.
+/// assert_eq!(casts::const_as!(258i32 => u16), 258u16);
+/// ```
+///
+/// A value that does not fit into the destination type fails to build:
+///
+/// ```ignore,compile_fail
+/// # use kernel::num::casts;
+/// // Fails to build: `0x10000` does not fit into a `u16`.
+/// const ID: u16 = casts::const_as!(0x10000u32 => u16);
+/// ```
+///
+/// Conversions that alter the value also fail to build:
+///
+/// ```ignore,compile_fail
+/// # use kernel::num::casts;
+/// // Fails to build: `-1i64 as u64` yields `u64::MAX`.
+/// const V: u64 = casts::const_as!(-1i64 => u64);
+/// ```
+///
+/// Runtime values are rejected:
+///
+/// ```ignore,compile_fail
+/// # use kernel::num::casts;
+/// fn f(v: u32) -> u16 {
+/// // Fails to build: `v` is not a constant expression.
+/// casts::const_as!(v => u16)
+/// }
+/// ```
+#[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"
+ );
+
+ r
}
}
- )*
};
}
-
-impl_const_into!(usize => { u8, u16, u32 });
-impl_const_into!(u64 => { u8, u16, u32 });
-impl_const_into!(u32 => { u8, u16 });
-impl_const_into!(u16 => { u8 });
+#[doc(inline)]
+pub use const_as;
/// Conversions that are only lossless for the current architecture.
///
--
2.55.0
next prev parent reply other threads:[~2026-08-25 2:45 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 2:44 [PATCH 0/2] rust: num: casts: replace const type narrowing methods with a macro Alexandre Courbot
2026-08-25 2:44 ` Alexandre Courbot [this message]
2026-08-25 7:18 ` [PATCH 1/2] " Eliot Courtney
2026-08-26 11:16 ` Alexandre Courbot
2026-08-26 13:27 ` Alexandre Courbot
2026-08-25 8:25 ` Miguel Ojeda
2026-08-25 12:01 ` Gary Guo
2026-08-25 14:26 ` Alexandre Courbot
2026-08-25 14:39 ` Gary Guo
2026-08-26 11:00 ` Alexandre Courbot
2026-08-26 12:06 ` Gary Guo
2026-08-25 13:54 ` Alexandre Courbot
2026-08-25 14:02 ` Danilo Krummrich
2026-08-25 14:11 ` Gary Guo
2026-08-25 14:30 ` Alexandre Courbot
2026-08-25 12:04 ` Gary Guo
2026-08-25 2:44 ` [PATCH 2/2] gpu: nova-core: use kernel lossless integer conversion module Alexandre Courbot
2026-08-25 5:27 ` Eliot Courtney
2026-08-27 1:36 ` Alexandre Courbot
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=20260825-const_as-v1-1-1ce712225fe2@nvidia.com \
--to=acourbot@nvidia.com \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=apopple@nvidia.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=ecourtney@nvidia.com \
--cc=gary@garyguo.net \
--cc=jhubbard@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=nova-gpu@lists.linux.dev \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=ttabi@nvidia.com \
--cc=work@onurozkan.dev \
--cc=yury.norov@gmail.com \
--cc=zhiw@nvidia.com \
/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.