Skip to content

Commit acd9d94

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

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

lib/Kconfig.debug

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

26422642
endchoice
26432643

2644+
choice
2645+
prompt "Build-time assertions"
2646+
default RUST_BUILD_ASSERT_DENY
2647+
help
2648+
Controls how are `build_error` and `build_assert!` handled during build.
2649+
2650+
If calls to them exist in the binary, it may indicates a violated invariant
2651+
or that the optimizer fails to verify the invariant during compilation.
2652+
You can choose to abort compilation or ignore them during build and let the
2653+
check be carried to runtime.
2654+
2655+
If optimizations are turned off, you can only select "Ignore".
2656+
2657+
If unsure, say "Deny".
2658+
2659+
config RUST_BUILD_ASSERT_IGNORE
2660+
bool "Ignore"
2661+
help
2662+
Unoptimized calls to `build_error` may be converted to `panic!`
2663+
and caught at runtime.
2664+
2665+
config RUST_BUILD_ASSERT_DENY
2666+
bool "Deny"
2667+
depends on !RUST_OPT_LEVEL_0
2668+
help
2669+
Unoptimized calls to `build_error` will abort compilation.
2670+
2671+
endchoice
2672+
2673+
26442674
endmenu # "Rust"
26452675

26462676
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+
ifdef CONFIG_RUST_BUILD_ASSERT_IGNORE
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: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
}

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+
"link_time_panic",
76+
srctree / "rust" / "link_time_panic.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", "link_time_panic"],
7987
cfg,
8088
)
8189
crates[-1]["env"]["RUST_BINDINGS_FILE"] = str(bindings_file.resolve(True))

0 commit comments

Comments
 (0)