All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexandre Courbot <acourbot@nvidia.com>
To: "Alexandre Courbot" <acourbot@nvidia.com>,
	"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>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>
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>,
	 linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
	 nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org
Subject: [PATCH v2 1/2] rust: add functions and traits for lossless integer conversions
Date: Thu, 06 Aug 2026 16:35:53 +0900	[thread overview]
Message-ID: <20260806-as_casts-v2-1-cb76a4d3a6ef@nvidia.com> (raw)
In-Reply-To: <20260806-as_casts-v2-0-cb76a4d3a6ef@nvidia.com>

The core library's `From` implementations do not cover conversions that
are not portable or future-proof. For instance, even though it is safe
today, `From<usize>` is not implemented for `u64` because of the
possibility of supporting larger-than-64bit architectures in the future.

However, the kernel supports a narrower set of architectures, with a
considerable amount of code that is architecture-specific. This makes it
helpful and desirable to provide more infallible conversions, lest we
rely on the `as` keyword and carry the risk of silently losing data.

Thus, introduce a new module `num::casts` that provides safe const
functions performing more conversions allowed by the build target, as
well as `FromSafeCast` and `IntoSafeCast` traits that are just
extensions of `From` and `Into` to conversions that are known to be
lossless.

Some conversions are architecture-specific: for instance, converting a
`u64` to a `usize` is only lossless on 64-bit platforms. These
conversions are made available via a dedicated `arch` sub-module.

Suggested-by: Danilo Krummrich <dakr@kernel.org>
Link: https://lore.kernel.org/rust-for-linux/DDK4KADWJHMG.1FUPL3SDR26XF@kernel.org/
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
 rust/kernel/num.rs       |   2 +
 rust/kernel/num/casts.rs | 298 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 300 insertions(+)

diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs
index 8532b511384c..dbe848e30efe 100644
--- a/rust/kernel/num.rs
+++ b/rust/kernel/num.rs
@@ -5,6 +5,8 @@
 use core::ops;
 
 pub mod bounded;
+pub mod casts;
+
 pub use bounded::*;
 
 /// Designates unsigned primitive types.
diff --git a/rust/kernel/num/casts.rs b/rust/kernel/num/casts.rs
new file mode 100644
index 000000000000..a44397541cfe
--- /dev/null
+++ b/rust/kernel/num/casts.rs
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Helpers for performing lossless integer casts.
+//!
+//! The `as` keyword can be used to perform casts between integer types, but it unfortunately makes
+//! no distinction between casts that are lossless, and casts from a larger type into a smaller one
+//! that might silently strip data away. Thus, its use in the kernel is discouraged in favor of
+//! [`From`] implementations.
+//!
+//! Conversely, there are casts that are lossless depending on the build architecture (such as
+//! casting [`usize`] to [`u64`] on 32 or 64 bit archs), but not supported by [`From`]
+//! implementations in the standard library because they are not portable. It does however make
+//! sense for the kernel to support these, if only for code that is architecture-specific.
+//!
+//! This module provides ways to perform such conversions safely:
+//!
+//! - A series of const functions (e.g. [`usize_as_u64`]) supporting safe conversions in const
+//!   context. Conversions supported by [`From`] implementations in the standard library are also
+//!   covered as the [`From`] trait cannot be used in const context.
+//! - 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.
+//! - 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.
+//!
+//! # Examples
+//!
+//! ```
+//! use kernel::num::casts::{self, FromSafeCast, IntoSafeCast};
+//!
+//! // Conversion from const context.
+//! const USIZED_CONST: usize = casts::u8_as_usize(255u8);
+//!
+//! // Non-const conversions.
+//! let a = u64::from_safe_cast(4096usize);
+//! let b: u64 = 4096usize.into_safe_cast();
+//! ```
+
+use crate::prelude::*;
+
+/// Implements safe `as` conversion functions from a given type into a series of target types.
+///
+/// These functions can be used in place of `as`, with the guarantee that they will be lossless.
+macro_rules! impl_safe_as {
+    ($from:ty as { $($into:ty),* }) => {
+        $(
+        $crate::macros::paste! {
+            #[doc = ::core::concat!(
+                "Losslessly converts a [`",
+                ::core::stringify!($from),
+                "`] into a [`",
+                ::core::stringify!($into),
+                "`].")]
+            ///
+            /// This conversion is allowed as it is always lossless. Prefer this over the `as`
+            /// keyword to ensure no lossy casts are performed.
+            ///
+            /// This is for use from a `const` context. For non `const` use, prefer the
+            /// [`FromSafeCast`] and [`IntoSafeCast`] traits.
+            ///
+            /// # Examples
+            ///
+            /// ```
+            /// use kernel::num::casts;
+            ///
+            #[doc = ::core::concat!(
+                "assert_eq!(casts::",
+                ::core::stringify!($from),
+                "_as_",
+                ::core::stringify!($into),
+                "(1",
+                ::core::stringify!($from),
+                "), 1",
+                ::core::stringify!($into),
+                ");")]
+            /// ```
+            #[inline]
+            pub const fn [<$from _as_ $into>](value: $from) -> $into {
+                $crate::static_assert!(size_of::<$into>() >= size_of::<$from>());
+
+                value as $into
+            }
+        }
+        )*
+    };
+}
+
+// Valid `Into` transformations.
+impl_safe_as!(u8 as { u16, u32, u64, usize });
+impl_safe_as!(u16 as { u32, u64, usize });
+impl_safe_as!(u32 as { u64 });
+// A `usize` fits into a `u64` on all supported platforms.
+impl_safe_as!(usize as { u64 });
+// A `u32` fits into a `usize` on all supported platforms.
+impl_safe_as!(u32 as { usize });
+
+/// Extension trait providing guaranteed lossless cast to `Self` from `T`.
+///
+/// The standard library's `From` implementations do not cover conversions that are not portable or
+/// future-proof. For instance, even though it is safe today, `From<usize>` is not implemented for
+/// [`u64`] because of the possibility of needing to support larger-than-64bit architectures in the
+/// future.
+///
+/// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that
+/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the
+/// destination type is smaller than the source.
+///
+/// Both options are hardly acceptable for the kernel. It is also a much more architecture
+/// dependent environment, supporting only 32 and 64 bit architectures, with some modules
+/// explicitly depending on a specific bus width that could greatly benefit from infallible
+/// conversion operations.
+///
+/// Thus this extension trait that provides, for all architectures supported by the kernel,
+/// conversion methods between types for which such a cast is lossless.
+///
+/// In other words, this trait is implemented if, for all supported targets and with `t: T`, the
+/// `t as Self` operation is completely lossless.
+///
+/// Prefer this over the `as` keyword to guarantee that no lossy casts are performed.
+///
+/// If you need to perform a conversion in `const` context, use [`u32_as_usize`], [`usize_as_u64`],
+/// etc.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::num::casts::FromSafeCast;
+///
+/// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00usize);
+/// ```
+pub trait FromSafeCast<T> {
+    /// Create a `Self` from `value`. This operation is guaranteed to be lossless.
+    fn from_safe_cast(value: T) -> Self;
+}
+
+// A `usize` fits into a `u64` on all supported platforms.
+impl FromSafeCast<usize> for u64 {
+    #[inline]
+    fn from_safe_cast(value: usize) -> Self {
+        usize_as_u64(value)
+    }
+}
+
+// A `u32` fits into a `usize` on all supported platforms.
+impl FromSafeCast<u32> for usize {
+    #[inline]
+    fn from_safe_cast(value: u32) -> Self {
+        u32_as_usize(value)
+    }
+}
+
+/// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`]
+/// is to [`From`].
+///
+/// See the documentation of [`FromSafeCast`] for the motivation.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::num::casts::IntoSafeCast;
+///
+/// assert_eq!(0xf00usize, 0xf00u32.into_safe_cast());
+/// ```
+pub trait IntoSafeCast<T> {
+    /// Convert `self` into a `T`. This operation is guaranteed to be lossless.
+    fn into_safe_cast(self) -> T;
+}
+
+/// Reverse operation for types implementing [`FromSafeCast`].
+impl<S, T> IntoSafeCast<T> for S
+where
+    T: FromSafeCast<S>,
+{
+    #[inline]
+    fn into_safe_cast(self) -> T {
+        T::from_safe_cast(self)
+    }
+}
+
+/// 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);
+
+                N as $into
+            }
+        }
+        )*
+    };
+}
+
+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 });
+
+/// Conversions that are only lossless for the current architecture.
+///
+/// # Portability
+///
+/// Callers of this module become dependent on the setting of `CONFIG_64BIT`. Use with caution, and
+/// never in code that is portable across pointer sizes.
+pub mod arch {
+    /// Trait identical to [`FromSafeCast`](super::FromSafeCast), but for conversions that are not
+    /// available on all architectures.
+    pub trait FromSafeCastArch<T> {
+        /// Create a `Self` from `value`. This operation is guaranteed to be lossless.
+        fn from_safe_cast_arch(value: T) -> Self;
+    }
+
+    /// Trait identical to [`IntoSafeCast`](super::IntoSafeCast), but for conversions that are not
+    /// available on all architectures.
+    pub trait IntoSafeCastArch<T> {
+        /// Convert `self` into a `T`. This operation is guaranteed to be lossless.
+        fn into_safe_cast_arch(self) -> T;
+    }
+
+    /// Reverse operation for types implementing [`FromSafeCastArch`].
+    impl<S, T> IntoSafeCastArch<T> for S
+    where
+        T: FromSafeCastArch<S>,
+    {
+        #[inline]
+        fn into_safe_cast_arch(self) -> T {
+            T::from_safe_cast_arch(self)
+        }
+    }
+
+    /// A `u64` fits into a `usize` on 64-bit platforms.
+    #[cfg(CONFIG_64BIT)]
+    #[inline]
+    pub const fn u64_as_usize(value: u64) -> usize {
+        value as usize
+    }
+
+    #[cfg(CONFIG_64BIT)]
+    impl FromSafeCastArch<u64> for usize {
+        #[inline]
+        fn from_safe_cast_arch(value: u64) -> Self {
+            u64_as_usize(value)
+        }
+    }
+
+    /// A `usize` fits into a `u32` on 32-bit platforms.
+    #[cfg(not(CONFIG_64BIT))]
+    #[inline]
+    pub const fn usize_as_u32(value: usize) -> u32 {
+        value as u32
+    }
+
+    #[cfg(not(CONFIG_64BIT))]
+    impl FromSafeCastArch<usize> for u32 {
+        #[inline]
+        fn from_safe_cast_arch(value: usize) -> Self {
+            usize_as_u32(value)
+        }
+    }
+}

-- 
2.55.0


  reply	other threads:[~2026-08-06  7:36 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-06  7:35 [PATCH v2 0/2] rust: add functions and traits for lossless integer conversions Alexandre Courbot
2026-08-06  7:35 ` Alexandre Courbot [this message]
2026-08-06  7:35 ` [PATCH v2 2/2] gpu: nova-core: use kernel lossless integer conversion module Alexandre Courbot
2026-08-06 21:10 ` [PATCH v2 0/2] rust: add functions and traits for lossless integer conversions Danilo Krummrich

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=20260806-as_casts-v2-1-cb76a4d3a6ef@nvidia.com \
    --to=acourbot@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --cc=airlied@gmail.com \
    --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=dri-devel@lists.freedesktop.org \
    --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=simona@ffwll.ch \
    --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.