Skip to content

Commit adbef76

Browse files
committed
WIP: use lambda for code contracts
1 parent 92da421 commit adbef76

File tree

3 files changed

+74
-88
lines changed

3 files changed

+74
-88
lines changed

src/ansi-c/c_typecheck_base.cpp

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Author: Daniel Kroening, [email protected]
1414
#include <util/c_types.h>
1515
#include <util/config.h>
1616
#include <util/expr_util.h>
17+
#include <util/mathematical_expr.h>
1718
#include <util/std_types.h>
1819

1920
#include "ansi_c_declaration.h"
@@ -743,56 +744,60 @@ void c_typecheck_baset::typecheck_declaration(
743744
code_with_contract_typet code_type =
744745
to_code_with_contract_type(new_symbol.type);
745746

746-
// ensure parameter declarations are in the symbol table
747-
std::vector<irep_idt> temporary_parameter_symbols;
747+
// ensure parameter declarations are available for type checking to
748+
// succeed
749+
binding_exprt::variablest temporary_parameter_symbols;
750+
751+
const auto &return_type = code_type.return_type();
752+
if(return_type.id() != ID_empty)
753+
{
754+
parameter_map[CPROVER_PREFIX "return_value"] = return_type;
755+
temporary_parameter_symbols.emplace_back(CPROVER_PREFIX "return_value", return_type);
756+
}
757+
758+
std::size_t anon_counter = 0;
748759
for(auto &p : code_type.parameters())
749760
{
761+
// may be anonymous
750762
if(p.get_base_name().empty())
751-
continue;
763+
p.set_base_name("#anon" + std::to_string(anon_counter++));
752764

753765
// produce identifier
754-
irep_idt base_name = p.get_base_name();
766+
const irep_idt &base_name = p.get_base_name();
755767
irep_idt parameter_identifier =
756768
id2string(identifier) + "::" + id2string(base_name);
757769

758770
p.set_identifier(parameter_identifier);
759771

760-
if(
761-
symbol_table.symbols.find(parameter_identifier) ==
762-
symbol_table.symbols.end())
763-
{
764-
parameter_symbolt p_symbol;
765-
766-
p_symbol.type = p.type();
767-
p_symbol.name = parameter_identifier;
768-
p_symbol.base_name = base_name;
769-
p_symbol.location = p.source_location();
770-
771-
symbolt *new_p_symbol;
772-
move_symbol(p_symbol, new_p_symbol);
773-
774-
temporary_parameter_symbols.push_back(parameter_identifier);
775-
}
772+
PRECONDITION(
773+
parameter_map.find(parameter_identifier) == parameter_map.end());
774+
parameter_map[parameter_identifier] = p.type();
775+
temporary_parameter_symbols.emplace_back(parameter_identifier, p.type());
776776
}
777777

778778
for(auto &expr : code_type.requires_contract())
779779
{
780780
typecheck_spec_function_pointer_obeys_contract(expr);
781781
check_history_expr(expr);
782+
lambda_exprt lambda{temporary_parameter_symbols, expr};
783+
expr.swap(lambda);
782784
}
783785

784786
for(auto &requires : code_type.requires())
785787
{
786788
typecheck_expr(requires);
787789
implicit_typecast_bool(requires);
788790
check_history_expr(requires);
791+
lambda_exprt lambda{temporary_parameter_symbols, requires};
792+
requires.swap(lambda);
789793
}
790794

791795
typecheck_spec_assigns(code_type.assigns());
792-
793-
const auto &return_type = code_type.return_type();
794-
if(return_type.id() != ID_empty)
795-
parameter_map[CPROVER_PREFIX "return_value"] = return_type;
796+
for(auto &assigns : code_type.assigns())
797+
{
798+
lambda_exprt lambda{temporary_parameter_symbols, assigns};
799+
assigns.swap(lambda);
800+
}
796801

797802
for(auto &expr : code_type.ensures_contract())
798803
{
@@ -801,6 +806,8 @@ void c_typecheck_baset::typecheck_declaration(
801806
expr,
802807
ID_loop_entry,
803808
CPROVER_PREFIX "loop_entry is not allowed in postconditions.");
809+
lambda_exprt lambda{temporary_parameter_symbols, expr};
810+
expr.swap(lambda);
804811
}
805812

806813
for(auto &ensures : code_type.ensures())
@@ -811,10 +818,12 @@ void c_typecheck_baset::typecheck_declaration(
811818
ensures,
812819
ID_loop_entry,
813820
CPROVER_PREFIX "loop_entry is not allowed in postconditions.");
821+
lambda_exprt lambda{temporary_parameter_symbols, ensures};
822+
ensures.swap(lambda);
814823
}
815824

816-
if(return_type.id() != ID_empty)
817-
parameter_map.erase(CPROVER_PREFIX "return_value");
825+
for(const auto &parameter_sym : temporary_parameter_symbols)
826+
parameter_map.erase(parameter_sym.get_identifier());
818827

819828
// create a contract symbol
820829
symbolt contract;
@@ -844,11 +853,6 @@ void c_typecheck_baset::typecheck_declaration(
844853
new_symbol.type.remove(ID_C_spec_ensures_contract);
845854
new_symbol.type.remove(ID_C_spec_requires);
846855
new_symbol.type.remove(ID_C_spec_requires_contract);
847-
848-
// remove parameter declarations if those were only added for type
849-
// checking the contract
850-
for(const auto &parameter_id : temporary_parameter_symbols)
851-
symbol_table.remove(parameter_id);
852856
}
853857
}
854858
}

src/goto-instrument/contracts/contracts.cpp

Lines changed: 39 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ Date: February 2016
2525
#include <util/message.h>
2626
#include <util/pointer_offset_size.h>
2727
#include <util/pointer_predicates.h>
28-
#include <util/replace_symbol.h>
2928
#include <util/std_code.h>
3029

3130
#include <goto-programs/goto_inline.h>
@@ -777,15 +776,12 @@ void code_contractst::apply_function_contract(
777776
// Isolate each component of the contract.
778777
const auto &type = get_contract(target_function, ns);
779778
auto assigns_clause = type.assigns();
780-
auto requires = conjunction(type.requires());
781-
auto ensures = conjunction(type.ensures());
782779
auto requires_contract = type.requires_contract();
783780
auto ensures_contract = type.ensures_contract();
784781

785-
// Create a replace_symbolt object, for replacing expressions in the callee
782+
// Prepare to instantiate expressions in the callee
786783
// with expressions from the call site (e.g. the return value).
787-
// This object tracks replacements that are common to ENSURES and REQUIRES.
788-
replace_symbolt common_replace;
784+
exprt::operandst instantiation_values;
789785

790786
// keep track of the call's return expression to make it nondet later
791787
optionalt<exprt> call_ret_opt = {};
@@ -804,8 +800,7 @@ void code_contractst::apply_function_contract(
804800
// x = foo() -> assume(__CPROVER_return_value > 5) -> assume(x > 5)
805801
auto &lhs_expr = const_target->call_lhs();
806802
call_ret_opt = lhs_expr;
807-
symbol_exprt ret_val(CPROVER_PREFIX "return_value", lhs_expr.type());
808-
common_replace.insert(ret_val, lhs_expr);
803+
instantiation_values.push_back(lhs_expr);
809804
}
810805
else
811806
{
@@ -827,28 +822,16 @@ void code_contractst::apply_function_contract(
827822
symbol_table.lookup_ref(target_function).mode,
828823
ns,
829824
symbol_table);
830-
symbol_exprt ret_val(
831-
CPROVER_PREFIX "return_value", function_type.return_type());
832825
auto fresh_sym_expr = fresh.symbol_expr();
833-
common_replace.insert(ret_val, fresh_sym_expr);
834826
call_ret_opt = fresh_sym_expr;
827+
instantiation_values.push_back(fresh_sym_expr);
835828
}
836829
}
837830
}
838831

839832
// Replace formal parameters
840833
const auto &arguments = const_target->call_arguments();
841-
auto a_it = arguments.begin();
842-
for(auto p_it = type.parameters().begin();
843-
p_it != type.parameters().end() && a_it != arguments.end();
844-
++p_it, ++a_it)
845-
{
846-
if(!p_it->get_identifier().empty())
847-
{
848-
symbol_exprt p(p_it->get_identifier(), p_it->type());
849-
common_replace.insert(p, *a_it);
850-
}
851-
}
834+
instantiation_values.insert(instantiation_values.end(), arguments.begin(), arguments.end());
852835

853836
const auto &mode = function_symbol.mode;
854837

@@ -860,11 +843,16 @@ void code_contractst::apply_function_contract(
860843
is_fresh.add_memory_map_decl(new_program);
861844

862845
// Insert assertion of the precondition immediately before the call site.
846+
exprt::operandst requires_conjuncts;
847+
for(const auto &r : type.requires())
848+
{
849+
requires_conjuncts.push_back(to_lambda_expr(r).application(instantiation_values));
850+
}
851+
auto requires = conjunction(requires_conjuncts);
863852
if(!requires.is_true())
864853
{
865854
if(has_subexpr(requires, ID_exists) || has_subexpr(requires, ID_forall))
866855
add_quantified_variable(requires, mode);
867-
common_replace(requires);
868856

869857
goto_programt assertion;
870858
converter.goto_convert(code_assertt(requires), assertion, mode);
@@ -882,21 +870,25 @@ void code_contractst::apply_function_contract(
882870
for(auto &expr : requires_contract)
883871
{
884872
assert_function_pointer_obeys_contract(
885-
to_function_pointer_obeys_contract_expr(expr),
873+
to_function_pointer_obeys_contract_expr(to_lambda_expr(expr).application(instantiation_values)),
886874
ID_precondition,
887-
common_replace,
888875
mode,
889876
new_program);
890877
}
891878

892879
// Gather all the instructions required to handle history variables
893880
// as well as the ensures clause
881+
exprt::operandst ensures_conjuncts;
882+
for(const auto &r : type.ensures())
883+
{
884+
ensures_conjuncts.push_back(to_lambda_expr(r).application(instantiation_values));
885+
}
886+
auto ensures = conjunction(ensures_conjuncts);
894887
std::pair<goto_programt, goto_programt> ensures_pair;
895888
if(!ensures.is_false())
896889
{
897890
if(has_subexpr(ensures, ID_exists) || has_subexpr(ensures, ID_forall))
898891
add_quantified_variable(ensures, mode);
899-
common_replace(ensures);
900892

901893
auto assumption = code_assumet(ensures);
902894
ensures_pair =
@@ -961,8 +953,7 @@ void code_contractst::apply_function_contract(
961953
for(auto &expr : ensures_contract)
962954
{
963955
assume_function_pointer_obeys_contract(
964-
to_function_pointer_obeys_contract_expr(expr),
965-
common_replace,
956+
to_function_pointer_obeys_contract_expr(to_lambda_expr(expr).application(instantiation_values)),
966957
mode,
967958
new_program);
968959
}
@@ -1399,7 +1390,6 @@ void code_contractst::enforce_contract(const irep_idt &function)
13991390
void code_contractst::assert_function_pointer_obeys_contract(
14001391
const function_pointer_obeys_contract_exprt &expr,
14011392
const irep_idt &property_class,
1402-
const replace_symbolt &replace,
14031393
const irep_idt &mode,
14041394
goto_programt &dest)
14051395
{
@@ -1411,9 +1401,7 @@ void code_contractst::assert_function_pointer_obeys_contract(
14111401
<< "' obeys contract '"
14121402
<< from_expr_using_mode(ns, mode, expr.contract()) << "'";
14131403
loc.set_comment(comment.str());
1414-
exprt function_pointer(expr.function_pointer());
1415-
replace(function_pointer);
1416-
code_assertt assert_expr(equal_exprt{function_pointer, expr.contract()});
1404+
code_assertt assert_expr(equal_exprt{expr.function_pointer(), expr.contract()});
14171405
assert_expr.add_source_location() = loc;
14181406
goto_programt instructions;
14191407
converter.goto_convert(assert_expr, instructions, mode);
@@ -1422,7 +1410,6 @@ void code_contractst::assert_function_pointer_obeys_contract(
14221410

14231411
void code_contractst::assume_function_pointer_obeys_contract(
14241412
const function_pointer_obeys_contract_exprt &expr,
1425-
const replace_symbolt &replace,
14261413
const irep_idt &mode,
14271414
goto_programt &dest)
14281415
{
@@ -1433,10 +1420,8 @@ void code_contractst::assume_function_pointer_obeys_contract(
14331420
<< "' obeys contract '"
14341421
<< from_expr_using_mode(ns, mode, expr.contract()) << "'";
14351422
loc.set_comment(comment.str());
1436-
exprt function_pointer(expr.function_pointer());
1437-
replace(function_pointer);
14381423
dest.add(
1439-
goto_programt::make_assignment(function_pointer, expr.contract(), loc));
1424+
goto_programt::make_assignment(expr.function_pointer(), expr.contract(), loc));
14401425
}
14411426

14421427
void code_contractst::add_contract_check(
@@ -1448,8 +1433,6 @@ void code_contractst::add_contract_check(
14481433

14491434
const auto &code_type = get_contract(wrapper_function, ns);
14501435
auto assigns = code_type.assigns();
1451-
auto requires = conjunction(code_type.requires());
1452-
auto ensures = conjunction(code_type.ensures());
14531436
auto requires_contract = code_type.requires_contract();
14541437
auto ensures_contract = code_type.ensures_contract();
14551438
// build:
@@ -1473,10 +1456,9 @@ void code_contractst::add_contract_check(
14731456
const symbolt &function_symbol = ns.lookup(mangled_function);
14741457
code_function_callt call(function_symbol.symbol_expr());
14751458

1476-
// Create a replace_symbolt object, for replacing expressions in the callee
1459+
// Prepare to instantiate expressions in the callee
14771460
// with expressions from the call site (e.g. the return value).
1478-
// This object tracks replacements that are common to ENSURES and REQUIRES.
1479-
replace_symbolt common_replace;
1461+
exprt::operandst instantiation_values;
14801462

14811463
// decl ret
14821464
optionalt<code_returnt> return_stmt;
@@ -1493,8 +1475,7 @@ void code_contractst::add_contract_check(
14931475
call.lhs() = r;
14941476
return_stmt = code_returnt(r);
14951477

1496-
symbol_exprt ret_val(CPROVER_PREFIX "return_value", call.lhs().type());
1497-
common_replace.insert(ret_val, r);
1478+
instantiation_values.push_back(r);
14981479
}
14991480

15001481
// decl parameter1 ...
@@ -1519,18 +1500,23 @@ void code_contractst::add_contract_check(
15191500

15201501
call.arguments().push_back(p);
15211502

1522-
common_replace.insert(parameter_symbol.symbol_expr(), p);
1503+
instantiation_values.push_back(p);
15231504
}
15241505

15251506
is_fresh_enforcet visitor(*this, log, wrapper_function);
15261507
visitor.create_declarations();
15271508
visitor.add_memory_map_decl(check);
15281509
// Generate: assume(requires)
1510+
exprt::operandst requires_conjuncts;
1511+
for(const auto &r : code_type.requires())
1512+
{
1513+
requires_conjuncts.push_back(to_lambda_expr(r).application(instantiation_values));
1514+
}
1515+
auto requires = conjunction(requires_conjuncts);
15291516
if(!requires.is_false())
15301517
{
15311518
if(has_subexpr(requires, ID_exists) || has_subexpr(requires, ID_forall))
15321519
add_quantified_variable(requires, function_symbol.mode);
1533-
common_replace(requires);
15341520

15351521
goto_programt assumption;
15361522
converter.goto_convert(
@@ -1543,11 +1529,16 @@ void code_contractst::add_contract_check(
15431529
std::pair<goto_programt, goto_programt> ensures_pair;
15441530

15451531
// Generate: copies for history variables
1532+
exprt::operandst ensures_conjuncts;
1533+
for(const auto &r : code_type.ensures())
1534+
{
1535+
ensures_conjuncts.push_back(to_lambda_expr(r).application(instantiation_values));
1536+
}
1537+
auto ensures = conjunction(ensures_conjuncts);
15461538
if(!ensures.is_true())
15471539
{
15481540
if(has_subexpr(ensures, ID_exists) || has_subexpr(ensures, ID_forall))
15491541
add_quantified_variable(ensures, function_symbol.mode);
1550-
common_replace(ensures);
15511542

15521543
// get all the relevant instructions related to history variables
15531544
auto assertion = code_assertt(ensures);
@@ -1570,8 +1561,7 @@ void code_contractst::add_contract_check(
15701561
for(auto &expr : requires_contract)
15711562
{
15721563
assume_function_pointer_obeys_contract(
1573-
to_function_pointer_obeys_contract_expr(expr),
1574-
common_replace,
1564+
to_function_pointer_obeys_contract_expr(to_lambda_expr(expr).application(instantiation_values)),
15751565
function_symbol.mode,
15761566
check);
15771567
}
@@ -1589,9 +1579,8 @@ void code_contractst::add_contract_check(
15891579
for(auto &expr : ensures_contract)
15901580
{
15911581
assert_function_pointer_obeys_contract(
1592-
to_function_pointer_obeys_contract_expr(expr),
1582+
to_function_pointer_obeys_contract_expr(to_lambda_expr(expr).application(instantiation_values)),
15931583
ID_postcondition,
1594-
common_replace,
15951584
function_symbol.mode,
15961585
check);
15971586
}

0 commit comments

Comments
 (0)