Conversation
library/std/src/lib.rs
Outdated
| kani::assert($cond, concat!("assertion: ", stringify!($cond))); | ||
| }; | ||
| ($cond:expr, $($arg:tt)+) => { | ||
| kani::assert($cond, concat!("assertion: ", stringify!($($arg)+))); |
There was a problem hiding this comment.
I guess in the future we should ensure that any variable access is valid for message arguments. Can you please add a fixme comment and an issue capturing that?
There was a problem hiding this comment.
You mean that the variable implements the Display trait for example?
There was a problem hiding this comment.
Yeah, something like that. However, I was just checking, it looks like the message side of the expression only executes if the condition fails. So I don't think it's that important.
There was a problem hiding this comment.
I see. In some sense, kani would be more lenient than rustc. For example, for
fn main() {
assert!(1 + 1 == 3, "Bad arithmetic: {}", foo);
}
rustc reports:
$ rustc no_display.rs
error[E0425]: cannot find value `foo` in this scope
--> no_display.rs:2:47
|
2 | assert!(1 + 1 == 3, "Bad arithmetic: {}", foo);
| ^^^ not found in this scope
whereas with this PR, kani doesn't complain:
$ kani no_display.rs
CBMC version 5.48.0 (cbmc-5.48.0) 64-bit x86_64 linux
Reading GOTO program from file no_display.goto
Generating GOTO Program
Adding CPROVER library (x86_64)
Removal of function pointers and virtual functions
Generic Property Instrumentation
Running with 16 object bits, 48 offset bits (user-specified)
Starting Bounded Model Checking
Runtime Symex: 0.00102581s
size of program expression: 75 steps
simple slicing removed 3 assignments
Generated 3 VCC(s), 1 remaining after simplification
Runtime Postprocess Equation: 7.25e-06s
Passing problem to propositional reduction
converting SSA
Runtime Convert SSA: 0.000213004s
Running propositional reduction
Post-processing
Runtime Post-process: 2.594e-05s
Solving with MiniSAT 2.2.1 with simplifier
100 variables, 33 clauses
SAT checker: instance is SATISFIABLE
Runtime Solver: 9.1981e-05s
Runtime decision procedure: 0.000343287s
** Results:
[assertion.1] Code generation sanity check: Correct CBMC vtable size for StructTag("tag-_4317141983261250885") (MIR type Closure(DefId(2:9147 ~ std[2f3c]::rt::lang_start::{closure#0}), [(), i8, extern "rust-call" fn(()) -> i32, (fn(),)])). Please report failures:
https://github.com/model-checking/kani/issues/new?template=bug_report.md: SUCCESS
[pointer_primitives.1] dead object in OBJECT_SIZE(&temp_0): SUCCESS
/home/ubuntu/examples/no_display.rs function main
[main.assertion.1] line 2 assertion: "Bad arithmetic: {}", foo: FAILURE
** 1 of 3 failed (2 iterations)
VERIFICATION FAILED
I'll file an issue and add a comment.
| vec![ | ||
| Stmt::decl(tmp.clone(), Some(cond), loc.clone()), | ||
| Stmt::assert(tmp.clone(), &msg, caller_loc.clone()), | ||
| Stmt::assume(tmp, loc.clone()), |
There was a problem hiding this comment.
I'm assuming the assumption here is to abort when the assertion fails, right?
There was a problem hiding this comment.
Yes, this is to maintain the current semantics of assert, which are currently roughly implemented as:
if !cond {
assert(false);
abort();
}
// cond must be true hereThere was a problem hiding this comment.
Can you use the original method codegen_fatal_error() instead?
There was a problem hiding this comment.
As far as I understand, codegen_fatal_error is meant for a panic statement, which unconditionally fails, whereas here, there is a condition involved.
The alternative I was considering is to do something similar to how rustc expands the assert macro, e.g.
cond.not().if_then_else(codegen_fatal_error(...), None)However, based on @danielsn's feedback, with an "if", we lose the structure of the original assert.
There was a problem hiding this comment.
Gotcha. I was just hoping that we could encapsulate and formalized what an assert in the rust / kani world means, which is different from the goto-c semantics. But we can do that in a follow up PR.
Maybe we can do that when we fix codegen TerminatorKind::Assert().
library/std/src/lib.rs
Outdated
| /// assert!(a + b == c, "The sum of {} and {} is {}", a, b, c); | ||
| /// ``` | ||
| /// the assert message will be: | ||
| /// assertion "The sum of {} and {} is {}", 1, 1, 2 |
There was a problem hiding this comment.
Why not keep it compatible with the std message?
There was a problem hiding this comment.
You mean remove the assertion: prefix? If so, the reason I added this prefix is to make it easy to match a reachability check with its assert. If not, can you clarify what you mean?
There was a problem hiding this comment.
The std library adds "assertion failed:" prefix to things that don't have a message. So I was wondering if we should match that.
Not sure I understand how you are planning to match the check with its assert though.
There was a problem hiding this comment.
Makes sense. The matching should be doable without the assertion: prefix. I'll remove it.
| let msg = fargs.remove(0); | ||
| let msg = utils::extract_const_message(&msg).unwrap(); | ||
| let target = target.unwrap(); | ||
| let loc = tcx.codegen_span_option(span); |
There was a problem hiding this comment.
Have you checked how the trace looks like in viewer? I'm wondering if this will look weird since we are using two different locations.
There was a problem hiding this comment.
The trace does look a bit weird in viewer (notice the missing files/lines):
Step 38: Function foo, File reach.rs, Line 4
assert!(x < 4);
var_5 = FALSE
Step 39: Function MISSING, File MISSING, Line 0
temp_0 = FALSE
Step 40: Function MISSING, File MISSING, Line 0
temp_0 = FALSE
Step 41: Function foo, File reach.rs, Line 4
assert!(x < 4);
failure: foo.assertion.1: assertion: x < 4
I'll see if changing all the locations to the "caller" location fixes it.
There was a problem hiding this comment.
Cool. BTW, the MISSING is a different problem. We should chat with @markrtuttle about this. Viewer doesn't seem to handle files that are not inside the current folder.
db21be9 to
f7e360c
Compare
f7e360c to
97ae95e
Compare
Description of changes:
Override the
assertmacro via calling a newkani::assertfunction that is implemented in codegen (via a hook). This gives us control over the implementation of the macro and allows us to inject any additional checks (e.g. reachability).For assertions with a message, e.g.
assert!(a == b, "a and b have the values {} and {}, respectively", a, b), it produces"a and b have the values {} and {}, respectively", a, binstead of the currentThis is a placeholder message; Kani doesn't support message formatted at runtime.Resolved issues:
Call-outs:
The handling of assertions with messages results in bypassing some compile-time checks. See #803 for details.
Testing:
How is this change tested? Current regressions
Is this a refactor change? No
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.