Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
ded56d8
add for loop transformation
thanhnguyen-aws Jun 18, 2025
2f782bf
add support for array, slice and Iter + expected tests
thanhnguyen-aws Jun 18, 2025
d0a902e
add support for Range, StepBy + expected tests
thanhnguyen-aws Jun 18, 2025
32e978c
add support for Vec + expected tests
thanhnguyen-aws Jun 18, 2025
8529488
add expected tests for quantifiers and tuples
thanhnguyen-aws Jun 18, 2025
d028bb0
add support for Zip, Chain, Map + expected tests
thanhnguyen-aws Jun 18, 2025
3fc2abc
add support for Enumerate + expected tests
thanhnguyen-aws Jun 18, 2025
8e69667
edit loop-contract docs
thanhnguyen-aws Jun 18, 2025
746311a
edit doc and some typos
thanhnguyen-aws Jun 19, 2025
475718e
Merge branch 'main' into forloopnew1
Jun 24, 2025
09f5dcc
merge origin
thanhnguyen-aws Jul 1, 2025
ded103c
restore mod.rs after merge
thanhnguyen-aws Jul 1, 2025
418f4f3
Merge branch 'main' into forloopnew1
thanhnguyen-aws Jul 14, 2025
2712069
handling StorageLive/StorageDead for forloop variables
thanhnguyen-aws Jul 17, 2025
da1666d
add expected tests for nested forloop
thanhnguyen-aws Jul 17, 2025
f9a3c2f
fix clippy
thanhnguyen-aws Jul 17, 2025
f8e2232
Merge branch 'main' into forloopnew1
thanhnguyen-aws Jul 18, 2025
8eb51e6
add for-loop rewrite method in loop-contract rfc
thanhnguyen-aws Jul 18, 2025
b4f470d
Merge branch 'forloopnew1' of https://github.com/thanhnguyen-aws/kani…
thanhnguyen-aws Jul 18, 2025
6c3e159
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Jul 22, 2025
fa5436e
Merge branch 'main' into forloopnew1
thanhnguyen-aws Jul 22, 2025
ceaed0b
add more docs
thanhnguyen-aws Jul 23, 2025
cbcc7c3
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 25, 2025
8618f55
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 25, 2025
42e6fc6
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 25, 2025
1e4ffdd
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 28, 2025
bb3f439
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 28, 2025
b716b97
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 28, 2025
e67148d
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Jul 28, 2025
860430f
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Jul 28, 2025
64833dd
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 28, 2025
202880e
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Jul 28, 2025
57950a5
add suuport for Take
thanhnguyen-aws Jul 31, 2025
4fc4402
fix the bug of using reference in for-loop pat
thanhnguyen-aws Jul 31, 2025
6cf3afc
gerge branch 'forloopnew1' of https://github.com/thanhnguyen-aws/kani…
thanhnguyen-aws Jul 31, 2025
8bc3f59
update upstream
thanhnguyen-aws Jul 31, 2025
dfbf9eb
fix import
thanhnguyen-aws Jul 31, 2025
cf41994
change kaniindex to kani_index + kaniiterlen to kani_iter_len
thanhnguyen-aws Aug 1, 2025
9b7f100
change loop-invariant to loop invariant
thanhnguyen-aws Aug 1, 2025
2d71127
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Aug 1, 2025
12b0fda
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Aug 1, 2025
2ec15b4
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Aug 1, 2025
a837f80
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Aug 1, 2025
558460b
change kani_index to kani::index and kani_iter_len to kani::iter_len
thanhnguyen-aws Aug 8, 2025
6926302
unchange loop contracts to loop invariants
thanhnguyen-aws Aug 8, 2025
969b414
add replacer for macro
thanhnguyen-aws Aug 8, 2025
0948b90
Merge remote-tracking branch 'upstream/main' into forloopnew1
thanhnguyen-aws Aug 8, 2025
94736ac
merge while-let loop
thanhnguyen-aws Aug 8, 2025
b28f1d5
fix clippy
thanhnguyen-aws Aug 8, 2025
0d1d2d2
add KaniIntoIter impl for Rev
thanhnguyen-aws Aug 11, 2025
8f7410b
fix variable name & indentation
thanhnguyen-aws Aug 11, 2025
333eeab
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Aug 11, 2025
fd2945d
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Aug 11, 2025
edf339d
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Aug 11, 2025
d1eee1f
Update rfc/src/rfcs/0012-loop-contracts.md
thanhnguyen-aws Aug 11, 2025
da30387
Merge branch 'forloopnew1' of https://github.com/thanhnguyen-aws/kani…
thanhnguyen-aws Aug 11, 2025
a121b1b
add underscores to function name
thanhnguyen-aws Aug 11, 2025
222f89c
fix typo comments
thanhnguyen-aws Aug 11, 2025
149674d
add impl KaniIter for u128 and i128 for Range
thanhnguyen-aws Aug 11, 2025
cff691a
change function name indexing to nth
thanhnguyen-aws Aug 11, 2025
b22d152
edit for-loop docs
thanhnguyen-aws Aug 12, 2025
1299e5a
add Rod Chapman example as fixme expected test
thanhnguyen-aws Aug 12, 2025
259fcf8
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Aug 13, 2025
d7b3f0a
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Aug 13, 2025
fcb696d
fix typo mistakes in doc
thanhnguyen-aws Aug 13, 2025
7d9257a
Update library/kani_macros/src/sysroot/loop_contracts/mod.rs
thanhnguyen-aws Aug 13, 2025
97aa688
add KaniIter impl for Range of i128 and u128 + remove comments
thanhnguyen-aws Aug 13, 2025
3dd9587
Merge branch 'forloopnew1' of https://github.com/thanhnguyen-aws/kani…
thanhnguyen-aws Aug 13, 2025
f90cb1a
remove comments + fix typos
thanhnguyen-aws Aug 13, 2025
0f9f59d
remove unneccessary mut
thanhnguyen-aws Aug 13, 2025
2edf3c7
fix typo
thanhnguyen-aws Aug 13, 2025
7e03b83
fix indentation
thanhnguyen-aws Aug 13, 2025
b4bffda
fix comments
thanhnguyen-aws Aug 13, 2025
80e8822
change traitbound for KaniRefIter & KaniPtrIter
thanhnguyen-aws Aug 13, 2025
a77f0cd
fix arr_inc test
thanhnguyen-aws Aug 13, 2025
1b613e9
Merge branch 'main' into forloopnew1
thanhnguyen-aws Aug 14, 2025
7a7e158
remove array clone in arr_inc test
thanhnguyen-aws Aug 14, 2025
b2d3758
Merge branch 'forloopnew1' of https://github.com/thanhnguyen-aws/kani…
thanhnguyen-aws Aug 14, 2025
1f99e4c
Update docs/src/reference/experimental/loop-contracts.md
thanhnguyen-aws Aug 14, 2025
67b38c1
fix typo mistakes
thanhnguyen-aws Aug 14, 2025
afc7322
revert loop contract -> loop invariant
thanhnguyen-aws Aug 15, 2025
c4915cc
fix typo
thanhnguyen-aws Aug 15, 2025
10db6f2
Merge branch 'main' into forloopnew1
thanhnguyen-aws Aug 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions docs/src/reference/experimental/loop-contracts.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Loop Contracts

Loop contract are used to specify invariants for loops for the sake of extending Kani's *bounded proofs* to *unbounded proofs*.
## Introduction

Loop contracts are used to specify invariants for loops for the sake of extending Kani's *bounded proofs* to *unbounded proofs*.
A [loop invariant](https://en.wikipedia.org/wiki/Loop_invariant) is an expression that holds upon entering a loop and after every execution of the loop body.
Loop contracts are composed of one or more loop invariants as well as optional `loop_modifies` attributes.
It captures something that does not change about every step of the loop.

It is worth revisiting the discussion about [bounded proof](../../tutorial-loop-unwinding.md#bounded-proof) and
Expand All @@ -24,7 +27,7 @@ fn simple_loop() {
```

In this program, the loop repeatedly decrements `x` until it equals `1`. Because we haven't specified an upper bound for `x`, to verify this function,
Kani needs to unwind the loop for `u64::MAX` iterations, which is computationally expensive. Loop contracts allow us to abstract the loop behavior, significantly reducing the verification cost.
Kani needs to unwind the loop for `u64::MAX` iterations, which is intractable. Loop contracts allow us to abstract the loop behavior, significantly reducing the verification cost.

With loop contracts, we can specify the loop’s behavior using invariants. For example:

Expand Down Expand Up @@ -61,7 +64,7 @@ Check 10: simple_loop_with_loop_contracts.loop_invariant_base.1
- Description: "Check invariant before entry for loop simple_loop_with_loop_contracts.0"
- Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts

Check 11: simple_loop_with_loop_contracts.loop_modifies.1
Check 11: simple_loop_with_loop_contracts.loop_assigns.1
- Status: SUCCESS
- Description: "Check assigns clause inclusion for loop simple_loop_with_loop_contracts.0"
- Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
Expand Down Expand Up @@ -93,19 +96,19 @@ Complete - 1 successfully verified harnesses, 0 failures, 1 total.
```


## Loop contracts for `while` loops
## Syntax and Semantics

### Syntax
>
> \#\[kani::loop_invariant\( [_Expression_](https://doc.rust-lang.org/reference/expressions.html) \)\]
>
> `while` [_Expression_](https://doc.rust-lang.org/reference/expressions.html)<sub>_except struct expression_</sub> [_BlockExpression_](https://doc.rust-lang.org/reference/expressions/block-expr.html)
> [_LoopExpression_](https://doc.rust-lang.org/reference/expressions/loop-expr.html#grammar-LoopExpression)


An invariant contract `#[kani::loop_invariant(cond)]` accepts a valid Boolean expression `cond` over the variables visible at the same scope as the loop.

### Semantics
A loop invariant contract expands to several assumptions and assertions:
A loop contract expands to several assumptions and assertions:
1. The invariant is asserted just before the first iteration.
2. The invariant is assumed on a non-deterministic state to model a non-deterministic iteration.
3. The invariant is finally asserted again to establish its inductiveness.
Expand Down Expand Up @@ -134,11 +137,57 @@ That is, we assume that we are in an arbitrary iteration after checking that the
we will either enter the loop (proof path 1) or leave the loop (proof path 2). We prove the two paths separately by killing path 1 with `kani::assume(false);`.
Note that all assertions after `kani::assume(false)` will be ignored as `false => p` can be deduced as `true` for any `p`.

In proof path 1, we prove properties inside the loop and at last check that the loop invariant is inductive.
In proof path 1, we prove properties inside the loop and at last check that the loop contract is inductive.

In proof path 2, we prove properties after leaving the loop. As we leave the loop only when the loop guard is violated, the post condition of the loop can be expressed as
`!guard && inv`, which is `x <= 1 && x >= 1` in the example. The postcondition implies `x == 1`—the property we want to prove at the end of `simple_loop_with_loop_contracts`.

## Historic values and extra variables

### Historic values

We support two notations for historic values in loop contracts:
1. `on_entry(expr)` : The value of the `expr` before entering the loop.
2. `prev(expr)` : the value of `expr` in the previous iteration. Note that Kani will assert that the loop has at least one iteration if `prev` is used in loop contracts.

Example:
```Rust
#[kani::proof]
pub fn loop_with_old_and_prev() {
let mut i = 100;
#[kani::loop_invariant((i >= 2) && (i <= 100) && (i % 2 == 0) && (on_entry(i) == 100) && (prev(i) == i + 2))]
while i > 2 {
if i == 1 {
break;
}
i = i - 2;
}
assert!(i == 2);
}
```

### `kani::index` variable in `for` loop

Kani provides an extra variable: `kani::index` that can be used in loop contracts of `for` loops.
`kani::index` presents the position (index) of the current iteration in the iterator
and is only associated with the `for` loop that immediately follows the loop contract.

Example:

```Rust
#[kani::proof]
fn forloop() {
let mut sum: u32 = 0;
let a: [u8; 10] = kani::any();
kani::assume(kani::forall!(|i in (0,10)| a[i] <= 20));
#[kani::loop_invariant(sum <= (kani::index as u32 * 20) )]
for (i, j) in a.iter().enumerate() {
sum = sum + (i as u32) ;
}
assert!(sum <= 200);
}
```

## Loop contracts inside functions with contracts
Kani supports using loop contracts together with function contracts, as demonstrated in the following example:
``` Rust
Expand Down Expand Up @@ -231,10 +280,10 @@ fn main() {

Loop contracts comes with the following limitations.

1. `while` loops and `loop` loops are supported. The other kinds of loops are not supported: [`while let` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops), and [`for` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#iterator-loops).
1. `while` loops, `loop` loops are supported. `for` loops are supported for array, slice, Iter, Vec, Range, StepBy, Chain, Zip, Map, and Enumerate. The other kinds of loops are not supported: [`while let` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops).
2. Kani infers *loop modifies* with alias analysis. Loop modifies are those variables we assume to be arbitrary in the inductive hypothesis, and should cover all memory locations that are written to during
the execution of the loops. A proof will fail if the inferred loop modifies misses some targets written in the loops.
We observed this happens when some fields of structs are modified by some other functions called in the loops.
3. Kani doesn't check if a loop will always terminate in proofs with loop contracts. So it could be that some properties are proved successfully with Kani but actually are unreachable due to the
non-termination of some loops.
4. We don't check if loop invariants are side-effect free. A loop invariant with a side effect could lead to an unsound proof result. Make sure that the specified loop contracts are side-effect free.
4. We don't check if loop contracts are side-effect free. A loop contract with a side effect could lead to an unsound proof result. Make sure that the specified loop contracts are side-effect free.
Loading
Loading