-
Notifications
You must be signed in to change notification settings - Fork 103
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
RFC for cover ctatement #1906
RFC for cover ctatement #1906
Conversation
122800f
to
cecc910
Compare
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.
Awesome! I have 2 questions about UX:
- What will be the
Description:
of a cover statement? - What about the summary message?
rfc/src/rfcs/0003-cover-statement.md
Outdated
|
||
The `cover` function instructs Kani to find _at least one_ possible execution that satisfies the specified condition at that line of code. If there is no such execution, verification *fails*. | ||
|
||
Each cover statements will be reported as a check whose description is "condition is satisfiable" and whose status is: |
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.
What about UNDETERMINED?
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.
Added a sentence to clarify.
rfc/src/rfcs/0003-cover-statement.md
Outdated
The `cover` function instructs Kani to find _at least one_ possible execution that satisfies the specified condition at that line of code. If there is no such execution, verification *fails*. | ||
|
||
Each cover statements will be reported as a check whose description is "condition is satisfiable" and whose status is: | ||
- `SUCCESS` (green): if Kani found an execution that would cover the condition |
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.
What about COVERED
and UNREACHABLE
?
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 was my original plan, but rethinking about it, it makes sense to consider an unsatisfiable cover a verification failure, in which case SUCCESS and FAILURE would fit well with the current summary, i.e. the summary line would include the failure in the ** 1 of 15 failed
line.
If we were to add separate results, e.g. SATISFIABLE
/UNSATISFIABLE
or COVERED
/UNCOVERABLE
, the summary would be
** 0 of 15 failed (1 unsatisfiable)
VERIFICATION:- FAILED
which might be less clear.
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.
|
||
## Other Considerations | ||
|
||
We need to make sure the concrete playback feature can be used with `cover` statements that were found to be coverable. |
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.
+1
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.
Not for this RFC, but food for thought: How could we help users debug cases where a cover
statement is not coverable? I suppose this would be similar to helping users understand why a program is in fact safe (i.e., why no bad state is reachable).
rfc/src/rfcs/0003-cover-statement.md
Outdated
|
||
Users may want to specify a custom message for the cover property, in which case, we may consider adding a separate function that takes a static `str`, e.g. | ||
```rust | ||
fn cover_msg(cond: bool, msg: &'static str) |
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 add a message now?
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 debated this a bit. If we make the message a required part of the basic cover
API, then the user may end up doing lots of kani::cover(c, "")
or kani::cover(c, None)
, so it seems desirable to not make the message a mandatory argument. The alternatives are:
- Add a separate function that takes a custom message.
- Make
cover
a macro instead of a function, similar to theassert
macro.
Thoughts?
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 like the idea of adding the function and the macro. The function takes a str
and the macro generates one using the user expression if no message was provided.
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.
Done
…le unsupported constructs
rfc/src/rfcs/0003-cover-statement.md
Outdated
|
||
Each cover statements will be reported as a check whose description is "condition is satisfiable" and whose status is: | ||
- `SUCCESS` (green): if Kani found an execution that satisfies the condition | ||
- `FAILURE` (red): if Kani proved that the condition cannot be satisfied |
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.
IMO we need better results here. There's three cases:
- Success: reachable, satisfiable
- Failure: reachable, not satisfiable
- Failure: unreachable
I think it's very important for debugging to distinguish these.
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 point! This would require that we generate a "cover current location" check for every cover property the user writes. I'll add that.
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.
Done
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.
While I agree it's an important detail for debugging, I think this would complicate our verification output by adding another "status". Perhaps it would be simpler to just generate two checks for each cover statement, one for satisfiability and another one for reachability.
The current workaround to accomplish the same effect of verifying that a condition can be covered is to use `assert!(!cond)`. | ||
However, if the condition can indeed be covered, verification would fail due to the failure of the assertion. | ||
|
||
## Open questions |
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.
Please comment on the ability to deprecate the current expect_fail
. I think we could deprecate and replace this with cover, right?
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.
Good point. I'll add that.
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.
Done
How does this all work with with linker slicing and multiple harnesses? Say for example I have the following code fn my_function() {
kani::cover!(true):
}
#[kani::proof]
fn proof_a() {
// Calls my function
my_function();
}
#[kani::proof]
fn proof_b() {
// Doesn't call my function - the cover isn't reachable from this proof.
// Should it fail?
} For On the one hand, if I know On the other hand, what if I want to make sure that |
Good questions @camshaft! I would expect cover properties to be primarily used within proof harnesses since But if a function is not "obviously" unused, e.g.: fn my_function() {
kani::cover!(true):
}
fn foo(x: bool) {
if x {
my_function();
}
}
#[kani::proof]
fn proof_a() {
my_function(true);
}
#[kani::proof]
fn proof_b() {
my_function(false);
} The cover property will fail for |
Hm, this is sort of tricky. For example, what if CBMC changed the way it does program slicing? That could change whether a proof harness passes or fails, right? If so, that behavior strikes me as surprising. Maybe we could restrict |
Yes this is a bit tricky. These As a side note, this got me thinking about how to reason about coverage of a program in relation to its inputs. I wrote up a quick sketch of what it could look like: https://gist.github.com/camshaft/9cc5e93c1bfdf96ec7dddfd1264a8b5d |
I don't know if we should limit cover statements to only be used inside a harness although I do agree that the current behavior would be rather cryptic. Ideally, we should report all properties that were dropped by CBMC as unreachable, and that should be the case for assertions and cover statements. This discussion made me wonder if we should really fail the verification because a cover statement. |
|
||
Users typically want to gain confidence that a proof checks what it is supposed to check, i.e. that properties are not passing vacuously due to an over-constrained environment. | ||
|
||
A new Kani macro, `cover` will be created that can be used for checking that a certain condition _can_ occur at a specific location in the code. |
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.
Something that isn't clear to me after reading this section is how cover
is different from assert
. If my understanding is correct, assert
checks a condition, while cover
checks a condition AND ensures that the statement itself is reachable.
If we agree on that, then the "can" in this sentence should be "will" instead.
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 conceptually they are similar. assert
says this condition must hold for all executions. cover
says this condition must hold for at least one execution.
rfc/src/rfcs/0003-cover-statement.md
Outdated
|
||
Each cover statements will be reported as a check whose description is "condition is satisfiable" and whose status is: | ||
- `SUCCESS` (green): if Kani found an execution that satisfies the condition | ||
- `FAILURE` (red): if Kani proved that the condition cannot be satisfied |
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.
While I agree it's an important detail for debugging, I think this would complicate our verification output by adding another "status". Perhaps it would be simpler to just generate two checks for each cover statement, one for satisfiability and another one for reachability.
Good discussion. How about we make an unsatisfiable cover property non-failing by default, but add an option, e.g. |
Updated RFC to reflect this discussion. |
rfc/src/rfcs/0003-cover-statement.md
Outdated
``` | ||
This is typically used to ensure that verified checks are not passing _vacuously_, e.g. due to overconstrained pre-conditions. | ||
|
||
The special case of verifying that a certain line of code is reachable can be achieved using `kani::cover!(true)`, e.g. |
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.
Since this is a macro, could we just allow users to do kani::cover!()
?
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.
Good idea! I'll update the RFC.
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.
Done.
|
||
### Impact on Overall Verification Status | ||
|
||
By default, unsatisfiable and unreachable `cover` checks will not impact verification success or failure. |
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.
Impact overall verification results
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.
What I meant is that they won't cause an otherwise successful verification run to fail (i.e. if all normal checks are passing). Should I clarify the sentence a bit, or did you mean something else?
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 it's OK
} | ||
``` | ||
|
||
We can consider adding an option that would cause verification to fail if a cover property was unsatisfiable or unreachable, e.g. `--fail-uncoverable`. |
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 part of the RFC or open questions?
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 move it to the open questions.
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.
Done.
- Allow `cover!()` without arguments - Move discussion on adding an option to fail verification to open questions
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 OK with the initial proposal. I think we can tune it as we try it out. Please don't forget to get a second approval.
Thanks!
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.
My main concern with the current proposal is the fact that unsatisfiable/unreachable cover
statements don't affect overall verification results, leading to surprises later. An alternative is to switch the proposal so the option becomes --allow-uncoverable
instead (think of it as a sign-off from the user).
However, there's no way to know if that's what we want at the moment, let's get an initial version out and see where it goes from here.
rfc/src/rfcs/0003-cover-statement.md
Outdated
Each cover statements will be reported as a check whose description is `cover condition "cond"` and whose status is: | ||
- `SATISFIED` (green): if Kani found an execution that satisfies the condition | ||
- `UNSATISFIABLE` (yellow): if Kani proved that the condition cannot be satisfied | ||
- `UNREACHABLE` (yellow): if Kani proved that the cover statement itself cannot be reached |
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.
Just a few suggestions in this paragraph:
- "statements" (1st occurrence) -> "statement"
- Dots at the end of each sentence.
- Maybe use
cover condition `<cond>`
?
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.
Fixed the first two.
For the third, I changed it to:
cover condition: cond
to mimic the assert message:
assertion failed: 1 + 1 == 3
I hope this is OK.
rfc/src/rfcs/0003-cover-statement.md
Outdated
Check 2: main.cover.2 | ||
- Status: SATISFIED | ||
- Description: "cover condition "a == 0"" | ||
- Location: foo.rs:9:5 in function 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.
Point 3 in my previous comment (use cover condition `<cond>`
) also applies here, because otherwise you end up with ""
at the end.
Description of changes:
An RFC for adding a new
cover
API to Kani.A rendered version can be found here.
Resolved issues:
Resolves #1905
Related RFC:
Optional #ISSUE-NUMBER.
Call-outs:
Testing:
How is this change tested?
Is this a refactor change?
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.