Skip to content

Commit 4f9b5cc

Browse files
committed
CONTRACTS: add inductive checks for assigns clauses
1 parent 70b7cf7 commit 4f9b5cc

File tree

6 files changed

+46
-28
lines changed

6 files changed

+46
-28
lines changed

regression/contracts/loop_assigns-02/test.desc

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ main.c
55
^SIGNAL=0$
66
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
77
^\[main.assigns.\d+\] .* Check that i is assignable: SUCCESS$
8-
^\[main.assigns.\d+\] .* Check that b->data\[(.*)i\] is assignable: FAILURE$
8+
^\[main.assigns.\d+\] .* Check that b->data\[(.*)i\] is inductively assignable: FAILURE$
99
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$
10-
^\[main.assertion.\d+\] .* assertion b->data\[5\] == 0: FAILURE$
10+
^\*\* 1 of 12 failed \(2 iterations\)
1111
^VERIFICATION FAILED$
1212
--
1313
--

src/goto-instrument/contracts/contracts.cpp

+19-8
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,9 @@ void code_contractst::check_apply_loop_contracts(
8989
//
9090
// ... preamble ...
9191
// ,- initialize loop_entry history vars;
92-
// | entered_loop = false
93-
// loop assigns check | initial_invariant_val = invariant_expr;
94-
// - unchecked, temps | in_base_case = true;
95-
// func assigns check | snapshot (write_set);
92+
// loop assigns check | entered_loop = false
93+
// - unchecked, temps | initial_invariant_val = invariant_expr;
94+
// func assigns check | in_base_case = true;
9695
// - disabled via pragma | goto HEAD;
9796
// | STEP:
9897
// --. | assert (initial_invariant_val);
@@ -104,10 +103,12 @@ void code_contractst::check_apply_loop_contracts(
104103
// loop assigns check ,- ... eval guard ...
105104
// + assertions added | if (!guard)
106105
// func assigns check | goto EXIT;
107-
// - disabled via pragma `- ... loop body ...
106+
// - disabled via pragma | snapshot (write_set);
107+
// `- ... loop body ...
108108
// ,- entered_loop = true
109109
// | if (in_base_case)
110110
// | goto STEP;
111+
// | assert CAR_begin in CAR_end
111112
// loop assigns check | assert (invariant_expr);
112113
// - unchecked, temps | new_variant_val = decreases_clause_expr;
113114
// func assigns check | assert (new_variant_val < old_variant_val);
@@ -256,9 +257,8 @@ void code_contractst::check_apply_loop_contracts(
256257

257258
// Insert instrumentation
258259
// This must be done before havocing the write set.
259-
// FIXME: this is not true for write set targets that
260-
// might depend on other write set targets.
261-
pre_loop_head_instrs.destructive_append(snapshot_instructions);
260+
goto_function.body.destructive_insert(
261+
std::next(loop_head), snapshot_instructions);
262262

263263
// Insert a jump to the loop head
264264
// (skipping over the step case initialization code below)
@@ -387,6 +387,17 @@ void code_contractst::check_apply_loop_contracts(
387387
pre_loop_end_instrs.add(goto_programt::make_goto(
388388
step_case_target, in_base_case, loop_head_location));
389389

390+
// Adding checks that demonstrating that the loop assigns clause is an
391+
// inductive invariant.
392+
// CAR_begin should be included in CAR_end
393+
goto_programt assigns_inductive_check_instrs;
394+
for(const auto &target : to_havoc)
395+
{
396+
instrument_spec_assigns.check_inclusion_induction(
397+
target, assigns_inductive_check_instrs);
398+
pre_loop_end_instrs.destructive_append(assigns_inductive_check_instrs);
399+
}
400+
390401
// The following code is only reachable in the step case,
391402
// i.e., when in_base_case == false,
392403
// because of the unconditional jump above.

src/goto-instrument/contracts/instrument_spec_assigns.cpp

+17-3
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,17 @@ void instrument_spec_assignst::check_inclusion_assignment(
149149
// create temporary car but do not track
150150
const auto car = create_car_expr(true_exprt{}, lhs);
151151
create_snapshot(car, dest);
152-
inclusion_check_assertion(car, false, true, dest);
152+
inclusion_check_assertion(car, false, true, false, dest);
153+
}
154+
155+
void instrument_spec_assignst::check_inclusion_induction(
156+
const exprt &lhs,
157+
goto_programt &dest) const
158+
{
159+
// create temporary car but do not track
160+
const auto car = create_car_expr(true_exprt{}, lhs);
161+
create_snapshot(car, dest);
162+
inclusion_check_assertion(car, false, true, true, dest);
153163
}
154164

155165
void instrument_spec_assignst::track_static_locals(goto_programt &dest)
@@ -313,7 +323,7 @@ void instrument_spec_assignst::
313323
create_snapshot(car, dest);
314324

315325
// check inclusion, allowing null and not allowing stack allocated locals
316-
inclusion_check_assertion(car, true, false, dest);
326+
inclusion_check_assertion(car, true, false, false, dest);
317327

318328
// invalidate aliases of the freed object
319329
invalidate_heap_and_spec_aliases(car, dest);
@@ -680,6 +690,7 @@ void instrument_spec_assignst::inclusion_check_assertion(
680690
const car_exprt &car,
681691
bool allow_null_lhs,
682692
bool include_stack_allocated,
693+
bool is_inductive_check,
683694
goto_programt &dest) const
684695
{
685696
source_locationt source_location(car.source_location());
@@ -705,7 +716,10 @@ void instrument_spec_assignst::inclusion_check_assertion(
705716
else
706717
comment += id2string(orig_comment);
707718

708-
comment += " is assignable";
719+
if(is_inductive_check)
720+
comment += " is inductively assignable";
721+
else
722+
comment += " is assignable";
709723
source_location.set_comment(comment);
710724

711725
dest.add(goto_programt::make_assertion(

src/goto-instrument/contracts/instrument_spec_assigns.h

+8
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,12 @@ class instrument_spec_assignst
442442
const exprt &expr,
443443
goto_programt &dest);
444444

445+
/// Generates inclusion check instructions for inductive checks of
446+
/// the write set
447+
/// \param lhs the assignment lhs or argument to havoc/havoc_object
448+
/// \param dest destination program to append instructions to
449+
void check_inclusion_induction(const exprt &lhs, goto_programt &dest) const;
450+
445451
/// Instruments a sequence of instructions with inclusion checks.
446452
/// If `pred` is not provided,
447453
/// then all instructions are instrumented.
@@ -547,11 +553,13 @@ class instrument_spec_assignst
547553
/// \param allow_null_lhs if true, allow the lhs to be NULL
548554
/// \param include_stack_allocated if true, include stack allocated targets
549555
/// in the inclusion check.
556+
/// \param is_inductive_check if true, assertions are for inductive check.
550557
/// \param dest destination program to append instructions to
551558
void inclusion_check_assertion(
552559
const car_exprt &lhs,
553560
bool allow_null_lhs,
554561
bool include_stack_allocated,
562+
bool is_inductive_check,
555563
goto_programt &dest) const;
556564

557565
/// \brief Adds an assignment in dest to invalidate the tracked car if

src/goto-instrument/havoc_utils.cpp

-9
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,6 @@ void havoc_utilst::append_havoc_code_for_expr(
3131
const exprt &expr,
3232
goto_programt &dest) const
3333
{
34-
if(expr.id() == ID_index || expr.id() == ID_dereference)
35-
{
36-
address_of_exprt address_of_expr(expr);
37-
if(!is_constant(address_of_expr))
38-
{
39-
append_object_havoc_code_for_expr(location, address_of_expr, dest);
40-
return;
41-
}
42-
}
4334
append_scalar_havoc_code_for_expr(location, expr, dest);
4435
}
4536

src/goto-instrument/havoc_utils.h

-6
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,6 @@ class havoc_utilst
6464

6565
/// \brief Append goto instructions to havoc a single expression `expr`
6666
///
67-
/// If `expr` is an array index or object dereference expression,
68-
/// with a non-constant offset, e.g. a[i] or *(b+i) with a non-constant `i`,
69-
/// then instructions are generated to havoc the entire underlying object.
70-
/// Otherwise, e.g. for a[0] or *(b+i) when `i` is a known constant,
71-
/// the instructions are generated to only havoc the scalar value of `expr`.
72-
///
7367
/// \param location The source location to annotate on the havoc instruction
7468
/// \param expr The expression to havoc
7569
/// \param dest The destination goto program to append the instructions to

0 commit comments

Comments
 (0)