Skip to content

Commit f72a9ca

Browse files
committed
Adds support for pointer history variables in function contracts
This adds support for history variables in function contracts. History variables are (1) declared and (2) assigned to the value that their corresponding variable has at function call time. Currently, only pointers are supported.
1 parent afd8a60 commit f72a9ca

File tree

27 files changed

+488
-16
lines changed

27 files changed

+488
-16
lines changed
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are supported for parameters of the
11+
the function under test. By using the --enforce-all-contracts flag,
12+
the post-condition (which contains the history variable) is asserted.
13+
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are supported for parameters of the
11+
the function under test. By using the --enforce-all-contracts flag,
12+
the post-condition (which contains the history variable) is asserted.
13+
In this case, this assertion should fail.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_requires(*x > 0 && *x < __INT_MAX__ - 5) __CPROVER_ensures(
3+
*x >= __CPROVER_old(*x) + 4 && *x <= __CPROVER_old(*x) + 6)
4+
{
5+
*x = *x + 5;
6+
}
7+
8+
int main()
9+
{
10+
int n;
11+
foo(&n);
12+
13+
return 0;
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts _ --trace
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are supported in the case where a
11+
history variable is referred to multiple times within an ensures clause.
12+
By using the --enforce-all-contracts flag, the post-condition (which contains
13+
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are supported in the case where the
11+
function under test has multiple parameters. By using the
12+
--enforce-all-contracts flag, the post-condition (which contains the history
13+
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are supported in the case where the
11+
function under test has multiple parameters. By using the
12+
--enforce-all-contracts flag, the post-condition (which contains the history
13+
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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are not supported when referred to from
11+
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=1$
5+
^SIGNAL=0$
6+
^CONVERSION ERROR$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables may only be used with existing
11+
symbols. In other words, including a new symbol as part of __CPROVER_old()
12+
is not alowed. In such a case, the program should not parse and there
13+
should be a conversion error.
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) __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;
12+
__CPROVER_assume(n > 0 && n < __INT_MAX__ - 2);
13+
foo(&n);
14+
15+
assert(n > 2);
16+
17+
return 0;
18+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are supported with the use of the
11+
--replace-all-calls-with-contracts flag. In this case, the post-condition
12+
(which contains the history variable) becomes an assumption. We then manually
13+
assert this assumption. For this test, the assertion should succeed.
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables are supported with the use of the
11+
--replace-all-calls-with-contracts flag. In this case, the post-condition
12+
(which contains the history variable) becomes an assumption. We then manually
13+
assert this assumption. For this test, the assertion should succeed.
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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
--
9+
Verification:
10+
This test checks that history variables cannot be used as part of the
11+
pre-condition contract. In this case, verification should fail.

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,20 @@ exprt c_typecheck_baset::do_special_functions(
25752575

25762576
return std::move(ok_expr);
25772577
}
2578+
else if(identifier == CPROVER_PREFIX "old")
2579+
{
2580+
if(expr.arguments().size() != 1)
2581+
{
2582+
error().source_location = f_op.source_location();
2583+
error() << identifier << " expects one operands" << eom;
2584+
throw 0;
2585+
}
2586+
2587+
old_exprt old_expr(ID_old, expr.arguments()[0]);
2588+
old_expr.add_source_location() = source_location;
2589+
2590+
return std::move(old_expr);
2591+
}
25782592
else if(identifier==CPROVER_PREFIX "isinff" ||
25792593
identifier==CPROVER_PREFIX "isinfd" ||
25802594
identifier==CPROVER_PREFIX "isinfld" ||

src/ansi-c/cprover_builtin_headers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ __CPROVER_size_t __CPROVER_zero_string_length(const void *);
1212
__CPROVER_size_t __CPROVER_buffer_size(const void *);
1313
__CPROVER_bool __CPROVER_r_ok(const void *, __CPROVER_size_t);
1414
__CPROVER_bool __CPROVER_w_ok(const void *, __CPROVER_size_t);
15+
void __CPROVER_old(const void *);
1516

1617
// bitvector analysis
1718
__CPROVER_bool __CPROVER_get_flag(const void *, const char *);

src/ansi-c/library/cprover.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ __CPROVER_size_t __CPROVER_zero_string_length(const void *);
3535
__CPROVER_size_t __CPROVER_buffer_size(const void *);
3636
__CPROVER_bool __CPROVER_r_ok(const void *, __CPROVER_size_t);
3737
__CPROVER_bool __CPROVER_w_ok(const void *, __CPROVER_size_t);
38+
void __CPROVER_old(const void *);
3839

3940
#if 0
4041
__CPROVER_bool __CPROVER_equal();

0 commit comments

Comments
 (0)