Skip to content

Commit 4e6f2a7

Browse files
committed
Add support for nested quantifiers in function contracts
This adds support for nested quantifiers in funtion contracts. We recursively handle quantifiers that are nested within other quantifiers or within Boolean connectives. f
1 parent d98b56b commit 4e6f2a7

File tree

14 files changed

+259
-31
lines changed

14 files changed

+259
-31
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// clang-format off
2+
int f1(int *arr) __CPROVER_ensures(__CPROVER_forall {
3+
int i;
4+
__CPROVER_forall
5+
{
6+
int j;
7+
(0 <= i && i < 10 && i <= j && j < 10) ==> arr[i] <= arr[j]
8+
}
9+
})
10+
// clang-format on
11+
{
12+
for(int i = 0; i < 10; i++)
13+
{
14+
arr[i] = i;
15+
}
16+
17+
return 0;
18+
}
19+
20+
int main()
21+
{
22+
int arr[10];
23+
f1(arr);
24+
}
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=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test case checks the handling of a forall expression
11+
nested within another forall expression.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// clang-format off
2+
int f1(int *arr) __CPROVER_requires(__CPROVER_forall {
3+
int i;
4+
0 <= i && i < 9 ==> __CPROVER_forall
5+
{
6+
int j;
7+
(i <= j && j < 10) ==> arr[i] <= arr[j]
8+
}
9+
}) __CPROVER_ensures(__CPROVER_return_value == 0)
10+
// clang-format on
11+
{
12+
return 0;
13+
}
14+
15+
int main()
16+
{
17+
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
18+
f1(arr);
19+
}
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=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test case checks the handling of a forall expression
11+
nested within an implication.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
int f1(int *arr)
2+
__CPROVER_ensures(__CPROVER_return_value == 0 && __CPROVER_exists {
3+
int i;
4+
(0 <= i && i < 10) && arr[i] == i
5+
})
6+
{
7+
for(int i = 0; i < 10; i++)
8+
{
9+
arr[i] = i;
10+
}
11+
12+
return 0;
13+
}
14+
15+
int main()
16+
{
17+
int arr[10];
18+
f1(arr);
19+
}
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=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test case checks the handling of an exists expression
11+
nested within a conjunction.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// clang-format off
2+
int f1(int *arr) __CPROVER_requires(
3+
__CPROVER_forall {
4+
int i;
5+
(0 <= i && i < 10) ==> arr[i] == 0
6+
} ||
7+
arr[9] == -1 ||
8+
__CPROVER_exists {
9+
int i;
10+
(0 <= i && i < 10) && arr[i] == i
11+
}) __CPROVER_ensures(__CPROVER_return_value == 0)
12+
// clang-format on
13+
{
14+
return 0;
15+
}
16+
17+
int main()
18+
{
19+
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
20+
f1(arr);
21+
}
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=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test case checks the handling of both a forall expression
11+
and an exists expression nested within a disjunction.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// clang-format off
2+
int f1(int *arr) __CPROVER_requires(!__CPROVER_forall {
3+
int i;
4+
(0 <= i && i < 10) ==> arr[i] == 0
5+
}) __CPROVER_ensures(__CPROVER_return_value == 0)
6+
// clang-format on
7+
{
8+
return 0;
9+
}
10+
11+
int main()
12+
{
13+
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
14+
f1(arr);
15+
}
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=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test case checks the handling of a forall expression
11+
nested within a negation.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// clang-format off
2+
int f1(int *arr) __CPROVER_requires(
3+
(__CPROVER_forall {
4+
int i;
5+
(0 <= i && i < 10) ==> arr[i] == 0
6+
} ? __CPROVER_exists {
7+
int i;
8+
(0 <= i && i < 10) ==> arr[i] == 0
9+
} : 1 == 0) &&
10+
(__CPROVER_forall {
11+
int i;
12+
(0 <= i && i < 10) ==> arr[i] == i
13+
} ? 1 == 0 :
14+
__CPROVER_forall {
15+
int i;
16+
(0 <= i && i < 10) ==> arr[i] == 0
17+
})) __CPROVER_ensures(__CPROVER_return_value == 0)
18+
// clang-format on
19+
{
20+
return 0;
21+
}
22+
23+
int main()
24+
{
25+
int arr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
26+
f1(arr);
27+
}
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=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Verification:
10+
This test case checks the handling of forall and exists expressions
11+
nested within ternary ITE expressions (condition ? true : false).

src/goto-instrument/code_contracts.cpp

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ Date: February 2016
2626
#include <util/expr_util.h>
2727
#include <util/format_type.h>
2828
#include <util/fresh_symbol.h>
29+
#include <util/mathematical_expr.h>
2930
#include <util/mathematical_types.h>
3031
#include <util/message.h>
3132
#include <util/pointer_offset_size.h>
3233
#include <util/pointer_predicates.h>
3334
#include <util/replace_symbol.h>
35+
#include <util/std_expr.h>
3436

3537
#include "loop_utils.h"
3638

@@ -168,35 +170,68 @@ void code_contractst::add_quantified_variable(
168170
replace_symbolt &replace,
169171
irep_idt mode)
170172
{
171-
// If the expression is a quantified expression, this function adds
172-
// the quantified variable to the symbol table and to the expression map
173-
174-
// TODO Currently only works if the contract contains only a single
175-
// quantified formula
176-
// i.e. (1) the top-level element is a quantifier formula
177-
// and (2) there are no inner quantifier formulae
178-
// This TODO is handled in PR #5968
179-
180-
if(expression.id() == ID_exists || expression.id() == ID_forall)
181-
{
182-
// get quantified symbol
183-
exprt tuple = expression.operands().front();
184-
exprt quantified_variable = tuple.operands().front();
185-
symbol_exprt quantified_symbol = to_symbol_expr(quantified_variable);
186-
187-
// create fresh symbol
188-
symbolt new_symbol = get_fresh_aux_symbol(
189-
quantified_symbol.type(),
190-
id2string(quantified_symbol.get_identifier()),
191-
"tmp",
192-
quantified_symbol.source_location(),
193-
mode,
194-
symbol_table);
195-
196-
// add created fresh symbol to expression map
197-
symbol_exprt q(
198-
quantified_symbol.get_identifier(), quantified_symbol.type());
199-
replace.insert(q, new_symbol.symbol_expr());
173+
if(expression.id() == ID_not || expression.id() == ID_typecast)
174+
{
175+
// For unary connectives, recursively check for
176+
// nested quantified formulae in the term
177+
const auto &unary_expression = to_unary_expr(expression);
178+
add_quantified_variable(unary_expression.op(), replace, mode);
179+
}
180+
if(expression.id() == ID_notequal || expression.id() == ID_implies)
181+
{
182+
// For binary connectives, recursively check for
183+
// nested quantified formulae in the left and right terms
184+
const auto &binary_expression = to_binary_expr(expression);
185+
add_quantified_variable(binary_expression.lhs(), replace, mode);
186+
add_quantified_variable(binary_expression.rhs(), replace, mode);
187+
}
188+
if(expression.id() == ID_if)
189+
{
190+
// For ternary connectives, recursively check for
191+
// nested quantified formulae in all three terms
192+
const auto &if_expression = to_if_expr(expression);
193+
add_quantified_variable(if_expression.cond(), replace, mode);
194+
add_quantified_variable(if_expression.true_case(), replace, mode);
195+
add_quantified_variable(if_expression.false_case(), replace, mode);
196+
}
197+
if(expression.id() == ID_and || expression.id() == ID_or)
198+
{
199+
// For multi-ary connectives, recursively check for
200+
// nested quantified formulae in all terms
201+
const auto &multi_ary_expression = to_multi_ary_expr(expression);
202+
for(const auto &operand : multi_ary_expression.operands())
203+
{
204+
add_quantified_variable(operand, replace, mode);
205+
}
206+
}
207+
else if(expression.id() == ID_exists || expression.id() == ID_forall)
208+
{
209+
// When a quantifier expression is found,
210+
// 1. get quantified variables
211+
const auto &quantifier_expression = to_quantifier_expr(expression);
212+
const auto &quantified_variables = quantifier_expression.variables();
213+
for(const auto &quantified_variable : quantified_variables)
214+
{
215+
// for each quantified variable...
216+
const auto &quantified_symbol = to_symbol_expr(quantified_variable);
217+
218+
// 1.1 create fresh symbol
219+
symbolt new_symbol = get_fresh_aux_symbol(
220+
quantified_symbol.type(),
221+
id2string(quantified_symbol.get_identifier()),
222+
"tmp",
223+
quantified_symbol.source_location(),
224+
mode,
225+
symbol_table);
226+
227+
// 1.2 add created fresh symbol to expression map
228+
symbol_exprt q(
229+
quantified_symbol.get_identifier(), quantified_symbol.type());
230+
replace.insert(q, new_symbol.symbol_expr());
231+
232+
// 1.3 recursively check for nested quantified formulae
233+
add_quantified_variable(quantifier_expression.where(), replace, mode);
234+
}
200235
}
201236
}
202237

src/goto-instrument/code_contracts.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ class code_contractst
167167
void
168168
add_contract_check(const irep_idt &, const irep_idt &, goto_programt &dest);
169169

170-
/// If the expression is a quantified expression, this function adds
171-
/// the quantified variable to the symbol table and to the expression map
170+
/// This function recursively searches the expression to find nested or
171+
/// non-nested quantified expressions. When a quantified expression is found,
172+
/// the quantified variable is added to the symbol table
173+
/// and to the expression map.
172174
void add_quantified_variable(
173175
exprt expression,
174176
replace_symbolt &replace,

0 commit comments

Comments
 (0)