Skip to content

Commit 521d2e3

Browse files
committed
Check side effect of loop contracts during instrumentation
1 parent a59768d commit 521d2e3

File tree

14 files changed

+168
-60
lines changed

14 files changed

+168
-60
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <stdlib.h>
2+
3+
int main()
4+
{
5+
unsigned i;
6+
7+
while(i > 1)
8+
__CPROVER_loop_invariant(({
9+
unsigned b = i >= 1;
10+
goto label;
11+
b = i < 1;
12+
label:
13+
b;
14+
}))
15+
{
16+
i--;
17+
}
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--dfcc main --apply-loop-contracts --disable-side-effect-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
This test demonstrates a case where the loop invariant
10+
is a side-effect free statement expression.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <stdlib.h>
2+
3+
int main()
4+
{
5+
unsigned i;
6+
7+
while(i > 1)
8+
__CPROVER_loop_invariant(({
9+
unsigned b = i >= 1;
10+
goto label;
11+
b = i < 1;
12+
label:
13+
b;
14+
}))
15+
{
16+
i--;
17+
}
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts --disable-side-effect-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
This test demonstrates a case where the loop invariant
10+
is a side-effect free statement expression.

src/ansi-c/goto-conversion/goto_convert.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,12 +1036,6 @@ void goto_convertt::convert_loop_contracts(
10361036
static_cast<const exprt &>(code.find(ID_C_spec_loop_invariant));
10371037
if(!invariant.is_nil())
10381038
{
1039-
if(has_subexpr(invariant, ID_side_effect))
1040-
{
1041-
throw incorrect_goto_program_exceptiont(
1042-
"Loop invariant is not side-effect free.", code.find_source_location());
1043-
}
1044-
10451039
PRECONDITION(loop->is_goto() || loop->is_incomplete_goto());
10461040
loop->condition_nonconst().add(ID_C_spec_loop_invariant).swap(invariant);
10471041
}
@@ -1050,13 +1044,6 @@ void goto_convertt::convert_loop_contracts(
10501044
static_cast<const exprt &>(code.find(ID_C_spec_decreases));
10511045
if(!decreases_clause.is_nil())
10521046
{
1053-
if(has_subexpr(decreases_clause, ID_side_effect))
1054-
{
1055-
throw incorrect_goto_program_exceptiont(
1056-
"Decreases clause is not side-effect free.",
1057-
code.find_source_location());
1058-
}
1059-
10601047
PRECONDITION(loop->is_goto() || loop->is_incomplete_goto());
10611048
loop->condition_nonconst().add(ID_C_spec_decreases).swap(decreases_clause);
10621049
}

src/cprover/instrument_given_invariants.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Author: Daniel Kroening, [email protected]
1111

1212
#include "instrument_given_invariants.h"
1313

14+
#include <util/expr_util.h>
15+
1416
#include <goto-programs/goto_model.h>
1517

1618
void instrument_given_invariants(goto_functionst::function_mapt::value_type &f)
@@ -25,6 +27,15 @@ void instrument_given_invariants(goto_functionst::function_mapt::value_type &f)
2527
{
2628
const auto &invariants = static_cast<const exprt &>(
2729
it->condition().find(ID_C_spec_loop_invariant));
30+
if(!invariants.is_nil())
31+
{
32+
if(has_subexpr(invariants, ID_side_effect))
33+
{
34+
throw incorrect_goto_program_exceptiont(
35+
"Loop invariant is not side-effect free.",
36+
it->condition().find_source_location());
37+
}
38+
}
2839

2940
for(const auto &invariant : invariants.operands())
3041
{

src/goto-instrument/contracts/contracts.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -983,12 +983,11 @@ void code_contractst::apply_loop_contract(
983983
goto_function.body, loop_head, goto_programt::make_skip());
984984
loop_end->set_target(loop_head);
985985

986-
exprt assigns_clause =
987-
static_cast<const exprt &>(loop_end->condition().find(ID_C_spec_assigns));
988-
exprt invariant = static_cast<const exprt &>(
989-
loop_end->condition().find(ID_C_spec_loop_invariant));
990-
exprt decreases_clause = static_cast<const exprt &>(
991-
loop_end->condition().find(ID_C_spec_decreases));
986+
exprt assigns_clause = get_loop_assigns(loop_end);
987+
exprt invariant =
988+
get_loop_invariants(loop_end, loop_contract_config.check_side_effect);
989+
exprt decreases_clause =
990+
get_loop_decreases(loop_end, loop_contract_config.check_side_effect);
992991

993992
if(invariant.is_nil())
994993
{

src/goto-instrument/contracts/contracts.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Date: February 2016
3333
#define HELP_LOOP_CONTRACTS \
3434
" {y--apply-loop-contracts} \t check and use loop contracts when provided\n"
3535

36+
#define FLAG_DISABLE_SIDE_EFFECT_CHECK "disable-side-effect-check"
37+
#define HELP_DISABLE_SIDE_EFFECT_CHECK \
38+
" {y--disable-side-effect-check} \t UNSOUND OPTION. do not check \t " \
39+
" side-effect of loop contracts\n"
3640
#define FLAG_LOOP_CONTRACTS_NO_UNWIND "loop-contracts-no-unwind"
3741
#define HELP_LOOP_CONTRACTS_NO_UNWIND \
3842
" {y--loop-contracts-no-unwind} \t do not unwind transformed loops\n"

src/goto-instrument/contracts/dynamic-frames/dfcc_cfg_info.cpp

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,15 @@ Date: March 2023
2929
#include "dfcc_root_object.h"
3030
#include "dfcc_utils.h"
3131

32-
/// Extracts the assigns clause expression from the latch condition
33-
static const exprt::operandst &
34-
get_assigns(const goto_programt::const_targett &latch_target)
35-
{
36-
return static_cast<const exprt &>(
37-
latch_target->condition().find(ID_C_spec_assigns))
38-
.operands();
39-
}
40-
41-
/// Extracts the invariant clause expression from the latch condition
42-
static const exprt::operandst &
43-
get_invariants(const goto_programt::const_targett &latch_target)
44-
{
45-
return static_cast<const exprt &>(
46-
latch_target->condition().find(ID_C_spec_loop_invariant))
47-
.operands();
48-
}
49-
50-
/// Extracts the decreases clause expression from the latch condition
51-
static const exprt::operandst &
52-
get_decreases(const goto_programt::const_targett &latch_target)
53-
{
54-
return static_cast<const exprt &>(
55-
latch_target->condition().find(ID_C_spec_decreases))
56-
.operands();
57-
}
58-
5932
/// Returns true iff some contract clause expression is attached
6033
/// to the latch condition of this loop
61-
static bool has_contract(const goto_programt::const_targett &latch_target)
34+
static bool has_contract(
35+
const goto_programt::const_targett &latch_target,
36+
const bool check_side_effect)
6237
{
63-
return !get_assigns(latch_target).empty() ||
64-
!get_invariants(latch_target).empty() ||
65-
!get_decreases(latch_target).empty();
38+
return get_loop_assigns(latch_target).is_not_nil() ||
39+
get_loop_invariants(latch_target, check_side_effect).is_not_nil() ||
40+
get_loop_decreases(latch_target, check_side_effect).is_not_nil();
6641
}
6742

6843
void dfcc_loop_infot::output(std::ostream &out) const
@@ -155,16 +130,20 @@ dfcc_loop_infot::find_latch(goto_programt &goto_program) const
155130
static std::optional<goto_programt::targett> check_has_contract_rec(
156131
const dfcc_loop_nesting_grapht &loop_nesting_graph,
157132
const std::size_t loop_idx,
158-
const bool must_have_contract)
133+
const bool must_have_contract,
134+
const bool check_side_effect)
159135
{
160136
const auto &node = loop_nesting_graph[loop_idx];
161-
if(must_have_contract && !has_contract(node.latch))
137+
if(must_have_contract && !has_contract(node.latch, check_side_effect))
162138
return node.head;
163139

164140
for(const auto pred_idx : loop_nesting_graph.get_predecessors(loop_idx))
165141
{
166142
auto result = check_has_contract_rec(
167-
loop_nesting_graph, pred_idx, has_contract(node.latch));
143+
loop_nesting_graph,
144+
pred_idx,
145+
has_contract(node.latch, check_side_effect),
146+
check_side_effect);
168147
if(result.has_value())
169148
return result;
170149
}
@@ -175,13 +154,15 @@ static std::optional<goto_programt::targett> check_has_contract_rec(
175154
/// loops nested in loops that have contracts also have contracts.
176155
/// Return the head of the first offending loop if it exists, nothing otherwise.
177156
static std::optional<goto_programt::targett> check_inner_loops_have_contracts(
178-
const dfcc_loop_nesting_grapht &loop_nesting_graph)
157+
const dfcc_loop_nesting_grapht &loop_nesting_graph,
158+
const bool check_side_effect)
179159
{
180160
for(std::size_t idx = 0; idx < loop_nesting_graph.size(); idx++)
181161
{
182162
if(loop_nesting_graph.get_successors(idx).empty())
183163
{
184-
auto result = check_has_contract_rec(loop_nesting_graph, idx, false);
164+
auto result = check_has_contract_rec(
165+
loop_nesting_graph, idx, false, check_side_effect);
185166
if(result.has_value())
186167
return result;
187168
}
@@ -349,18 +330,21 @@ static struct contract_clausest default_loop_contract_clauses(
349330
const std::size_t loop_id,
350331
const irep_idt &function_id,
351332
local_may_aliast &local_may_alias,
333+
const bool check_side_effect,
352334
message_handlert &message_handler,
353335
const namespacet &ns)
354336
{
355337
messaget log(message_handler);
356338
const auto &loop = loop_nesting_graph[loop_id];
357339

358340
// Process loop contract clauses
359-
exprt::operandst invariant_clauses = get_invariants(loop.latch);
360-
exprt::operandst assigns_clauses = get_assigns(loop.latch);
341+
exprt::operandst invariant_clauses =
342+
get_loop_invariants(loop.latch, check_side_effect).operands();
343+
exprt::operandst assigns_clauses = get_loop_assigns(loop.latch).operands();
361344

362345
// Initialise defaults
363-
struct contract_clausest result(get_decreases(loop.latch));
346+
struct contract_clausest result(
347+
get_loop_decreases(loop.latch, check_side_effect).operands());
364348

365349
// Generate defaults for all clauses if at least one type of clause is defined
366350
if(
@@ -435,6 +419,7 @@ static dfcc_loop_infot gen_dfcc_loop_info(
435419
const std::map<std::size_t, dfcc_loop_infot> &loop_info_map,
436420
dirtyt &dirty,
437421
local_may_aliast &local_may_alias,
422+
const bool check_side_effect,
438423
message_handlert &message_handler,
439424
dfcc_libraryt &library,
440425
symbol_table_baset &symbol_table)
@@ -454,6 +439,7 @@ static dfcc_loop_infot gen_dfcc_loop_info(
454439
loop_id,
455440
function_id,
456441
local_may_alias,
442+
check_side_effect,
457443
message_handler,
458444
ns);
459445

@@ -523,7 +509,8 @@ dfcc_cfg_infot::dfcc_cfg_infot(
523509
dfcc_check_loop_normal_form(goto_program, log);
524510
loop_nesting_graph = build_loop_nesting_graph(goto_program);
525511

526-
const auto head = check_inner_loops_have_contracts(loop_nesting_graph);
512+
const auto head = check_inner_loops_have_contracts(
513+
loop_nesting_graph, loop_contract_config.check_side_effect);
527514
if(head.has_value())
528515
{
529516
throw invalid_source_file_exceptiont(
@@ -571,6 +558,7 @@ dfcc_cfg_infot::dfcc_cfg_infot(
571558
loop_info_map,
572559
dirty,
573560
local_may_alias,
561+
loop_contract_config.check_side_effect,
574562
message_handler,
575563
library,
576564
symbol_table)});

src/goto-instrument/contracts/utils.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,48 @@ get_loop_head(const unsigned int target_loop_number, goto_functiont &function)
682682
return get_loop_head_or_end(target_loop_number, function, true);
683683
}
684684

685+
exprt get_loop_invariants(
686+
const goto_programt::const_targett &loop_end,
687+
const bool check_side_effect)
688+
{
689+
auto invariant = static_cast<const exprt &>(
690+
loop_end->condition().find(ID_C_spec_loop_invariant));
691+
if(!invariant.is_nil() && check_side_effect)
692+
{
693+
if(has_subexpr(invariant, ID_side_effect))
694+
{
695+
throw incorrect_goto_program_exceptiont(
696+
"Loop invariant is not side-effect free.",
697+
loop_end->condition().find_source_location());
698+
}
699+
}
700+
return invariant;
701+
}
702+
703+
exprt get_loop_assigns(const goto_programt::const_targett &loop_end)
704+
{
705+
return static_cast<const exprt &>(
706+
loop_end->condition().find(ID_C_spec_assigns));
707+
}
708+
709+
exprt get_loop_decreases(
710+
const goto_programt::const_targett &loop_end,
711+
const bool check_side_effect)
712+
{
713+
auto decreases_clause =
714+
static_cast<const exprt &>(loop_end->condition().find(ID_C_spec_decreases));
715+
if(!decreases_clause.is_nil() && check_side_effect)
716+
{
717+
if(has_subexpr(decreases_clause, ID_side_effect))
718+
{
719+
throw incorrect_goto_program_exceptiont(
720+
"Decreases clause is not side-effect free.",
721+
loop_end->condition().find_source_location());
722+
}
723+
}
724+
return decreases_clause;
725+
}
726+
685727
void annotate_invariants(
686728
const invariant_mapt &invariant_map,
687729
goto_modelt &goto_model)

src/goto-instrument/contracts/utils.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,23 @@ get_loop_end(const unsigned int loop_number, goto_functiont &function);
313313
goto_programt::targett
314314
get_loop_head(const unsigned int loop_number, goto_functiont &function);
315315

316+
/// Extract loop invariants from annotated loop end.
317+
/// Will check if the loop invariant is side-effect free if
318+
/// \p check_side_effect` is set.
319+
exprt get_loop_invariants(
320+
const goto_programt::const_targett &loop_end,
321+
const bool check_side_effect = true);
322+
323+
/// Extract loop assigns from annotated loop end.
324+
exprt get_loop_assigns(const goto_programt::const_targett &loop_end);
325+
326+
/// Extract loop decreases from annotated loop end.
327+
/// Will check if the loop decreases is side-effect free if
328+
/// \p check_side_effect` is set.
329+
exprt get_loop_decreases(
330+
const goto_programt::const_targett &loop_end,
331+
const bool check_side_effect = true);
332+
316333
/// Annotate the invariants in `invariant_map` to their corresponding
317334
/// loops. Corresponding loops are specified by keys of `invariant_map`
318335
void annotate_invariants(

src/goto-instrument/goto_instrument_parse_options.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,8 @@ void goto_instrument_parse_optionst::instrument_goto_program()
11691169
// Initialize loop contract config from cmdline.
11701170
loop_contract_configt loop_contract_config = {
11711171
cmdline.isset(FLAG_LOOP_CONTRACTS),
1172-
!cmdline.isset(FLAG_LOOP_CONTRACTS_NO_UNWIND)};
1172+
!cmdline.isset(FLAG_LOOP_CONTRACTS_NO_UNWIND),
1173+
!cmdline.isset(FLAG_DISABLE_SIDE_EFFECT_CHECK)};
11731174

11741175
if(
11751176
cmdline.isset(FLAG_LOOP_CONTRACTS) &&
@@ -2029,6 +2030,7 @@ void goto_instrument_parse_optionst::help()
20292030
"Code contracts:\n"
20302031
HELP_DFCC
20312032
HELP_LOOP_CONTRACTS
2033+
HELP_DISABLE_SIDE_EFFECT_CHECK
20322034
HELP_LOOP_CONTRACTS_NO_UNWIND
20332035
HELP_LOOP_CONTRACTS_FILE
20342036
HELP_REPLACE_CALL

src/goto-instrument/goto_instrument_parse_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ Author: Daniel Kroening, [email protected]
102102
"(horn)(skip-loops):(model-argc-argv):" \
103103
OPT_DFCC \
104104
"(" FLAG_LOOP_CONTRACTS ")" \
105+
"(" FLAG_DISABLE_SIDE_EFFECT_CHECK ")" \
105106
"(" FLAG_LOOP_CONTRACTS_NO_UNWIND ")" \
106107
"(" FLAG_LOOP_CONTRACTS_FILE "):" \
107108
"(" FLAG_REPLACE_CALL "):" \

src/linking/remove_internal_symbols.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Author: Daniel Kroening
2323

2424
#include "static_lifetime_init.h"
2525

26+
// Loop contracts subs in which we don't want to remove the symbols
2627
static std::unordered_set<irep_idt> loop_contracts_subs{
2728
ID_C_spec_loop_invariant,
2829
ID_C_spec_decreases};

0 commit comments

Comments
 (0)