Skip to content

Commit 31fe7de

Browse files
author
Long Pham
committed
Multidimensional decreases clauses
1 parent 03213c1 commit 31fe7de

File tree

20 files changed

+342
-85
lines changed

20 files changed

+342
-85
lines changed

regression/contracts/variant_missing_invariant_warning/test.desc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
CORE
22
main.c
33
--apply-loop-contracts
4-
activate-multi-line-match
54
^The loop at file main.c line 4 function main does not have a loop invariant, but has a decreases clause. Hence, a default loop invariant \('true'\) is being used.$
6-
^\[main.1\].*Check loop invariant before entry: SUCCESS$
7-
^\[main.2\].*Check that loop invariant is preserved: SUCCESS$
85
^\[main.3\].*Check decreases clause on loop iteration: SUCCESS$
96
^EXIT=0$
107
^SIGNAL=0$
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <stdio.h>
2+
3+
int ackermann(int m, int n);
4+
5+
int main()
6+
{
7+
int m = 3;
8+
int n = 5;
9+
int result = ackermann(m, n);
10+
11+
printf("Result of the Ackermann function: %d\n", result);
12+
return 0;
13+
}
14+
15+
int ackermann(int m, int n)
16+
// clang-format off
17+
__CPROVER_requires(0 <= m && 0 <= n)
18+
__CPROVER_ensures(__CPROVER_return_value >= 0)
19+
// clang-format on
20+
{
21+
while(m > 0)
22+
// clang-format off
23+
__CPROVER_loop_invariant(0 <= m && 0 <= n)
24+
__CPROVER_decreases(m, n)
25+
// clang-format on
26+
{
27+
if(n == 0)
28+
{
29+
m--;
30+
n = 1;
31+
}
32+
else
33+
{
34+
n = ackermann(m, n - 1);
35+
m--;
36+
}
37+
}
38+
39+
return n + 1;
40+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts --replace-all-calls-with-contracts
4+
^\[ackermann.3\] .* Check decreases clause on loop iteration: SUCCESS$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test succeeds. It tests whether we can prove (only partially) the
10+
termination of the Ackermann function using a multidimensional decreases clause.
11+
12+
Note that this particular implementation of the Ackermann function contains
13+
both a while-loop and recursion. Therefore, to fully prove the termination of
14+
the Ackermann function, we must prove both
15+
(i) the termination of the while-loop and
16+
(ii) the termination of the recursion.
17+
Because CBMC does not support termination proofs of recursions (yet), we cannot
18+
prove the latter, but the former. Hence, the termination proof in the code is
19+
only "partial."
20+
21+
Furthermore, the Ackermann function has a function contract that the result
22+
is always non-negative. This post-condition is necessary for establishing
23+
the loop invariant. However, in this test, we do not enforce the function
24+
contract. Instead, we assume that the function contract is correct and use it
25+
(i.e. replace a recursive call of the Ackermann function with its contract).
26+
27+
We cannot verify/enforce the function contract of the Ackermann function, since
28+
CBMC does not support function contracts for recursively defined functions.
29+
As of now, CBMC only supports function contracts for non-recursive functions.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
int main()
3+
{
4+
int i = 0;
5+
int j = 0;
6+
int N = 10;
7+
while(i < N)
8+
// clang-format off
9+
__CPROVER_loop_invariant(0 <= i && i <= N && 0 <= j && j <= N)
10+
__CPROVER_decreases(N - i, j)
11+
// clang-format on
12+
{
13+
if(j < N)
14+
{
15+
j++;
16+
}
17+
else
18+
{
19+
i++;
20+
j = 0;
21+
}
22+
}
23+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^\[main.3\] .* Check decreases clause on loop iteration: FAILURE$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test fails because the given multidimensional decreases clause does not
10+
monotonically decrease. A mistake is in the second component of the
11+
decreases clause: j. It should instead be N - j.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
int main()
3+
{
4+
int i = 0;
5+
int j = 0;
6+
int N = 10;
7+
while(i < N)
8+
// clang-format off
9+
__CPROVER_loop_invariant(0 <= i && i <= N && 0 <= j && j <= N)
10+
__CPROVER_decreases(i, N - j)
11+
// clang-format on
12+
{
13+
if(j < N)
14+
{
15+
j++;
16+
}
17+
else
18+
{
19+
i++;
20+
j = 0;
21+
}
22+
}
23+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^\[main.3\] .* Check decreases clause on loop iteration: FAILURE$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test fails because the given multidimensional decreases clause does not
10+
monotonically decrease. A mistake is in the first component of the
11+
decreases clause: i. It should instead be N - i.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
int main()
3+
{
4+
int i = 0;
5+
int j = 0;
6+
int N = 10;
7+
while(i < N)
8+
// clang-format off
9+
__CPROVER_loop_invariant(0 <= i && i <= N && 0 <= j && j <= N)
10+
__CPROVER_decreases(N - i, N - j)
11+
// clang-format on
12+
{
13+
if(j < N)
14+
{
15+
j++;
16+
}
17+
else
18+
{
19+
i++;
20+
j = 0;
21+
}
22+
}
23+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^\[main.3\].*Check decreases clause on loop iteration: SUCCESS$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test succeeds. It tests whether a multidimensional decreases clause works
10+
properly.

regression/contracts/variant_not_decreasing_fail/test.desc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
CORE
22
main.c
33
--apply-loop-contracts
4-
^main.c function main$
5-
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
6-
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
74
^\[main.3\] .* Check decreases clause on loop iteration: FAILURE$
8-
^.* 1 of 3 failed \(2 iterations\)$
9-
^VERIFICATION FAILED$
105
^EXIT=10$
116
^SIGNAL=0$
127
--

0 commit comments

Comments
 (0)