-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix assert!() and panic!() code generation #687
Conversation
I refactored how we codegen panic statements so it now it terminate the program per its spec. I have also removed the hack we had to try to get the assert location. Since we currently do not support stack unwinding, the panic codegen will always terminate immediately and it will not try to unwind the stack. I added an option to RMC to force the compiler to use abort as the panic strategy and a check to rmc codegen that will fail if the user tries to override that. Another note is that we currently do not support `#[track_caller]` and I have not changed that. This change fixes #67, fixes model-checking#466, fixes model-checking#543, and fixes model-checking#636. This change also mitigates model-checking#545.
@@ -207,6 +207,10 @@ impl Stmt { | |||
) | |||
} | |||
|
|||
pub fn assume_false(loc: Location) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doc comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually removing this since I am adding a builtin abort()
} | ||
} | ||
} else { | ||
tracing::error!(?values, "Oh no"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More informative message please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooops... That was a debug statement. =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove it!
); | ||
if let Some(values) = fargs[0].struct_expr_values() { | ||
if !values.is_empty() { | ||
if let ExprValue::AddressOf(msg_value) = values[0].value() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any other times we might want to do this? Could be a utility function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. I figured that this code has a lot of assumptions about the format of the input, so I was thinking about leaving here for now and pulled it out if we start seeing this pattern in other places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Jai had a utility function that does this in his first PR (that's still open). Maybe just steal it from there?
pub fn codegen_fatal_error(&self, msg: &str, span: Option<Span>) -> Stmt { | ||
let loc = self.codegen_caller_span(&span); | ||
Stmt::block( | ||
vec![Stmt::assert_false(msg, loc.clone()), Stmt::assume_false(loc.clone())], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we assume(false)
or abort()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question. What do you think? I can try it out with abort and see how CBMC behaves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I replaced this by calling abort()
instead of assume(false)
, but I still see this message on CBMC output:
aborting path on assume(false) at file <builtin-library-abort> line 6 function abort thread 0
So I don't think it will make much difference. I'll use abort() to improve readability.
Some(def_id) == tcx.lang_items().panic_fn() | ||
|| Some(def_id) == tcx.lang_items().panic_display() | ||
|| Some(def_id) == tcx.lang_items().begin_panic_fn() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
loc, | ||
) | ||
let msg = format!( | ||
"a panicking function {} is invoked", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can give a clearer message here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Can I do it in a follow up PR since this will likely impact a lot of expected tests. I think this PR is already big enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -56,19 +56,22 @@ fn ibxor_test(a: i32, b: i32, correct: i32, wrong: i32) { | |||
} | |||
|
|||
fn main() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IS this to prevent one failure from hiding others?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Otherwise everything passed after iadd_test()
because the checks become unreachable.
match rmc::nondet::<u8>() { | ||
// at this point, f == 0.0 | ||
// should succeed | ||
0 => assert!(1.0 / f != 0.0 / f), | ||
// should succeed | ||
1 => assert!(!(1.0 / f == 0.0 / f)), | ||
// should fail | ||
2 => assert!(1.0 / f == 0.0 / f), | ||
// should fail | ||
3 => assert!(0.0 / f == 0.0 / f), | ||
// should suceed | ||
_ => assert!(0.0 / f != 0.0 / f), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense to have a macro for this pattern
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured it wasn't worth the effort. There are a few differences in them and a macro would probably make it harder to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should have the original, primitive cbmc assert available still.
Then our tests could still be straight-line code, just using rmc::assert()
instead of assert!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about adding a function rmc::check() that had the behaviour you mentioned. What do you think? I would prefer not having two types of assert.
That said, I would prefer adding it in a different PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, once we have the #[proof]
attribute integrated, we should be able to easily split tests like this into multiple harnesses in the same file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm cool with a different name (brainstorming: rmc::probe
? check
also seems good) and in a follow up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created #695 to capture the work.
|
||
/* The function dynamic object has no associated data */ | ||
rmc::expect_fail(size_from_vtable(vtable!(fun)) != 0, "Wrong size"); | ||
} | ||
|
||
pub fn unit_to_u32() -> u32 { | ||
assert!(false); | ||
if rmc::nondet::<bool>() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need the turbofish here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh... that's a good point. I guess because it's inside an if
, we probably don't. Let me check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was it necessary?
); | ||
if let Some(values) = fargs[0].struct_expr_values() { | ||
if !values.is_empty() { | ||
if let ExprValue::AddressOf(msg_value) = values[0].value() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Jai had a utility function that does this in his first PR (that's still open). Maybe just steal it from there?
src/test/rmc/Options/check_tests.rs
Outdated
@@ -4,7 +4,7 @@ | |||
// Check that we can verify test harnesses using the --tests argument. | |||
// Note: We need to provide the compile-flags because compile test runs rustc directly and via rmc. | |||
|
|||
// compile-flags: --test | |||
// compile-flags: --test --C panic=abort |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me double check. I think you are correct though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was it necessary?
match rmc::nondet::<u8>() { | ||
// at this point, f == 0.0 | ||
// should succeed | ||
0 => assert!(1.0 / f != 0.0 / f), | ||
// should succeed | ||
1 => assert!(!(1.0 / f == 0.0 / f)), | ||
// should fail | ||
2 => assert!(1.0 / f == 0.0 / f), | ||
// should fail | ||
3 => assert!(0.0 / f == 0.0 / f), | ||
// should suceed | ||
_ => assert!(0.0 / f != 0.0 / f), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should have the original, primitive cbmc assert available still.
Then our tests could still be straight-line code, just using rmc::assert()
instead of assert!
- Use abort instead of assume(false). - Add better method doc. - Create utility function to extract string constant. - Add fixme test and link to issue to add support to stack unwind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is good to merge. I really want rmc::check
or whatever we call it, but whenever we get to it I guess. :)
There were two very minor things I didn't see addressed that I added comments to, also.
|
||
/* The function dynamic object has no associated data */ | ||
rmc::expect_fail(size_from_vtable(vtable!(fun)) != 0, "Wrong size"); | ||
} | ||
|
||
pub fn unit_to_u32() -> u32 { | ||
assert!(false); | ||
if rmc::nondet::<bool>() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was it necessary?
src/test/rmc/Options/check_tests.rs
Outdated
@@ -4,7 +4,7 @@ | |||
// Check that we can verify test harnesses using the --tests argument. | |||
// Note: We need to provide the compile-flags because compile test runs rustc directly and via rmc. | |||
|
|||
// compile-flags: --test | |||
// compile-flags: --test --C panic=abort |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was it necessary?
My bad. I had missed them. Thanks for pointing it out. |
I refactored how we codegen panic statements so it now it terminate the program per its spec. I have also removed the hack we had to try to get the assert location. Since we currently do not support stack unwinding, the panic codegen will always terminate immediately and it will not try to unwind the stack. I added an option to RMC to force the compiler to use abort as the panic strategy and a check to rmc codegen that will fail if the user tries to override that. Another note is that we currently do not support `#[track_caller]` and I have not changed that. This change fixes model-checking#67, fixes model-checking#466, fixes model-checking#543, and fixes model-checking#636. This change also mitigates model-checking#545.
I refactored how we codegen panic statements so it now it terminate the program per its spec. I have also removed the hack we had to try to get the assert location. Since we currently do not support stack unwinding, the panic codegen will always terminate immediately and it will not try to unwind the stack. I added an option to RMC to force the compiler to use abort as the panic strategy and a check to rmc codegen that will fail if the user tries to override that. Another note is that we currently do not support `#[track_caller]` and I have not changed that. This change fixes model-checking#67, fixes model-checking#466, fixes model-checking#543, and fixes model-checking#636. This change also mitigates model-checking#545.
I refactored how we codegen panic statements so it now it terminate the program per its spec. I have also removed the hack we had to try to get the assert location. Since we currently do not support stack unwinding, the panic codegen will always terminate immediately and it will not try to unwind the stack. I added an option to RMC to force the compiler to use abort as the panic strategy and a check to rmc codegen that will fail if the user tries to override that. Another note is that we currently do not support `#[track_caller]` and I have not changed that. This change fixes model-checking#67, fixes model-checking#466, fixes model-checking#543, and fixes model-checking#636. This change also mitigates model-checking#545.
I refactored how we codegen panic statements so it now it terminate the program per its spec. I have also removed the hack we had to try to get the assert location. Since we currently do not support stack unwinding, the panic codegen will always terminate immediately and it will not try to unwind the stack. I added an option to RMC to force the compiler to use abort as the panic strategy and a check to rmc codegen that will fail if the user tries to override that. Another note is that we currently do not support `#[track_caller]` and I have not changed that. This change fixes #67, fixes #466, fixes #543, and fixes #636. This change also mitigates #545.
Description of changes:
I refactored how we codegen panic statements so it now it terminate the program per its spec. I have also removed the hack we had to try to get the assert location.
Since we currently do not support stack unwinding, the panic codegen will always terminate immediately and it will not try to unwind the stack. I added an option to RMC to force the compiler to use abort as the panic strategy and a check to rmc codegen that will fail if the user tries to override that.
Another note is that we currently do not support
#[track_caller]
and I have not changed that.This change fixes #67, fixes #466, fixes #543, and fixes #636. This change also mitigates #545.
Resolved issues:
Resolves #67
Resolves #466
Resolves #543
Resolves #636
Call-outs:
I split this into two commits. One with the changes to the compiler and new test cases. The second commit is adjustments to our existing tests.
Testing:
How is this change tested? New and old test cases.
Is this a refactor change? Yes
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.