summaryrefslogtreecommitdiffstats
path: root/rust
diff options
context:
space:
mode:
authorGary Guo <gary@garyguo.net>2022-11-10 17:41:38 +0100
committerMiguel Ojeda <ojeda@kernel.org>2022-12-04 01:59:16 +0100
commit0f595bab9d1c1a10c6ab5ff3f9140cfc26600349 (patch)
tree848a0f13def8d161ca436769396231263e188b8b /rust
parentrust: add `build_error` crate (diff)
downloadlinux-0f595bab9d1c1a10c6ab5ff3f9140cfc26600349.tar.xz
linux-0f595bab9d1c1a10c6ab5ff3f9140cfc26600349.zip
rust: build_assert: add `build_{error,assert}!` macros
Add the `build_error!` and `build_assert!` macros which leverage the previously introduced `build_error` crate. Do so in a new module, called `build_assert`. The former fails the build if the code path calling it can possibly be executed. The latter asserts that a boolean expression is `true` at compile time. In particular, `build_assert!` can be used in some contexts where `static_assert!` cannot: fn f1<const N: usize>() { static_assert!(N > 1);` // Error. build_assert!(N > 1); // Build-time check. assert!(N > 1); // Run-time check. } #[inline] fn f2(n: usize) { static_assert!(n > 1); // Error. build_assert!(n > 1); // Build-time check. assert!(n > 1); // Run-time check. } Signed-off-by: Gary Guo <gary@garyguo.net> Reviewed-by: Wei Liu <wei.liu@kernel.org> [Reworded, adapted for upstream and applied latest changes] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Diffstat (limited to 'rust')
-rw-r--r--rust/kernel/build_assert.rs82
-rw-r--r--rust/kernel/lib.rs4
-rw-r--r--rust/kernel/prelude.rs2
3 files changed, 88 insertions, 0 deletions
diff --git a/rust/kernel/build_assert.rs b/rust/kernel/build_assert.rs
new file mode 100644
index 000000000000..659542393c09
--- /dev/null
+++ b/rust/kernel/build_assert.rs
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Build-time assert.
+
+/// 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.
+/// If the compiler or optimizer cannot guarantee that `build_error!` can never
+/// be called, a build error will be triggered.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::build_error;
+/// #[inline]
+/// fn foo(a: usize) -> usize {
+/// a.checked_add(1).unwrap_or_else(|| build_error!("overflow"))
+/// }
+///
+/// assert_eq!(foo(usize::MAX - 1), usize::MAX); // OK.
+/// // foo(usize::MAX); // Fails to compile.
+/// ```
+#[macro_export]
+macro_rules! build_error {
+ () => {{
+ $crate::build_error("")
+ }};
+ ($msg:expr) => {{
+ $crate::build_error($msg)
+ }};
+}
+
+/// Asserts that a boolean expression is `true` at compile time.
+///
+/// If the condition is evaluated to `false` in const context, `build_assert!`
+/// 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.
+///
+/// # Examples
+///
+/// These examples show that different types of [`assert!`] will trigger errors
+/// at different stage of compilation. It is preferred to err as early as
+/// possible, so [`static_assert!`] should be used whenever possible.
+/// ```ignore
+/// fn foo() {
+/// static_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.
+/// ```
+/// fn foo<const N: usize>() {
+/// // `static_assert!(N > 1);` is not allowed
+/// build_assert!(N > 1); // Build-time check
+/// assert!(N > 1); // Run-time check
+/// }
+///
+/// #[inline]
+/// fn bar(n: usize) {
+/// // `static_assert!(n > 1);` is not allowed
+/// build_assert!(n > 1); // Build-time check
+/// assert!(n > 1); // Run-time check
+/// }
+/// ```
+#[macro_export]
+macro_rules! build_assert {
+ ($cond:expr $(,)?) => {{
+ if !$cond {
+ $crate::build_error(concat!("assertion failed: ", stringify!($cond)));
+ }
+ }};
+ ($cond:expr, $msg:expr) => {{
+ if !$cond {
+ $crate::build_error($msg);
+ }
+ }};
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9b83ef736298..a3abc110ff97 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -23,6 +23,7 @@ compile_error!("Missing kernel configuration for conditional compilation");
#[cfg(not(test))]
#[cfg(not(testlib))]
mod allocator;
+mod build_assert;
pub mod error;
pub mod prelude;
pub mod print;
@@ -35,6 +36,9 @@ pub mod str;
pub use bindings;
pub use macros;
+#[doc(hidden)]
+pub use build_error::build_error;
+
/// Prefix to appear before log messages printed from within the `kernel` crate.
const __LOG_PREFIX: &[u8] = b"rust_kernel\0";
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 178fe8e6cb6d..7a90249ee9b9 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -17,6 +17,8 @@ pub use alloc::{boxed::Box, vec::Vec};
pub use macros::{module, vtable};
+pub use super::build_assert;
+
pub use super::{dbg, pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
pub use super::static_assert;