cprover
Loading...
Searching...
No Matches
contracts.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Verify and use annotated loop and function contracts
4
5Author: Michael Tautschnig
6
7Date: February 2016
8
9\*******************************************************************/
10
13
14#include "contracts.h"
15
16#include <util/c_types.h>
18#include <util/format_expr.h>
19#include <util/fresh_symbol.h>
21
25
29
30#include "cfg_info.h"
32#include "inlining_decorator.h"
34#include "memory_predicates.h"
35#include "utils.h"
36
37#include <algorithm>
38#include <map>
39
41 const irep_idt &function_name,
43 const local_may_aliast &local_may_alias,
44 goto_programt::targett loop_head,
46 const loopt &loop,
47 exprt assigns_clause,
48 exprt invariant,
49 exprt decreases_clause,
50 const irep_idt &mode)
51{
52 const auto loop_head_location = loop_head->source_location();
53 const auto loop_number = loop_end->loop_number;
54
55 // Vector representing a (possibly multidimensional) decreases clause
56 const auto &decreases_clause_exprs = decreases_clause.operands();
57
58 // Temporary variables for storing the multidimensional decreases clause
59 // at the start of and end of a loop body
60 std::vector<symbol_exprt> old_decreases_vars, new_decreases_vars;
61
62 // instrument
63 //
64 // ... preamble ...
65 // HEAD:
66 // ... eval guard ...
67 // if (!guard)
68 // goto EXIT;
69 // ... loop body ...
70 // goto HEAD;
71 // EXIT:
72 // ... postamble ...
73 //
74 // to
75 //
76 // ... preamble ...
77 // ,- initialize loop_entry history vars;
78 // | entered_loop = false
79 // loop assigns check | initial_invariant_val = invariant_expr;
80 // - unchecked, temps | in_base_case = true;
81 // func assigns check | snapshot (write_set);
82 // - disabled via pragma | goto HEAD;
83 // | STEP:
84 // --. | assert (initial_invariant_val);
85 // loop assigns check | | in_base_case = false;
86 // - not applicable >======= in_loop_havoc_block = true;
87 // func assigns check | | havoc (assigns_set);
88 // + deferred | | in_loop_havoc_block = false;
89 // --' | assume (invariant_expr);
90 // `- old_variant_val = decreases_clause_expr;
91 // * HEAD:
92 // loop assigns check ,- ... eval guard ...
93 // + assertions added | if (!guard)
94 // func assigns check | goto EXIT;
95 // - disabled via pragma `- ... loop body ...
96 // ,- entered_loop = true
97 // | if (in_base_case)
98 // | goto STEP;
99 // loop assigns check | assert (invariant_expr);
100 // - unchecked, temps | new_variant_val = decreases_clause_expr;
101 // func assigns check | assert (new_variant_val < old_variant_val);
102 // - disabled via pragma | dead old_variant_val, new_variant_val;
103 // | * assume (false);
104 // | * EXIT:
105 // To be inserted at ,~~~|~~~~ assert (entered_loop ==> !in_base_case);
106 // every unique EXIT | | dead loop_entry history vars, in_base_case;
107 // (break, goto etc.) `~~~`- ~~ dead initial_invariant_val, entered_loop;
108 // ... postamble ..
109 //
110 // Asterisks (*) denote anchor points (goto targets) for instrumentations.
111 // We insert generated code above and/below these targets.
112 //
113 // Assertions for assigns clause checking are inserted in the loop body.
114
115 // Process "loop_entry" history variables.
116 // We find and replace all "__CPROVER_loop_entry" subexpressions in invariant.
117 auto replace_history_result = replace_history_loop_entry(
118 symbol_table, invariant, loop_head_location, mode);
119 invariant.swap(replace_history_result.expression_after_replacement);
120 const auto &history_var_map = replace_history_result.parameter_to_history;
121 // an intermediate goto_program to store generated instructions
122 // to be inserted before the loop head
123 goto_programt &pre_loop_head_instrs =
124 replace_history_result.history_construction;
125
126 // Create a temporary to track if we entered the loop,
127 // i.e., the loop guard was satisfied.
128 const auto entered_loop =
130 bool_typet(),
131 id2string(loop_head_location.get_function()),
132 std::string(ENTERED_LOOP) + "__" + std::to_string(loop_number),
133 loop_head_location,
134 mode,
136 .symbol_expr();
137 pre_loop_head_instrs.add(
138 goto_programt::make_decl(entered_loop, loop_head_location));
139 pre_loop_head_instrs.add(
141
142 // Create a snapshot of the invariant so that we can check the base case,
143 // if the loop is not vacuous and must be abstracted with contracts.
144 const auto initial_invariant_val =
146 bool_typet(),
147 id2string(loop_head_location.get_function()),
149 loop_head_location,
150 mode,
152 .symbol_expr();
153 pre_loop_head_instrs.add(
154 goto_programt::make_decl(initial_invariant_val, loop_head_location));
155 {
156 // Although the invariant at this point will not have side effects,
157 // it is still a C expression, and needs to be "goto_convert"ed.
158 // Note that this conversion may emit many GOTO instructions.
159 code_assignt initial_invariant_value_assignment{
160 initial_invariant_val, invariant};
161 initial_invariant_value_assignment.add_source_location() =
162 loop_head_location;
163
164 goto_convertt converter(symbol_table, log.get_message_handler());
165 converter.goto_convert(
166 initial_invariant_value_assignment, pre_loop_head_instrs, mode);
167 }
168
169 // Create a temporary variable to track base case vs inductive case
170 // instrumentation of the loop.
171 const auto in_base_case = get_fresh_aux_symbol(
172 bool_typet(),
173 id2string(loop_head_location.get_function()),
174 "__in_base_case",
175 loop_head_location,
176 mode,
178 .symbol_expr();
179 pre_loop_head_instrs.add(
180 goto_programt::make_decl(in_base_case, loop_head_location));
181 pre_loop_head_instrs.add(
183
184 // CAR snapshot instructions for checking assigns clause
185 goto_programt snapshot_instructions;
186
187 loop_cfg_infot cfg_info(goto_function, loop);
188
189 // track static local symbols
190 instrument_spec_assignst instrument_spec_assigns(
191 function_name,
193 cfg_info,
195 log.get_message_handler());
196
197 instrument_spec_assigns.track_static_locals_between(
198 loop_head, loop_end, snapshot_instructions);
199
200 // set of targets to havoc
201 assignst to_havoc;
202
203 if(assigns_clause.is_nil())
204 {
205 // No assigns clause was specified for this loop.
206 // Infer memory locations assigned by the loop from the loop instructions
207 // and the inferred aliasing relation.
208 try
209 {
210 infer_loop_assigns(local_may_alias, loop, to_havoc);
211
212 // remove loop-local symbols from the inferred set
213 cfg_info.erase_locals(to_havoc);
214
215 // If the set contains pairs (i, a[i]),
216 // we widen them to (i, __CPROVER_POINTER_OBJECT(a))
217 widen_assigns(to_havoc, ns);
218
219 log.debug() << "No loop assigns clause provided. Inferred targets: {";
220 // Add inferred targets to the loop assigns clause.
221 bool ran_once = false;
222 for(const auto &target : to_havoc)
223 {
224 if(ran_once)
225 log.debug() << ", ";
226 ran_once = true;
227 log.debug() << format(target);
228 instrument_spec_assigns.track_spec_target(
229 target, snapshot_instructions);
230 }
231 log.debug() << "}" << messaget::eom;
232
233 instrument_spec_assigns.get_static_locals(
234 std::inserter(to_havoc, to_havoc.end()));
235 }
236 catch(const analysis_exceptiont &exc)
237 {
238 log.error() << "Failed to infer variables being modified by the loop at "
239 << loop_head_location
240 << ".\nPlease specify an assigns clause.\nReason:"
241 << messaget::eom;
242 throw exc;
243 }
244 }
245 else
246 {
247 // An assigns clause was specified for this loop.
248 // Add the targets to the set of expressions to havoc.
249 for(const auto &target : assigns_clause.operands())
250 {
251 to_havoc.insert(target);
252 instrument_spec_assigns.track_spec_target(target, snapshot_instructions);
253 }
254 }
255
256 // Insert instrumentation
257 // This must be done before havocing the write set.
258 // FIXME: this is not true for write set targets that
259 // might depend on other write set targets.
260 pre_loop_head_instrs.destructive_append(snapshot_instructions);
261
262 // Insert a jump to the loop head
263 // (skipping over the step case initialization code below)
264 pre_loop_head_instrs.add(
265 goto_programt::make_goto(loop_head, true_exprt{}, loop_head_location));
266
267 // The STEP case instructions follow.
268 // We skip past it initially, because of the unconditional jump above,
269 // but jump back here if we get past the loop guard while in_base_case.
270
271 const auto step_case_target =
272 pre_loop_head_instrs.add(goto_programt::make_assignment(
273 in_base_case, false_exprt{}, loop_head_location));
274
275 // If we jump here, then the loop runs at least once,
276 // so add the base case assertion:
277 // assert(initial_invariant_val)
278 // We use a block scope for assertion, since it's immediately goto converted,
279 // and doesn't need to be kept around.
280 {
281 code_assertt assertion{initial_invariant_val};
282 assertion.add_source_location() = loop_head_location;
284 "Check loop invariant before entry");
285
286 goto_convertt converter(symbol_table, log.get_message_handler());
287 converter.goto_convert(assertion, pre_loop_head_instrs, mode);
288 }
289
290 // Insert the first block of pre_loop_head_instrs,
291 // with a pragma to disable assigns clause checking.
292 // All of the initialization code so far introduces local temporaries,
293 // which need not be checked against the enclosing scope's assigns clause.
294 goto_function.body.destructive_insert(
295 loop_head, add_pragma_disable_assigns_check(pre_loop_head_instrs));
296
297 // Generate havocing code for assignment targets.
298 // ASSIGN in_loop_havoc_block = true;
299 // havoc (assigns_set);
300 // ASSIGN in_loop_havoc_block = false;
301 const auto in_loop_havoc_block =
303 bool_typet(),
304 id2string(loop_head_location.get_function()),
305 std::string(IN_LOOP_HAVOC_BLOCK) + +"__" + std::to_string(loop_number),
306 loop_head_location,
307 mode,
309 .symbol_expr();
310 pre_loop_head_instrs.add(
311 goto_programt::make_decl(in_loop_havoc_block, loop_head_location));
312 pre_loop_head_instrs.add(
313 goto_programt::make_assignment(in_loop_havoc_block, true_exprt{}));
314 havoc_assigns_targetst havoc_gen(
315 to_havoc, symbol_table, log.get_message_handler(), mode);
316 havoc_gen.append_full_havoc_code(loop_head_location, pre_loop_head_instrs);
317 pre_loop_head_instrs.add(
318 goto_programt::make_assignment(in_loop_havoc_block, false_exprt{}));
319
320 // Insert the second block of pre_loop_head_instrs: the havocing code.
321 // We do not `add_pragma_disable_assigns_check`,
322 // so that the enclosing scope's assigns clause instrumentation
323 // would pick these havocs up for inclusion (subset) checks.
324 goto_function.body.destructive_insert(loop_head, pre_loop_head_instrs);
325
326 // Generate: assume(invariant) just after havocing
327 // We use a block scope for assumption, since it's immediately goto converted,
328 // and doesn't need to be kept around.
329 {
330 code_assumet assumption{invariant};
331 assumption.add_source_location() = loop_head_location;
332
333 goto_convertt converter(symbol_table, log.get_message_handler());
334 converter.goto_convert(assumption, pre_loop_head_instrs, mode);
335 }
336
337 // Create fresh temporary variables that store the multidimensional
338 // decreases clause's value before and after the loop
339 for(const auto &clause : decreases_clause.operands())
340 {
341 const auto old_decreases_var =
343 clause.type(),
344 id2string(loop_head_location.get_function()),
345 "tmp_cc",
346 loop_head_location,
347 mode,
349 .symbol_expr();
350 pre_loop_head_instrs.add(
351 goto_programt::make_decl(old_decreases_var, loop_head_location));
352 old_decreases_vars.push_back(old_decreases_var);
353
354 const auto new_decreases_var =
356 clause.type(),
357 id2string(loop_head_location.get_function()),
358 "tmp_cc",
359 loop_head_location,
360 mode,
362 .symbol_expr();
363 pre_loop_head_instrs.add(
364 goto_programt::make_decl(new_decreases_var, loop_head_location));
365 new_decreases_vars.push_back(new_decreases_var);
366 }
367
368 // TODO: Fix loop contract handling for do/while loops.
369 if(loop_end->is_goto() && loop_end->condition() != true)
370 {
371 log.error() << "Loop contracts are unsupported on do/while loops: "
372 << loop_head_location << messaget::eom;
373 throw 0;
374
375 // non-deterministically skip the loop if it is a do-while loop.
376 // pre_loop_head_instrs.add(goto_programt::make_goto(
377 // loop_end, side_effect_expr_nondett(bool_typet(), loop_head_location)));
378 }
379
380 // Generate: assignments to store the multidimensional decreases clause's
381 // value just before the loop_head
382 if(!decreases_clause.is_nil())
383 {
384 for(size_t i = 0; i < old_decreases_vars.size(); i++)
385 {
386 code_assignt old_decreases_assignment{
387 old_decreases_vars[i], decreases_clause_exprs[i]};
388 old_decreases_assignment.add_source_location() = loop_head_location;
389
390 goto_convertt converter(symbol_table, log.get_message_handler());
391 converter.goto_convert(
392 old_decreases_assignment, pre_loop_head_instrs, mode);
393 }
394
395 goto_function.body.destructive_insert(
396 loop_head, add_pragma_disable_assigns_check(pre_loop_head_instrs));
397 }
398
399 // Insert the third and final block of pre_loop_head_instrs,
400 // with a pragma to disable assigns clause checking.
401 // The variables to store old variant value are local temporaries,
402 // which need not be checked against the enclosing scope's assigns clause.
403 goto_function.body.destructive_insert(
404 loop_head, add_pragma_disable_assigns_check(pre_loop_head_instrs));
405
406 // Perform write set instrumentation on the entire loop.
407 instrument_spec_assigns.instrument_instructions(
408 goto_function.body,
409 loop_head,
410 loop_end,
411 [&loop](const goto_programt::targett &t) { return loop.contains(t); });
412
413 // Now we begin instrumenting at the loop_end.
414 // `pre_loop_end_instrs` are to be inserted before `loop_end`.
415 goto_programt pre_loop_end_instrs;
416
417 // Record that we entered the loop.
418 pre_loop_end_instrs.add(
420
421 // Jump back to the step case to havoc the write set, assume the invariant,
422 // and execute an arbitrary iteration.
423 pre_loop_end_instrs.add(goto_programt::make_goto(
424 step_case_target, in_base_case, loop_head_location));
425
426 // The following code is only reachable in the step case,
427 // i.e., when in_base_case == false,
428 // because of the unconditional jump above.
429
430 // Generate the inductiveness check:
431 // assert(invariant)
432 // We use a block scope for assertion, since it's immediately goto converted,
433 // and doesn't need to be kept around.
434 {
435 code_assertt assertion{invariant};
436 assertion.add_source_location() = loop_head_location;
438 "Check that loop invariant is preserved");
439
440 goto_convertt converter(symbol_table, log.get_message_handler());
441 converter.goto_convert(assertion, pre_loop_end_instrs, mode);
442 }
443
444 // Generate: assignments to store the multidimensional decreases clause's
445 // value after one iteration of the loop
446 if(!decreases_clause.is_nil())
447 {
448 for(size_t i = 0; i < new_decreases_vars.size(); i++)
449 {
450 code_assignt new_decreases_assignment{
451 new_decreases_vars[i], decreases_clause_exprs[i]};
452 new_decreases_assignment.add_source_location() = loop_head_location;
453
454 goto_convertt converter(symbol_table, log.get_message_handler());
455 converter.goto_convert(
456 new_decreases_assignment, pre_loop_end_instrs, mode);
457 }
458
459 // Generate: assertion that the multidimensional decreases clause's value
460 // after the loop is lexicographically smaller than its initial value.
461 code_assertt monotonic_decreasing_assertion{
463 new_decreases_vars, old_decreases_vars)};
464 monotonic_decreasing_assertion.add_source_location() = loop_head_location;
465 monotonic_decreasing_assertion.add_source_location().set_comment(
466 "Check decreases clause on loop iteration");
467
468 goto_convertt converter(symbol_table, log.get_message_handler());
469 converter.goto_convert(
470 monotonic_decreasing_assertion, pre_loop_end_instrs, mode);
471
472 // Discard the temporary variables that store decreases clause's value
473 for(size_t i = 0; i < old_decreases_vars.size(); i++)
474 {
475 pre_loop_end_instrs.add(
476 goto_programt::make_dead(old_decreases_vars[i], loop_head_location));
477 pre_loop_end_instrs.add(
478 goto_programt::make_dead(new_decreases_vars[i], loop_head_location));
479 }
480 }
481
483 goto_function.body,
484 loop_end,
485 add_pragma_disable_assigns_check(pre_loop_end_instrs));
486
487 // change the back edge into assume(false) or assume(guard)
488 loop_end->turn_into_assume();
489 loop_end->condition_nonconst() = boolean_negate(loop_end->condition());
490
491 std::set<goto_programt::targett, goto_programt::target_less_than>
492 seen_targets;
493 // Find all exit points of the loop, make temporary variables `DEAD`,
494 // and check that step case was checked for non-vacuous loops.
495 for(const auto &t : loop)
496 {
497 if(!t->is_goto())
498 continue;
499
500 auto exit_target = t->get_target();
501 if(
502 loop.contains(exit_target) ||
503 seen_targets.find(exit_target) != seen_targets.end())
504 continue;
505
506 seen_targets.insert(exit_target);
507
508 goto_programt pre_loop_exit_instrs;
509 // Assertion to check that step case was checked if we entered the loop.
510 source_locationt annotated_location = loop_head_location;
511 annotated_location.set_comment(
512 "Check that loop instrumentation was not truncated");
513 pre_loop_exit_instrs.add(goto_programt::make_assertion(
514 or_exprt{not_exprt{entered_loop}, not_exprt{in_base_case}},
515 annotated_location));
516 // Instructions to make all the temporaries go dead.
517 pre_loop_exit_instrs.add(
518 goto_programt::make_dead(in_base_case, loop_head_location));
519 pre_loop_exit_instrs.add(
520 goto_programt::make_dead(initial_invariant_val, loop_head_location));
521 for(const auto &v : history_var_map)
522 {
523 pre_loop_exit_instrs.add(
524 goto_programt::make_dead(to_symbol_expr(v.second), loop_head_location));
525 }
526 // Insert these instructions, preserving the loop end target.
528 goto_function.body, exit_target, pre_loop_exit_instrs);
529 }
530}
531
534static void throw_on_unsupported(const goto_programt &program)
535{
536 for(const auto &it : program.instructions)
537 {
538 if(
539 it.is_function_call() && it.call_function().id() == ID_symbol &&
540 to_symbol_expr(it.call_function()).get_identifier() == CPROVER_PREFIX
541 "obeys_contract")
542 {
544 CPROVER_PREFIX "obeys_contract is not supported in this version",
545 it.source_location());
546 }
547 }
548}
549
553 symbol_tablet &symbol_table,
554 goto_convertt &converter,
555 exprt &instantiated_clause,
556 const irep_idt &mode,
557 const std::function<void(goto_programt &)> &is_fresh_update,
558 goto_programt &program,
559 const source_locationt &location)
560{
561 goto_programt constraint;
562 if(location.get_property_class() == ID_assume)
563 {
564 code_assumet assumption(instantiated_clause);
565 assumption.add_source_location() = location;
566 converter.goto_convert(assumption, constraint, mode);
567 }
568 else
569 {
570 code_assertt assertion(instantiated_clause);
571 assertion.add_source_location() = location;
572 converter.goto_convert(assertion, constraint, mode);
573 }
574 is_fresh_update(constraint);
575 throw_on_unsupported(constraint);
576 program.destructive_append(constraint);
577}
578
579static const code_with_contract_typet &
580get_contract(const irep_idt &function, const namespacet &ns)
581{
582 const std::string &function_str = id2string(function);
583 const auto &function_symbol = ns.lookup(function);
584
585 const symbolt *contract_sym;
586 if(ns.lookup("contract::" + function_str, contract_sym))
587 {
588 // no contract provided in the source or just an empty assigns clause
589 return to_code_with_contract_type(function_symbol.type);
590 }
591
592 const auto &type = to_code_with_contract_type(contract_sym->type);
594 type == function_symbol.type,
595 "front-end should have rejected re-declarations with a different type");
596
597 return type;
598}
599
601 const irep_idt &function,
602 const source_locationt &location,
603 goto_programt &function_body,
605{
606 const auto &const_target =
607 static_cast<const goto_programt::targett &>(target);
608 // Return if the function is not named in the call; currently we don't handle
609 // function pointers.
610 PRECONDITION(const_target->call_function().id() == ID_symbol);
611
612 const irep_idt &target_function =
613 to_symbol_expr(const_target->call_function()).get_identifier();
614 const symbolt &function_symbol = ns.lookup(target_function);
615 const code_typet &function_type = to_code_type(function_symbol.type);
616
617 // Check if the function actually has a contract before attempting to use it.
618 // If not, produce a hard error for soundness.
619 const symbolt *contract_sym;
620 if(ns.lookup("contract::" + id2string(target_function), contract_sym))
621 {
623 "Function '" + id2string(target_function) +
624 "' does not have a contract. " +
625 "A contract must be explicitly provided. If you need a trivial " +
626 "contract, please add explicit " +
627 CPROVER_PREFIX "requires and " CPROVER_PREFIX
628 "ensures clauses to the function.");
629 }
630
631 // Isolate each component of the contract.
632 const auto &type = get_contract(target_function, ns);
633
634 // Prepare to instantiate expressions in the callee
635 // with expressions from the call site (e.g. the return value).
636 exprt::operandst instantiation_values;
637
638 // keep track of the call's return expression to make it nondet later
639 std::optional<exprt> call_ret_opt = {};
640
641 // if true, the call return variable variable was created during replacement
642 bool call_ret_is_fresh_var = false;
643
644 if(function_type.return_type() != empty_typet())
645 {
646 // Check whether the function's return value is not disregarded.
647 if(target->call_lhs().is_not_nil())
648 {
649 // If so, have it replaced appropriately.
650 // For example, if foo() ensures that its return value is > 5, then
651 // rewrite calls to foo as follows:
652 // x = foo() -> assume(__CPROVER_return_value > 5) -> assume(x > 5)
653 auto &lhs_expr = const_target->call_lhs();
654 call_ret_opt = lhs_expr;
655 instantiation_values.push_back(lhs_expr);
656 }
657 else
658 {
659 // If the function does return a value, but the return value is
660 // disregarded, check if the postcondition includes the return value.
661 if(std::any_of(
662 type.c_ensures().begin(),
663 type.c_ensures().end(),
664 [](const exprt &e) {
665 return has_symbol_expr(
666 to_lambda_expr(e).where(), CPROVER_PREFIX "return_value", true);
667 }))
668 {
669 // The postcondition does mention __CPROVER_return_value, so mint a
670 // fresh variable to replace __CPROVER_return_value with.
671 call_ret_is_fresh_var = true;
672 const symbolt &fresh = get_fresh_aux_symbol(
673 function_type.return_type(),
674 id2string(target_function),
675 "ignored_return_value",
676 const_target->source_location(),
677 symbol_table.lookup_ref(target_function).mode,
678 ns,
680 auto fresh_sym_expr = fresh.symbol_expr();
681 call_ret_opt = fresh_sym_expr;
682 instantiation_values.push_back(fresh_sym_expr);
683 }
684 else
685 {
686 // unused, add a dummy with the right type
687 instantiation_values.push_back(
688 exprt{ID_nil, function_type.return_type()});
689 }
690 }
691 }
692
693 // Replace formal parameters
694 const auto &arguments = const_target->call_arguments();
695 instantiation_values.insert(
696 instantiation_values.end(), arguments.begin(), arguments.end());
697
698 const auto &mode = function_symbol.mode;
699
700 // new program where all contract-derived instructions are added
701 goto_programt new_program;
702
703 is_fresh_replacet is_fresh(
704 goto_model, log.get_message_handler(), target_function);
705 is_fresh.create_declarations();
706 is_fresh.add_memory_map_decl(new_program);
707
708 // Generate: assert(requires)
709 for(const auto &clause : type.c_requires())
710 {
711 auto instantiated_clause =
712 to_lambda_expr(clause).application(instantiation_values);
713 source_locationt _location = clause.source_location();
714 _location.set_line(location.get_line());
715 _location.set_comment(std::string("Check requires clause of ")
716 .append(target_function.c_str())
717 .append(" in ")
718 .append(function.c_str()));
719 _location.set_property_class(ID_precondition);
720 goto_convertt converter(symbol_table, log.get_message_handler());
723 converter,
724 instantiated_clause,
725 mode,
726 [&is_fresh](goto_programt &c_requires) {
727 is_fresh.update_requires(c_requires);
728 },
729 new_program,
730 _location);
731 }
732
733 // Generate all the instructions required to initialize history variables
734 exprt::operandst instantiated_ensures_clauses;
735 for(auto clause : type.c_ensures())
736 {
737 auto instantiated_clause =
738 to_lambda_expr(clause).application(instantiation_values);
739 instantiated_clause.add_source_location() = clause.source_location();
741 symbol_table, instantiated_clause, mode, new_program);
742 instantiated_ensures_clauses.push_back(instantiated_clause);
743 }
744
745 // ASSIGNS clause should not refer to any quantified variables,
746 // and only refer to the common symbols to be replaced.
747 exprt::operandst targets;
748 for(auto &target : type.c_assigns())
749 targets.push_back(to_lambda_expr(target).application(instantiation_values));
750
751 // Create a sequence of non-deterministic assignments ...
752
753 // ... for the assigns clause targets and static locals
754 goto_programt havoc_instructions;
755 function_cfg_infot cfg_info({});
757 target_function,
758 targets,
760 cfg_info,
761 location,
763 log.get_message_handler());
764
765 havocker.get_instructions(havoc_instructions);
766
767 // ... for the return value
768 if(call_ret_opt.has_value())
769 {
770 auto &call_ret = call_ret_opt.value();
771 auto &loc = call_ret.source_location();
772 auto &type = call_ret.type();
773
774 // Declare if fresh
775 if(call_ret_is_fresh_var)
776 havoc_instructions.add(
778
779 side_effect_expr_nondett expr(type, location);
780 havoc_instructions.add(goto_programt::make_assignment(
781 code_assignt{call_ret, std::move(expr), loc}, loc));
782 }
783
784 // Insert havoc instructions immediately before the call site.
785 new_program.destructive_append(havoc_instructions);
786
787 // Generate: assume(ensures)
788 for(auto &clause : instantiated_ensures_clauses)
789 {
790 if(clause == false)
791 {
793 std::string("Attempt to assume false at ")
794 .append(clause.source_location().as_string())
795 .append(".\nPlease update ensures clause to avoid vacuity."));
796 }
797 source_locationt _location = clause.source_location();
798 _location.set_comment("Assume ensures clause");
799 _location.set_property_class(ID_assume);
800
801 goto_convertt converter(symbol_table, log.get_message_handler());
804 converter,
805 clause,
806 mode,
807 [&is_fresh](goto_programt &ensures) { is_fresh.update_ensures(ensures); },
808 new_program,
809 _location);
810 }
811
812 // Kill return value variable if fresh
813 if(call_ret_is_fresh_var)
814 {
815 log.conditional_output(
816 log.warning(), [&target](messaget::mstreamt &mstream) {
817 target->output(mstream);
818 mstream << messaget::eom;
819 });
820 auto dead_inst =
821 goto_programt::make_dead(to_symbol_expr(call_ret_opt.value()), location);
822 function_body.insert_before_swap(target, dead_inst);
823 ++target;
824 }
825
826 is_fresh.add_memory_map_dead(new_program);
827
828 // Erase original function call
829 *target = goto_programt::make_skip();
830
831 // insert contract replacement instructions
832 insert_before_swap_and_advance(function_body, target, new_program);
833
834 // Add this function to the set of replaced functions.
835 summarized.insert(target_function);
836
837 // restore global goto_program consistency
838 goto_functions.update();
839}
840
842 const irep_idt &function_name,
843 goto_functionst::goto_functiont &goto_function)
844{
845 const bool may_have_loops = std::any_of(
846 goto_function.body.instructions.begin(),
847 goto_function.body.instructions.end(),
848 [](const goto_programt::instructiont &instruction) {
849 return instruction.is_backwards_goto();
850 });
851
852 if(!may_have_loops)
853 return;
854
855 inlining_decoratort decorated(log.get_message_handler());
856 goto_function_inline(goto_functions, function_name, ns, decorated);
857
858 decorated.throw_on_recursive_calls(log, 0);
859
860 // restore internal invariants
861 goto_functions.update();
862
863 local_may_aliast local_may_alias(goto_function);
864 natural_loops_mutablet natural_loops(goto_function.body);
865
866 // A graph node type that stores information about a loop.
867 // We create a DAG representing nesting of various loops in goto_function,
868 // sort them topologically, and instrument them in the top-sorted order.
869 //
870 // The goal here is not avoid explicit "subset checks" on nested write sets.
871 // When instrumenting in a top-sorted order,
872 // the outer loop would no longer observe the inner loop's write set,
873 // but only corresponding `havoc` statements,
874 // which can be instrumented in the usual way to achieve a "subset check".
875
876 struct loop_graph_nodet : public graph_nodet<empty_edget>
877 {
878 const typename natural_loops_mutablet::loopt &content;
879 const goto_programt::targett head_target, end_target;
880 exprt assigns_clause, invariant, decreases_clause;
881
882 loop_graph_nodet(
883 const typename natural_loops_mutablet::loopt &loop,
884 const goto_programt::targett head,
885 const goto_programt::targett end,
886 const exprt &assigns,
887 const exprt &inv,
888 const exprt &decreases)
889 : content(loop),
890 head_target(head),
891 end_target(end),
892 assigns_clause(assigns),
893 invariant(inv),
894 decreases_clause(decreases)
895 {
896 }
897 };
898 grapht<loop_graph_nodet> loop_nesting_graph;
899
900 std::list<size_t> to_check_contracts_on_children;
901
902 std::map<
904 std::pair<goto_programt::targett, natural_loops_mutablet::loopt>,
906 loop_head_ends;
907
908 for(const auto &loop_head_and_content : natural_loops.loop_map)
909 {
910 const auto &loop_body = loop_head_and_content.second;
911 // Skip empty loops and self-looped node.
912 if(loop_body.size() <= 1)
913 continue;
914
915 auto loop_head = loop_head_and_content.first;
916 auto loop_end = loop_head;
917
918 // Find the last back edge to `loop_head`
919 for(const auto &t : loop_body)
920 {
921 if(
922 t->is_goto() && t->get_target() == loop_head &&
923 t->location_number > loop_end->location_number)
924 loop_end = t;
925 }
926
927 if(loop_end == loop_head)
928 {
929 log.error() << "Could not find end of the loop starting at: "
930 << loop_head->source_location() << messaget::eom;
931 throw 0;
932 }
933
934 // By definition the `loop_body` is a set of instructions computed
935 // by `natural_loops` based on the CFG.
936 // Since we perform assigns clause instrumentation by sequentially
937 // traversing instructions from `loop_head` to `loop_end`,
938 // here we ensure that all instructions in `loop_body` belong within
939 // the [loop_head, loop_end] target range.
940
941 // Check 1. (i \in loop_body) ==> loop_head <= i <= loop_end
942 for(const auto &i : loop_body)
943 {
944 if(
945 loop_head->location_number > i->location_number ||
946 i->location_number > loop_end->location_number)
947 {
948 log.conditional_output(
949 log.error(), [&i, &loop_head](messaget::mstreamt &mstream) {
950 mstream << "Computed loop at " << loop_head->source_location()
951 << "contains an instruction beyond [loop_head, loop_end]:"
952 << messaget::eom;
953 i->output(mstream);
954 mstream << messaget::eom;
955 });
956 throw 0;
957 }
958 }
959
960 if(!loop_head_ends.emplace(loop_head, std::make_pair(loop_end, loop_body))
961 .second)
963 }
964
965 for(auto &loop_head_end : loop_head_ends)
966 {
967 auto loop_head = loop_head_end.first;
968 auto loop_end = loop_head_end.second.first;
969 // After loop-contract instrumentation, jumps to the `loop_head` will skip
970 // some instrumented instructions. So we want to make sure that there is
971 // only one jump targeting `loop_head` from `loop_end` before loop-contract
972 // instrumentation.
973 // Add a skip before `loop_head` and let all jumps (except for the
974 // `loop_end`) that target to the `loop_head` target to the skip
975 // instead.
977 goto_function.body, loop_head, goto_programt::make_skip());
978 loop_end->set_target(loop_head);
979
980 exprt assigns_clause = get_loop_assigns(loop_end);
981 exprt invariant =
982 get_loop_invariants(loop_end, loop_contract_config.check_side_effect);
983 exprt decreases_clause =
984 get_loop_decreases(loop_end, loop_contract_config.check_side_effect);
985
986 if(invariant.is_nil())
987 {
988 if(decreases_clause.is_not_nil() || assigns_clause.is_not_nil())
989 {
990 invariant = true_exprt{};
991 // assigns clause is missing; we will try to automatic inference
992 log.warning()
993 << "The loop at " << loop_head->source_location().as_string()
994 << " does not have an invariant in its contract.\n"
995 << "Hence, a default invariant ('true') is being used.\n"
996 << "This choice is sound, but verification may fail"
997 << " if it is be too weak to prove the desired properties."
998 << messaget::eom;
999 }
1000 }
1001 else
1002 {
1003 invariant = conjunction(invariant.operands());
1004 if(decreases_clause.is_nil())
1005 {
1006 log.warning() << "The loop at "
1007 << loop_head->source_location().as_string()
1008 << " does not have a decreases clause in its contract.\n"
1009 << "Termination of this loop will not be verified."
1010 << messaget::eom;
1011 }
1012 }
1013
1014 const auto idx = loop_nesting_graph.add_node(
1015 loop_head_end.second.second,
1016 loop_head,
1017 loop_end,
1018 assigns_clause,
1019 invariant,
1020 decreases_clause);
1021
1022 if(
1023 assigns_clause.is_nil() && invariant.is_nil() &&
1024 decreases_clause.is_nil())
1025 continue;
1026
1027 to_check_contracts_on_children.push_back(idx);
1028 }
1029
1030 for(size_t outer = 0; outer < loop_nesting_graph.size(); ++outer)
1031 {
1032 for(size_t inner = 0; inner < loop_nesting_graph.size(); ++inner)
1033 {
1034 if(inner == outer)
1035 continue;
1036
1037 if(loop_nesting_graph[outer].content.contains(
1038 loop_nesting_graph[inner].head_target))
1039 {
1040 if(!loop_nesting_graph[outer].content.contains(
1041 loop_nesting_graph[inner].end_target))
1042 {
1043 log.error()
1044 << "Overlapping loops at:\n"
1045 << loop_nesting_graph[outer].head_target->source_location()
1046 << "\nand\n"
1047 << loop_nesting_graph[inner].head_target->source_location()
1048 << "\nLoops must be nested or sequential for contracts to be "
1049 "enforced."
1050 << messaget::eom;
1051 }
1052 loop_nesting_graph.add_edge(inner, outer);
1053 }
1054 }
1055 }
1056
1057 // make sure all children of a contractified loop also have contracts
1058 while(!to_check_contracts_on_children.empty())
1059 {
1060 const auto loop_idx = to_check_contracts_on_children.front();
1061 to_check_contracts_on_children.pop_front();
1062
1063 const auto &loop_node = loop_nesting_graph[loop_idx];
1064 if(
1065 loop_node.assigns_clause.is_nil() && loop_node.invariant.is_nil() &&
1066 loop_node.decreases_clause.is_nil())
1067 {
1068 log.error()
1069 << "Inner loop at: " << loop_node.head_target->source_location()
1070 << " does not have contracts, but an enclosing loop does.\n"
1071 << "Please provide contracts for this loop, or unwind it first."
1072 << messaget::eom;
1073 throw 0;
1074 }
1075
1076 for(const auto child_idx : loop_nesting_graph.get_predecessors(loop_idx))
1077 to_check_contracts_on_children.push_back(child_idx);
1078 }
1079
1080 // Iterate over the (natural) loops in the function, in topo-sorted order,
1081 // and apply any loop contracts that we find.
1082 for(const auto &idx : loop_nesting_graph.topsort())
1083 {
1084 const auto &loop_node = loop_nesting_graph[idx];
1085 if(
1086 loop_node.assigns_clause.is_nil() && loop_node.invariant.is_nil() &&
1087 loop_node.decreases_clause.is_nil())
1088 continue;
1089
1090 // Computed loop "contents" needs to be refreshed to include any newly
1091 // introduced instrumentation, e.g. havoc instructions for assigns clause,
1092 // on inner loops in to the outer loop's contents.
1093 // Else, during the outer loop instrumentation these instructions will be
1094 // "masked" just as any other instruction not within its "contents."
1095
1096 goto_functions.update();
1097 natural_loops_mutablet updated_loops(goto_function.body);
1098
1099 // We will unwind all transformed loops twice. Store the names of
1100 // to-unwind-loops here and perform the unwinding after transformation done.
1101 // As described in `check_apply_loop_contracts`, loops with loop contracts
1102 // will be transformed to a loop that execute exactly twice: one for base
1103 // case and one for step case. So we unwind them here to avoid users
1104 // incorrectly unwind them only once.
1105 std::string to_unwind = id2string(function_name) + "." +
1106 std::to_string(loop_node.end_target->loop_number) +
1107 ":2";
1108 loop_names.push_back(to_unwind);
1109
1111 function_name,
1112 goto_function,
1113 local_may_alias,
1114 loop_node.head_target,
1115 loop_node.end_target,
1116 updated_loops.loop_map[loop_node.head_target],
1117 loop_node.assigns_clause,
1118 loop_node.invariant,
1119 loop_node.decreases_clause,
1120 symbol_table.lookup_ref(function_name).mode);
1121 }
1122}
1123
1125{
1126 // Get the function object before instrumentation.
1127 auto function_obj = goto_functions.function_map.find(function);
1128
1129 INVARIANT(
1130 function_obj != goto_functions.function_map.end(),
1131 "Function '" + id2string(function) + "'must exist in the goto program");
1132
1133 const auto &goto_function = function_obj->second;
1134 auto &function_body = function_obj->second.body;
1135
1136 function_cfg_infot cfg_info(goto_function);
1137
1138 instrument_spec_assignst instrument_spec_assigns(
1139 function,
1141 cfg_info,
1143 log.get_message_handler());
1144
1145 // Detect and track static local variables before inlining
1146 goto_programt snapshot_static_locals;
1147 instrument_spec_assigns.track_static_locals(snapshot_static_locals);
1148
1149 // Full inlining of the function body
1150 // Inlining is performed so that function calls to a same function
1151 // occurring under different write sets get instrumented specifically
1152 // for each write set
1153 inlining_decoratort decorated(log.get_message_handler());
1154 goto_function_inline(goto_functions, function, ns, decorated);
1155
1156 decorated.throw_on_recursive_calls(log, 0);
1157
1158 // Clean up possible fake loops that are due to `IF 0!=0 GOTO i` instructions
1159 simplify_gotos(function_body, ns);
1160
1161 // Restore internal coherence in the programs
1162 goto_functions.update();
1163
1164 INVARIANT(
1165 is_loop_free(function_body, ns, log),
1166 "Loops remain in function '" + id2string(function) +
1167 "', assigns clause checking instrumentation cannot be applied.");
1168
1169 // Start instrumentation
1170 auto instruction_it = function_body.instructions.begin();
1171
1172 // Inject local static snapshots
1174 function_body, instruction_it, snapshot_static_locals);
1175
1176 // Track targets mentioned in the specification
1177 const symbolt &function_symbol = ns.lookup(function);
1178 const code_typet &function_type = to_code_type(function_symbol.type);
1179 exprt::operandst instantiation_values;
1180 // assigns clauses cannot refer to the return value, but we still need an
1181 // element in there to apply the lambda function consistently
1182 if(function_type.return_type() != empty_typet())
1183 instantiation_values.push_back(exprt{ID_nil, function_type.return_type()});
1184 for(const auto &param : function_type.parameters())
1185 {
1186 instantiation_values.push_back(
1187 ns.lookup(param.get_identifier()).symbol_expr());
1188 }
1189 for(auto &target : get_contract(function, ns).c_assigns())
1190 {
1191 goto_programt payload;
1192 instrument_spec_assigns.track_spec_target(
1193 to_lambda_expr(target).application(instantiation_values), payload);
1194 insert_before_swap_and_advance(function_body, instruction_it, payload);
1195 }
1196
1197 // Track formal parameters
1198 goto_programt snapshot_function_parameters;
1199 for(const auto &param : function_type.parameters())
1200 {
1201 goto_programt payload;
1202 instrument_spec_assigns.track_stack_allocated(
1203 ns.lookup(param.get_identifier()).symbol_expr(), payload);
1204 insert_before_swap_and_advance(function_body, instruction_it, payload);
1205 }
1206
1207 // Restore internal coherence in the programs
1208 goto_functions.update();
1209
1210 // Insert write set inclusion checks.
1211 instrument_spec_assigns.instrument_instructions(
1212 function_body, instruction_it, function_body.instructions.end());
1213}
1214
1216{
1217 // Add statements to the source function
1218 // to ensure assigns clause is respected.
1220
1221 // Rename source function
1222 std::stringstream ss;
1223 ss << CPROVER_PREFIX << "contracts_original_" << function;
1224 const irep_idt mangled(ss.str());
1225 const irep_idt original(function);
1226
1227 auto old_function = goto_functions.function_map.find(original);
1228 INVARIANT(
1229 old_function != goto_functions.function_map.end(),
1230 "Function to replace must exist in the program.");
1231
1232 std::swap(goto_functions.function_map[mangled], old_function->second);
1233 goto_functions.function_map.erase(old_function);
1234
1235 // Place a new symbol with the mangled name into the symbol table
1237 sl.set_file("instrumented for code contracts");
1238 sl.set_line("0");
1239 const symbolt *original_sym = symbol_table.lookup(original);
1240 symbolt mangled_sym = *original_sym;
1241 mangled_sym.name = mangled;
1242 mangled_sym.base_name = mangled;
1243 mangled_sym.location = sl;
1244 const auto mangled_found = symbol_table.insert(std::move(mangled_sym));
1245 INVARIANT(
1246 mangled_found.second,
1247 "There should be no existing function called " + ss.str() +
1248 " in the symbol table because that name is a mangled name");
1249
1250 // Insert wrapper function into goto_functions
1251 auto nexist_old_function = goto_functions.function_map.find(original);
1252 INVARIANT(
1253 nexist_old_function == goto_functions.function_map.end(),
1254 "There should be no function called " + id2string(function) +
1255 " in the function map because that function should have had its"
1256 " name mangled");
1257
1258 auto mangled_fun = goto_functions.function_map.find(mangled);
1259 INVARIANT(
1260 mangled_fun != goto_functions.function_map.end(),
1261 "There should be a function called " + ss.str() +
1262 " in the function map because we inserted a fresh goto-program"
1263 " with that mangled name");
1264
1265 goto_functiont &wrapper = goto_functions.function_map[original];
1266 wrapper.parameter_identifiers = mangled_fun->second.parameter_identifiers;
1268 add_contract_check(original, mangled, wrapper.body);
1269}
1270
1272 const irep_idt &wrapper_function,
1273 const irep_idt &mangled_function,
1274 goto_programt &dest)
1275{
1276 PRECONDITION(!dest.instructions.empty());
1277
1278 // build:
1279 // decl ret
1280 // decl parameter1 ...
1281 // decl history_parameter1 ... [optional]
1282 // assume(requires) [optional]
1283 // ret=function(parameter1, ...)
1284 // assert(ensures)
1285
1286 const auto &code_type = get_contract(wrapper_function, ns);
1287 goto_programt check;
1288
1289 // prepare function call including all declarations
1290 const symbolt &function_symbol = ns.lookup(mangled_function);
1291 code_function_callt call(function_symbol.symbol_expr());
1292
1293 // Prepare to instantiate expressions in the callee
1294 // with expressions from the call site (e.g. the return value).
1295 exprt::operandst instantiation_values;
1296
1297 source_locationt source_location = function_symbol.location;
1298 // Set function in source location to original function
1299 source_location.set_function(wrapper_function);
1300
1301 // decl ret
1302 std::optional<code_returnt> return_stmt;
1303 if(code_type.return_type() != empty_typet())
1304 {
1306 code_type.return_type(),
1307 id2string(source_location.get_function()),
1308 "tmp_cc",
1309 source_location,
1310 function_symbol.mode,
1312 .symbol_expr();
1313 check.add(goto_programt::make_decl(r, source_location));
1314
1315 call.lhs() = r;
1316 return_stmt = code_returnt(r);
1317
1318 instantiation_values.push_back(r);
1319 }
1320
1321 // decl parameter1 ...
1322 goto_functionst::function_mapt::iterator f_it =
1323 goto_functions.function_map.find(mangled_function);
1324 PRECONDITION(f_it != goto_functions.function_map.end());
1325
1326 const goto_functionst::goto_functiont &gf = f_it->second;
1327 for(const auto &parameter : gf.parameter_identifiers)
1328 {
1329 PRECONDITION(!parameter.empty());
1330 const symbolt &parameter_symbol = ns.lookup(parameter);
1332 parameter_symbol.type,
1333 id2string(source_location.get_function()),
1334 "tmp_cc",
1335 source_location,
1336 parameter_symbol.mode,
1338 .symbol_expr();
1339 check.add(goto_programt::make_decl(p, source_location));
1341 p, parameter_symbol.symbol_expr(), source_location));
1342
1343 call.arguments().push_back(p);
1344
1345 instantiation_values.push_back(p);
1346 }
1347
1348 is_fresh_enforcet visitor(
1349 goto_model, log.get_message_handler(), wrapper_function);
1350 visitor.create_declarations();
1351 visitor.add_memory_map_decl(check);
1352
1353 // Generate: assume(requires)
1354 for(const auto &clause : code_type.c_requires())
1355 {
1356 auto instantiated_clause =
1357 to_lambda_expr(clause).application(instantiation_values);
1358 if(instantiated_clause == false)
1359 {
1361 std::string("Attempt to assume false at ")
1362 .append(clause.source_location().as_string())
1363 .append(".\nPlease update requires clause to avoid vacuity."));
1364 }
1365 source_locationt _location = clause.source_location();
1366 _location.set_comment("Assume requires clause");
1367 _location.set_property_class(ID_assume);
1368 goto_convertt converter(symbol_table, log.get_message_handler());
1371 converter,
1372 instantiated_clause,
1373 function_symbol.mode,
1374 [&visitor](goto_programt &c_requires) {
1375 visitor.update_requires(c_requires);
1376 },
1377 check,
1378 _location);
1379 }
1380
1381 // Generate all the instructions required to initialize history variables
1382 exprt::operandst instantiated_ensures_clauses;
1383 for(auto clause : code_type.c_ensures())
1384 {
1385 auto instantiated_clause =
1386 to_lambda_expr(clause).application(instantiation_values);
1387 instantiated_clause.add_source_location() = clause.source_location();
1389 symbol_table, instantiated_clause, function_symbol.mode, check);
1390 instantiated_ensures_clauses.push_back(instantiated_clause);
1391 }
1392
1393 // ret=mangled_function(parameter1, ...)
1394 check.add(goto_programt::make_function_call(call, source_location));
1395
1396 // Generate: assert(ensures)
1397 for(auto &clause : instantiated_ensures_clauses)
1398 {
1399 source_locationt _location = clause.source_location();
1400 _location.set_comment("Check ensures clause");
1401 _location.set_property_class(ID_postcondition);
1402 goto_convertt converter(symbol_table, log.get_message_handler());
1405 converter,
1406 clause,
1407 function_symbol.mode,
1408 [&visitor](goto_programt &ensures) { visitor.update_ensures(ensures); },
1409 check,
1410 _location);
1411 }
1412
1413 if(code_type.return_type() != empty_typet())
1414 {
1416 return_stmt.value().return_value(), source_location));
1417 }
1418
1419 // kill the is_fresh memory map
1420 visitor.add_memory_map_dead(check);
1421
1422 // prepend the new code to dest
1423 dest.destructive_insert(dest.instructions.begin(), check);
1424
1425 // restore global goto_program consistency
1426 goto_functions.update();
1427}
1428
1430 const std::set<std::string> &functions) const
1431{
1432 for(const auto &function : functions)
1433 {
1434 if(
1435 goto_functions.function_map.find(function) ==
1436 goto_functions.function_map.end())
1437 {
1439 "Function '" + function + "' was not found in the GOTO program.");
1440 }
1441 }
1442}
1443
1444void code_contractst::replace_calls(const std::set<std::string> &to_replace)
1445{
1446 if(to_replace.empty())
1447 return;
1448
1449 log.status() << "Replacing function calls with contracts" << messaget::eom;
1450
1451 check_all_functions_found(to_replace);
1452
1453 for(auto &goto_function : goto_functions.function_map)
1454 {
1455 Forall_goto_program_instructions(ins, goto_function.second.body)
1456 {
1457 if(ins->is_function_call())
1458 {
1459 if(ins->call_function().id() != ID_symbol)
1460 continue;
1461
1462 const irep_idt &called_function =
1463 to_symbol_expr(ins->call_function()).get_identifier();
1464 auto found = std::find(
1465 to_replace.begin(), to_replace.end(), id2string(called_function));
1466 if(found == to_replace.end())
1467 continue;
1468
1470 goto_function.first,
1471 ins->source_location(),
1472 goto_function.second.body,
1473 ins);
1474 }
1475 }
1476 }
1477
1478 for(auto &goto_function : goto_functions.function_map)
1479 remove_skip(goto_function.second.body);
1480
1481 goto_functions.update();
1482}
1483
1485 const std::set<std::string> &to_exclude_from_nondet_init)
1486{
1487 for(auto &goto_function : goto_functions.function_map)
1488 apply_loop_contract(goto_function.first, goto_function.second);
1489
1490 log.status() << "Adding nondeterministic initialization "
1491 "of static/global variables."
1492 << messaget::eom;
1493 nondet_static(goto_model, to_exclude_from_nondet_init);
1494
1495 // unwind all transformed loops twice.
1496 if(loop_contract_config.unwind_transformed_loops)
1497 {
1498 unwindsett unwindset;
1499 unwindset.parse_unwindset(
1500 loop_names, goto_model, log.get_message_handler());
1501 goto_unwindt goto_unwind;
1502 goto_unwind(goto_model, unwindset, goto_unwindt::unwind_strategyt::ASSUME);
1503 }
1504
1506
1507 // Record original loop number for some instrumented instructions.
1508 for(auto &goto_function_entry : goto_functions.function_map)
1509 {
1510 auto &goto_function = goto_function_entry.second;
1511 bool is_in_loop_havoc_block = false;
1512
1513 unsigned loop_number_of_loop_havoc = 0;
1514 for(goto_programt::const_targett it_instr =
1515 goto_function.body.instructions.begin();
1516 it_instr != goto_function.body.instructions.end();
1517 it_instr++)
1518 {
1519 // Don't override original loop numbers.
1520 if(original_loop_number_map.count(it_instr) != 0)
1521 continue;
1522
1523 // Store loop number for two type of instrumented instructions.
1524 // ASSIGN ENTERED_LOOP = false --- head of transformed loops.
1525 // ASSIGN ENTERED_LOOP = true --- end of transformed loops.
1526 if(
1528 {
1529 const auto &assign_lhs =
1530 expr_try_dynamic_cast<symbol_exprt>(it_instr->assign_lhs());
1532 id2string(assign_lhs->get_identifier()),
1533 std::string(ENTERED_LOOP) + "__");
1534 continue;
1535 }
1536
1537 // Loop havocs are assignments between
1538 // ASSIGN IN_LOOP_HAVOC_BLOCK = true
1539 // and
1540 // ASSIGN IN_LOOP_HAVOC_BLOCK = false
1541
1542 // Entering the loop-havoc block.
1544 {
1545 is_in_loop_havoc_block = it_instr->assign_rhs() == true_exprt();
1546 const auto &assign_lhs =
1547 expr_try_dynamic_cast<symbol_exprt>(it_instr->assign_lhs());
1548 loop_number_of_loop_havoc = get_suffix_unsigned(
1549 id2string(assign_lhs->get_identifier()),
1550 std::string(IN_LOOP_HAVOC_BLOCK) + "__");
1551 continue;
1552 }
1553
1554 // Assignments in loop-havoc block are loop havocs.
1555 if(is_in_loop_havoc_block && it_instr->is_assign())
1556 {
1557 loop_havoc_set.emplace(it_instr);
1558
1559 // Store loop number for loop havoc.
1560 original_loop_number_map[it_instr] = loop_number_of_loop_havoc;
1561 }
1562 }
1563 }
1564}
1565
1567 const std::set<std::string> &to_enforce,
1568 const std::set<std::string> &to_exclude_from_nondet_init)
1569{
1570 if(to_enforce.empty())
1571 return;
1572
1573 log.status() << "Enforcing contracts" << messaget ::eom;
1574
1575 check_all_functions_found(to_enforce);
1576
1577 for(const auto &function : to_enforce)
1578 enforce_contract(function);
1579
1580 log.status() << "Adding nondeterministic initialization "
1581 "of static/global variables."
1582 << messaget::eom;
1583 nondet_static(goto_model, to_exclude_from_nondet_init);
1584}
const code_with_contract_typet & to_code_with_contract_type(const typet &type)
Cast a typet to a code_with_contract_typet.
Definition c_types.h:467
Classes providing CFG-based information about symbols to guide simplifications in function and loop a...
Thrown when an unexpected error occurs during the analysis (e.g., when the SAT solver returns an erro...
The Boolean type.
Definition std_types.h:36
A non-fatal assertion, which checks a condition then permits execution to continue.
Definition std_code.h:270
A goto_instruction_codet representing an assignment in the program.
An assumption, which must hold in subsequent code.
Definition std_code.h:217
void apply_function_contract(const irep_idt &function, const source_locationt &location, goto_programt &function_body, goto_programt::targett &target)
Replaces function calls with assertions based on requires clauses, non-deterministic assignments for ...
void enforce_contract(const irep_idt &function)
Enforce contract of a single function.
goto_modelt & goto_model
Definition contracts.h:153
void check_apply_loop_contracts(const irep_idt &function_name, goto_functionst::goto_functiont &goto_function, const local_may_aliast &local_may_alias, goto_programt::targett loop_head, goto_programt::targett loop_end, const loopt &loop, exprt assigns_clause, exprt invariant, exprt decreases_clause, const irep_idt &mode)
Definition contracts.cpp:40
void apply_loop_contract(const irep_idt &function, goto_functionst::goto_functiont &goto_function)
Apply loop contracts, whenever available, to all loops in function.
void check_all_functions_found(const std::set< std::string > &functions) const
Throws an exception if some function functions is found in the program.
void check_frame_conditions_function(const irep_idt &function)
Instrument functions to check frame conditions.
messaget & log
Definition contracts.h:157
void apply_loop_contracts(const std::set< std::string > &to_exclude_from_nondet_init={})
Applies loop contract transformations.
symbol_tablet & symbol_table
Definition contracts.h:154
void enforce_contracts(const std::set< std::string > &to_enforce, const std::set< std::string > &to_exclude_from_nondet_init={})
Turn requires & ensures into assumptions and assertions for each of the named functions.
void replace_calls(const std::set< std::string > &to_replace)
Replace all calls to each function in the to_replace set with that function's contract.
std::unordered_set< irep_idt > summarized
Definition contracts.h:159
const namespacet ns
Definition contracts.h:150
std::unordered_set< goto_programt::const_targett, const_target_hash > loop_havoc_set
Loop havoc instructions instrumented during applying loop contracts.
Definition contracts.h:173
loop_contract_configt loop_contract_config
Definition contracts.h:176
std::list< std::string > loop_names
Name of loops we are going to unwind.
Definition contracts.h:162
goto_functionst & goto_functions
Definition contracts.h:155
std::unordered_map< goto_programt::const_targett, unsigned, const_target_hash > original_loop_number_map
Store the map from instrumented instructions for loop contracts to their original loop numbers.
Definition contracts.h:169
void add_contract_check(const irep_idt &wrapper_function, const irep_idt &mangled_function, goto_programt &dest)
Instruments wrapper_function adding assumptions based on requires clauses and assertions based on ens...
goto_instruction_codet representation of a function call statement.
goto_instruction_codet representation of a "return from afunction" statement.
Base type of functions.
Definition std_types.h:583
const parameterst & parameters() const
Definition std_types.h:699
const typet & return_type() const
Definition std_types.h:689
const char * c_str() const
Definition dstring.h:116
The empty type.
Definition std_types.h:51
Base class for all expressions.
Definition expr.h:57
std::vector< exprt > operandst
Definition expr.h:59
operandst & operands()
Definition expr.h:95
source_locationt & add_source_location()
Definition expr.h:241
The Boolean constant false.
Definition std_expr.h:3125
void goto_convert(const codet &code, goto_programt &dest, const irep_idt &mode)
::goto_functiont goto_functiont
A goto function, consisting of function body (see body) and parameter identifiers (see parameter_iden...
goto_programt body
parameter_identifierst parameter_identifiers
The identifiers of the parameters of this function.
This class represents an instruction in the GOTO intermediate representation.
A generic container class for the GOTO intermediate representation of one function.
instructionst instructions
The list of instructions in the goto program.
static instructiont make_set_return_value(exprt return_value, const source_locationt &l=source_locationt::nil())
static instructiont make_dead(const symbol_exprt &symbol, const source_locationt &l=source_locationt::nil())
void insert_before_swap(targett target)
Insertion that preserves jumps to "target".
void destructive_insert(const_targett target, goto_programt &p)
Inserts the given program p before target.
static instructiont make_end_function(const source_locationt &l=source_locationt::nil())
instructionst::iterator targett
instructionst::const_iterator const_targett
void destructive_append(goto_programt &p)
Appends the given program p to *this. p is destroyed.
static instructiont make_assignment(const code_assignt &_code, const source_locationt &l=source_locationt::nil())
Create an assignment instruction.
static instructiont make_skip(const source_locationt &l=source_locationt::nil())
static instructiont make_function_call(const code_function_callt &_code, const source_locationt &l=source_locationt::nil())
Create a function call instruction.
instructiont::target_less_than target_less_than
targett add(instructiont &&instruction)
Adds a given instruction at the end.
static instructiont make_goto(targett _target, const source_locationt &l=source_locationt::nil())
static instructiont make_decl(const symbol_exprt &symbol, const source_locationt &l=source_locationt::nil())
static instructiont make_assertion(const exprt &g, const source_locationt &l=source_locationt::nil())
This class represents a node in a directed graph.
Definition graph.h:35
A generic directed graph with a parametric node type.
Definition graph.h:167
std::vector< node_indext > get_predecessors(const node_indext &n) const
Definition graph.h:943
node_indext add_node(arguments &&... values)
Definition graph.h:180
void add_edge(node_indext a, node_indext b)
Definition graph.h:232
std::size_t size() const
Definition graph.h:212
std::list< node_indext > topsort() const
Find a topological order of the nodes if graph is DAG, return empty list for non-DAG or empty graph.
Definition graph.h:879
Class to generate havocking instructions for target expressions of a function contract's assign claus...
A class that further overrides the "safe" havoc utilities, and adds support for havocing pointer_obje...
Definition utils.h:90
void append_full_havoc_code(const source_locationt location, goto_programt &dest)
Append goto instructions to havoc the full assigns set.
Decorator for a message_handlert used during function inlining that collect names of GOTO functions c...
void throw_on_recursive_calls(messaget &log, const int error_code)
Throws the given error code if recursive call warnings happend during inlining.
A class that generates instrumentation for assigns clause checking.
void track_spec_target(const exprt &expr, goto_programt &dest)
Track an assigns clause target and generate snaphsot instructions and well-definedness assertions in ...
void track_stack_allocated(const symbol_exprt &symbol_expr, goto_programt &dest)
Track a stack-allocated object and generate snaphsot instructions in dest.
void track_static_locals(goto_programt &dest)
Searches the goto instructions reachable from the start to the end of the instrumented function's ins...
void instrument_instructions(goto_programt &body, goto_programt::targett instruction_it, const goto_programt::targett &instruction_end, const std::function< bool(const goto_programt::targett &)> &pred={})
Instruments a sequence of instructions with inclusion checks.
void track_static_locals_between(goto_programt::const_targett it, const goto_programt::const_targett end, goto_programt &dest)
Searches the goto instructions reachable between the given it and end target instructions to identify...
void get_static_locals(std::insert_iterator< C > inserter) const
Inserts the detected static local symbols into a target container.
Thrown when user-provided input cannot be processed.
Thrown when we can't handle something in an input source file.
bool is_not_nil() const
Definition irep.h:372
void swap(irept &irep)
Definition irep.h:434
bool is_nil() const
Definition irep.h:368
void add_memory_map_decl(goto_programt &program)
void update_requires(goto_programt &requires_)
void add_memory_map_dead(goto_programt &program)
void update_ensures(goto_programt &ensures)
virtual void create_declarations()
virtual void create_declarations()
exprt application(const operandst &arguments) const
loop_mapt loop_map
loop_templatet< T, C > loopt
void erase_locals(std::set< exprt > &exprs)
Definition cfg_info.h:171
static eomt eom
Definition message.h:289
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Boolean negation.
Definition std_expr.h:2378
Boolean OR All operands must be boolean, and the result is always boolean.
Definition std_expr.h:2183
A side_effect_exprt that returns a non-deterministically chosen value.
Definition std_code.h:1520
void set_comment(const irep_idt &comment)
void set_property_class(const irep_idt &property_class)
const irep_idt & get_function() const
const irep_idt & get_line() const
const irep_idt & get_property_class() const
void set_file(const irep_idt &file)
void set_line(const irep_idt &line)
void set_function(const irep_idt &function)
Expression to hold a symbol (variable).
Definition std_expr.h:132
const irep_idt & get_identifier() const
Definition std_expr.h:161
The symbol table.
Symbol table entry.
Definition symbol.h:28
irep_idt base_name
Base (non-scoped) name.
Definition symbol.h:46
source_locationt location
Source code location of definition of symbol.
Definition symbol.h:37
class symbol_exprt symbol_expr() const
Produces a symbol_exprt for a symbol.
Definition symbol.cpp:121
typet type
Type of symbol.
Definition symbol.h:31
irep_idt name
The unique identifier.
Definition symbol.h:40
irep_idt mode
Language mode.
Definition symbol.h:49
The Boolean constant true.
Definition std_expr.h:3116
void parse_unwindset(const std::list< std::string > &unwindset, abstract_goto_modelt &goto_model, message_handlert &message_handler)
static void generate_contract_constraints(symbol_tablet &symbol_table, goto_convertt &converter, exprt &instantiated_clause, const irep_idt &mode, const std::function< void(goto_programt &)> &is_fresh_update, goto_programt &program, const source_locationt &location)
This function generates instructions for all contract constraint, i.e., assumptions and assertions ba...
static const code_with_contract_typet & get_contract(const irep_idt &function, const namespacet &ns)
static void throw_on_unsupported(const goto_programt &program)
Throws an exception if a contract uses unsupported constructs like:
Verify and use annotated invariants and pre/post-conditions.
#define CPROVER_PREFIX
static const std::map< dfcc_funt, int > to_unwind
set of functions that need to be unwound to assigns clause size with corresponding loop identifiers.
auto expr_try_dynamic_cast(TExpr &base) -> typename detail::expr_try_dynamic_cast_return_typet< T, TExpr >::type
Try to cast a reference to a generic exprt to a specific derived class.
Definition expr_cast.h:81
exprt boolean_negate(const exprt &src)
negate a Boolean expression, possibly removing a not_exprt, and swapping false and true
Definition expr_util.cpp:98
static format_containert< T > format(const T &o)
Definition format.h:37
symbolt & get_fresh_aux_symbol(const typet &type, const std::string &name_prefix, const std::string &basename_prefix, const source_locationt &source_location, const irep_idt &symbol_mode, const namespacet &ns, symbol_table_baset &symbol_table)
Installs a fresh-named symbol with respect to the given namespace ns with the requested name pattern ...
Fresh auxiliary symbol creation.
void goto_function_inline(goto_modelt &goto_model, const irep_idt function, message_handlert &message_handler, bool adjust_function, bool caching)
Transitively inline all function calls made from a particular function.
Function Inlining This gives a number of different interfaces to the function inlining functionality ...
#define Forall_goto_program_instructions(it, program)
Havoc function assigns clauses.
std::set< exprt > assignst
Definition havoc_utils.h:24
std::optional< code_with_contract_typet > get_contract(const irep_idt &function_identifier, const namespacet &ns)
void add_pragma_disable_assigns_check(source_locationt &location)
Adds a pragma on a source_locationt to disable inclusion checking.
Specify write set in function contracts.
const std::string & id2string(const irep_idt &d)
Definition irep.h:44
static int8_t r
Definition irep_hash.h:60
Field-insensitive, location-sensitive may-alias analysis.
natural_loops_mutablet::natural_loopt loopt
Definition loop_utils.h:20
API to expression classes for 'mathematical' expressions.
const lambda_exprt & to_lambda_expr(const exprt &expr)
Cast an exprt to a lambda_exprt.
Predicates to specify memory footprint in function contracts.
natural_loops_templatet< goto_programt, goto_programt::targett, goto_programt::target_less_than > natural_loops_mutablet
static void nondet_static(const namespacet &ns, goto_modelt &goto_model, const irep_idt &fct_name)
Nondeterministically initializes global scope variables in a goto-function.
Nondeterministically initializes global scope variables, except for constants (such as string literal...
void remove_skip(goto_programt &goto_program, goto_programt::targett begin, goto_programt::targett end)
remove unnecessary skip statements
Program Transformation.
#define UNREACHABLE
This should be used to mark dead code.
Definition invariant.h:525
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition invariant.h:534
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
exprt conjunction(exprt a, exprt b)
Conjunction of two expressions.
Definition std_expr.cpp:252
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:211
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition std_types.h:788
Loop unwinding.
Loop unwinding.
void generate_history_variables_initialization(symbol_table_baset &symbol_table, exprt &clause, const irep_idt &mode, goto_programt &program)
This function generates all the instructions required to initialize history variables.
Definition utils.cpp:494
bool is_assignment_to_instrumented_variable(const goto_programt::const_targett &target, std::string var_name)
Return true if target is an assignment to an instrumented variable with name var_name.
Definition utils.cpp:524
void insert_before_and_update_jumps(goto_programt &destination, goto_programt::targett &target, const goto_programt::instructiont &i)
Insert a goto instruction before a target instruction iterator and update targets of all jumps that p...
Definition utils.cpp:247
void infer_loop_assigns(const local_may_aliast &local_may_alias, const loopt &loop, assignst &assigns)
Infer loop assigns using alias analysis result local_may_alias.
Definition utils.cpp:344
bool is_loop_free(const goto_programt &goto_program, const namespacet &ns, messaget &log)
Returns true iff the given program is loop-free, i.e.
Definition utils.cpp:271
exprt get_loop_assigns(const goto_programt::const_targett &loop_end)
Definition utils.cpp:683
replace_history_parametert replace_history_loop_entry(symbol_table_baset &symbol_table, const exprt &expr, const source_locationt &location, const irep_idt &mode)
This function recursively identifies the "loop_entry" expressions within expr and replaces them with ...
Definition utils.cpp:475
bool is_transformed_loop_head(const goto_programt::const_targett &target)
Return true if target is the head of some transformed loop.
Definition utils.cpp:508
void insert_before_swap_and_advance(goto_programt &destination, goto_programt::targett &target, goto_programt &payload)
Insert a goto program before a target instruction iterator and advance the iterator.
Definition utils.cpp:237
void widen_assigns(assignst &assigns, const namespacet &ns)
Widen expressions in assigns with the following strategy.
Definition utils.cpp:360
void simplify_gotos(goto_programt &goto_program, const namespacet &ns)
Turns goto instructions IF cond GOTO label where the condition statically simplifies to false into SK...
Definition utils.cpp:260
exprt get_loop_invariants(const goto_programt::const_targett &loop_end, const bool check_side_effect)
Definition utils.cpp:666
bool is_transformed_loop_end(const goto_programt::const_targett &target)
Return true if target is the end of some transformed loop.
Definition utils.cpp:516
exprt get_loop_decreases(const goto_programt::const_targett &loop_end, const bool check_side_effect)
Definition utils.cpp:688
unsigned get_suffix_unsigned(const std::string &str, const std::string &prefix)
Convert the suffix digits right after prefix of str into unsigned.
Definition utils.cpp:546
exprt generate_lexicographic_less_than_check(const std::vector< symbol_exprt > &lhs, const std::vector< symbol_exprt > &rhs)
Generate a lexicographic less-than comparison over ordered tuples.
Definition utils.cpp:193
#define ENTERED_LOOP
Definition utils.h:24
#define INIT_INVARIANT
Definition utils.h:26
#define IN_LOOP_HAVOC_BLOCK
Definition utils.h:25
dstringt irep_idt