Re: [PATCH 2/2] rust: add `const_assert!` macro

From: Yury Norov

Date: Thu Feb 12 2026 - 20:16:42 EST


On Fri, Feb 06, 2026 at 05:12:50PM +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.
>
> The `build_assert!` doc is refined to recommend it where possible.

This is a good place to actually explain where this thing is possible.

> 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 | 55 +++++++++++++++++++++++++++++++++----
> rust/kernel/num/bounded.rs | 24 ++++++----------
> rust/kernel/prelude.rs | 2 +-
> rust/kernel/ptr.rs | 18 ++++++------
> scripts/Makefile.build | 3 +-
> 5 files changed, 71 insertions(+), 31 deletions(-)
>
> diff --git a/rust/kernel/build_assert.rs b/rust/kernel/build_assert.rs
> index d464494d430a..e40f0227e1ef 100644
> --- a/rust/kernel/build_assert.rs
> +++ b/rust/kernel/build_assert.rs
> @@ -41,6 +41,45 @@ macro_rules! static_assert {
> };
> }
>
> +/// Assertion during constant evaluation.
> +///
> +/// This is a more powerful version of `static_assert` that can refer to generics inside functions
> +/// or implementation blocks. However, it also have a limitation where it can only appear in places

"However, it also has a limitation", I guess?

> +/// where statements can appear; for example, you cannot use it as an item in the module.
> +///
> +/// [`static_assert!`] should be preferred where possible.
> +///
> +/// # Examples
> +///
> +/// When the condition refers to generic parameters [`static_assert!`] cannot be used.
> +/// Use `const_assert!` in this scenario.
> +/// ```
> +/// fn foo<const N: usize>() {
> +/// // `static_assert!(N > 1);` is not allowed
> +/// const_assert!(N > 1); // Compile-time check
> +/// build_assert!(N > 1); // Build-time check

In the other email you say: the assertion failure mechanism is undefined
symbol and linker error.

So, maybe:

const_assert!(N > 1); // Build-time check at compilation
build_assert!(N > 1); // Build-time check at linkage

Because compilation is a part of build process, and referring them one
vs another may confuse.

> +/// assert!(N > 1); // Run-time check
> +/// }
> +///
> +///
> +/// Note that `const_assert!` cannot be used when referring to function parameter, then
> +/// `const_assert!` cannot be used even if the function is going to be called during const
> +/// evaluation. Use `build_assert!` in this case.
> +/// ```
> +/// const fn foo(n: usize) {
> +/// // `const_assert!(n > 1);` is not allowed
> +/// build_assert!(n > 1);
> +/// }
> +///
> +/// const _: () = foo(2); // Evaluate during const evaluation
> +/// ```

This part confused me the most. But after all, parameters in rust
are never constants, and even if foo() is used with '2' only, it
appears to be a non-constant from the const_assert!() POV.

Seemingly, there are only 3 objects in the language that can be
specified with the 'const': functions, items and generics. And
const_assert!() makes sense (doesn't break the build) only for
them.

So, the difference between const vs build assertions is that const
version is only applicable to a certain type of objects and is
supported by language. Contrary, build_assert!() is not a part
of the language and in fact is based on a linkage trick, while
allows broader set of build-time expressions.

And altogether they make sense and even nice.

Can you please consider to add the above passage to your reply in
the other email, and place them in the documentation?

With that (or without),

Reviewed-by: Yury Norov <ynorov@xxxxxxxxxx>

> +#[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.
> @@ -74,7 +113,8 @@ macro_rules! build_error {
> /// will panic. If the compiler or optimizer cannot guarantee the condition will
> /// be evaluated to `true`, a build error will be triggered.
> ///
> -/// [`static_assert!`] should be preferred to `build_assert!` whenever possible.
> +/// [`static_assert!`] or [`const_assert!`] should be preferred to `build_assert!` whenever
> +/// possible.
> ///
> /// # Examples
> ///
> @@ -84,24 +124,27 @@ macro_rules! build_error {
> /// ```ignore
> /// fn foo() {
> /// static_assert!(1 > 1); // Compile-time error
> +/// const_assert!(1 > 1); // Compile-time error
> /// build_assert!(1 > 1); // Build-time error
> /// assert!(1 > 1); // Run-time error
> /// }
> /// ```
> ///
> -/// When the condition refers to generic parameters or parameters of an inline function,
> -/// [`static_assert!`] cannot be used. Use `build_assert!` in this scenario.
> +/// When the condition refers to generic parameters [`static_assert!`] cannot be used.
> +/// `build_assert!` is usable in this scenario, however you should prefer `const_assert!`.
> /// ```
> /// fn foo<const N: usize>() {
> /// // `static_assert!(N > 1);` is not allowed
> +/// const_assert!(N > 1); // Compile-time check
> /// build_assert!(N > 1); // Build-time check
> /// assert!(N > 1); // Run-time check
> /// }
> /// ```
> ///
> -/// When a condition depends on a function argument, the function must be annotated with
> -/// `#[inline(always)]`. Without this attribute, the compiler may choose to not inline the
> -/// function, preventing it from optimizing out the error path.
> +/// When the condition refers to parameters of an inline function, neither [`static_assert!`] or
> +/// [`const_assert!`] can be used. You may use `build_assert!` in this scenario, however you must
> +/// annotate the function `#[inline(always)]`. Without this attribute, the compiler may choose to
> +/// not inline the function, preventing it from optimizing out the error path.
> /// ```
> /// #[inline(always)]
> /// fn bar(n: usize) {
> 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..75c52b5879e3 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -29,7 +29,7 @@
>
> 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 5b6a382637fe..0b6acd112c4f 100644
> --- a/rust/kernel/ptr.rs
> +++ b/rust/kernel/ptr.rs
> @@ -2,8 +2,12 @@
>
> //! Types and functions to work with pointers and addresses.
>
> -use core::mem::align_of;
> -use core::num::NonZero;
> +use core::{
> + mem::align_of,
> + num::NonZero, //
> +};
> +
> +use crate::const_assert;
>
> /// Type representing an alignment, which is always a power of two.
> ///
> @@ -38,12 +42,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 0c838c467c76..204e58dd1bb0 100644
> --- a/scripts/Makefile.build
> +++ b/scripts/Makefile.build
> @@ -308,6 +308,7 @@ $(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(inline_const)`.
> # - 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)`.
> @@ -317,7 +318,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,used_with_arg
> +rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,inline_const,lint_reasons,offset_of_nested,raw_ref_op,used_with_arg
>
> # `--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