Skip to content

Commit 04c723d

Browse files
committed
Adds support for pointer ghost variables in function contracts
This adds support for ghost variables in function contracts. Ghost variables are (1) declared and (2) assigned to the value that their corresponding parameter has at function call time. Currently, only pointers are supported.
1 parent d79e5f0 commit 04c723d

File tree

22 files changed

+362
-1
lines changed

22 files changed

+362
-1
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
void foo(int *x) __CPROVER_assigns(*x) __CPROVER_ensures(*x == *x_ + 5)
2+
{
3+
*x = *x + 5;
4+
}
5+
6+
int main()
7+
{
8+
int n;
9+
foo(&n);
10+
11+
return 0;
12+
}
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 ghost variables are supported for variables that are
11+
being assigned by the function under test. By using the
12+
--enforce-all-contracts flag, the post-condition (which contains the ghost
13+
vriable) is asserted. In this case, this assertion should pass.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
void foo(int *x) __CPROVER_assigns(*x) __CPROVER_ensures(*x < *x_ + 5)
2+
{
3+
*x = *x + 5;
4+
}
5+
6+
int main()
7+
{
8+
int n;
9+
foo(&n);
10+
11+
return 0;
12+
}
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 ghost variables are supported for variables that are
11+
being assigned by the function under test. By using the
12+
--enforce-all-contracts flag, the post-condition (which contains the ghost
13+
vriable) is asserted. In this case, this assertion should fail.
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 == *y_ + 1 && *y == *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 ghost variables are supported in the case where multiple
11+
variables are being assgned by the function under test. By using the
12+
--enforce-all-contracts flag, the post-condition (which contains the ghost
13+
variable) 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 == *x_ + 2 || *y == *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 ghost variables are supported in the case where multiple
11+
variables are being assgned by the function under test. By using the
12+
--enforce-all-contracts flag, the post-condition (which contains the ghost
13+
variable) is asserted. 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) __CPROVER_requires(*x != 3)
2+
__CPROVER_ensures(*x == *x_ + 5 && *x != 8)
3+
{
4+
int *x_ = 3;
5+
*x = *x + 5;
6+
}
7+
8+
int main()
9+
{
10+
int n;
11+
foo(&n);
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=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test checks ensures that the declaration of a new variable (in the
11+
function-under-test) with the same name as the ghost variable does not
12+
interfere with the ghost variable handling. For this test, we use the
13+
--enforce-all-contracts flag. As a result, the post-condition is asserted,
14+
and is expected to pass.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <assert.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x) __CPROVER_ensures(*x == *x_ + 5)
4+
{
5+
assert(*x_ == *x);
6+
*x = *x + 5;
7+
}
8+
9+
int main()
10+
{
11+
int n;
12+
foo(&n);
13+
14+
return 0;
15+
}
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=1$
5+
^SIGNAL=0$
6+
^CONVERSION ERROR$
7+
--
8+
--
9+
Verification:
10+
This test checks that ghost variables are not supported when referred to from
11+
a function body. In such a case, the program should not parse and there
12+
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 == *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 ghost 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 ghost 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 > *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 ghost 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 ghost 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 != *x_)
4+
__CPROVER_ensures(*x == *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=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
--
9+
Verification:
10+
This test checks that ghost variables may be used as part of the
11+
pre-condition contract, while keeping their usual semantic meaning.
12+
For this test, we use the --replace-all-calls-with-contracts flag, which
13+
asserts the pre-condition. In this case, the assertion is expected to fail.

src/ansi-c/ansi_c_parser.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,24 @@ void ansi_c_parsert::add_declarator(
142142
current_scope().prefix+id2string(base_name);
143143
new_declarator.set_name(prefixed_name);
144144

145+
// For every parameter...
146+
if(is_parameter)
147+
{
148+
// 1. create a new identifier that corresponds to the ghost variable.
149+
ansi_c_identifiert ghost_identifier = scope.name_map[base_name];
150+
ghost_identifier.id_class = id_class;
151+
152+
irep_idt ghost_prefixed_name =
153+
current_scope().prefix + id2string(base_name) + "_";
154+
ghost_identifier.prefixed_name = ghost_prefixed_name;
155+
156+
// 2. Add the new ghost variable identifier to the name map
157+
// Note that the "ghost_base_name" irep corresponds to the
158+
// ghost variable syntax.
159+
irep_idt ghost_base_name = id2string(base_name) + "_";
160+
scope.name_map[ghost_base_name] = ghost_identifier;
161+
}
162+
145163
// add to scope
146164
ansi_c_identifiert &identifier=scope.name_map[base_name];
147165
identifier.id_class=id_class;

src/ansi-c/c_typecheck_base.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,23 @@ void c_typecheck_baset::typecheck_function_body(symbolt &symbol)
540540

541541
symbolt *new_p_symbol;
542542
move_symbol(p_symbol, new_p_symbol);
543+
544+
// Handling ghost variables...
545+
546+
// 1. Produce a symbol for the ghost variable corresponding to the
547+
// parameter.
548+
irep_idt identifier_ghost =
549+
id2string(symbol.name) + "::" + id2string(base_name) + "_";
550+
551+
parameter_symbolt p_symbol_ghost;
552+
p_symbol_ghost.type = p.type();
553+
p_symbol_ghost.name = identifier_ghost;
554+
p_symbol_ghost.base_name = base_name;
555+
p_symbol_ghost.location = p.source_location();
556+
557+
// 2. Add the ghost variable symbol to the symbol table.
558+
symbolt *new_p_symbol_ghost;
559+
move_symbol(p_symbol_ghost, new_p_symbol_ghost);
543560
}
544561

545562
// typecheck the body code

0 commit comments

Comments
 (0)