Skip to content

Commit 790ad51

Browse files
committed
Handle functions with mangled names in contract replacement
Signed-off-by: Felipe R. Monteiro <[email protected]>
1 parent 98bbe74 commit 790ad51

File tree

4 files changed

+64
-30
lines changed

4 files changed

+64
-30
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <stdlib.h>
2+
3+
int x;
4+
int *z = &x;
5+
6+
void bar() __CPROVER_assigns(*z)
7+
{
8+
}
9+
10+
static void foo() __CPROVER_assigns(*z)
11+
{
12+
bar();
13+
}
14+
15+
int main()
16+
{
17+
foo();
18+
return 0;
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--replace-call-with-contract bar
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
\[\d+\] file main.c line \d+ Check that bar\'s assigns clause is a subset of foo\'s assigns clause: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
^Condition: \!not\_found$
10+
--
11+
Checks whether CBMC properly evaluates subset relationship on assigns
12+
during replacement with static functions.

src/goto-instrument/contracts/contracts.cpp

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -473,18 +473,19 @@ code_contractst::create_ensures_instruction(
473473
}
474474

475475
bool code_contractst::apply_function_contract(
476-
goto_programt &goto_program,
477-
goto_programt::targett target)
476+
const irep_idt &function,
477+
goto_programt &function_body,
478+
goto_programt::targett &target)
478479
{
479480
// Return if the function is not named in the call; currently we don't handle
480481
// function pointers.
481-
PRECONDITION(as_const(*target).call_function().id() == ID_symbol);
482+
PRECONDITION(as_const(target)->call_function().id() == ID_symbol);
482483

483484
// Retrieve the function type, which is needed to extract the contract
484485
// components.
485-
const irep_idt &function =
486-
to_symbol_expr(as_const(*target).call_function()).get_identifier();
487-
const symbolt &function_symbol = ns.lookup(function);
486+
const irep_idt &target_function =
487+
to_symbol_expr(as_const(target)->call_function()).get_identifier();
488+
const symbolt &function_symbol = ns.lookup(target_function);
488489
const auto &type = to_code_with_contract_type(function_symbol.type);
489490

490491
// Isolate each component of the contract.
@@ -499,15 +500,15 @@ bool code_contractst::apply_function_contract(
499500
if(type.return_type() != empty_typet())
500501
{
501502
// Check whether the function's return value is not disregarded.
502-
if(as_const(*target).call_lhs().is_not_nil())
503+
if(target->call_lhs().is_not_nil())
503504
{
504505
// If so, have it replaced appropriately.
505506
// For example, if foo() ensures that its return value is > 5, then
506507
// rewrite calls to foo as follows:
507508
// x = foo() -> assume(__CPROVER_return_value > 5) -> assume(x > 5)
508509
symbol_exprt ret_val(
509-
CPROVER_PREFIX "return_value", as_const(*target).call_lhs().type());
510-
common_replace.insert(ret_val, as_const(*target).call_lhs());
510+
CPROVER_PREFIX "return_value", as_const(target)->call_lhs().type());
511+
common_replace.insert(ret_val, as_const(target)->call_lhs());
511512
}
512513
else
513514
{
@@ -521,10 +522,10 @@ bool code_contractst::apply_function_contract(
521522
// fresh variable to replace __CPROVER_return_value with.
522523
const symbolt &fresh = get_fresh_aux_symbol(
523524
type.return_type(),
524-
id2string(function),
525+
id2string(target_function),
525526
"ignored_return_value",
526-
target->source_location,
527-
symbol_table.lookup_ref(function).mode,
527+
as_const(target)->source_location,
528+
symbol_table.lookup_ref(target_function).mode,
528529
ns,
529530
symbol_table);
530531
symbol_exprt ret_val(CPROVER_PREFIX "return_value", type.return_type());
@@ -534,7 +535,7 @@ bool code_contractst::apply_function_contract(
534535
}
535536

536537
// Replace formal parameters
537-
const auto &arguments = target->call_arguments();
538+
const auto &arguments = as_const(target)->call_arguments();
538539
auto a_it = arguments.begin();
539540
for(auto p_it = type.parameters().begin();
540541
p_it != type.parameters().end() && a_it != arguments.end();
@@ -551,9 +552,9 @@ bool code_contractst::apply_function_contract(
551552
// and only refer to the common symbols to be replaced.
552553
common_replace(assigns);
553554

554-
const auto &mode = symbol_table.lookup_ref(function).mode;
555+
const auto &mode = symbol_table.lookup_ref(target_function).mode;
555556

556-
is_fresh_replacet is_fresh(*this, log, function);
557+
is_fresh_replacet is_fresh(*this, log, target_function);
557558
is_fresh.create_declarations();
558559

559560
// Insert assertion of the precondition immediately before the call site.
@@ -567,14 +568,14 @@ bool code_contractst::apply_function_contract(
567568
converter.goto_convert(
568569
code_assertt(requires),
569570
assertion,
570-
symbol_table.lookup_ref(function).mode);
571+
symbol_table.lookup_ref(target_function).mode);
571572
assertion.instructions.back().source_location = requires.source_location();
572573
assertion.instructions.back().source_location.set_comment(
573574
"Check requires clause");
574575
assertion.instructions.back().source_location.set_property_class(
575576
ID_precondition);
576577
is_fresh.update_requires(assertion);
577-
insert_before_swap_and_advance(goto_program, target, assertion);
578+
insert_before_swap_and_advance(function_body, target, assertion);
578579
}
579580

580581
// Gather all the instructions required to handle history variables
@@ -590,11 +591,11 @@ bool code_contractst::apply_function_contract(
590591
ensures_pair = create_ensures_instruction(
591592
assumption,
592593
ensures.source_location(),
593-
symbol_table.lookup_ref(function).mode);
594+
symbol_table.lookup_ref(target_function).mode);
594595

595596
// add all the history variable initialization instructions
596597
// to the goto program
597-
insert_before_swap_and_advance(goto_program, target, ensures_pair.second);
598+
insert_before_swap_and_advance(function_body, target, ensures_pair.second);
598599
}
599600

600601
// Create a series of non-deterministic assignments to havoc the variables
@@ -604,9 +605,8 @@ bool code_contractst::apply_function_contract(
604605
assigns_clauset assigns_clause(assigns, log, ns);
605606

606607
// Retrieve assigns clause of the caller function if exists.
607-
const irep_idt &caller_function = target->source_location.get_function();
608608
auto caller_assigns =
609-
to_code_with_contract_type(ns.lookup(caller_function).type).assigns();
609+
to_code_with_contract_type(ns.lookup(function).type).assigns();
610610

611611
if(caller_assigns.is_not_nil())
612612
{
@@ -617,31 +617,32 @@ bool code_contractst::apply_function_contract(
617617
caller_assigns_clause.generate_subset_check(assigns_clause),
618618
assigns_clause.location));
619619
subset_check_assertion.instructions.back().source_location.set_comment(
620-
"Check that " + id2string(function) +
621-
"'s assigns clause is a subset of " + id2string(caller_function) +
620+
"Check that " + id2string(target_function) +
621+
"'s assigns clause is a subset of " +
622+
id2string(as_const(target)->source_location.get_function()) +
622623
"'s assigns clause");
623624
insert_before_swap_and_advance(
624-
goto_program, target, subset_check_assertion);
625+
function_body, target, subset_check_assertion);
625626
}
626627

627628
// Havoc all targets in global write set
628629
auto assigns_havoc = assigns_clause.generate_havoc_code();
629630

630631
// Insert the non-deterministic assignment immediately before the call site.
631-
insert_before_swap_and_advance(goto_program, target, assigns_havoc);
632+
insert_before_swap_and_advance(function_body, target, assigns_havoc);
632633
}
633634

634635
// To remove the function call, insert statements related to the assumption.
635636
// Then, replace the function call with a SKIP statement.
636637
if(!ensures.is_false())
637638
{
638639
is_fresh.update_ensures(ensures_pair.first);
639-
insert_before_swap_and_advance(goto_program, target, ensures_pair.first);
640+
insert_before_swap_and_advance(function_body, target, ensures_pair.first);
640641
}
641642
*target = goto_programt::make_skip();
642643

643644
// Add this function to the set of replaced functions.
644-
summarized.insert(function);
645+
summarized.insert(target_function);
645646
return false;
646647
}
647648

@@ -1223,7 +1224,8 @@ bool code_contractst::replace_calls(const std::set<std::string> &functions)
12231224
if(found == functions.end())
12241225
continue;
12251226

1226-
fail |= apply_function_contract(goto_function.second.body, ins);
1227+
fail |= apply_function_contract(
1228+
goto_function.first, goto_function.second.body, ins);
12271229
}
12281230
}
12291231
}

src/goto-instrument/contracts/contracts.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,9 @@ class code_contractst
195195
/// non-deterministic assignments for the write set, and assumptions
196196
/// based on ensures clauses.
197197
bool apply_function_contract(
198-
goto_programt &goto_program,
199-
goto_programt::targett target);
198+
const irep_idt &,
199+
goto_programt &,
200+
goto_programt::targett &);
200201

201202
/// Instruments `wrapper_function` adding assumptions based on requires
202203
/// clauses and assertions based on ensures clauses.

0 commit comments

Comments
 (0)