Skip to content

Commit ed4b77f

Browse files
committed
CONTRACTS: do not rely on replace_symbol for bound variables
replace_symbolt will cease to support bound variables, which presently is not well documented behaviour. See #6827 for upcoming changes.
1 parent 40f6d43 commit ed4b77f

File tree

2 files changed

+51
-55
lines changed

2 files changed

+51
-55
lines changed

src/goto-instrument/contracts/contracts.cpp

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ void code_contractst::check_apply_loop_contracts(
154154
// at the start of and end of a loop body
155155
std::vector<symbol_exprt> old_decreases_vars, new_decreases_vars;
156156

157-
replace_symbolt replace;
158-
code_contractst::add_quantified_variable(invariant, replace, mode);
159-
replace(invariant);
157+
// replace bound variables by fresh instances
158+
if(has_subexpr(invariant, ID_exists) || has_subexpr(invariant, ID_forall))
159+
add_quantified_variable(invariant, mode);
160160

161161
// instrument
162162
//
@@ -567,68 +567,73 @@ void code_contractst::check_apply_loop_contracts(
567567
}
568568

569569
void code_contractst::add_quantified_variable(
570-
const exprt &expression,
571-
replace_symbolt &replace,
570+
exprt &expression,
572571
const irep_idt &mode)
573572
{
574573
if(expression.id() == ID_not || expression.id() == ID_typecast)
575574
{
576575
// For unary connectives, recursively check for
577576
// nested quantified formulae in the term
578-
const auto &unary_expression = to_unary_expr(expression);
579-
add_quantified_variable(unary_expression.op(), replace, mode);
577+
auto &unary_expression = to_unary_expr(expression);
578+
add_quantified_variable(unary_expression.op(), mode);
580579
}
581580
if(expression.id() == ID_notequal || expression.id() == ID_implies)
582581
{
583582
// For binary connectives, recursively check for
584583
// nested quantified formulae in the left and right terms
585-
const auto &binary_expression = to_binary_expr(expression);
586-
add_quantified_variable(binary_expression.lhs(), replace, mode);
587-
add_quantified_variable(binary_expression.rhs(), replace, mode);
584+
auto &binary_expression = to_binary_expr(expression);
585+
add_quantified_variable(binary_expression.lhs(), mode);
586+
add_quantified_variable(binary_expression.rhs(), mode);
588587
}
589588
if(expression.id() == ID_if)
590589
{
591590
// For ternary connectives, recursively check for
592591
// nested quantified formulae in all three terms
593-
const auto &if_expression = to_if_expr(expression);
594-
add_quantified_variable(if_expression.cond(), replace, mode);
595-
add_quantified_variable(if_expression.true_case(), replace, mode);
596-
add_quantified_variable(if_expression.false_case(), replace, mode);
592+
auto &if_expression = to_if_expr(expression);
593+
add_quantified_variable(if_expression.cond(), mode);
594+
add_quantified_variable(if_expression.true_case(), mode);
595+
add_quantified_variable(if_expression.false_case(), mode);
597596
}
598597
if(expression.id() == ID_and || expression.id() == ID_or)
599598
{
600599
// For multi-ary connectives, recursively check for
601600
// nested quantified formulae in all terms
602-
const auto &multi_ary_expression = to_multi_ary_expr(expression);
603-
for(const auto &operand : multi_ary_expression.operands())
601+
auto &multi_ary_expression = to_multi_ary_expr(expression);
602+
for(auto &operand : multi_ary_expression.operands())
604603
{
605-
add_quantified_variable(operand, replace, mode);
604+
add_quantified_variable(operand, mode);
606605
}
607606
}
608607
else if(expression.id() == ID_exists || expression.id() == ID_forall)
609608
{
610-
// When a quantifier expression is found,
611-
// for each quantified variable ...
612-
const auto &quantifier_expression = to_quantifier_expr(expression);
609+
// When a quantifier expression is found, create a fresh symbol for each
610+
// quantified variable and rewrite the expression to use those fresh
611+
// symbols.
612+
auto &quantifier_expression = to_quantifier_expr(expression);
613+
std::vector<symbol_exprt> fresh_variables;
614+
fresh_variables.reserve(quantifier_expression.variables().size());
613615
for(const auto &quantified_variable : quantifier_expression.variables())
614616
{
615-
const auto &quantified_symbol = to_symbol_expr(quantified_variable);
616-
617617
// 1. create fresh symbol
618618
symbolt new_symbol = new_tmp_symbol(
619-
quantified_symbol.type(),
620-
quantified_symbol.source_location(),
619+
quantified_variable.type(),
620+
quantified_variable.source_location(),
621621
mode,
622622
symbol_table);
623623

624624
// 2. add created fresh symbol to expression map
625-
symbol_exprt q(
626-
quantified_symbol.get_identifier(), quantified_symbol.type());
627-
replace.insert(q, new_symbol.symbol_expr());
628-
629-
// 3. recursively check for nested quantified formulae
630-
add_quantified_variable(quantifier_expression.where(), replace, mode);
625+
fresh_variables.push_back(new_symbol.symbol_expr());
631626
}
627+
628+
// use fresh symbols
629+
exprt where = quantifier_expression.instantiate(fresh_variables);
630+
631+
// recursively check for nested quantified formulae
632+
add_quantified_variable(where, mode);
633+
634+
// replace previous variables and body
635+
quantifier_expression.variables() = fresh_variables;
636+
quantifier_expression.where() = std::move(where);
632637
}
633638
}
634639

@@ -831,9 +836,9 @@ void code_contractst::apply_function_contract(
831836
// Insert assertion of the precondition immediately before the call site.
832837
if(!requires.is_true())
833838
{
834-
replace_symbolt replace(common_replace);
835-
code_contractst::add_quantified_variable(requires, replace, mode);
836-
replace(requires);
839+
if(has_subexpr(requires, ID_exists) || has_subexpr(requires, ID_forall))
840+
add_quantified_variable(requires, mode);
841+
common_replace(requires);
837842

838843
goto_programt assertion;
839844
converter.goto_convert(code_assertt(requires), assertion, mode);
@@ -852,9 +857,9 @@ void code_contractst::apply_function_contract(
852857
std::pair<goto_programt, goto_programt> ensures_pair;
853858
if(!ensures.is_false())
854859
{
855-
replace_symbolt replace(common_replace);
856-
code_contractst::add_quantified_variable(ensures, replace, mode);
857-
replace(ensures);
860+
if(has_subexpr(ensures, ID_exists) || has_subexpr(ensures, ID_forall))
861+
add_quantified_variable(ensures, mode);
862+
common_replace(ensures);
858863

859864
auto assumption = code_assumet(ensures);
860865
ensures_pair =
@@ -1430,12 +1435,9 @@ void code_contractst::add_contract_check(
14301435
// Generate: assume(requires)
14311436
if(!requires.is_false())
14321437
{
1433-
// extend common_replace with quantified variables in REQUIRES,
1434-
// and then do the replacement
1435-
replace_symbolt replace(common_replace);
1436-
code_contractst::add_quantified_variable(
1437-
requires, replace, function_symbol.mode);
1438-
replace(requires);
1438+
if(has_subexpr(requires, ID_exists) || has_subexpr(requires, ID_forall))
1439+
add_quantified_variable(requires, function_symbol.mode);
1440+
common_replace(requires);
14391441

14401442
goto_programt assumption;
14411443
converter.goto_convert(
@@ -1450,12 +1452,9 @@ void code_contractst::add_contract_check(
14501452
// Generate: copies for history variables
14511453
if(!ensures.is_true())
14521454
{
1453-
// extend common_replace with quantified variables in ENSURES,
1454-
// and then do the replacement
1455-
replace_symbolt replace(common_replace);
1456-
code_contractst::add_quantified_variable(
1457-
ensures, replace, function_symbol.mode);
1458-
replace(ensures);
1455+
if(has_subexpr(ensures, ID_exists) || has_subexpr(ensures, ID_forall))
1456+
add_quantified_variable(ensures, function_symbol.mode);
1457+
common_replace(ensures);
14591458

14601459
// get all the relevant instructions related to history variables
14611460
auto assertion = code_assertt(ensures);

src/goto-instrument/contracts/contracts.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,11 @@ class code_contractst
156156
const irep_idt &mangled_function,
157157
goto_programt &dest);
158158

159-
/// This function recursively searches the expression to find nested or
159+
/// This function recursively searches \p expression to find nested or
160160
/// non-nested quantified expressions. When a quantified expression is found,
161-
/// the quantified variable is added to the symbol table
162-
/// and to the expression map.
163-
void add_quantified_variable(
164-
const exprt &expression,
165-
replace_symbolt &replace,
166-
const irep_idt &mode);
161+
/// a fresh quantified variable is added to the symbol table and \p expression
162+
/// is updated to use this fresh variable.
163+
void add_quantified_variable(exprt &expression, const irep_idt &mode);
167164

168165
/// This function recursively identifies the "old" expressions within expr
169166
/// and replaces them with correspoding history variables.

0 commit comments

Comments
 (0)