Skip to content

Commit 1662e44

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

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ 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+
extra-$(CONFIG_RUST) += exports_build_assert_generated.h
15+
endif
16+
1217
obj-$(CONFIG_RUST) += exports.o
1318

1419
RUSTDOC = rustdoc
@@ -32,6 +37,7 @@ rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE
3237
$(call if_changed,rustdoc)
3338

3439
rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
40+
--extern build_assert \
3541
--extern module=$(objtree)/rust/libmodule.so
3642
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \
3743
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
@@ -96,6 +102,9 @@ $(objtree)/rust/exports_core_generated.h: $(objtree)/rust/core.o FORCE
96102
$(objtree)/rust/exports_alloc_generated.h: $(objtree)/rust/alloc.o FORCE
97103
$(call if_changed,exports)
98104

105+
$(objtree)/rust/exports_build_assert_generated.h: $(objtree)/rust/build_assert.o FORCE
106+
$(call if_changed,exports)
107+
99108
$(objtree)/rust/exports_kernel_generated.h: $(objtree)/rust/kernel.o FORCE
100109
$(call if_changed,exports)
101110

@@ -147,9 +156,15 @@ $(objtree)/rust/alloc.o: $$(RUST_LIB_SRC)/alloc/src/lib.rs \
147156
$(objtree)/rust/compiler_builtins.o FORCE
148157
$(call if_changed_dep,rustc_library)
149158

159+
$(objtree)/rust/build_assert.o: $(srctree)/rust/build_assert.rs \
160+
$(objtree)/rust/compiler_builtins.o FORCE
161+
$(call if_changed_dep,rustc_library)
162+
150163
# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935
151164
$(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
165+
--extern build_assert \
152166
--extern module=$(objtree)/rust/libmodule.so
153167
$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
168+
$(objtree)/rust/build_assert.o \
154169
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
155170
$(call if_changed_dep,rustc_library)

rust/build_assert.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
#[track_caller]
21+
pub const fn build_error(msg: &'static str) {
22+
// Could also be panic!(msg) to avoid using unstable feature `core_panic`,
23+
// but it is not allowed in Rust 2021, while panic!("{}", msg) could not
24+
// yet be used in const context.
25+
core::panicking::panic(msg);
26+
}
27+
28+
/// Asserts that a boolean expression is `true` at compile time.
29+
///
30+
/// This will invoke the [`build_error`] function if the compiler or optimizer
31+
/// cannot guarantee the condition will be evaluated to `true`.
32+
#[macro_export]
33+
macro_rules! build_assert {
34+
($cond:expr $(,)?) => {{
35+
if !$cond {
36+
$crate::build_error(concat!("assertion failed: ", stringify!($cond)));
37+
}
38+
}};
39+
($cond:expr, $msg:expr) => {{
40+
if !$cond {
41+
$crate::build_error($msg);
42+
}
43+
}};
44+
}

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)