60 exprt decreases_clause,
63 const auto loop_head_location = loop_head->source_location();
66 const auto &decreases_clause_exprs = decreases_clause.
operands();
70 std::vector<symbol_exprt> old_decreases_vars, new_decreases_vars;
133 std::map<exprt, exprt> history_var_map;
139 pre_loop_head_instrs,
144 const auto entered_loop =
148 pre_loop_head_instrs.
add(
150 pre_loop_head_instrs.
add(
155 const auto initial_invariant_val =
159 pre_loop_head_instrs.
add(
166 initial_invariant_val, invariant};
170 initial_invariant_value_assignment, pre_loop_head_instrs, mode);
175 const auto in_base_case =
179 pre_loop_head_instrs.
add(
181 pre_loop_head_instrs.
add(
198 loop_head, loop_end, snapshot_instructions);
203 if(assigns_clause.
is_nil())
217 log.
debug() <<
"No loop assigns clause provided. Inferred targets: {";
219 bool ran_once =
false;
220 for(
const auto &target : to_havoc)
227 target, snapshot_instructions);
232 std::inserter(to_havoc, to_havoc.end()));
236 log.
error() <<
"Failed to infer variables being modified by the loop at "
237 << loop_head_location
238 <<
".\nPlease specify an assigns clause.\nReason:"
247 for(
const auto &target : assigns_clause.
operands())
249 to_havoc.insert(target);
262 pre_loop_head_instrs.
add(
269 const auto step_case_target =
283 .source_location_nonconst()
284 .set_comment(
"Check loop invariant before entry");
291 goto_function.body.destructive_insert(
302 goto_function.body.destructive_insert(loop_head, pre_loop_head_instrs);
315 for(
const auto &clause : decreases_clause.
operands())
317 const auto old_decreases_var =
320 pre_loop_head_instrs.
add(
322 old_decreases_vars.push_back(old_decreases_var);
324 const auto new_decreases_var =
327 pre_loop_head_instrs.
add(
329 new_decreases_vars.push_back(new_decreases_var);
333 if(loop_end->is_goto() && !loop_end->condition().is_true())
335 log.
error() <<
"Loop contracts are unsupported on do/while loops: "
346 if(!decreases_clause.
is_nil())
348 for(
size_t i = 0; i < old_decreases_vars.size(); i++)
351 old_decreases_vars[i], decreases_clause_exprs[i]};
352 old_decreases_assignment.add_source_location() = loop_head_location;
354 old_decreases_assignment, pre_loop_head_instrs, mode);
357 goto_function.body.destructive_insert(
365 goto_function.body.destructive_insert(
380 pre_loop_end_instrs.
add(
386 step_case_target, in_base_case, loop_head_location));
401 .source_location_nonconst()
402 .set_comment(
"Check that loop invariant is preserved");
407 if(!decreases_clause.
is_nil())
409 for(
size_t i = 0; i < new_decreases_vars.size(); i++)
412 new_decreases_vars[i], decreases_clause_exprs[i]};
413 new_decreases_assignment.add_source_location() = loop_head_location;
415 new_decreases_assignment, pre_loop_end_instrs, mode);
422 new_decreases_vars, old_decreases_vars)};
425 monotonic_decreasing_assertion, pre_loop_end_instrs, mode);
427 .source_location_nonconst()
428 .set_comment(
"Check decreases clause on loop iteration");
431 for(
size_t i = 0; i < old_decreases_vars.size(); i++)
433 pre_loop_end_instrs.
add(
435 pre_loop_end_instrs.
add(
446 loop_end->turn_into_assume();
447 loop_end->condition_nonconst() =
boolean_negate(loop_end->condition());
449 std::set<goto_programt::targett> seen_targets;
452 for(
const auto &t : loop)
457 auto exit_target = t->get_target();
459 loop.contains(exit_target) ||
460 seen_targets.find(exit_target) != seen_targets.end())
463 seen_targets.insert(exit_target);
469 loop_head_location));
471 .source_location_nonconst()
472 .set_comment(
"Check that loop instrumentation was not truncated");
474 pre_loop_exit_instrs.
add(
476 pre_loop_exit_instrs.
add(
478 for(
const auto &v : history_var_map)
480 pre_loop_exit_instrs.
add(
485 goto_function.body, exit_target, pre_loop_exit_instrs);
493 if(expression.
id() == ID_not || expression.
id() == ID_typecast)
500 if(expression.
id() == ID_notequal || expression.
id() == ID_implies)
508 if(expression.
id() == ID_if)
517 if(expression.
id() == ID_and || expression.
id() == ID_or)
522 for(
auto &operand : multi_ary_expression.operands())
527 else if(expression.
id() == ID_exists || expression.
id() == ID_forall)
533 std::vector<symbol_exprt> fresh_variables;
534 fresh_variables.reserve(quantifier_expression.variables().size());
535 for(
const auto &quantified_variable : quantifier_expression.variables())
539 quantified_variable.type(),
540 quantified_variable.source_location(),
545 fresh_variables.push_back(new_symbol.
symbol_expr());
549 exprt where = quantifier_expression.instantiate(fresh_variables);
555 quantifier_expression.variables() = fresh_variables;
556 quantifier_expression.where() = std::move(where);
562 std::map<exprt, exprt> ¶meter2history,
571 op, parameter2history, location, mode, history,
id);
574 if(expr.
id() == ID_old || expr.
id() == ID_loop_entry)
578 const auto &
id = parameter.
id();
580 id == ID_dereference ||
id == ID_member ||
id == ID_symbol ||
581 id == ID_ptrmember ||
id == ID_constant ||
id == ID_typecast ||
584 auto it = parameter2history.find(parameter);
586 if(it == parameter2history.end())
590 const auto skip_target =
601 parameter2history[parameter] = tmp_symbol;
623 expr = parameter2history[parameter];
627 log.
error() <<
"Tracking history of " << parameter.id()
634 std::pair<goto_programt, goto_programt>
640 std::map<exprt, exprt> parameter2history;
645 expression, parameter2history, location, mode, history, ID_old);
654 return std::make_pair(std::move(ensures_program), std::move(history));
660 const std::string &function_str =
id2string(
function);
661 const auto &function_symbol = ns.
lookup(
function);
664 if(ns.
lookup(
"contract::" + function_str, contract_sym))
671 if(type != function_symbol.type)
674 "Contract of '" + function_str +
"' has different signature.");
686 const auto &const_target =
690 PRECONDITION(const_target->call_function().id() == ID_symbol);
699 auto assigns_clause = type.assigns();
700 auto requires_contract = type.requires_contract();
701 auto ensures_contract = type.ensures_contract();
711 bool call_ret_is_fresh_var =
false;
716 if(target->call_lhs().is_not_nil())
722 auto &lhs_expr = const_target->call_lhs();
723 call_ret_opt = lhs_expr;
724 instantiation_values.push_back(lhs_expr);
731 type.ensures().begin(), type.ensures().end(), [](
const exprt &e) {
732 return has_symbol_expr(
733 to_lambda_expr(e).where(), CPROVER_PREFIX
"return_value", true);
738 call_ret_is_fresh_var =
true;
742 "ignored_return_value",
743 const_target->source_location(),
748 call_ret_opt = fresh_sym_expr;
749 instantiation_values.push_back(fresh_sym_expr);
754 instantiation_values.push_back(
761 const auto &arguments = const_target->call_arguments();
762 instantiation_values.insert(
763 instantiation_values.end(), arguments.begin(), arguments.end());
765 const auto &mode = function_symbol.
mode;
776 for(
const auto &
r : type.requires())
778 requires_conjuncts.push_back(
782 requires.add_source_location() =
783 requires_conjuncts.empty() ? type.source_location()
784 : type.requires().front().source_location();
785 if(!requires.is_true())
792 assertion.
instructions.back().source_location_nonconst() =
793 requires.source_location();
794 assertion.
instructions.back().source_location_nonconst().set_comment(
795 "Check requires clause");
796 assertion.
instructions.back().source_location_nonconst().set_property_class(
803 for(
auto &expr : requires_contract)
816 for(
const auto &
r : type.ensures())
818 ensures_conjuncts.push_back(
822 ensures.add_source_location() = ensures_conjuncts.empty()
823 ? type.source_location()
824 : type.ensures().front().source_location();
825 std::pair<goto_programt, goto_programt> ensures_pair;
826 if(!ensures.is_false())
842 for(
auto &target : assigns_clause)
843 targets.push_back(
to_lambda_expr(target).application(instantiation_values));
859 havocker.get_instructions(havoc_instructions);
862 if(call_ret_opt.has_value())
864 auto &call_ret = call_ret_opt.value();
865 auto &loc = call_ret.source_location();
866 auto &type = call_ret.type();
869 if(call_ret_is_fresh_var)
870 havoc_instructions.
add(
874 auto target = havoc_instructions.
add(
876 target->code_nonconst().add_source_location() = loc;
884 if(!ensures.is_false())
891 for(
auto &expr : ensures_contract)
901 if(call_ret_is_fresh_var)
905 target->output(mstream);
906 mstream << messaget::eom;
933 const bool may_have_loops = std::any_of(
934 goto_function.body.instructions.begin(),
935 goto_function.body.instructions.end(),
937 return instruction.is_backwards_goto();
949 "Recursive functions found during inlining");
967 struct loop_graph_nodet :
public graph_nodet<empty_edget>
971 exprt assigns_clause, invariant, decreases_clause;
977 const exprt &assigns,
979 const exprt &decreases)
983 assigns_clause(assigns),
985 decreases_clause(decreases)
991 std::list<size_t> to_check_contracts_on_children;
993 for(
const auto &loop_head_and_content : natural_loops.
loop_map)
995 const auto &loop_content = loop_head_and_content.second;
996 if(loop_content.empty())
999 auto loop_head = loop_head_and_content.first;
1000 auto loop_end = loop_head;
1003 for(
const auto &t : loop_content)
1006 t->is_goto() && t->get_target() == loop_head &&
1007 t->location_number > loop_end->location_number)
1011 if(loop_end == loop_head)
1013 log.
error() <<
"Could not find end of the loop starting at: "
1018 exprt assigns_clause =
1019 static_cast<const exprt &
>(loop_end->condition().find(ID_C_spec_assigns));
1020 exprt invariant =
static_cast<const exprt &
>(
1021 loop_end->condition().find(ID_C_spec_loop_invariant));
1022 exprt decreases_clause =
static_cast<const exprt &
>(
1023 loop_end->condition().find(ID_C_spec_decreases));
1033 <<
" does not have an invariant in its contract.\n"
1034 <<
"Hence, a default invariant ('true') is being used.\n"
1035 <<
"This choice is sound, but verification may fail"
1036 <<
" if it is be too weak to prove the desired properties."
1043 if(decreases_clause.
is_nil())
1047 <<
" does not have a decreases clause in its contract.\n"
1048 <<
"Termination of this loop will not be verified."
1053 const auto idx = loop_nesting_graph.
add_node(
1063 decreases_clause.
is_nil())
1066 to_check_contracts_on_children.push_back(idx);
1076 for(
const auto &i : loop_content)
1078 if(std::distance(loop_head, i) < 0 || std::distance(i, loop_end) < 0)
1082 mstream <<
"Computed loop at " << loop_head->source_location()
1083 <<
"contains an instruction beyond [loop_head, loop_end]:"
1086 mstream << messaget::eom;
1093 for(
size_t outer = 0; outer < loop_nesting_graph.
size(); ++outer)
1095 for(
size_t inner = 0; inner < loop_nesting_graph.
size(); ++inner)
1100 if(loop_nesting_graph[outer].content.contains(
1101 loop_nesting_graph[inner].head_target))
1103 if(!loop_nesting_graph[outer].content.contains(
1104 loop_nesting_graph[inner].end_target))
1107 <<
"Overlapping loops at:\n"
1108 << loop_nesting_graph[outer].head_target->source_location()
1110 << loop_nesting_graph[inner].head_target->source_location()
1111 <<
"\nLoops must be nested or sequential for contracts to be "
1115 loop_nesting_graph.
add_edge(inner, outer);
1121 while(!to_check_contracts_on_children.empty())
1123 const auto loop_idx = to_check_contracts_on_children.front();
1124 to_check_contracts_on_children.pop_front();
1126 const auto &loop_node = loop_nesting_graph[loop_idx];
1128 loop_node.assigns_clause.is_nil() && loop_node.invariant.is_nil() &&
1129 loop_node.decreases_clause.is_nil())
1133 <<
" does not have contracts, but an enclosing loop does.\n"
1134 <<
"Please provide contracts for this loop, or unwind it first."
1140 to_check_contracts_on_children.push_back(child_idx);
1145 for(
const auto &idx : loop_nesting_graph.
topsort())
1147 const auto &loop_node = loop_nesting_graph[idx];
1149 loop_node.assigns_clause.is_nil() && loop_node.invariant.is_nil() &&
1150 loop_node.decreases_clause.is_nil())
1166 loop_node.head_target,
1167 loop_node.end_target,
1168 updated_loops.
loop_map[loop_node.head_target],
1169 loop_node.assigns_clause,
1170 loop_node.invariant,
1171 loop_node.decreases_clause,
1193 "Function '" +
id2string(
function) +
"'must exist in the goto program");
1195 const auto &goto_function = function_obj->second;
1196 auto &function_body = function_obj->second.body;
1228 "Loops remain in function '" +
id2string(
function) +
1229 "', assigns clause checking instrumentation cannot be applied.");
1232 auto instruction_it = function_body.instructions.begin();
1236 function_body, instruction_it, snapshot_static_locals);
1245 instantiation_values.push_back(
exprt{ID_nil, function_type.
return_type()});
1246 for(
const auto ¶m : function_type.
parameters())
1248 instantiation_values.push_back(
1249 ns.
lookup(param.get_identifier()).symbol_expr());
1255 to_lambda_expr(target).application(instantiation_values), payload);
1261 for(
const auto ¶m : function_type.
parameters())
1265 ns.
lookup(param.get_identifier()).symbol_expr(), payload);
1274 function_body, instruction_it, function_body.instructions.end());
1284 std::stringstream ss;
1292 "Function to replace must exist in the program.");
1300 mangled_sym = *original_sym;
1301 mangled_sym.
name = mangled;
1306 mangled_found.second,
1307 "There should be no existing function called " + ss.str() +
1308 " in the symbol table because that name is a mangled name");
1314 "There should be no function called " +
id2string(
function) +
1315 " in the function map because that function should have had its"
1321 "There should be a function called " + ss.str() +
1322 " in the function map because we inserted a fresh goto-program"
1323 " with that mangled name");
1340 comment <<
"Assert function pointer '"
1342 <<
"' obeys contract '"
1360 comment <<
"Assume function pointer '"
1362 <<
"' obeys contract '"
1377 auto assigns = code_type.assigns();
1378 auto requires_contract = code_type.requires_contract();
1379 auto ensures_contract = code_type.ensures_contract();
1407 code_type.return_type(),
1409 function_symbol.
mode,
1417 instantiation_values.push_back(
r);
1421 goto_functionst::function_mapt::iterator f_it =
1426 for(
const auto ¶meter : gf.parameter_identifiers)
1431 parameter_symbol.
type,
1433 parameter_symbol.
mode,
1438 p, parameter_symbol.
symbol_expr(), source_location));
1442 instantiation_values.push_back(p);
1450 for(
const auto &
r : code_type.requires())
1452 requires_conjuncts.push_back(
1456 requires.add_source_location() =
1457 requires_conjuncts.empty() ? code_type.source_location()
1458 : code_type.requires().front().source_location();
1459 if(!requires.is_false())
1472 std::pair<goto_programt, goto_programt> ensures_pair;
1476 for(
const auto &
r : code_type.ensures())
1478 ensures_conjuncts.push_back(
1482 ensures.add_source_location() =
1483 ensures_conjuncts.empty() ? code_type.source_location()
1484 : code_type.ensures().front().source_location();
1485 if(!ensures.is_true())
1492 assertion.add_source_location() = ensures.source_location();
1494 assertion, ensures.source_location(), function_symbol.
mode);
1495 ensures_pair.first.instructions.back()
1496 .source_location_nonconst()
1497 .set_comment(
"Check ensures clause");
1498 ensures_pair.first.instructions.back()
1499 .source_location_nonconst()
1500 .set_property_class(ID_postcondition);
1508 for(
auto &expr : requires_contract)
1513 function_symbol.
mode,
1521 if(ensures.is_not_nil())
1527 for(
auto &expr : ensures_contract)
1533 function_symbol.
mode,
1539 return_stmt.value().return_value(), source_location));
1553 const std::set<std::string> &functions)
const
1555 for(
const auto &
function : functions)
1562 "Function '" +
function +
"' was not found in the GOTO program.");
1569 if(to_replace.empty())
1580 if(ins->is_function_call())
1582 if(ins->call_function().id() != ID_symbol)
1587 auto found = std::find(
1588 to_replace.begin(), to_replace.end(),
id2string(called_function));
1589 if(found == to_replace.end())
1593 goto_function.first,
1594 ins->source_location(),
1595 goto_function.second.body,
1608 const std::set<std::string> &to_exclude_from_nondet_init)
1613 log.
status() <<
"Adding nondeterministic initialization "
1614 "of static/global variables."
1620 const std::set<std::string> &to_enforce,
1621 const std::set<std::string> &to_exclude_from_nondet_init)
1623 if(to_enforce.empty())
1630 for(
const auto &
function : to_enforce)
1633 log.
status() <<
"Adding nondeterministic initialization "
1634 "of static/global variables."