-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix contract expansion for
old
(#3491)
Fixes the macro expansion for contracts to properly place history expressions. ## Problem Before this PR, instantiations of "remembers variables" (i.e., the variables that save the value before the function executes) were always put *above* any statements from previous macro expansions. For example, for this code (from #3359): ```rust #[kani::requires(val < i32::MAX)] #[kani::ensures(|result| *result == old(val + 1))] pub fn next(mut val: i32) -> i32 { val + 1 } ``` Kani would first expand the `requires` attribute and insert `kani::assume(val < i32::MAX)`. The expansion of `ensures` would then put the remembers variables first, generating this: ``` let remember_kani_internal_1e725538cd5566b8 = val + 1; kani::assume(val < i32::MAX); ``` which causes an integer overflow because we don't restrict the value of `val` before adding 1. Instead, we want: ``` kani::assume(val < i32::MAX); let remember_kani_internal_1e725538cd5566b8 = val + 1; ``` ## Solution The solution is to insert the remembers variables immediately after preconditions--that way, they respect the preconditions but are still declared before the function under contract executes. When we're expanding an `ensures` clause, we iterate through each of the already-generated statements, find the position where the preconditions end, then insert the remembers variables there. For instance: ``` kani::assume(x < 100); kani::assume(y < 10); kani::assume(x + y < 105); <-- remembers variables go here --> let _wrapper_arg = ... ``` --- Resolves #3359 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
- Loading branch information
1 parent
4a9a70c
commit 33e3c36
Showing
10 changed files
with
152 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...xpected/function-contract/history/respects-preconditions/ensures_before_requires.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
next\ | ||
- Status: SUCCESS\ | ||
- Description: "attempt to add with overflow" | ||
|
||
VERIFICATION:- SUCCESSFUL |
18 changes: 18 additions & 0 deletions
18
tests/expected/function-contract/history/respects-preconditions/ensures_before_requires.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright Kani Contributors | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
// kani-flags: -Zfunction-contracts | ||
|
||
// Demonstrate that when the ensures contract is before the requires contract, | ||
// the history expression respects the upper bound on x, so x + 1 doesn't overflow | ||
// This example is taken from https://github.com/model-checking/kani/issues/3359 | ||
|
||
#[kani::ensures(|result| *result == old(val + 1))] | ||
#[kani::requires(val < i32::MAX)] | ||
pub fn next(val: i32) -> i32 { | ||
val + 1 | ||
} | ||
|
||
#[kani::proof_for_contract(next)] | ||
pub fn check_next() { | ||
let _ = next(kani::any()); | ||
} |
5 changes: 5 additions & 0 deletions
5
tests/expected/function-contract/history/respects-preconditions/modifies.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
modify\ | ||
- Status: SUCCESS\ | ||
- Description: "attempt to add with overflow" | ||
|
||
VERIFICATION:- SUCCESSFUL |
29 changes: 29 additions & 0 deletions
29
tests/expected/function-contract/history/respects-preconditions/modifies.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright Kani Contributors | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
// kani-flags: -Zfunction-contracts | ||
|
||
// Demonstrate the the history expression respects preconditions | ||
// with multiple interleaved preconditions, modifies contracts, and history expressions | ||
|
||
#[derive(kani::Arbitrary)] | ||
struct Point<X, Y> { | ||
x: X, | ||
y: Y, | ||
} | ||
|
||
#[kani::requires(ptr.x < 100)] | ||
#[kani::ensures(|result| old(ptr.x + 1) == ptr.x)] | ||
#[kani::modifies(&mut ptr.x)] | ||
#[kani::ensures(|result| old(ptr.y - 1) == ptr.y)] | ||
#[kani::modifies(&mut ptr.y)] | ||
#[kani::requires(ptr.y > 0)] | ||
fn modify(ptr: &mut Point<u32, u32>) { | ||
ptr.x += 1; | ||
ptr.y -= 1; | ||
} | ||
|
||
#[kani::proof_for_contract(modify)] | ||
fn main() { | ||
let mut p: Point<u32, u32> = kani::any(); | ||
modify(&mut p); | ||
} |
5 changes: 5 additions & 0 deletions
5
...xpected/function-contract/history/respects-preconditions/requires_before_ensures.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
next\ | ||
- Status: SUCCESS\ | ||
- Description: "attempt to add with overflow" | ||
|
||
VERIFICATION:- SUCCESSFUL |
17 changes: 17 additions & 0 deletions
17
tests/expected/function-contract/history/respects-preconditions/requires_before_ensures.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright Kani Contributors | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
// kani-flags: -Zfunction-contracts | ||
|
||
// Demonstrate that when the requires contract is before the ensures contract, the history expression respects the upper bound on x, so x + 1 doesn't overflow | ||
// This example is taken from https://github.com/model-checking/kani/issues/3359 | ||
|
||
#[kani::requires(val < i32::MAX)] | ||
#[kani::ensures(|result| *result == old(val + 1))] | ||
pub fn next(val: i32) -> i32 { | ||
val + 1 | ||
} | ||
|
||
#[kani::proof_for_contract(next)] | ||
pub fn check_next() { | ||
let _ = next(kani::any()); | ||
} |