-
Notifications
You must be signed in to change notification settings - Fork 110
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 the bug: Loop contracts are not composable with function contracts #3979
base: main
Are you sure you want to change the base?
Fix the bug: Loop contracts are not composable with function contracts #3979
Conversation
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.
Thanks for fixing this! Can you also update our loop contracts documentation in our book to talk about this change, and more generally, explain when to use #[kani::proof]
vs. #[kani::proof_for_contract]
? If I just have a loop contract without a function contract, can I use #[kani::proof_for_contract]
?
(I haven't reviewed the code, I'm leaving that to @qinheping).
Using |
Thanks for the test, but please also update the book documentation so that our users know how to use this feature. I find it confusing that the annotation is irrelevant--why would we have both be acceptable if they do the same thing? Can you do some more thinking about the UX here before we merge this? We want to make sure that our users are set up to use this correctly. |
Let me explain it more clearly. This PR does not add any new feature, it just fixed the bug that when a function has contract, then the loop inside it will be unwinded instead of contracted even if there is a loop invariant and kani flag -Z loop-contracts is enabled. There is no change in the definitions of #[kani::proof] or #[kani::proof_for_contract] whether there are loops/loop-invariants inside the function or not, and the two harnesses are different. As you can see the verification results (in two expected files) are different, #[kani::proof_for_contract] succeed because it assumes the requires clause, while #[kani::proof] failed because it asserts (not assumes) the requires clause. Therefore, I think that the users need no additional knowledge in this case. |
That makes sense.
I'm not sure that's true. This PR changes the behavior of Kani such that you can use
I think our documentation does need some kind of example about composing loop contracts with function contracts, since there's nothing in our book to suggest that this is even possible (all of our loop contract examples use What I really want is documentation that tells users which kind of harness they should use for each case:
|
This PR does not change the behavior of Kani for
I strongly agree that the easiest way to get rid of the confusion is removing the name "loop contract" and consistently calling it "loop-invariant". If everyone else agree, we can create a separate PR to change the flag name and all related tests. |
I strongly agree that this is the right move. Since this is a breaking change, maybe bring it up at staff meeting before making a PR. Also cc @qinheping.
It does change the behavior--you can now use |
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.
Can you also add this test?
// -Z loop-contracts -Z function-contracts --no-assert-contracts
#![feature(proc_macro_hygiene)]
#![feature(stmt_expr_attributes)]
#[kani::requires(i>=2)]
#[kani::ensures(|ret| *ret == 2)]
pub fn has_loop(mut i: u16) -> u16 {
#[kani::loop_invariant(i>=2)]
while i > 2 {
i = i - 1
}
i
}
#[kani::proof]
fn contract_proof() {
let i: u16 = kani::any();
let j = has_loop(i);
}
This proof should let you just prove the loop invariant while ignoring the contract.
tests/expected/loop-contract/contract_proof_function_with_loop.rs
Outdated
Show resolved
Hide resolved
Co-authored-by: Carolyn Zech <[email protected]>
Co-authored-by: Carolyn Zech <[email protected]>
This PR fixes the bug of loop contracts are not composable with function contracts.
Previously, using loop-contract inside a function with contract may result in unwinding the loop instead of contracting it.
Resolves #3910
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.