Skip to content

Commit dc346e6

Browse files
committed
Implement build-time error and assertion
Signed-off-by: Gary Guo <[email protected]>
1 parent caa9a60 commit dc346e6

File tree

4 files changed

+107
-1
lines changed

4 files changed

+107
-1
lines changed

lib/Kconfig.debug

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,43 @@ config RUST_OPT_LEVEL_Z
26412641

26422642
endchoice
26432643

2644+
choice
2645+
prompt "Build-time assertions"
2646+
default RUST_BUILD_ASSERT_ALLOW if RUST_OPT_LEVEL_0
2647+
default RUST_BUILD_ASSERT_DENY if !RUST_OPT_LEVEL_0
2648+
help
2649+
Controls how are `build_error` and `build_assert!` handled during build.
2650+
2651+
If calls to them exist in the binary, it may indicates a violated invariant
2652+
or that the optimizer fails to verify the invariant during compilation.
2653+
You can choose to abort compilation or ignore them during build and let the
2654+
check be carried to runtime.
2655+
2656+
If optimizations are turned off, you cannot select "Deny".
2657+
2658+
If unsure, say "Deny".
2659+
2660+
config RUST_BUILD_ASSERT_ALLOW
2661+
bool "Allow"
2662+
help
2663+
Unoptimized calls to `build_error` will be converted to `panic!`
2664+
and checked at runtime.
2665+
2666+
config RUST_BUILD_ASSERT_WARN
2667+
bool "Warn"
2668+
help
2669+
Unoptimized calls to `build_error` will be converted to `panic!`
2670+
and checked at runtime, but warnings will be generated when building.
2671+
2672+
config RUST_BUILD_ASSERT_DENY
2673+
bool "Deny"
2674+
depends on !RUST_OPT_LEVEL_0
2675+
help
2676+
Unoptimized calls to `build_error` will abort compilation.
2677+
2678+
endchoice
2679+
2680+
26442681
endmenu # "Rust"
26452682

26462683
source "Documentation/Kconfig"

rust/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ extra-$(CONFIG_RUST) += bindings_generated.rs
99
obj-$(CONFIG_RUST) += alloc.o kernel.o
1010
extra-$(CONFIG_RUST) += exports_alloc_generated.h exports_kernel_generated.h
1111

12+
ifndef CONFIG_RUST_BUILD_ASSERT_DENY
13+
obj-$(CONFIG_RUST) += build_assert.o
14+
endif
15+
1216
obj-$(CONFIG_RUST) += exports.o
1317

1418
RUSTDOC = rustdoc
@@ -32,6 +36,7 @@ rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE
3236
$(call if_changed,rustdoc)
3337

3438
rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
39+
--extern build_assert \
3540
--extern module=$(objtree)/rust/libmodule.so
3641
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \
3742
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
@@ -147,9 +152,15 @@ $(objtree)/rust/alloc.o: $$(RUST_LIB_SRC)/alloc/src/lib.rs \
147152
$(objtree)/rust/compiler_builtins.o FORCE
148153
$(call if_changed_dep,rustc_library)
149154

155+
$(objtree)/rust/build_assert.o: $(srctree)/rust/build_assert.rs \
156+
$(objtree)/rust/compiler_builtins.o FORCE
157+
$(call if_changed_dep,rustc_library)
158+
150159
# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935
151160
$(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
161+
--extern build_assert \
152162
--extern module=$(objtree)/rust/libmodule.so
153163
$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
164+
$(objtree)/rust/build_assert.o \
154165
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
155166
$(call if_changed_dep,rustc_library)

rust/build_assert.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Build-time assertion.
4+
//!
5+
//! In Rust it is very common to use assertions extensively and defensively.
6+
//! Sometimes we know a condition could be checked statically, but it could not
7+
//! be enforced in Rust (e.g. perform some checks in const functions, but those
8+
//! functions could still be called in the runtime).
9+
//!
10+
//! This crate provides a method `build_error`, which will panic in
11+
//! compile-time if executed in const context, and will cause a build error
12+
//! if not executed in compile time and optimizer does not optimise away the
13+
//! call.
14+
15+
#![no_std]
16+
#![feature(const_panic, core_panic)]
17+
18+
/// Panics if executed in const context, or triggers a build error if not.
19+
#[inline(never)]
20+
#[no_mangle]
21+
#[track_caller]
22+
pub const fn build_error(msg: &'static str) {
23+
// Could also be panic!(msg) to avoid using unstable feature `core_panic`,
24+
// but it is not allowed in Rust 2021, while panic!("{}", msg) could not
25+
// yet be used in const context.
26+
core::panicking::panic(msg);
27+
}
28+
29+
/// Asserts that a boolean expression is `true` at compile time.
30+
///
31+
/// This will invoke the [`build_error`] function if the compiler or optimizer
32+
/// cannot guarantee the condition will be evaluated to `true`.
33+
#[macro_export]
34+
macro_rules! build_assert {
35+
($cond:expr $(,)?) => {{
36+
if !$cond {
37+
$crate::build_error(concat!("assertion failed: ", stringify!($cond)));
38+
}
39+
}};
40+
($cond:expr, $msg:expr) => {{
41+
if !$cond {
42+
$crate::build_error($msg);
43+
}
44+
}};
45+
}
46+
47+
#[cfg(CONFIG_RUST_BUILD_ASSERT_WARN)]
48+
#[link_section = ".gnu.warning.build_error"]
49+
#[used]
50+
static BUILD_ERROR_WARNING: [u8; 45] = *b"call to build_error present after compilation";

scripts/generate_rust_analyzer.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,19 @@ def append_crate(display_name, root_module, is_workspace_member, deps, cfg):
7171
)
7272
crates[-1]["proc_macro_dylib_path"] = "rust/libmodule.so"
7373

74+
append_crate(
75+
"build_assert",
76+
srctree / "rust" / "build_assert.rs",
77+
True,
78+
["core", "compiler_builtins"],
79+
[],
80+
)
81+
7482
append_crate(
7583
"kernel",
7684
srctree / "rust" / "kernel" / "lib.rs",
7785
True,
78-
["core", "alloc", "module"],
86+
["core", "alloc", "module", "build_assert"],
7987
cfg,
8088
)
8189
crates[-1]["env"]["RUST_BINDINGS_FILE"] = str(bindings_file.resolve(True))

0 commit comments

Comments
 (0)