mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-15 01:32:27 -05:00
`build_assert` relies on the compiler to optimize out its error path,
lest build fails with the dreaded error:
ERROR: modpost: "rust_build_error" [path/to/module.ko] undefined!
It has been observed that very trivial code performing I/O accesses
(sometimes even using an immediate value) would seemingly randomly fail
with this error whenever `CLIPPY=1` was set. The same behavior was also
observed until different, very similar conditions [1][2].
The cause appears to be that the failing function is eventually using
`build_assert` with its argument, but is only annotated with
`#[inline]`. This gives the compiler freedom to not inline the function,
which it notably did when Clippy was active, triggering the error.
The fix is to annotate functions passing their argument to
`build_assert` with `#[inline(always)]`, telling the compiler to be as
aggressive as possible with their inlining. This is also the correct
behavior as inlining is mandatory for correct behavior in these cases.
Add a paragraph instructing to annotate such functions with
`#[inline(always)]` in `build_assert`'s documentation, and split its
example to illustrate.
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://patch.msgid.link/20251208-io-build-assert-v3-1-98aded02c1ea@nvidia.com
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
92 lines
2.8 KiB
Rust
92 lines
2.8 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Build-time assert.
|
|
|
|
#[doc(hidden)]
|
|
pub use build_error::build_error;
|
|
|
|
/// 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
|
|
///
|
|
/// ```
|
|
/// #[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_assert::build_error("")
|
|
}};
|
|
($msg:expr) => {{
|
|
$crate::build_assert::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
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// 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.
|
|
/// ```
|
|
/// #[inline(always)]
|
|
/// fn bar(n: usize) {
|
|
/// // `static_assert!(n > 1);` is not allowed
|
|
/// build_assert!(n > 1); // Build-time check
|
|
/// assert!(n > 1); // Run-time check
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// [`static_assert!`]: crate::static_assert!
|
|
#[macro_export]
|
|
macro_rules! build_assert {
|
|
($cond:expr $(,)?) => {{
|
|
if !$cond {
|
|
$crate::build_assert::build_error(concat!("assertion failed: ", stringify!($cond)));
|
|
}
|
|
}};
|
|
($cond:expr, $msg:expr) => {{
|
|
if !$cond {
|
|
$crate::build_assert::build_error($msg);
|
|
}
|
|
}};
|
|
}
|