Skip to content

Commit 9d41110

Browse files
committed
CONTRACTS: Generator for contracts constraints
Creates common function to generate all contract constraint, i.e., assumptions and assertions based on requires and ensures clauses. Signed-off-by: Felipe R. Monteiro <[email protected]>
1 parent 7c1f67a commit 9d41110

File tree

2 files changed

+135
-93
lines changed

2 files changed

+135
-93
lines changed

src/goto-instrument/contracts/contracts.cpp

Lines changed: 129 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,39 @@ code_contractst::create_ensures_instruction(
654654
return std::make_pair(std::move(ensures_program), std::move(history));
655655
}
656656

657+
/// This function generates instructions for all contract constraint, i.e.,
658+
/// assumptions and assertions based on requires and ensures clauses.
659+
static void generate_contract_constraints(
660+
code_contractst &contract,
661+
exprt &instantiated_clause,
662+
const irep_idt &mode,
663+
const std::function<void(goto_programt &)> &is_fresh_update,
664+
goto_programt &program,
665+
const source_locationt &location)
666+
{
667+
if(
668+
has_subexpr(instantiated_clause, ID_exists) ||
669+
has_subexpr(instantiated_clause, ID_forall))
670+
{
671+
contract.add_quantified_variable(instantiated_clause, mode);
672+
}
673+
674+
goto_programt constraint;
675+
if(location.get_property_class() == ID_assume)
676+
{
677+
contract.get_converter().goto_convert(
678+
code_assumet(instantiated_clause), constraint, mode);
679+
}
680+
else
681+
{
682+
contract.get_converter().goto_convert(
683+
code_assertt(instantiated_clause), constraint, mode);
684+
}
685+
constraint.instructions.back().source_location_nonconst() = location;
686+
is_fresh_update(constraint);
687+
program.destructive_append(constraint);
688+
}
689+
657690
static const code_with_contract_typet &
658691
get_contract(const irep_idt &function, const namespacet &ns)
659692
{
@@ -768,36 +801,27 @@ void code_contractst::apply_function_contract(
768801
is_fresh.create_declarations();
769802
is_fresh.add_memory_map_decl(new_program);
770803

771-
// Insert assertion of the precondition immediately before the call site.
804+
// Generate: assert(requires)
772805
for(const auto &clause : type.requires())
773806
{
774807
auto instantiated_clause =
775808
to_lambda_expr(clause).application(instantiation_values);
776-
if(!instantiated_clause.is_true())
777-
{
778-
if(
779-
has_subexpr(instantiated_clause, ID_exists) ||
780-
has_subexpr(instantiated_clause, ID_forall))
781-
add_quantified_variable(instantiated_clause, mode);
782-
783-
goto_programt assertion;
784-
converter.goto_convert(
785-
code_assertt(instantiated_clause), assertion, mode);
786-
assertion.instructions.back().source_location_nonconst() =
787-
clause.source_location();
788-
assertion.instructions.back().source_location_nonconst().set_line(
789-
location.get_line());
790-
assertion.instructions.back().source_location_nonconst().set_comment(
791-
std::string("Check ")
792-
.append(target_function.c_str())
793-
.append("'s requires clause in ")
794-
.append(function.c_str()));
795-
assertion.instructions.back()
796-
.source_location_nonconst()
797-
.set_property_class(ID_precondition);
798-
is_fresh.update_requires(assertion);
799-
new_program.destructive_append(assertion);
800-
}
809+
source_locationt _location = clause.source_location();
810+
_location.set_line(location.get_line());
811+
_location.set_comment(std::string("Check requires clause of ")
812+
.append(target_function.c_str())
813+
.append(" in ")
814+
.append(function.c_str()));
815+
_location.set_property_class(ID_precondition);
816+
generate_contract_constraints(
817+
*this,
818+
instantiated_clause,
819+
mode,
820+
[&is_fresh](goto_programt &requires) {
821+
is_fresh.update_requires(requires);
822+
},
823+
new_program,
824+
_location);
801825
}
802826

803827
// Translate requires_contract(ptr, contract) clauses to assertions
@@ -812,26 +836,25 @@ void code_contractst::apply_function_contract(
812836
}
813837

814838
// Gather all the instructions required to handle history variables
815-
// as well as the ensures clause
816-
std::vector<std::pair<goto_programt, goto_programt>> ensures_clauses;
817-
for(const auto &clause : type.ensures())
839+
std::vector<exprt> instantiated_ensures_clauses;
840+
for(auto clause : type.ensures())
818841
{
819842
auto instantiated_clause =
820843
to_lambda_expr(clause).application(instantiation_values);
821-
if(!instantiated_clause.is_false())
822-
{
823-
if(
824-
has_subexpr(instantiated_clause, ID_exists) ||
825-
has_subexpr(instantiated_clause, ID_forall))
826-
add_quantified_variable(instantiated_clause, mode);
827-
828-
auto assumption = code_assumet(instantiated_clause);
829-
ensures_clauses.push_back(
830-
create_ensures_instruction(assumption, clause.source_location(), mode));
831-
832-
// add all the history variable initialization instructions
833-
new_program.destructive_append(ensures_clauses.back().second);
834-
}
844+
instantiated_clause.add_source_location() = clause.source_location();
845+
std::map<exprt, exprt> parameter2history;
846+
goto_programt history;
847+
// Find and replace "old" expression in the "expression" variable
848+
replace_history_parameter(
849+
instantiated_clause,
850+
parameter2history,
851+
clause.source_location(),
852+
mode,
853+
history,
854+
ID_old);
855+
instantiated_ensures_clauses.push_back(instantiated_clause);
856+
// Add all the history variable initialization instructions
857+
new_program.destructive_append(history);
835858
}
836859

837860
// ASSIGNS clause should not refer to any quantified variables,
@@ -877,12 +900,26 @@ void code_contractst::apply_function_contract(
877900
// Insert havoc instructions immediately before the call site.
878901
new_program.destructive_append(havoc_instructions);
879902

880-
// To remove the function call, insert statements related to the assumption.
881-
// Then, replace the function call with a SKIP statement.
882-
for(auto &clause : ensures_clauses)
903+
// Generate: assume(ensures)
904+
for(auto &clause : instantiated_ensures_clauses)
883905
{
884-
is_fresh.update_ensures(clause.first);
885-
new_program.destructive_append(clause.first);
906+
if(clause.is_false())
907+
{
908+
throw invalid_input_exceptiont(
909+
std::string("Attempt to assume false at ")
910+
.append(clause.source_location().as_string())
911+
.append(".\nPlease update ensures clause to avoid vacuity."));
912+
}
913+
source_locationt _location = clause.source_location();
914+
_location.set_comment("Assume ensures clause");
915+
_location.set_property_class(ID_assume);
916+
generate_contract_constraints(
917+
*this,
918+
clause,
919+
mode,
920+
[&is_fresh](goto_programt &ensures) { is_fresh.update_ensures(ensures); },
921+
new_program,
922+
_location);
886923
}
887924

888925
// Translate ensures_contract(ptr, contract) clauses to assumptions
@@ -1446,57 +1483,47 @@ void code_contractst::add_contract_check(
14461483
{
14471484
auto instantiated_clause =
14481485
to_lambda_expr(clause).application(instantiation_values);
1449-
1450-
if(!instantiated_clause.is_false())
1486+
if(instantiated_clause.is_false())
14511487
{
1452-
if(
1453-
has_subexpr(instantiated_clause, ID_exists) ||
1454-
has_subexpr(instantiated_clause, ID_forall))
1455-
add_quantified_variable(instantiated_clause, function_symbol.mode);
1456-
1457-
goto_programt assumption;
1458-
converter.goto_convert(
1459-
code_assumet(instantiated_clause), assumption, function_symbol.mode);
1460-
1461-
visitor.update_requires(assumption);
1462-
check.destructive_append(assumption);
1488+
throw invalid_input_exceptiont(
1489+
std::string("Attempt to assume false at ")
1490+
.append(clause.source_location().as_string())
1491+
.append(".\nPlease update requires clause to avoid vacuity."));
14631492
}
1493+
source_locationt _location = clause.source_location();
1494+
_location.set_comment("Assume requires clause");
1495+
_location.set_property_class(ID_assume);
1496+
generate_contract_constraints(
1497+
*this,
1498+
instantiated_clause,
1499+
function_symbol.mode,
1500+
[&visitor](goto_programt &requires) {
1501+
visitor.update_requires(requires);
1502+
},
1503+
check,
1504+
_location);
14641505
}
14651506

14661507
// Gather all the instructions required to handle history variables
1467-
// as well as the ensures clause
1468-
std::vector<std::pair<goto_programt, goto_programt>> ensures_clauses;
1469-
for(const auto &clause : code_type.ensures())
1508+
std::vector<exprt> instantiated_ensures_clauses;
1509+
for(auto clause : code_type.ensures())
14701510
{
1471-
// Generate: copies for history variables
14721511
auto instantiated_clause =
14731512
to_lambda_expr(clause).application(instantiation_values);
1474-
1475-
if(!instantiated_clause.is_true())
1476-
{
1477-
if(
1478-
has_subexpr(instantiated_clause, ID_exists) ||
1479-
has_subexpr(instantiated_clause, ID_forall))
1480-
add_quantified_variable(instantiated_clause, function_symbol.mode);
1481-
1482-
// get all the relevant instructions related to history variables
1483-
auto assertion = code_assertt(instantiated_clause);
1484-
assertion.add_source_location() = clause.source_location();
1485-
ensures_clauses.push_back(create_ensures_instruction(
1486-
assertion, clause.source_location(), function_symbol.mode));
1487-
ensures_clauses.back()
1488-
.first.instructions.back()
1489-
.source_location_nonconst()
1490-
.set_comment("Check ensures clause");
1491-
ensures_clauses.back()
1492-
.first.instructions.back()
1493-
.source_location_nonconst()
1494-
.set_property_class(ID_postcondition);
1495-
1496-
// add all the history variable initializations
1497-
visitor.update_ensures(ensures_clauses.back().first);
1498-
check.destructive_append(ensures_clauses.back().second);
1499-
}
1513+
instantiated_clause.add_source_location() = clause.source_location();
1514+
std::map<exprt, exprt> parameter2history;
1515+
goto_programt history;
1516+
// Find and replace "old" expression in the "expression" variable
1517+
replace_history_parameter(
1518+
instantiated_clause,
1519+
parameter2history,
1520+
clause.source_location(),
1521+
function_symbol.mode,
1522+
history,
1523+
ID_old);
1524+
instantiated_ensures_clauses.push_back(instantiated_clause);
1525+
// Add all the history variable initialization instructions
1526+
check.destructive_append(history);
15001527
}
15011528

15021529
// Translate requires_contract(ptr, contract) clauses to assumptions
@@ -1513,9 +1540,18 @@ void code_contractst::add_contract_check(
15131540
check.add(goto_programt::make_function_call(call, source_location));
15141541

15151542
// Generate: assert(ensures)
1516-
for(auto &pair : ensures_clauses)
1543+
for(auto &clause : instantiated_ensures_clauses)
15171544
{
1518-
check.destructive_append(pair.first);
1545+
source_locationt _location = clause.source_location();
1546+
_location.set_comment("Check ensures clause");
1547+
_location.set_property_class(ID_postcondition);
1548+
generate_contract_constraints(
1549+
*this,
1550+
clause,
1551+
function_symbol.mode,
1552+
[&visitor](goto_programt &ensures) { visitor.update_ensures(ensures); },
1553+
check,
1554+
_location);
15191555
}
15201556

15211557
// Translate ensures_contract(ptr, contract) clauses to assertions

src/goto-instrument/contracts/contracts.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,12 @@ class code_contractst
219219
codet &expression,
220220
source_locationt location,
221221
const irep_idt &mode);
222+
223+
// for "auxiliary" functions generate contract constrainst
224+
goto_convertt &get_converter()
225+
{
226+
return converter;
227+
}
222228
};
223229

224230
#endif // CPROVER_GOTO_INSTRUMENT_CONTRACTS_CONTRACTS_H

0 commit comments

Comments
 (0)