Skip to content

Commit f4a850d

Browse files
committed
Restrict with_exprt to exactly three operands
We cannot build a C++ API for the previous >= 3, odd-number operand variant. Alas, we ended up with various places in the code base silently ignoring additional operands, which led to wrong verification results in Kani as recent changes in CBMC made increasing use of the value set (which is one of the places that silently ignored additional operands). Resolves: #7272
1 parent 48490fb commit f4a850d

File tree

13 files changed

+257
-338
lines changed

13 files changed

+257
-338
lines changed

src/goto-programs/graphml_witness.cpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -170,26 +170,24 @@ std::string graphml_witnesst::convert_assign_rec(
170170
else if(assign.rhs().id() == ID_with)
171171
{
172172
const with_exprt &with_expr = to_with_expr(assign.rhs());
173-
const auto &ops = with_expr.operands();
174173

175-
for(std::size_t i = 1; i < ops.size(); i += 2)
176-
{
177-
if(!result.empty())
178-
result += ' ';
174+
if(!result.empty())
175+
result += ' ';
179176

180-
if(ops[i].id() == ID_member_name)
181-
{
182-
const member_exprt member{
183-
assign.lhs(), ops[i].get(ID_component_name), ops[i + 1].type()};
184-
result +=
185-
convert_assign_rec(identifier, code_assignt(member, ops[i + 1]));
186-
}
187-
else
188-
{
189-
const index_exprt index{assign.lhs(), ops[i]};
190-
result +=
191-
convert_assign_rec(identifier, code_assignt(index, ops[i + 1]));
192-
}
177+
if(with_expr.where().id() == ID_member_name)
178+
{
179+
const member_exprt member{
180+
assign.lhs(),
181+
with_expr.where().get(ID_component_name),
182+
with_expr.new_value().type()};
183+
result += convert_assign_rec(
184+
identifier, code_assignt(member, with_expr.new_value()));
185+
}
186+
else
187+
{
188+
const index_exprt index{assign.lhs(), with_expr.where()};
189+
result += convert_assign_rec(
190+
identifier, code_assignt(index, with_expr.new_value()));
193191
}
194192
}
195193
else

src/solvers/flattening/arrays.cpp

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,8 @@ void arrayst::collect_arrays(const exprt &a)
145145
collect_arrays(with_expr.old());
146146

147147
// make sure this shows as an application
148-
for(std::size_t i = 1; i < with_expr.operands().size(); i += 2)
149-
{
150-
index_exprt index_expr(with_expr.old(), with_expr.operands()[i]);
151-
record_array_index(index_expr);
152-
}
148+
index_exprt index_expr(with_expr.old(), with_expr.where());
149+
record_array_index(index_expr);
153150
}
154151
else if(a.id()==ID_update)
155152
{
@@ -574,31 +571,24 @@ void arrayst::add_array_constraints_with(
574571
const index_sett &index_set,
575572
const with_exprt &expr)
576573
{
577-
// We got x=(y with [i:=v, j:=w, ...]).
578-
// First add constraints x[i]=v, x[j]=w, ...
574+
// We got x=(y with [i:=v]).
575+
// First add constraint x[i]=v
579576
std::unordered_set<exprt, irep_hash> updated_indices;
580577

581-
const exprt::operandst &operands = expr.operands();
582-
for(std::size_t i = 1; i + 1 < operands.size(); i += 2)
583-
{
584-
const exprt &index = operands[i];
585-
const exprt &value = operands[i + 1];
586-
587-
index_exprt index_expr(
588-
expr, index, to_array_type(expr.type()).element_type());
578+
index_exprt index_expr(
579+
expr, expr.where(), to_array_type(expr.type()).element_type());
589580

590-
DATA_INVARIANT_WITH_DIAGNOSTICS(
591-
index_expr.type() == value.type(),
592-
"with-expression operand should match array element type",
593-
irep_pretty_diagnosticst{expr});
581+
DATA_INVARIANT_WITH_DIAGNOSTICS(
582+
index_expr.type() == expr.new_value().type(),
583+
"with-expression operand should match array element type",
584+
irep_pretty_diagnosticst{expr});
594585

595-
lazy_constraintt lazy(
596-
lazy_typet::ARRAY_WITH, equal_exprt(index_expr, value));
597-
add_array_constraint(lazy, false); // added immediately
598-
array_constraint_count[constraint_typet::ARRAY_WITH]++;
586+
lazy_constraintt lazy(
587+
lazy_typet::ARRAY_WITH, equal_exprt(index_expr, expr.new_value()));
588+
add_array_constraint(lazy, false); // added immediately
589+
array_constraint_count[constraint_typet::ARRAY_WITH]++;
599590

600-
updated_indices.insert(index);
601-
}
591+
updated_indices.insert(expr.where());
602592

603593
// For all other indices use the existing value, i.e., add constraints
604594
// x[I]=y[I] for I!=i,j,...

src/solvers/flattening/boolbv_with.cpp

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,16 @@ Author: Daniel Kroening, [email protected]
1616

1717
bvt boolbvt::convert_with(const with_exprt &expr)
1818
{
19+
DATA_INVARIANT(
20+
expr.operands().size() == 3,
21+
"with_exprt with more than 3 operands should no longer exist");
22+
1923
auto &type = expr.type();
2024

2125
if(
2226
type.id() == ID_bv || type.id() == ID_unsignedbv ||
2327
type.id() == ID_signedbv)
2428
{
25-
if(expr.operands().size() > 3)
26-
{
27-
std::size_t s = expr.operands().size();
28-
29-
// strip off the trailing two operands
30-
with_exprt tmp = expr;
31-
tmp.operands().resize(s - 2);
32-
33-
with_exprt new_with_expr(
34-
tmp, expr.operands()[s - 2], expr.operands().back());
35-
36-
// recursive call
37-
return convert_with(new_with_expr);
38-
}
39-
40-
PRECONDITION(expr.operands().size() == 3);
4129
if(expr.new_value().type().id() == ID_bool)
4230
{
4331
return convert_bv(
@@ -50,7 +38,7 @@ bvt boolbvt::convert_with(const with_exprt &expr)
5038
}
5139
}
5240

53-
bvt bv = convert_bv(expr.old());
41+
bvt bv_old = convert_bv(expr.old());
5442

5543
std::size_t width = boolbv_width(type);
5644

@@ -61,21 +49,14 @@ bvt boolbvt::convert_with(const with_exprt &expr)
6149
}
6250

6351
DATA_INVARIANT_WITH_DIAGNOSTICS(
64-
bv.size() == width,
52+
bv_old.size() == width,
6553
"unexpected operand 0 width",
6654
irep_pretty_diagnosticst{expr});
6755

68-
bvt prev_bv;
69-
prev_bv.resize(width);
70-
71-
const exprt::operandst &ops=expr.operands();
56+
bvt bv;
57+
bv.resize(width);
7258

73-
for(std::size_t op_no=1; op_no<ops.size(); op_no+=2)
74-
{
75-
bv.swap(prev_bv);
76-
77-
convert_with(expr.old().type(), ops[op_no], ops[op_no + 1], prev_bv, bv);
78-
}
59+
convert_with(expr.old().type(), expr.where(), expr.new_value(), bv_old, bv);
7960

8061
return bv;
8162
}

src/solvers/smt2/smt2_conv.cpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4308,27 +4308,9 @@ void smt2_convt::convert_floatbv_rem(const binary_exprt &expr)
43084308

43094309
void smt2_convt::convert_with(const with_exprt &expr)
43104310
{
4311-
// get rid of "with" that has more than three operands
4312-
4313-
if(expr.operands().size()>3)
4314-
{
4315-
std::size_t s=expr.operands().size();
4316-
4317-
// strip off the trailing two operands
4318-
with_exprt tmp = expr;
4319-
tmp.operands().resize(s-2);
4320-
4321-
with_exprt new_with_expr(
4322-
tmp, expr.operands()[s - 2], expr.operands().back());
4323-
4324-
// recursive call
4325-
return convert_with(new_with_expr);
4326-
}
4327-
43284311
INVARIANT(
43294312
expr.operands().size() == 3,
4330-
"with expression should have been converted to a version with three "
4331-
"operands above");
4313+
"with expression should have exactly three operands");
43324314

43334315
const typet &expr_type = expr.type();
43344316

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,12 +1005,9 @@ static smt_termt convert_array_update_to_smt(
10051005
const sub_expression_mapt &converted)
10061006
{
10071007
smt_termt array = converted.at(with.old());
1008-
for(auto it = ++with.operands().begin(); it != with.operands().end(); it += 2)
1009-
{
1010-
const smt_termt &index_term = converted.at(it[0]);
1011-
const smt_termt &value_term = converted.at(it[1]);
1012-
array = smt_array_theoryt::store(array, index_term, value_term);
1013-
}
1008+
const smt_termt &index_term = converted.at(with.where());
1009+
const smt_termt &value_term = converted.at(with.new_value());
1010+
array = smt_array_theoryt::store(array, index_term, value_term);
10141011
return array;
10151012
}
10161013

src/solvers/smt2_incremental/encoding/struct_encoding.cpp

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -71,52 +71,25 @@ typet struct_encodingt::encode(typet type) const
7171
return type;
7272
}
7373

74-
/// \brief Extracts the component/field names and new values from a `with_exprt`
75-
/// which expresses a new struct value where one or more members of a
76-
/// struct have had their values substituted with new values.
77-
/// \note This is implemented using direct access to the operands and other
78-
/// underlying irept interfaces, because the interface for `with_exprt`
79-
/// only supports a single `where` / `new_value` pair and does not
80-
/// support extracting the name from the `where` operand.
81-
static std::unordered_map<irep_idt, exprt>
82-
extricate_updates(const with_exprt &struct_expr)
83-
{
84-
std::unordered_map<irep_idt, exprt> pairs;
85-
auto current_operand = struct_expr.operands().begin();
86-
// Skip the struct being updated in order to begin with the pairs.
87-
current_operand++;
88-
while(current_operand != struct_expr.operands().end())
89-
{
90-
INVARIANT(
91-
current_operand->id() == ID_member_name,
92-
"operand is expected to be the name of a member");
93-
auto name = current_operand->find(ID_component_name).id();
94-
++current_operand;
95-
INVARIANT(
96-
current_operand != struct_expr.operands().end(),
97-
"every name is expected to be followed by a paired value");
98-
pairs[name] = *current_operand;
99-
++current_operand;
100-
}
101-
POSTCONDITION(!pairs.empty());
102-
return pairs;
103-
}
104-
10574
static exprt encode(const with_exprt &with, const namespacet &ns)
10675
{
10776
const auto tag_type = type_checked_cast<struct_tag_typet>(with.type());
10877
const auto struct_type = ns.follow_tag(tag_type);
109-
const auto updates = extricate_updates(with);
78+
INVARIANT(
79+
with.where().id() == ID_member_name,
80+
"operand is expected to be the name of a member");
81+
const auto update_name = with.where().find(ID_component_name).id();
11082
const auto components =
11183
make_range(struct_type.components())
112-
.map([&](const struct_union_typet::componentt &component) -> exprt {
113-
const auto &update = updates.find(component.get_name());
114-
if(update != updates.end())
115-
return update->second;
116-
else
117-
return member_exprt{
118-
with.old(), component.get_name(), component.type()};
119-
})
84+
.map(
85+
[&](const struct_union_typet::componentt &component) -> exprt
86+
{
87+
if(component.get_name() == update_name)
88+
return with.new_value();
89+
else
90+
return member_exprt{
91+
with.old(), component.get_name(), component.type()};
92+
})
12093
.collect<exprt::operandst>();
12194
return struct_exprt{components, tag_type};
12295
}
@@ -209,8 +182,9 @@ exprt struct_encodingt::encode_member(const member_exprt &member_expr) const
209182
return count_trailing_bit_width(
210183
struct_type, member_expr.get_component_name(), *boolbv_width);
211184
}();
212-
return extractbits_exprt{
213-
member_expr.compound(), offset_bits, member_expr.type()};
185+
return simplify_expr(
186+
extractbits_exprt{member_expr.compound(), offset_bits, member_expr.type()},
187+
ns);
214188
}
215189

216190
exprt struct_encodingt::encode(exprt expr) const
@@ -250,7 +224,7 @@ exprt struct_encodingt::encode(exprt expr) const
250224
for(auto &operand : current.operands())
251225
work_queue.push(&operand);
252226
}
253-
return expr;
227+
return simplify_expr(expr, ns);
254228
}
255229

256230
exprt struct_encodingt::decode(

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -334,16 +334,14 @@ void smt2_incremental_decision_proceduret::ensure_handle_for_expr_defined(
334334
void smt2_incremental_decision_proceduret::define_index_identifiers(
335335
const exprt &expr)
336336
{
337-
expr.visit_pre([&](const exprt &expr_node) {
338-
if(!can_cast_type<array_typet>(expr_node.type()))
339-
return;
340-
if(const auto with_expr = expr_try_dynamic_cast<with_exprt>(expr_node))
337+
expr.visit_post(
338+
[&](const exprt &expr_node)
341339
{
342-
for(auto operand_ite = ++with_expr->operands().begin();
343-
operand_ite != with_expr->operands().end();
344-
operand_ite += 2)
340+
if(!can_cast_type<array_typet>(expr_node.type()))
341+
return;
342+
if(const auto with_expr = expr_try_dynamic_cast<with_exprt>(expr_node))
345343
{
346-
const auto index_expr = *operand_ite;
344+
const auto index_expr = with_expr->where();
347345
const auto index_term = convert_expr_to_smt(index_expr);
348346
const auto index_identifier =
349347
"index_" + std::to_string(index_sequence());
@@ -356,8 +354,7 @@ void smt2_incremental_decision_proceduret::define_index_identifiers(
356354
solver_process->send(
357355
smt_define_function_commandt{index_identifier, {}, index_term});
358356
}
359-
}
360-
});
357+
});
361358
}
362359

363360
exprt smt2_incremental_decision_proceduret::substitute_defined_padding(

src/util/lower_byte_operators.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,10 +1607,7 @@ static exprt lower_byte_update_byte_array_vector(
16071607
index_exprt{src.op(), where}};
16081608
}
16091609

1610-
if(result.id() != ID_with)
1611-
result = with_exprt{result, std::move(where), std::move(update_value)};
1612-
else
1613-
result.add_to_operands(std::move(where), std::move(update_value));
1610+
result = with_exprt{result, std::move(where), std::move(update_value)};
16141611
}
16151612

16161613
return simplify_expr(std::move(result), ns);
@@ -1892,7 +1889,7 @@ static exprt lower_byte_update_array_vector_non_const(
18921889
src.get_bits_per_byte()},
18931890
ns);
18941891

1895-
result.add_to_operands(std::move(where), std::move(element));
1892+
result = with_exprt{result, std::move(where), std::move(element)};
18961893
};
18971894

18981895
std::size_t i = 1;

0 commit comments

Comments
 (0)