// 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 + ops::AddAssign + ops::Sub + ops::SubAssign + ops::Mul + ops::MulAssign + ops::Div + ops::DivAssign + ops::Rem + ops::RemAssign + ops::BitAnd + ops::BitAndAssign + ops::BitOr + ops::BitOrAssign + ops::BitXor + ops::BitXorAssign + ops::Shl + ops::ShlAssign + ops::Shr + ops::ShrAssign + 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 = cv!(8); /// assert_eq!(v.get(), 8); /// /// // Any integer constant expression works, not only literals. /// let m: NonZero = cv!(usize::MAX); /// assert_eq!(m.get(), usize::MAX); /// /// let b: Bounded = 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 = 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() -> 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::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: 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 FromConst 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 FromConst for core::num::NonZero<$type> { const VALUE: Self = match core::num::NonZero::new(<$type as FromConst>::VALUE) { Some(value) => value, None => panic!("constant cannot be zero"), }; } )* }; } impl_from_const!( u8 u16 u32 u64 usize i8 i16 i32 i64 isize );