[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260206171253.2704684-2-gary@kernel.org>
Date: Fri, 6 Feb 2026 17:12:50 +0000
From: Gary Guo <gary@...nel.org>
To: Miguel Ojeda <ojeda@...nel.org>,
Boqun Feng <boqun@...nel.org>,
Gary Guo <gary@...yguo.net>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>,
Benno Lossin <lossin@...nel.org>,
Andreas Hindborg <a.hindborg@...nel.org>,
Alice Ryhl <aliceryhl@...gle.com>,
Trevor Gross <tmgross@...ch.edu>,
Danilo Krummrich <dakr@...nel.org>,
Alexandre Courbot <acourbot@...dia.com>,
Yury Norov <yury.norov@...il.com>,
Nathan Chancellor <nathan@...nel.org>,
Nicolas Schier <nsc@...nel.org>
Cc: linux-kernel@...r.kernel.org,
rust-for-linux@...r.kernel.org,
linux-kbuild@...r.kernel.org
Subject: [PATCH 2/2] rust: add `const_assert!` macro
From: Gary Guo <gary@...yguo.net>
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.
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@...yguo.net>
---
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
+/// 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
+/// 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
+/// ```
+#[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
Powered by blists - more mailing lists