Skip to content

Commit 47f2715

Browse files
Gnurouojeda
authored andcommitted
rust: add functions and traits for lossless integer conversions
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> Reviewed-by: Danilo Krummrich <dakr@kernel.org> Link: https://patch.msgid.link/20260806-as_casts-v2-1-cb76a4d3a6ef@nvidia.com [ Added a few more intra-doc links. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
1 parent a2595ed commit 47f2715

2 files changed

Lines changed: 300 additions & 0 deletions

File tree

rust/kernel/num.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use core::ops;
66

77
pub mod bounded;
8+
pub mod casts;
9+
810
pub use bounded::*;
911

1012
/// Designates unsigned primitive types.

rust/kernel/num/casts.rs

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Helpers for performing lossless integer casts.
4+
//!
5+
//! The `as` keyword can be used to perform casts between integer types, but it unfortunately makes
6+
//! no distinction between casts that are lossless, and casts from a larger type into a smaller one
7+
//! that might silently strip data away. Thus, its use in the kernel is discouraged in favor of
8+
//! [`From`] implementations.
9+
//!
10+
//! Conversely, there are casts that are lossless depending on the build architecture (such as
11+
//! casting [`usize`] to [`u64`] on 32 or 64 bit archs), but not supported by [`From`]
12+
//! implementations in the standard library because they are not portable. It does however make
13+
//! sense for the kernel to support these, if only for code that is architecture-specific.
14+
//!
15+
//! This module provides ways to perform such conversions safely:
16+
//!
17+
//! - A series of const functions (e.g. [`usize_as_u64`]) supporting safe conversions in const
18+
//! context. Conversions supported by [`From`] implementations in the standard library are also
19+
//! covered as the [`From`] trait cannot be used in const context.
20+
//! - Two extension traits, [`FromSafeCast`] and [`IntoSafeCast`], providing conversion methods
21+
//! similar to [`From`] and [`Into`] for conversions that are safe to perform in the kernel, but
22+
//! not supported by the standard library.
23+
//! - Another series of const functions (e.g. [`u64_into_u8`]) supporting the conversion of a const
24+
//! value from a larger type into a smaller one, provided the value fits into the destination
25+
//! type. This is useful if a constant is defined as a larger type, but needs to be used as a
26+
//! smaller one.
27+
//! - An [`arch`] sub-module, defining more conversion functions that are only guaranteed to be
28+
//! lossless for a given pointer size. These can only be used in code that is specific to a
29+
//! given pointer size.
30+
//!
31+
//! # Examples
32+
//!
33+
//! ```
34+
//! use kernel::num::casts::{self, FromSafeCast, IntoSafeCast};
35+
//!
36+
//! // Conversion from const context.
37+
//! const USIZED_CONST: usize = casts::u8_as_usize(255u8);
38+
//!
39+
//! // Non-const conversions.
40+
//! let a = u64::from_safe_cast(4096usize);
41+
//! let b: u64 = 4096usize.into_safe_cast();
42+
//! ```
43+
44+
use crate::prelude::*;
45+
46+
/// Implements safe `as` conversion functions from a given type into a series of target types.
47+
///
48+
/// These functions can be used in place of `as`, with the guarantee that they will be lossless.
49+
macro_rules! impl_safe_as {
50+
($from:ty as { $($into:ty),* }) => {
51+
$(
52+
$crate::macros::paste! {
53+
#[doc = ::core::concat!(
54+
"Losslessly converts a [`",
55+
::core::stringify!($from),
56+
"`] into a [`",
57+
::core::stringify!($into),
58+
"`].")]
59+
///
60+
/// This conversion is allowed as it is always lossless. Prefer this over the `as`
61+
/// keyword to ensure no lossy casts are performed.
62+
///
63+
/// This is for use from a `const` context. For non `const` use, prefer the
64+
/// [`FromSafeCast`] and [`IntoSafeCast`] traits.
65+
///
66+
/// # Examples
67+
///
68+
/// ```
69+
/// use kernel::num::casts;
70+
///
71+
#[doc = ::core::concat!(
72+
"assert_eq!(casts::",
73+
::core::stringify!($from),
74+
"_as_",
75+
::core::stringify!($into),
76+
"(1",
77+
::core::stringify!($from),
78+
"), 1",
79+
::core::stringify!($into),
80+
");")]
81+
/// ```
82+
#[inline]
83+
pub const fn [<$from _as_ $into>](value: $from) -> $into {
84+
$crate::static_assert!(size_of::<$into>() >= size_of::<$from>());
85+
86+
value as $into
87+
}
88+
}
89+
)*
90+
};
91+
}
92+
93+
// Valid `Into` transformations.
94+
impl_safe_as!(u8 as { u16, u32, u64, usize });
95+
impl_safe_as!(u16 as { u32, u64, usize });
96+
impl_safe_as!(u32 as { u64 });
97+
// A `usize` fits into a `u64` on all supported platforms.
98+
impl_safe_as!(usize as { u64 });
99+
// A `u32` fits into a `usize` on all supported platforms.
100+
impl_safe_as!(u32 as { usize });
101+
102+
/// Extension trait providing guaranteed lossless cast to [`Self`] from `T`.
103+
///
104+
/// The standard library's [`From`] implementations do not cover conversions that are not portable
105+
/// or future-proof. For instance, even though it is safe today, [`From<usize>`] is not implemented
106+
/// for [`u64`] because of the possibility of needing to support larger-than-64bit architectures in
107+
/// the future.
108+
///
109+
/// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that
110+
/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the
111+
/// destination type is smaller than the source.
112+
///
113+
/// Both options are hardly acceptable for the kernel. It is also a much more architecture
114+
/// dependent environment, supporting only 32 and 64 bit architectures, with some modules
115+
/// explicitly depending on a specific bus width that could greatly benefit from infallible
116+
/// conversion operations.
117+
///
118+
/// Thus this extension trait that provides, for all architectures supported by the kernel,
119+
/// conversion methods between types for which such a cast is lossless.
120+
///
121+
/// In other words, this trait is implemented if, for all supported targets and with `t: T`, the
122+
/// `t as Self` operation is completely lossless.
123+
///
124+
/// Prefer this over the `as` keyword to guarantee that no lossy casts are performed.
125+
///
126+
/// If you need to perform a conversion in `const` context, use [`u32_as_usize`], [`usize_as_u64`],
127+
/// etc.
128+
///
129+
/// # Examples
130+
///
131+
/// ```
132+
/// use kernel::num::casts::FromSafeCast;
133+
///
134+
/// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00usize);
135+
/// ```
136+
pub trait FromSafeCast<T> {
137+
/// Create a [`Self`] from `value`. This operation is guaranteed to be lossless.
138+
fn from_safe_cast(value: T) -> Self;
139+
}
140+
141+
// A `usize` fits into a `u64` on all supported platforms.
142+
impl FromSafeCast<usize> for u64 {
143+
#[inline]
144+
fn from_safe_cast(value: usize) -> Self {
145+
usize_as_u64(value)
146+
}
147+
}
148+
149+
// A `u32` fits into a `usize` on all supported platforms.
150+
impl FromSafeCast<u32> for usize {
151+
#[inline]
152+
fn from_safe_cast(value: u32) -> Self {
153+
u32_as_usize(value)
154+
}
155+
}
156+
157+
/// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`]
158+
/// is to [`From`].
159+
///
160+
/// See the documentation of [`FromSafeCast`] for the motivation.
161+
///
162+
/// # Examples
163+
///
164+
/// ```
165+
/// use kernel::num::casts::IntoSafeCast;
166+
///
167+
/// assert_eq!(0xf00usize, 0xf00u32.into_safe_cast());
168+
/// ```
169+
pub trait IntoSafeCast<T> {
170+
/// Convert `self` into a `T`. This operation is guaranteed to be lossless.
171+
fn into_safe_cast(self) -> T;
172+
}
173+
174+
/// Reverse operation for types implementing [`FromSafeCast`].
175+
impl<S, T> IntoSafeCast<T> for S
176+
where
177+
T: FromSafeCast<S>,
178+
{
179+
#[inline]
180+
fn into_safe_cast(self) -> T {
181+
T::from_safe_cast(self)
182+
}
183+
}
184+
185+
/// Implements lossless conversion of a constant from a larger type into a smaller one.
186+
macro_rules! impl_const_into {
187+
($from:ty => { $($into:ty),* }) => {
188+
$(
189+
$crate::macros::paste! {
190+
#[doc = ::core::concat!(
191+
"Performs a build-time safe conversion of a [`",
192+
::core::stringify!($from),
193+
"`] constant value into a [`",
194+
::core::stringify!($into),
195+
"`].")]
196+
///
197+
/// This checks at compile-time that the conversion is lossless, and triggers a build
198+
/// error if it isn't.
199+
///
200+
/// # Examples
201+
///
202+
/// ```
203+
/// use kernel::num::casts;
204+
///
205+
/// // Succeeds because the value of the source fits into the destination's type.
206+
#[doc = ::core::concat!(
207+
"assert_eq!(casts::",
208+
::core::stringify!($from),
209+
"_into_",
210+
::core::stringify!($into),
211+
"::<1",
212+
::core::stringify!($from),
213+
">(), 1",
214+
::core::stringify!($into),
215+
");")]
216+
/// ```
217+
#[inline]
218+
pub const fn [<$from _into_ $into>]<const N: $from>() -> $into {
219+
// Make sure that the target type is smaller than the source one.
220+
$crate::static_assert!($from::BITS >= $into::BITS);
221+
// CAST: we statically enforced above that `$from` is larger than `$into`, so the
222+
// `as` conversion will be lossless.
223+
$crate::const_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);
224+
225+
N as $into
226+
}
227+
}
228+
)*
229+
};
230+
}
231+
232+
impl_const_into!(usize => { u8, u16, u32 });
233+
impl_const_into!(u64 => { u8, u16, u32 });
234+
impl_const_into!(u32 => { u8, u16 });
235+
impl_const_into!(u16 => { u8 });
236+
237+
/// Conversions that are only lossless for the current architecture.
238+
///
239+
/// # Portability
240+
///
241+
/// Callers of this module become dependent on the setting of `CONFIG_64BIT`. Use with caution, and
242+
/// never in code that is portable across pointer sizes.
243+
pub mod arch {
244+
/// Trait identical to [`FromSafeCast`](super::FromSafeCast), but for conversions that are not
245+
/// available on all architectures.
246+
pub trait FromSafeCastArch<T> {
247+
/// Create a [`Self`] from `value`. This operation is guaranteed to be lossless.
248+
fn from_safe_cast_arch(value: T) -> Self;
249+
}
250+
251+
/// Trait identical to [`IntoSafeCast`](super::IntoSafeCast), but for conversions that are not
252+
/// available on all architectures.
253+
pub trait IntoSafeCastArch<T> {
254+
/// Convert `self` into a `T`. This operation is guaranteed to be lossless.
255+
fn into_safe_cast_arch(self) -> T;
256+
}
257+
258+
/// Reverse operation for types implementing [`FromSafeCastArch`].
259+
impl<S, T> IntoSafeCastArch<T> for S
260+
where
261+
T: FromSafeCastArch<S>,
262+
{
263+
#[inline]
264+
fn into_safe_cast_arch(self) -> T {
265+
T::from_safe_cast_arch(self)
266+
}
267+
}
268+
269+
/// A [`u64`] fits into a [`usize`] on 64-bit platforms.
270+
#[cfg(CONFIG_64BIT)]
271+
#[inline]
272+
pub const fn u64_as_usize(value: u64) -> usize {
273+
value as usize
274+
}
275+
276+
#[cfg(CONFIG_64BIT)]
277+
impl FromSafeCastArch<u64> for usize {
278+
#[inline]
279+
fn from_safe_cast_arch(value: u64) -> Self {
280+
u64_as_usize(value)
281+
}
282+
}
283+
284+
/// A [`usize`] fits into a [`u32`] on 32-bit platforms.
285+
#[cfg(not(CONFIG_64BIT))]
286+
#[inline]
287+
pub const fn usize_as_u32(value: usize) -> u32 {
288+
value as u32
289+
}
290+
291+
#[cfg(not(CONFIG_64BIT))]
292+
impl FromSafeCastArch<usize> for u32 {
293+
#[inline]
294+
fn from_safe_cast_arch(value: usize) -> Self {
295+
usize_as_u32(value)
296+
}
297+
}
298+
}

0 commit comments

Comments
 (0)