Skip to content

Commit c2f8b7b

Browse files
committed
Wrap contract expressions in a lambda
This makes sure contracts are self-contained in that any parameter names that may have only existed in the scope of a function declaration are now bound variables.
1 parent 28c5734 commit c2f8b7b

File tree

3 files changed

+75
-67
lines changed

3 files changed

+75
-67
lines changed

src/ansi-c/c_typecheck_base.cpp

Lines changed: 15 additions & 1 deletion
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"
@@ -745,7 +746,7 @@ void c_typecheck_baset::typecheck_declaration(
745746

746747
// ensure parameter declarations are available for type checking to
747748
// succeed
748-
std::vector<symbol_exprt> temporary_parameter_symbols;
749+
binding_exprt::variablest temporary_parameter_symbols;
749750

750751
const auto &return_type = code_type.return_type();
751752
if(return_type.id() != ID_empty)
@@ -780,16 +781,25 @@ void c_typecheck_baset::typecheck_declaration(
780781
{
781782
typecheck_spec_function_pointer_obeys_contract(expr);
782783
check_history_expr(expr);
784+
lambda_exprt lambda{temporary_parameter_symbols, expr};
785+
expr.swap(lambda);
783786
}
784787

785788
for(auto &requires : code_type.requires())
786789
{
787790
typecheck_expr(requires);
788791
implicit_typecast_bool(requires);
789792
check_history_expr(requires);
793+
lambda_exprt lambda{temporary_parameter_symbols, requires};
794+
requires.swap(lambda);
790795
}
791796

792797
typecheck_spec_assigns(code_type.assigns());
798+
for(auto &assigns : code_type.assigns())
799+
{
800+
lambda_exprt lambda{temporary_parameter_symbols, assigns};
801+
assigns.swap(lambda);
802+
}
793803

794804
for(auto &expr : code_type.ensures_contract())
795805
{
@@ -798,6 +808,8 @@ void c_typecheck_baset::typecheck_declaration(
798808
expr,
799809
ID_loop_entry,
800810
CPROVER_PREFIX "loop_entry is not allowed in postconditions.");
811+
lambda_exprt lambda{temporary_parameter_symbols, expr};
812+
expr.swap(lambda);
801813
}
802814

803815
for(auto &ensures : code_type.ensures())
@@ -808,6 +820,8 @@ void c_typecheck_baset::typecheck_declaration(
808820
ensures,
809821
ID_loop_entry,
810822
CPROVER_PREFIX "loop_entry is not allowed in postconditions.");
823+
lambda_exprt lambda{temporary_parameter_symbols, ensures};
824+
ensures.swap(lambda);
811825
}
812826

813827
for(const auto &parameter_sym : temporary_parameter_symbols)

src/goto-instrument/contracts/contracts.cpp

Lines changed: 60 additions & 59 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,17 +800,17 @@ 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
{
812807
// If the function does return a value, but the return value is
813808
// disregarded, check if the postcondition includes the return value.
814-
if(has_subexpr(ensures, [](const exprt &e) {
815-
return e.id() == ID_symbol && to_symbol_expr(e).get_identifier() ==
816-
CPROVER_PREFIX "return_value";
817-
}))
809+
if(std::any_of(
810+
type.ensures().begin(), type.ensures().end(), [](const exprt &e) {
811+
return has_symbol_expr(
812+
to_lambda_expr(e).where(), CPROVER_PREFIX "return_value", true);
813+
}))
818814
{
819815
// The postcondition does mention __CPROVER_return_value, so mint a
820816
// fresh variable to replace __CPROVER_return_value with.
@@ -827,28 +823,19 @@ void code_contractst::apply_function_contract(
827823
symbol_table.lookup_ref(target_function).mode,
828824
ns,
829825
symbol_table);
830-
symbol_exprt ret_val(
831-
CPROVER_PREFIX "return_value", function_type.return_type());
832826
auto fresh_sym_expr = fresh.symbol_expr();
833-
common_replace.insert(ret_val, fresh_sym_expr);
834827
call_ret_opt = fresh_sym_expr;
828+
instantiation_values.push_back(fresh_sym_expr);
835829
}
830+
else
831+
instantiation_values.push_back(nil_exprt{});
836832
}
837833
}
838834

839835
// Replace formal parameters
840836
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-
}
837+
instantiation_values.insert(
838+
instantiation_values.end(), arguments.begin(), arguments.end());
852839

853840
const auto &mode = function_symbol.mode;
854841

@@ -860,11 +847,17 @@ void code_contractst::apply_function_contract(
860847
is_fresh.add_memory_map_decl(new_program);
861848

862849
// Insert assertion of the precondition immediately before the call site.
850+
exprt::operandst requires_conjuncts;
851+
for(const auto &r : type.requires())
852+
{
853+
requires_conjuncts.push_back(
854+
to_lambda_expr(r).application(instantiation_values));
855+
}
856+
auto requires = conjunction(requires_conjuncts);
863857
if(!requires.is_true())
864858
{
865859
if(has_subexpr(requires, ID_exists) || has_subexpr(requires, ID_forall))
866860
add_quantified_variable(requires, mode);
867-
common_replace(requires);
868861

869862
goto_programt assertion;
870863
converter.goto_convert(code_assertt(requires), assertion, mode);
@@ -882,21 +875,27 @@ void code_contractst::apply_function_contract(
882875
for(auto &expr : requires_contract)
883876
{
884877
assert_function_pointer_obeys_contract(
885-
to_function_pointer_obeys_contract_expr(expr),
878+
to_function_pointer_obeys_contract_expr(
879+
to_lambda_expr(expr).application(instantiation_values)),
886880
ID_precondition,
887-
common_replace,
888881
mode,
889882
new_program);
890883
}
891884

892885
// Gather all the instructions required to handle history variables
893886
// as well as the ensures clause
887+
exprt::operandst ensures_conjuncts;
888+
for(const auto &r : type.ensures())
889+
{
890+
ensures_conjuncts.push_back(
891+
to_lambda_expr(r).application(instantiation_values));
892+
}
893+
auto ensures = conjunction(ensures_conjuncts);
894894
std::pair<goto_programt, goto_programt> ensures_pair;
895895
if(!ensures.is_false())
896896
{
897897
if(has_subexpr(ensures, ID_exists) || has_subexpr(ensures, ID_forall))
898898
add_quantified_variable(ensures, mode);
899-
common_replace(ensures);
900899

901900
auto assumption = code_assumet(ensures);
902901
ensures_pair =
@@ -908,10 +907,9 @@ void code_contractst::apply_function_contract(
908907

909908
// ASSIGNS clause should not refer to any quantified variables,
910909
// and only refer to the common symbols to be replaced.
911-
exprt targets;
910+
exprt::operandst targets;
912911
for(auto &target : assigns_clause)
913-
targets.add_to_operands(std::move(target));
914-
common_replace(targets);
912+
targets.push_back(to_lambda_expr(target).application(instantiation_values));
915913

916914
// Create a sequence of non-deterministic assignments ...
917915

@@ -920,7 +918,7 @@ void code_contractst::apply_function_contract(
920918

921919
havoc_assigns_clause_targetst havocker(
922920
target_function,
923-
targets.operands(),
921+
targets,
924922
goto_functions,
925923
location,
926924
symbol_table,
@@ -961,8 +959,8 @@ void code_contractst::apply_function_contract(
961959
for(auto &expr : ensures_contract)
962960
{
963961
assume_function_pointer_obeys_contract(
964-
to_function_pointer_obeys_contract_expr(expr),
965-
common_replace,
962+
to_function_pointer_obeys_contract_expr(
963+
to_lambda_expr(expr).application(instantiation_values)),
966964
mode,
967965
new_program);
968966
}
@@ -1399,7 +1397,6 @@ void code_contractst::enforce_contract(const irep_idt &function)
13991397
void code_contractst::assert_function_pointer_obeys_contract(
14001398
const function_pointer_obeys_contract_exprt &expr,
14011399
const irep_idt &property_class,
1402-
const replace_symbolt &replace,
14031400
const irep_idt &mode,
14041401
goto_programt &dest)
14051402
{
@@ -1411,9 +1408,8 @@ void code_contractst::assert_function_pointer_obeys_contract(
14111408
<< "' obeys contract '"
14121409
<< from_expr_using_mode(ns, mode, expr.contract()) << "'";
14131410
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()});
1411+
code_assertt assert_expr(
1412+
equal_exprt{expr.function_pointer(), expr.contract()});
14171413
assert_expr.add_source_location() = loc;
14181414
goto_programt instructions;
14191415
converter.goto_convert(assert_expr, instructions, mode);
@@ -1422,7 +1418,6 @@ void code_contractst::assert_function_pointer_obeys_contract(
14221418

14231419
void code_contractst::assume_function_pointer_obeys_contract(
14241420
const function_pointer_obeys_contract_exprt &expr,
1425-
const replace_symbolt &replace,
14261421
const irep_idt &mode,
14271422
goto_programt &dest)
14281423
{
@@ -1433,10 +1428,8 @@ void code_contractst::assume_function_pointer_obeys_contract(
14331428
<< "' obeys contract '"
14341429
<< from_expr_using_mode(ns, mode, expr.contract()) << "'";
14351430
loc.set_comment(comment.str());
1436-
exprt function_pointer(expr.function_pointer());
1437-
replace(function_pointer);
1438-
dest.add(
1439-
goto_programt::make_assignment(function_pointer, expr.contract(), loc));
1431+
dest.add(goto_programt::make_assignment(
1432+
expr.function_pointer(), expr.contract(), loc));
14401433
}
14411434

14421435
void code_contractst::add_contract_check(
@@ -1448,8 +1441,6 @@ void code_contractst::add_contract_check(
14481441

14491442
const auto &code_type = get_contract(wrapper_function, ns);
14501443
auto assigns = code_type.assigns();
1451-
auto requires = conjunction(code_type.requires());
1452-
auto ensures = conjunction(code_type.ensures());
14531444
auto requires_contract = code_type.requires_contract();
14541445
auto ensures_contract = code_type.ensures_contract();
14551446
// build:
@@ -1468,10 +1459,9 @@ void code_contractst::add_contract_check(
14681459
const symbolt &function_symbol = ns.lookup(mangled_function);
14691460
code_function_callt call(function_symbol.symbol_expr());
14701461

1471-
// Create a replace_symbolt object, for replacing expressions in the callee
1462+
// Prepare to instantiate expressions in the callee
14721463
// with expressions from the call site (e.g. the return value).
1473-
// This object tracks replacements that are common to ENSURES and REQUIRES.
1474-
replace_symbolt common_replace;
1464+
exprt::operandst instantiation_values;
14751465

14761466
const auto &source_location = function_symbol.location;
14771467

@@ -1490,8 +1480,7 @@ void code_contractst::add_contract_check(
14901480
call.lhs() = r;
14911481
return_stmt = code_returnt(r);
14921482

1493-
symbol_exprt ret_val(CPROVER_PREFIX "return_value", call.lhs().type());
1494-
common_replace.insert(ret_val, r);
1483+
instantiation_values.push_back(r);
14951484
}
14961485

14971486
// decl parameter1 ...
@@ -1516,18 +1505,24 @@ void code_contractst::add_contract_check(
15161505

15171506
call.arguments().push_back(p);
15181507

1519-
common_replace.insert(parameter_symbol.symbol_expr(), p);
1508+
instantiation_values.push_back(p);
15201509
}
15211510

15221511
is_fresh_enforcet visitor(*this, log, wrapper_function);
15231512
visitor.create_declarations();
15241513
visitor.add_memory_map_decl(check);
15251514
// Generate: assume(requires)
1515+
exprt::operandst requires_conjuncts;
1516+
for(const auto &r : code_type.requires())
1517+
{
1518+
requires_conjuncts.push_back(
1519+
to_lambda_expr(r).application(instantiation_values));
1520+
}
1521+
auto requires = conjunction(requires_conjuncts);
15261522
if(!requires.is_false())
15271523
{
15281524
if(has_subexpr(requires, ID_exists) || has_subexpr(requires, ID_forall))
15291525
add_quantified_variable(requires, function_symbol.mode);
1530-
common_replace(requires);
15311526

15321527
goto_programt assumption;
15331528
converter.goto_convert(
@@ -1540,11 +1535,17 @@ void code_contractst::add_contract_check(
15401535
std::pair<goto_programt, goto_programt> ensures_pair;
15411536

15421537
// Generate: copies for history variables
1538+
exprt::operandst ensures_conjuncts;
1539+
for(const auto &r : code_type.ensures())
1540+
{
1541+
ensures_conjuncts.push_back(
1542+
to_lambda_expr(r).application(instantiation_values));
1543+
}
1544+
auto ensures = conjunction(ensures_conjuncts);
15431545
if(!ensures.is_true())
15441546
{
15451547
if(has_subexpr(ensures, ID_exists) || has_subexpr(ensures, ID_forall))
15461548
add_quantified_variable(ensures, function_symbol.mode);
1547-
common_replace(ensures);
15481549

15491550
// get all the relevant instructions related to history variables
15501551
auto assertion = code_assertt(ensures);
@@ -1567,8 +1568,8 @@ void code_contractst::add_contract_check(
15671568
for(auto &expr : requires_contract)
15681569
{
15691570
assume_function_pointer_obeys_contract(
1570-
to_function_pointer_obeys_contract_expr(expr),
1571-
common_replace,
1571+
to_function_pointer_obeys_contract_expr(
1572+
to_lambda_expr(expr).application(instantiation_values)),
15721573
function_symbol.mode,
15731574
check);
15741575
}
@@ -1586,9 +1587,9 @@ void code_contractst::add_contract_check(
15861587
for(auto &expr : ensures_contract)
15871588
{
15881589
assert_function_pointer_obeys_contract(
1589-
to_function_pointer_obeys_contract_expr(expr),
1590+
to_function_pointer_obeys_contract_expr(
1591+
to_lambda_expr(expr).application(instantiation_values)),
15901592
ID_postcondition,
1591-
common_replace,
15921593
function_symbol.mode,
15931594
check);
15941595
}

0 commit comments

Comments
 (0)