Skip to content

Commit e645434

Browse files
authored
Merge pull request #6025 from ArenBabikian/ghost-variables
Supports history variables in function contracts
2 parents 8e95986 + 6ce96c7 commit e645434

File tree

33 files changed

+577
-16
lines changed

33 files changed

+577
-16
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <stdio.h>
2+
3+
void bar(int *l) __CPROVER_assigns(*l) __CPROVER_requires(l != NULL)
4+
__CPROVER_ensures(__CPROVER_old(*l) == *l)
5+
{
6+
}
7+
8+
void foo(int *n) __CPROVER_assigns(*n) __CPROVER_requires(n != NULL)
9+
__CPROVER_ensures(__CPROVER_old(*n) == *n)
10+
{
11+
bar(n);
12+
}
13+
14+
int main()
15+
{
16+
int m;
17+
foo(&m);
18+
19+
return 0;
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo --replace-call-with-contract bar
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSUME tmp_cc\$\d != \(\(signed int \*\)NULL\)
8+
ASSERT n != \(\(signed int \*\)NULL\)
9+
ASSUME tmp_cc == \*n
10+
ASSERT tmp_cc\$\d == \*tmp_cc\$\d
11+
--
12+
--
13+
Verification:
14+
This test checks that history variables are supported for parameters of the
15+
the function under test. By using the --enforce-all-contracts flag,
16+
the post-condition (which contains the history variable) is asserted.
17+
In this case, this assertion should pass.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 5)
3+
{
4+
*x = *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n;
10+
foo(&n);
11+
12+
return 0;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*tmp_cc\$\d == tmp_cc\$\d \+ 5
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported for parameters of the
12+
the function under test. By using the --enforce-all-contracts flag,
13+
the post-condition (which contains the history variable) is asserted.
14+
In this case, this assertion should pass.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_ensures(*x < __CPROVER_old(*x) + 5)
3+
{
4+
*x = *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n;
10+
foo(&n);
11+
12+
return 0;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
ASSERT \*tmp_cc\$\d < tmp_cc\$\d \+ 5
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported for parameters of the
12+
the function under test. By using the --enforce-all-contracts flag,
13+
the post-condition (which contains the history variable) is asserted.
14+
In this case, this assertion should fail.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <limits.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_requires(*x > 0 && *x < INT_MAX - 5) __CPROVER_ensures(
5+
*x >= __CPROVER_old(*x) + 4 && *x <= __CPROVER_old(*x) + 6)
6+
{
7+
*x = *x + 5;
8+
}
9+
10+
int main()
11+
{
12+
int n;
13+
foo(&n);
14+
15+
return 0;
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT tmp_if_expr\$\d
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported in the case where a
12+
history variable is referred to multiple times within an ensures clause.
13+
By using the --enforce-all-contracts flag, the post-condition (which contains
14+
the history variable) is asserted. In this case, this assertion should pass.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
void foo(int *x, int *y) __CPROVER_assigns(*x, *y)
2+
__CPROVER_ensures(*x == __CPROVER_old(*y) + 1 && *y == __CPROVER_old(*x) + 2)
3+
{
4+
int x_initial = *x;
5+
*x = *y + 1;
6+
*y = x_initial + 2;
7+
}
8+
9+
int main()
10+
{
11+
int x, y;
12+
foo(&x, &y);
13+
14+
return 0;
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT tmp_if_expr
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported in the case where the
12+
function under test has multiple parameters. By using the
13+
--enforce-all-contracts flag, the post-condition (which contains the history
14+
variables) is asserted. In this case, this assertion should pass.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
void foo(int *x, int *y) __CPROVER_assigns(*x, *y)
2+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 2 || *y == __CPROVER_old(*y) + 3)
3+
{
4+
*x = *x + 1;
5+
*y = *y + 2;
6+
}
7+
8+
int main()
9+
{
10+
int x, y;
11+
foo(&x, &y);
12+
13+
return 0;
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
ASSERT tmp_if_expr
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported in the case where the
12+
function under test has multiple parameters. By using the
13+
--enforce-all-contracts flag, the post-condition (which contains the history
14+
variables) is asserted. In this case, this assertion should fail.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <assert.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 5)
5+
{
6+
assert(__CPROVER_old(*x) == *x);
7+
*x = *x + 5;
8+
}
9+
10+
int main()
11+
{
12+
int n;
13+
foo(&n);
14+
15+
return 0;
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
warning: ignoring old
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are not supported when referred to from
12+
a function body. In such a case, verification should fail.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_ensures(*x == __CPROVER_old(*y) + 5)
3+
{
4+
*x = *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n;
10+
foo(&n);
11+
12+
return 0;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=(1|64)$
5+
^SIGNAL=0$
6+
^CONVERSION ERROR$
7+
error: failed to find symbol 'y'
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables may only be used with existing
12+
symbols. In other words, including a new symbol as part of __CPROVER_old()
13+
is not alowed. In such a case, the program should not parse and there
14+
should be a conversion error.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <stdlib.h>
2+
3+
struct pair
4+
{
5+
int *x;
6+
int *y;
7+
};
8+
9+
void foo(struct pair p) __CPROVER_assigns(*(p.y))
10+
__CPROVER_ensures(*(p.y) == __CPROVER_old(*(p.y)) + 5)
11+
{
12+
*(p.y) = *(p.y) + 5;
13+
}
14+
15+
int main()
16+
{
17+
struct pair p;
18+
p.x = malloc(sizeof(int));
19+
p.y = malloc(sizeof(int));
20+
21+
foo(p);
22+
23+
return 0;
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*tmp_cc\$\d\.y == tmp_cc\$\d \+ 5
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported for dereferences over
12+
pointers to struct members. By using the --enforce-all-contracts flag, the
13+
post-condition (which contains the history variable) is asserted. In this
14+
case, this assertion should pass.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
#include <limits.h>
3+
4+
void foo(int *x) __CPROVER_assigns(*x) __CPROVER_requires(*x > 0)
5+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 2)
6+
{
7+
*x = *x + 2;
8+
}
9+
10+
int main()
11+
{
12+
int n;
13+
__CPROVER_assume(n > 0 && n < INT_MAX - 2);
14+
foo(&n);
15+
16+
assert(n > 2);
17+
18+
return 0;
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*\(&n\) > 0
8+
ASSUME \*\(&n\) == tmp_cc[\$\d]? \+ 2
9+
--
10+
--
11+
Verification:
12+
This test checks that history variables are supported with the use of the
13+
--replace-all-calls-with-contracts flag. In this case, the pre-condition
14+
becomes an assertion and the post-condition (which contains the history
15+
variable) becomes an assumption.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <assert.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x) __CPROVER_requires(*x == 0)
4+
__CPROVER_ensures(*x >= __CPROVER_old(*x) + 2)
5+
{
6+
*x = *x + 2;
7+
}
8+
9+
int main()
10+
{
11+
int n = 0;
12+
foo(&n);
13+
14+
assert(n >= 2);
15+
16+
return 0;
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*\(&n\) == 0
8+
ASSUME \*\(&n\) >= tmp_cc[\$\d]? \+ 2
9+
--
10+
--
11+
Verification:
12+
This test checks that history variables are supported with the use of the
13+
--replace-all-calls-with-contracts flag. In this case, the pre-condition
14+
becomes an assertion and the post-condition (which contains the history
15+
variable) becomes an assumption.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_requires(*x == __CPROVER_old(*x))
5+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 2)
6+
{
7+
*x = *x + 2;
8+
}
9+
10+
int main()
11+
{
12+
int n = 0;
13+
foo(&n);
14+
15+
assert(n == 2);
16+
17+
return 0;
18+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=(1|64)$
5+
^SIGNAL=0$
6+
^CONVERSION ERROR$
7+
error: __CPROVER_old expressions are not allowed in __CPROVER_requires clauses
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables cannot be used as part of the
12+
pre-condition contract. In this case, verification should fail.

0 commit comments

Comments
 (0)