Skip to content

Commit 440ad9c

Browse files
Merge pull request #5016 from romainbrenguier/enhance/solver-equations
Handle more formulas than just equations in string solver
2 parents 8c7609e + 462d355 commit 440ad9c

File tree

8 files changed

+365
-71
lines changed

8 files changed

+365
-71
lines changed

Diff for: jbmc/unit/solvers/strings/string_refinement/dependency_graph.cpp

+9-6
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,15 @@ SCENARIO("dependency_graph", "[core][solvers][refinement][string_refinement]")
8989
symbol_generatort generator;
9090
array_poolt array_pool(generator);
9191

92-
bool success = add_node(dependencies, equation1, array_pool);
93-
REQUIRE(success);
94-
success = add_node(dependencies, equation2, array_pool);
95-
REQUIRE(success);
96-
success = add_node(dependencies, equation3, array_pool);
97-
REQUIRE(success);
92+
optionalt<exprt> new_equation1 =
93+
add_node(dependencies, equation1, array_pool, generator);
94+
REQUIRE(new_equation1.has_value());
95+
optionalt<exprt> new_equation2 =
96+
add_node(dependencies, equation2, array_pool, generator);
97+
REQUIRE(new_equation2.has_value());
98+
optionalt<exprt> new_equation3 =
99+
add_node(dependencies, equation3, array_pool, generator);
100+
REQUIRE(new_equation3.has_value());
98101

99102
#ifdef DEBUG // useful output for visualizing the graph
100103
{

Diff for: src/solvers/strings/string_dependencies.cpp

+28-11
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ string_dependenciest::node_at(const array_string_exprt &e) const
9090
}
9191

9292
string_dependenciest::builtin_function_nodet &string_dependenciest::make_node(
93-
std::unique_ptr<string_builtin_functiont> &builtin_function)
93+
std::unique_ptr<string_builtin_functiont> builtin_function)
9494
{
9595
builtin_function_nodes.emplace_back(
9696
std::move(builtin_function), builtin_function_nodes.size());
@@ -195,22 +195,39 @@ void string_dependenciest::clean_cache()
195195
e.reset();
196196
}
197197

198-
bool add_node(
198+
optionalt<exprt> add_node(
199199
string_dependenciest &dependencies,
200-
const equal_exprt &equation,
201-
array_poolt &array_pool)
200+
const exprt &expr,
201+
array_poolt &array_pool,
202+
symbol_generatort &fresh_symbol)
202203
{
203-
const auto fun_app =
204-
expr_try_dynamic_cast<function_application_exprt>(equation.rhs());
204+
const auto fun_app = expr_try_dynamic_cast<function_application_exprt>(expr);
205205
if(!fun_app)
206-
return false;
206+
{
207+
exprt copy = expr;
208+
bool copy_differs_from_expr = false;
209+
for(std::size_t i = 0; i < expr.operands().size(); ++i)
210+
{
211+
auto new_op =
212+
add_node(dependencies, expr.operands()[i], array_pool, fresh_symbol);
213+
if(new_op)
214+
{
215+
copy.operands()[i] = *new_op;
216+
copy_differs_from_expr = true;
217+
}
218+
}
219+
if(copy_differs_from_expr)
220+
return copy;
221+
return {};
222+
}
207223

224+
const exprt return_code = fresh_symbol("string_builtin_return", expr.type());
208225
auto builtin_function =
209-
to_string_builtin_function(*fun_app, equation.lhs(), array_pool);
226+
to_string_builtin_function(*fun_app, return_code, array_pool);
210227

211228
CHECK_RETURN(builtin_function != nullptr);
212-
const auto &builtin_function_node = dependencies.make_node(builtin_function);
213-
// Warning: `builtin_function` has been emptied and should not be used anymore
229+
const auto &builtin_function_node =
230+
dependencies.make_node(std::move(builtin_function));
214231

215232
if(
216233
const auto &string_result =
@@ -233,7 +250,7 @@ bool add_node(
233250
add_dependency_to_string_subexprs(
234251
dependencies, *fun_app, builtin_function_node, array_pool);
235252

236-
return true;
253+
return return_code;
237254
}
238255

239256
void string_dependenciest::for_each_dependency(

Diff for: src/solvers/strings/string_dependencies.h

+13-9
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,8 @@ class string_dependenciest
7979
std::unique_ptr<const string_nodet>
8080
node_at(const array_string_exprt &e) const;
8181

82-
/// `builtin_function` is reset to an empty pointer after the node is created
8382
builtin_function_nodet &
84-
make_node(std::unique_ptr<string_builtin_functiont> &builtin_function);
83+
make_node(std::unique_ptr<string_builtin_functiont> builtin_function);
8584
const string_builtin_functiont &
8685
get_builtin_function(const builtin_function_nodet &node) const;
8786

@@ -183,22 +182,27 @@ class string_dependenciest
183182
const std::function<void(const nodet &)> &f) const;
184183
};
185184

186-
/// When right hand side of equation is a builtin_function add
185+
/// When a sub-expression of \p expr is a builtin_function, add
187186
/// a "string_builtin_function" node to the graph and connect it to the strings
188187
/// on which it depends and which depends on it.
189188
/// If the string builtin_function is not a supported one, mark all the string
190189
/// arguments as depending on an unknown builtin_function.
191190
/// \param dependencies: graph to which we add the node
192-
/// \param equation: an equation whose right hand side is possibly a call to a
191+
/// \param expr: an expression which may contain a call to a
193192
/// string builtin_function.
194193
/// \param array_pool: array pool containing arrays corresponding to the string
195194
/// exprt arguments of the builtin_function call
196-
/// \return true if a node was added, if false is returned it either means that
197-
/// the right hand side is not a function application
195+
/// \param fresh_symbol: used to create new symbols for the return values of
196+
/// builtin functions
197+
/// \return An expression in which function applications have been replaced
198+
/// by symbols corresponding to the `return_value` field of the associated
199+
/// builtin function. Or an empty optional when no function applications has
200+
/// been encountered
198201
/// \todo there should be a class with just the three functions we require here
199-
bool add_node(
202+
optionalt<exprt> add_node(
200203
string_dependenciest &dependencies,
201-
const equal_exprt &equation,
202-
array_poolt &array_pool);
204+
const exprt &expr,
205+
array_poolt &array_pool,
206+
symbol_generatort &fresh_symbol);
203207

204208
#endif // CPROVER_SOLVERS_STRINGS_STRING_DEPENDENCIES_H

Diff for: src/solvers/strings/string_refinement.cpp

+65-43
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Author: Alberto Griggio, [email protected]
2727
#include <util/expr_iterator.h>
2828
#include <util/expr_util.h>
2929
#include <util/magic.h>
30+
#include <util/range.h>
3031
#include <util/simplify_expr.h>
3132

3233
#include "equation_symbol_mapping.h"
@@ -239,23 +240,24 @@ static std::vector<exprt> generate_instantiations(
239240
return lemmas;
240241
}
241242

242-
/// Fill the array_pointer correspondence and replace the right hand sides of
243-
/// the corresponding equations
243+
/// If \p expr is an equation whose right-hand-side is a
244+
/// associate_array_to_pointer call, add the correspondence and replace the call
245+
/// by an expression representing its result.
244246
static void make_char_array_pointer_associations(
245247
string_constraint_generatort &generator,
246-
std::vector<equal_exprt> &equations)
248+
exprt &expr)
247249
{
248-
for(equal_exprt &eq : equations)
250+
if(const auto equal_expr = expr_try_dynamic_cast<equal_exprt>(expr))
249251
{
250252
if(
251-
const auto fun_app =
252-
expr_try_dynamic_cast<function_application_exprt>(eq.rhs()))
253+
const auto fun_app = expr_try_dynamic_cast<function_application_exprt>(
254+
as_const(equal_expr->rhs())))
253255
{
254256
const auto new_equation =
255-
generator.make_array_pointer_association(eq.lhs(), *fun_app);
257+
generator.make_array_pointer_association(equal_expr->lhs(), *fun_app);
256258
if(new_equation)
257259
{
258-
eq =
260+
expr =
259261
equal_exprt{from_integer(true, new_equation->type()), *new_equation};
260262
}
261263
}
@@ -281,18 +283,10 @@ void string_refinementt::set_to(const exprt &expr, bool value)
281283
{
282284
PRECONDITION(expr.type().id() == ID_bool);
283285
PRECONDITION(equality_propagation);
284-
285-
if(expr.id() == ID_equal && value)
286-
{
287-
const equal_exprt &eq_expr = to_equal_expr(expr);
288-
equations.push_back(eq_expr);
289-
}
286+
if(!value)
287+
equations.push_back(not_exprt{expr});
290288
else
291-
{
292-
INVARIANT(
293-
!has_char_array_subexpr(expr, ns), "char array only appear in equations");
294-
supert::set_to(expr, value);
295-
}
289+
equations.push_back(expr);
296290
}
297291

298292
/// Add association for each char pointer in the equation
@@ -305,16 +299,19 @@ void string_refinementt::set_to(const exprt &expr, bool value)
305299
/// by an equation are associated to the same element
306300
static void add_equations_for_symbol_resolution(
307301
union_find_replacet &symbol_solver,
308-
const std::vector<equal_exprt> &equations,
302+
const std::vector<exprt> &equations,
309303
const namespacet &ns,
310304
messaget::mstreamt &stream)
311305
{
312306
const std::string log_message =
313307
"WARNING string_refinement.cpp generate_symbol_resolution_from_equations:";
314-
for(const equal_exprt &eq : equations)
308+
auto equalities = make_range(equations).filter(
309+
[&](const exprt &e) { return can_cast_expr<equal_exprt>(e); });
310+
for(const exprt &e : equalities)
315311
{
316-
const exprt &lhs = eq.lhs();
317-
const exprt &rhs = eq.rhs();
312+
const equal_exprt &eq = to_equal_expr(e);
313+
const exprt &lhs = to_equal_expr(eq).lhs();
314+
const exprt &rhs = to_equal_expr(eq).rhs();
318315
if(lhs.id() != ID_symbol)
319316
{
320317
stream << log_message << "non symbol lhs: " << format(lhs)
@@ -463,7 +460,7 @@ static void add_string_equation_to_symbol_resolution(
463460
/// \param stream: output stream
464461
/// \return union_find_replacet structure containing the correspondences.
465462
union_find_replacet string_identifiers_resolution_from_equations(
466-
std::vector<equal_exprt> &equations,
463+
const std::vector<equal_exprt> &equations,
467464
const namespacet &ns,
468465
messaget::mstreamt &stream)
469466
{
@@ -534,13 +531,11 @@ union_find_replacet string_identifiers_resolution_from_equations(
534531

535532
#ifdef DEBUG
536533
/// Output a vector of equations to the given stream, used for debugging.
537-
static void output_equations(
538-
std::ostream &output,
539-
const std::vector<equal_exprt> &equations)
534+
static void
535+
output_equations(std::ostream &output, const std::vector<exprt> &equations)
540536
{
541537
for(std::size_t i = 0; i < equations.size(); ++i)
542-
output << " [" << i << "] " << format(equations[i].lhs())
543-
<< " == " << format(equations[i].rhs()) << std::endl;
538+
output << " [" << i << "] " << format(equations[i]) << std::endl;
544539
}
545540
#endif
546541

@@ -629,7 +624,18 @@ decision_proceduret::resultt string_refinementt::dec_solve()
629624
#endif
630625

631626
const union_find_replacet string_id_symbol_resolve =
632-
string_identifiers_resolution_from_equations(equations, ns, log.debug());
627+
string_identifiers_resolution_from_equations(
628+
[&] {
629+
std::vector<equal_exprt> equalities;
630+
for(const auto &eq : equations)
631+
{
632+
if(auto equal_expr = expr_try_dynamic_cast<equal_exprt>(eq))
633+
equalities.push_back(*equal_expr);
634+
}
635+
return equalities;
636+
}(),
637+
ns,
638+
log.debug());
633639
#ifdef DEBUG
634640
log.debug() << "symbol resolve string:" << messaget::eom;
635641
for(const auto &pair : string_id_symbol_resolve.to_vector())
@@ -639,39 +645,55 @@ decision_proceduret::resultt string_refinementt::dec_solve()
639645
}
640646
#endif
641647

642-
log.debug() << "dec_solve: Replacing string ids in function applications"
648+
log.debug() << "dec_solve: Replacing string ids and simplifying arguments"
649+
" in function applications"
643650
<< messaget::eom;
644-
for(equal_exprt &eq : equations)
651+
for(exprt &expr : equations)
645652
{
646-
if(can_cast_expr<function_application_exprt>(eq.rhs()))
653+
auto it = expr.depth_begin();
654+
while(it != expr.depth_end())
647655
{
648-
simplify(eq.rhs(), ns);
649-
string_id_symbol_resolve.replace_expr(eq.rhs());
656+
if(can_cast_expr<function_application_exprt>(*it))
657+
{
658+
// Simplification is required because the array pool may not realize
659+
// that an expression like
660+
// `(unsignedbv[16]*)((signedbv[8]*)&constarray[0] + 0)` is the
661+
// same pointer as `&constarray[0]
662+
simplify(it.mutate(), ns);
663+
string_id_symbol_resolve.replace_expr(it.mutate());
664+
it.next_sibling_or_parent();
665+
}
666+
else
667+
++it;
650668
}
651669
}
652670

653671
// Constraints start clear at each `dec_solve` call.
654672
string_constraintst constraints;
655-
make_char_array_pointer_associations(generator, equations);
673+
for(auto &expr : equations)
674+
make_char_array_pointer_associations(generator, expr);
656675

657676
#ifdef DEBUG
658677
output_equations(log.debug(), equations);
659678
#endif
660679

661680
log.debug() << "dec_solve: compute dependency graph and remove function "
662681
<< "applications captured by the dependencies:" << messaget::eom;
663-
std::vector<equal_exprt> local_equations;
664-
for(const equal_exprt &eq : equations)
682+
std::vector<exprt> local_equations;
683+
for(const exprt &eq : equations)
665684
{
666685
// Ensures that arrays that are equal, are associated to the same nodes
667686
// in the graph.
668-
const equal_exprt eq_with_char_array_replaced_with_representative_elements =
669-
to_equal_expr(replace_expr_copy(symbol_resolve, eq));
670-
const bool node_added = add_node(
687+
const exprt eq_with_char_array_replaced_with_representative_elements =
688+
replace_expr_copy(symbol_resolve, eq);
689+
const optionalt<exprt> new_equation = add_node(
671690
dependencies,
672691
eq_with_char_array_replaced_with_representative_elements,
673-
generator.array_pool);
674-
if(!node_added)
692+
generator.array_pool,
693+
generator.fresh_symbol);
694+
if(new_equation)
695+
local_equations.push_back(*new_equation);
696+
else
675697
local_equations.push_back(eq);
676698
}
677699
equations.clear();

Diff for: src/solvers/strings/string_refinement.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class string_refinementt final : public bv_refinementt
111111
index_set_pairt index_sets;
112112
union_find_replacet symbol_resolve;
113113

114-
std::vector<equal_exprt> equations;
114+
std::vector<exprt> equations;
115115

116116
string_dependenciest dependencies;
117117

@@ -122,7 +122,7 @@ exprt substitute_array_lists(exprt expr, std::size_t string_max_length);
122122

123123
// Declaration required for unit-test:
124124
union_find_replacet string_identifiers_resolution_from_equations(
125-
std::vector<equal_exprt> &equations,
125+
const std::vector<equal_exprt> &equations,
126126
const namespacet &ns,
127127
messaget::mstreamt &stream);
128128

Diff for: unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ SRC += analyses/ai/ai.cpp \
5757
solvers/strings/string_format_builtin_function/length_of_decimal_int.cpp \
5858
solvers/strings/string_refinement/concretize_array.cpp \
5959
solvers/strings/string_refinement/sparse_array.cpp \
60+
solvers/strings/string_refinement/string_refinement.cpp \
6061
solvers/strings/string_refinement/substitute_array_list.cpp \
6162
solvers/strings/string_refinement/union_find_replace.cpp \
6263
util/allocate_objects.cpp \

Diff for: unit/solvers/strings/string_refinement/module_dependencies.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
solvers/refinement
2+
solvers/sat
23
solvers/strings
34
string_refinement
45
testing-utils

0 commit comments

Comments
 (0)