Re: [PATCH v2 2/4] rust: add `const_assert!` macro
From: Yury Norov
Date: Mon Mar 16 2026 - 13:37:48 EST
On Mon, Mar 16, 2026 at 03:07:13PM +0000, Gary Guo wrote:
> From: Gary Guo <gary@xxxxxxxxxxx>
>
> The macro is a more powerful version of `static_assert!` for use inside
> function contexts. This is powered by inline consts, so enable the feature
> for old compiler versions that does not have it stably.
>
> While it is possible already to write `const { assert!(...) }`, this
> provides a short hand that is more uniform with other assertions. It also
> formats nicer with rustfmt where it will not be formatted into multiple
> lines.
>
> Two users that would route via the Rust tree are converted.
>
> Signed-off-by: Gary Guo <gary@xxxxxxxxxxx>
> ---
> rust/kernel/build_assert.rs | 24 ++++++++++++++++++++++++
> rust/kernel/num/bounded.rs | 24 +++++++++---------------
> rust/kernel/prelude.rs | 7 ++++++-
> rust/kernel/ptr.rs | 12 ++++++------
> scripts/Makefile.build | 5 +++--
> 5 files changed, 48 insertions(+), 24 deletions(-)
>
> diff --git a/rust/kernel/build_assert.rs b/rust/kernel/build_assert.rs
> index d464494d430a..51c0f85a9014 100644
> --- a/rust/kernel/build_assert.rs
> +++ b/rust/kernel/build_assert.rs
> @@ -41,6 +41,30 @@ macro_rules! static_assert {
> };
> }
>
> +/// Assertion during constant evaluation.
> +///
> +/// This is a more powerful version of [`static_assert!`] that can refer to generics inside functions
This line is 101 lanes. Can you split it?
> +/// or implementation blocks. However, it also has a limitation where it can only appear in places
> +/// where statements can appear; for example, you cannot use it as an item in the module.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// fn foo<const N: usize>() {
> +/// const_assert!(N > 1);
> +/// }
> +///
> +/// fn bar<T>() {
> +/// const_assert!(size_of::<T>() > 0, "T cannot be ZST");
> +/// }
> +/// ```
> +#[macro_export]
> +macro_rules! const_assert {
> + ($condition:expr $(,$arg:literal)?) => {
> + const { ::core::assert!($condition $(,$arg)?) };
> + };
> +}
> +
> /// Fails the build if the code path calling `build_error!` can possibly be executed.
> ///
> /// If the macro is executed in const context, `build_error!` will panic.
> diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
> index fa81acbdc8c2..54d0ce3ba595 100644
> --- a/rust/kernel/num/bounded.rs
> +++ b/rust/kernel/num/bounded.rs
> @@ -255,9 +255,7 @@ impl<const N: u32> Bounded<$type, N> {
> /// ```
> pub const fn new<const VALUE: $type>() -> Self {
> // Statically assert that `VALUE` fits within the set number of bits.
> - const {
> - assert!(fits_within!(VALUE, $type, N));
> - }
> + const_assert!(fits_within!(VALUE, $type, N));
>
> // SAFETY: `fits_within` confirmed that `VALUE` can be represented within
> // `N` bits.
> @@ -287,12 +285,10 @@ impl<T, const N: u32> Bounded<T, N>
> /// The caller must ensure that `value` can be represented within `N` bits.
> const unsafe fn __new(value: T) -> Self {
> // Enforce the type invariants.
> - const {
> - // `N` cannot be zero.
> - assert!(N != 0);
> - // The backing type is at least as large as `N` bits.
> - assert!(N <= T::BITS);
> - }
> + // `N` cannot be zero.
> + const_assert!(N != 0);
> + // The backing type is at least as large as `N` bits.
> + const_assert!(N <= T::BITS);
>
> // INVARIANT: The caller ensures `value` fits within `N` bits.
> Self(value)
> @@ -406,12 +402,10 @@ pub fn get(self) -> T {
> /// assert_eq!(larger_v, v);
> /// ```
> pub const fn extend<const M: u32>(self) -> Bounded<T, M> {
> - const {
> - assert!(
> - M >= N,
> - "Requested number of bits is less than the current representation."
> - );
> - }
> + const_assert!(
> + M >= N,
> + "Requested number of bits is less than the current representation."
> + );
>
> // SAFETY: The value did fit within `N` bits, so it will all the more fit within
> // the larger `M` bits.
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index c7e91b80d301..6a54597fa0a2 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -29,7 +29,12 @@
>
> pub use pin_init::{init, pin_data, pin_init, pinned_drop, InPlaceWrite, Init, PinInit, Zeroable};
>
> -pub use super::{build_assert, build_error, static_assert};
> +pub use super::{
> + build_assert,
> + build_error,
> + const_assert,
> + static_assert, //
> +};
>
> // `super::std_vendor` is hidden, which makes the macro inline for some reason.
> #[doc(no_inline)]
> diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs
> index bdc2d79ff669..d05e5888e80c 100644
> --- a/rust/kernel/ptr.rs
> +++ b/rust/kernel/ptr.rs
> @@ -11,6 +11,8 @@
> };
> use core::num::NonZero;
>
> +use crate::const_assert;
> +
> /// Type representing an alignment, which is always a power of two.
> ///
> /// It is used to validate that a given value is a valid alignment, and to perform masking and
> @@ -44,12 +46,10 @@ impl Alignment {
> /// ```
> #[inline(always)]
> pub const fn new<const ALIGN: usize>() -> Self {
> - const {
> - assert!(
> - ALIGN.is_power_of_two(),
> - "Provided alignment is not a power of two."
> - );
> - }
> + const_assert!(
> + ALIGN.is_power_of_two(),
> + "Provided alignment is not a power of two."
> + );
>
> // INVARIANT: `align` is a power of two.
> // SAFETY: `align` is a power of two, and thus non-zero.
> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> index 3652b85be545..960b4630cb2c 100644
> --- a/scripts/Makefile.build
> +++ b/scripts/Makefile.build
> @@ -310,7 +310,8 @@ $(obj)/%.lst: $(obj)/%.c FORCE
>
> # The features in this list are the ones allowed for non-`rust/` code.
> #
> -# - Stable since Rust 1.79.0: `feature(slice_ptr_len)`.
> +# - Stable since Rust 1.79.0: `feature(inline_const)`,
> +# `feature(slice_ptr_len)`,
> # - Stable since Rust 1.81.0: `feature(lint_reasons)`.
> # - Stable since Rust 1.82.0: `feature(asm_const)`,
> # `feature(offset_of_nested)`, `feature(raw_ref_op)`.
> @@ -321,7 +322,7 @@ $(obj)/%.lst: $(obj)/%.c FORCE
> #
> # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
> # the unstable features in use.
> -rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,slice_ptr_len,strict_provenance,used_with_arg
> +rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,inline_const,lint_reasons,offset_of_nested,raw_ref_op,slice_ptr_len,strict_provenance,used_with_arg
Same here. Miguel mentioned in the other thread that newer version of
rust will make this list shorter, but once that didn't happen, let's
follow rules?
>
> # `--out-dir` is required to avoid temporaries being created by `rustc` in the
> # current working directory, which may be not accessible in the out-of-tree
> --
> 2.51.2