NVIDIA GPU driver infrastructure
 help / color / mirror / Atom feed
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


  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox