Skip to content

Commit efdb0b2

Browse files
authored
Use assert args in an unreachable block to prevent spurious warnings and to check for errors (rust-lang#1561)
1 parent d2546b3 commit efdb0b2

File tree

4 files changed

+43
-6
lines changed

4 files changed

+43
-6
lines changed

library/std/src/lib.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,28 @@ pub mod process;
3434
/// assert!(a + b == c, "The sum of {} and {} is {}", a, b, c);
3535
/// ```
3636
/// the assert message will be:
37-
/// "The sum of {} and {} is {}", 1, 1, 2
37+
/// "The sum of {} and {} is {}", a, b, c
3838
#[macro_export]
3939
macro_rules! assert {
4040
($cond:expr $(,)?) => {
4141
kani::assert($cond, concat!("assertion failed: ", stringify!($cond)));
4242
};
4343
($cond:expr, $($arg:tt)+) => {
44-
// Note that by stringifying the arguments to the custom message, any
45-
// compile-time checks on those arguments (e.g. checking that the symbol
46-
// is defined and that it implements the Display trait) are bypassed:
47-
// https://github.com/model-checking/kani/issues/803
4844
kani::assert($cond, concat!(stringify!($($arg)+)));
45+
// Process the arguments of the assert inside an unreachable block. This
46+
// is to make sure errors in the arguments (e.g. an unknown variable or
47+
// an argument that does not implement the Display or Debug traits) are
48+
// reported, without creating any overhead on verification performance
49+
// that may arise from processing strings involved in the arguments.
50+
// Note that this approach is only correct with the "abort" panic
51+
// strategy, but is unsound with the "unwind" panic strategy which
52+
// requires evaluating the arguments (because they might have side
53+
// effects). This is fine until we add support for the "unwind" panic
54+
// strategy, which is tracked in
55+
// https://github.com/model-checking/kani/issues/692
56+
if false {
57+
let _ = format_args!($($arg)+);
58+
}
4959
};
5060
}
5161

@@ -100,7 +110,7 @@ macro_rules! debug_assert_ne {
100110
($($x:tt)*) => ({ $crate::assert_ne!($($x)*); })
101111
}
102112

103-
// Override the print macros to skip all the formatting functionality (which
113+
// Override the print macros to skip all the printing functionality (which
104114
// is not relevant for verification)
105115
#[macro_export]
106116
macro_rules! print {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cannot find value `foo` in this scope
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! This test checks that Kani processes arguments of assert macros and produces
5+
//! an error for invalid arguments (e.g. unknown variable)
6+
7+
#[kani::proof]
8+
fn check_invalid_value_error() {
9+
assert!(1 + 1 == 2, "An assertion message that references an unknown variable {}", foo);
10+
}

tests/kani/Assert/var_in_arg.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! This test makes sure Kani does not emit "unused variable" warnings for
5+
//! variables that are only used in arguments of assert macros
6+
7+
// Promote "unused variable" warnings to an error so that this test fails if
8+
// Kani's overridden version of the assert macros drops variables used as
9+
// arguments of those macros
10+
#![deny(unused_variables)]
11+
12+
#[kani::proof]
13+
fn check_assert_with_arg() {
14+
let s = "foo";
15+
assert!(1 + 1 == 2, "An assertion message that refers to a variable {}", s);
16+
}

0 commit comments

Comments
 (0)