mirror of
https://github.com/torvalds/linux.git
synced 2026-10-23 15:32:04 +02:00
Currently, using NonZero/Bounded constants is quite verbose. It's
unfortunate because it disincentivizes using it in interface boundaries.
Introduce a macro to make it nicer to use. The macro `cv!` (for constant
value) takes a const integer expression and widens it to i128 (at build
time only) before passing it as a const generic value to a new trait
`FromConst`. The value is then converted and appears in the
associated constant `FromConst::VALUE`. The trait is implemented by
NonZero, Bounded, and Alignment and lets values of each be constructed
from constants without a verbose turbofish syntax.
For example, `const { NonZero::new(1).unwrap() }` can be written as
`cv!(1)`.
Add the `cv!` macro to the prelude so it doesn't need to be imported
explicitly to use it.
Suggested-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
Acked-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://patch.msgid.link/20261013-cv-v5-1-113a538dfaef@nvidia.com
[ Added a few intra-doc links. - Miguel ]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
238 lines
6.9 KiB
Rust
238 lines
6.9 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Additional numerical features for the kernel.
|
|
|
|
use core::ops;
|
|
|
|
pub mod bounded;
|
|
pub mod casts;
|
|
|
|
pub use bounded::*;
|
|
|
|
/// Designates unsigned primitive types.
|
|
pub enum Unsigned {}
|
|
|
|
/// Designates signed primitive types.
|
|
pub enum Signed {}
|
|
|
|
mod private {
|
|
pub trait Sealed {}
|
|
}
|
|
|
|
/// Describes core properties of integer types.
|
|
pub trait Integer:
|
|
private::Sealed
|
|
+ Sized
|
|
+ Copy
|
|
+ Clone
|
|
+ PartialEq
|
|
+ Eq
|
|
+ PartialOrd
|
|
+ Ord
|
|
+ ops::Add<Output = Self>
|
|
+ ops::AddAssign
|
|
+ ops::Sub<Output = Self>
|
|
+ ops::SubAssign
|
|
+ ops::Mul<Output = Self>
|
|
+ ops::MulAssign
|
|
+ ops::Div<Output = Self>
|
|
+ ops::DivAssign
|
|
+ ops::Rem<Output = Self>
|
|
+ ops::RemAssign
|
|
+ ops::BitAnd<Output = Self>
|
|
+ ops::BitAndAssign
|
|
+ ops::BitOr<Output = Self>
|
|
+ ops::BitOrAssign
|
|
+ ops::BitXor<Output = Self>
|
|
+ ops::BitXorAssign
|
|
+ ops::Shl<u32, Output = Self>
|
|
+ ops::ShlAssign<u32>
|
|
+ ops::Shr<u32, Output = Self>
|
|
+ ops::ShrAssign<u32>
|
|
+ ops::Not
|
|
{
|
|
/// Whether this type is [`Signed`] or [`Unsigned`].
|
|
type Signedness;
|
|
|
|
/// Number of bits used for value representation.
|
|
const BITS: u32;
|
|
}
|
|
|
|
macro_rules! impl_integer {
|
|
($($type:ty: $signedness:ty), *) => {
|
|
$(
|
|
impl private::Sealed for $type {}
|
|
|
|
impl Integer for $type {
|
|
type Signedness = $signedness;
|
|
|
|
const BITS: u32 = <$type>::BITS;
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl_integer!(
|
|
u8: Unsigned,
|
|
u16: Unsigned,
|
|
u32: Unsigned,
|
|
u64: Unsigned,
|
|
u128: Unsigned,
|
|
usize: Unsigned,
|
|
i8: Signed,
|
|
i16: Signed,
|
|
i32: Signed,
|
|
i64: Signed,
|
|
i128: Signed,
|
|
isize: Signed
|
|
);
|
|
|
|
/// Creates a constant value ("cv") from an integer constant expression, with validity checked at
|
|
/// compile time.
|
|
///
|
|
/// This works for any type that implements [`FromConst`], with the target type inferred from
|
|
/// the context, or named explicitly with `cv!(value => Type)`. It can also be used in const
|
|
/// contexts.
|
|
///
|
|
/// # Pitfalls
|
|
///
|
|
/// Only the target type is inferred from the context. The expression passed to [`cv!`] is typed by
|
|
/// itself. So, unsuffixed integer literal expressions default to [`i32`]. For example
|
|
/// `cv!(2 << 31)` does not evaluate to the same value as `cv!(2i64 << 31)`, since `2 << 31` is
|
|
/// treated as an [`i32`] before being widened to [`i128`] internally. Literals and arithmetic
|
|
/// expressions that overflow [`i32`] are a compile error, it's just left shifts that require care.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use core::num::NonZero;
|
|
///
|
|
/// use kernel::{
|
|
/// num::Bounded,
|
|
/// ptr::Alignment, //
|
|
/// };
|
|
///
|
|
/// let v: NonZero<usize> = cv!(8);
|
|
/// assert_eq!(v.get(), 8);
|
|
///
|
|
/// // Any integer constant expression works, not only literals.
|
|
/// let m: NonZero<usize> = cv!(usize::MAX);
|
|
/// assert_eq!(m.get(), usize::MAX);
|
|
///
|
|
/// let b: Bounded<u32, 4> = cv!(15);
|
|
/// assert_eq!(b.get(), 15);
|
|
///
|
|
/// let a: Alignment = cv!(4096);
|
|
/// assert_eq!(a.as_usize(), 4096);
|
|
///
|
|
/// // Checked narrowing of integer constants, including in `const` items.
|
|
/// const SMALL: u8 = cv!(200u32);
|
|
/// assert_eq!(SMALL, 200);
|
|
///
|
|
/// const N: NonZero<u8> = cv!(5);
|
|
/// assert_eq!(N.get(), 5);
|
|
///
|
|
/// // The target type can be given explicitly.
|
|
/// let e = cv!(200u32 => u8);
|
|
/// assert_eq!(e, 200);
|
|
///
|
|
/// // With an explicit primitive target, the expression can use generic parameters.
|
|
/// const fn as_u64<const KEY: u16>() -> u64 {
|
|
/// cv!(KEY => u64)
|
|
/// }
|
|
/// assert_eq!(as_u64::<0x40>(), 0x40);
|
|
/// ```
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
macro_rules! cv {
|
|
(@cast $v:expr => $t:ty) => {
|
|
const {
|
|
#[allow(unused_comparisons, unused_assignments, clippy::as_underscore)]
|
|
{
|
|
let v = $v;
|
|
let r = v as $t;
|
|
// Pin `back` to `v`'s type so `as _` casts back to the source type.
|
|
let mut back = v;
|
|
back = r as _;
|
|
|
|
::core::assert!(
|
|
back == v && (v < 0) == (r < 0),
|
|
"value does not fit into the target type"
|
|
);
|
|
|
|
r
|
|
}
|
|
}
|
|
};
|
|
|
|
// Using `FromConst<V>::VALUE` means const generic expressions can't be used, since it requires
|
|
// the `generic_const_exprs` feature. Provide a special path for each primitive type that allows
|
|
// const generic expressions.
|
|
($v:expr => u8) => { $crate::cv!(@cast $v => u8) };
|
|
($v:expr => u16) => { $crate::cv!(@cast $v => u16) };
|
|
($v:expr => u32) => { $crate::cv!(@cast $v => u32) };
|
|
($v:expr => u64) => { $crate::cv!(@cast $v => u64) };
|
|
($v:expr => u128) => { $crate::cv!(@cast $v => u128) };
|
|
($v:expr => usize) => { $crate::cv!(@cast $v => usize) };
|
|
($v:expr => i8) => { $crate::cv!(@cast $v => i8) };
|
|
($v:expr => i16) => { $crate::cv!(@cast $v => i16) };
|
|
($v:expr => i32) => { $crate::cv!(@cast $v => i32) };
|
|
($v:expr => i64) => { $crate::cv!(@cast $v => i64) };
|
|
($v:expr => i128) => { $crate::cv!(@cast $v => i128) };
|
|
($v:expr => isize) => { $crate::cv!(@cast $v => isize) };
|
|
($v:expr => $t:ty) => {
|
|
<$t as $crate::num::FromConst<{ $crate::cv!(@cast $v => i128) }>>::VALUE
|
|
};
|
|
($v:expr) => {
|
|
<_ as $crate::num::FromConst<{ $crate::cv!(@cast $v => i128) }>>::VALUE
|
|
};
|
|
}
|
|
#[doc(inline)]
|
|
pub use cv;
|
|
|
|
/// Types that can be created from an integer constant expression validated at compile time.
|
|
///
|
|
/// Implement this trait to make a type usable with [`cv!`]. Use the [`cv!`] macro, not this trait
|
|
/// directly, for creating values.
|
|
#[diagnostic::on_unimplemented(message = "`{Self}` cannot be converted from a constant")]
|
|
pub trait FromConst<const V: i128>: Sized {
|
|
/// The value that corresponds to the constant `V`.
|
|
///
|
|
/// Fails the build if `V` is not a valid value for [`Self`].
|
|
const VALUE: Self;
|
|
}
|
|
|
|
/// Implements [`FromConst`] for primitive integer types and their [`NonZero`](core::num::NonZero)
|
|
/// versions.
|
|
macro_rules! impl_from_const {
|
|
($($type:ty)*) => {
|
|
$(
|
|
impl<const V: i128> FromConst<V> for $type {
|
|
const VALUE: Self = {
|
|
// CAST: The macro is only used for types up to 64 bits wide, so `MIN` and `MAX`
|
|
// widen to `i128` losslessly.
|
|
assert!(
|
|
V >= <$type>::MIN as i128 && V <= <$type>::MAX as i128,
|
|
"constant cannot be represented by the target type"
|
|
);
|
|
|
|
// CAST: The assert above confirmed that `V` fits in `$type`.
|
|
V as $type
|
|
};
|
|
}
|
|
|
|
impl<const V: i128> FromConst<V> for core::num::NonZero<$type> {
|
|
const VALUE: Self = match core::num::NonZero::new(<$type as FromConst<V>>::VALUE) {
|
|
Some(value) => value,
|
|
None => panic!("constant cannot be zero"),
|
|
};
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl_from_const!(
|
|
u8 u16 u32 u64 usize
|
|
i8 i16 i32 i64 isize
|
|
);
|