@@ -189,6 +189,10 @@ void code_contractst::check_apply_loop_contracts(
189
189
insert_before_swap_and_advance (
190
190
goto_function.body , loop_head, snapshot_instructions);
191
191
};
192
+
193
+ // Perform write set instrumentation on the entire loop.
194
+ check_frame_conditions (
195
+ function_name, goto_function.body , loop_head, loop_end, loop_assigns);
192
196
}
193
197
194
198
havoc_assigns_targetst havoc_gen (modifies, ns);
@@ -251,29 +255,28 @@ void code_contractst::check_apply_loop_contracts(
251
255
252
256
// Assume invariant & decl the variant temporaries (just before loop guard).
253
257
// Use insert_before_swap to preserve jumps to loop head.
254
- insert_before_swap_and_advance (goto_function.body , loop_head, generated_code);
258
+ insert_before_swap_and_advance (
259
+ goto_function.body ,
260
+ loop_head,
261
+ add_pragma_disable_assigns_check (generated_code));
255
262
256
263
// Forward the loop_head iterator until the start of the body.
257
264
// This is necessary because complex C loop_head conditions could be
258
265
// converted to multiple GOTO instructions (e.g. temporaries are introduced).
266
+ // If the loop_head location shifts to a different function,
267
+ // assume that it's an inlined function and keep skipping.
259
268
// FIXME: This simple approach wouldn't work when
260
269
// the loop guard in the source file is split across multiple lines.
261
270
const auto head_loc = loop_head->source_location ();
262
- while (loop_head->source_location () == head_loc)
271
+ while (loop_head->source_location () == head_loc ||
272
+ loop_head->source_location ().get_function () != head_loc.get_function ())
263
273
loop_head++;
264
274
265
275
// At this point, we are just past the loop head,
266
276
// so at the beginning of the loop body.
267
277
auto loop_body = loop_head;
268
278
loop_head--;
269
279
270
- // Perform write set instrumentation before adding anything else to loop body.
271
- if (assigns.is_not_nil ())
272
- {
273
- check_frame_conditions (
274
- function_name, goto_function.body , loop_body, loop_end, loop_assigns);
275
- }
276
-
277
280
// Generate: assignments to store the multidimensional decreases clause's
278
281
// value just before the loop body (but just after the loop guard)
279
282
if (!decreases_clause.is_nil ())
@@ -287,7 +290,8 @@ void code_contractst::check_apply_loop_contracts(
287
290
converter.goto_convert (old_decreases_assignment, generated_code, mode);
288
291
}
289
292
290
- goto_function.body .destructive_insert (loop_body, generated_code);
293
+ goto_function.body .destructive_insert (
294
+ loop_body, add_pragma_disable_assigns_check (generated_code));
291
295
}
292
296
293
297
// Generate: assert(invariant) just after the loop exits
@@ -337,7 +341,10 @@ void code_contractst::check_apply_loop_contracts(
337
341
}
338
342
}
339
343
340
- insert_before_swap_and_advance (goto_function.body , loop_end, generated_code);
344
+ insert_before_swap_and_advance (
345
+ goto_function.body ,
346
+ loop_end,
347
+ add_pragma_disable_assigns_check (generated_code));
341
348
342
349
// change the back edge into assume(false) or assume(guard)
343
350
loop_end->turn_into_assume ();
@@ -678,6 +685,12 @@ void code_contractst::apply_loop_contract(
678
685
local_may_aliast local_may_alias (goto_function);
679
686
natural_loops_mutablet natural_loops (goto_function.body );
680
687
688
+ if (!natural_loops.loop_map .size ())
689
+ return ;
690
+
691
+ goto_function_inline (
692
+ goto_functions, function_name, ns, log .get_message_handler ());
693
+
681
694
// A graph node type that stores information about a loop.
682
695
// We create a DAG representing nesting of various loops in goto_function,
683
696
// sort them topologically, and instrument them in the top-sorted order.
@@ -992,7 +1005,11 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
992
1005
function_obj->second .body , instruction_it, snapshot_instructions);
993
1006
};
994
1007
995
- // Insert aliasing assertions
1008
+ // Inline all function calls.
1009
+ goto_function_inline (
1010
+ goto_functions, function_obj->first , ns, log .get_message_handler ());
1011
+
1012
+ // Insert write set inclusion checks.
996
1013
check_frame_conditions (
997
1014
function_obj->first ,
998
1015
function_obj->second .body ,
@@ -1010,8 +1027,6 @@ void code_contractst::check_frame_conditions(
1010
1027
const goto_programt::targett &instruction_end,
1011
1028
assigns_clauset &assigns)
1012
1029
{
1013
- goto_function_inline (goto_functions, function, ns, log .get_message_handler ());
1014
-
1015
1030
for (; instruction_it != instruction_end; ++instruction_it)
1016
1031
{
1017
1032
const auto &pragmas = instruction_it->source_location ().get_pragmas ();
@@ -1054,20 +1069,28 @@ void code_contractst::check_frame_conditions(
1054
1069
assigns_clauset::conditional_address_ranget{assigns, symbol});
1055
1070
if (symbol_car != assigns.get_write_set ().end ())
1056
1071
{
1057
- instruction_it++;
1058
1072
auto invalidation_assignment = goto_programt::make_assignment (
1059
1073
symbol_car->validity_condition_var ,
1060
1074
false_exprt{},
1061
1075
instruction_it->source_location ());
1062
- // note that instruction_it is not advanced by this call,
1063
- // so no need to move it backwards
1064
- body.insert_before_swap (instruction_it, invalidation_assignment);
1076
+ // note that the CAR must be invalidated _after_ the DEAD instruction
1077
+ body.insert_after (
1078
+ instruction_it,
1079
+ add_pragma_disable_assigns_check (invalidation_assignment));
1065
1080
}
1066
1081
else
1067
1082
{
1068
- throw incorrect_goto_program_exceptiont (
1069
- " Found a `DEAD` variable without corresponding `DECL`!" ,
1070
- instruction_it->source_location ());
1083
+ // For loops, the loop_head appears after the DECL of counters,
1084
+ // and any other temporaries introduced during "initialization".
1085
+ // However, they go `DEAD` (possible conditionally) inside the loop,
1086
+ // in presence of return statements.
1087
+ // Notice that for them those variables be writable,
1088
+ // they must appear as assigns targets anyway,
1089
+ // but their DECL statements are outside of the loop.
1090
+ log .warning () << " Found a `DEAD` variable "
1091
+ << name2string (symbol.get_identifier ())
1092
+ << " without corresponding `DECL`, at: "
1093
+ << instruction_it->source_location () << messaget::eom;
1071
1094
}
1072
1095
}
1073
1096
else if (
0 commit comments