27#include <unordered_set>
33 bool _get_array_constraints)
36 log(_message_handler),
63 "record_array_equality got equality without matching types",
67 op0.
type().
id() == ID_array,
68 "record_array_equality parameter should be array-typed");
76 arrays.make_union(op0, op1);
88 symbol.
type().
id() == ID_array,
89 "record_array_let_binding parameter should be array-typed");
93 prop.l_set_to_true(eq_lit);
98 for(std::size_t i=0; i<
arrays.size(); i++)
106 if(expr.
id()!=ID_index)
108 if(expr.
id() == ID_array_comprehension)
112 for(
const auto &op : expr.
operands())
131 if(array_op_type.
id()==ID_array)
153 array_type == with_expr.
old().
type(),
154 "collect_arrays got 'with' without matching types",
164 else if(a.
id()==ID_update)
169 array_type == update_expr.
old().
type(),
170 "collect_arrays got 'update' without matching types",
182 else if(a.
id()==ID_if)
188 "collect_arrays got if without matching types",
193 "collect_arrays got if without matching types",
201 else if(a.
id()==ID_symbol)
204 else if(a.
id()==ID_nondet_symbol)
207 else if(a.
id()==ID_member)
212 struct_op.id() == ID_symbol || struct_op.id() == ID_nondet_symbol,
213 "unexpected array expression: member with '" + struct_op.id_string() +
216 else if(a.
is_constant() || a.
id() == ID_array || a.
id() == ID_string_constant)
219 else if(a.
id()==ID_array_of)
222 else if(a.
id()==ID_byte_update_little_endian ||
223 a.
id()==ID_byte_update_big_endian)
227 "byte_update should be removed before collect_arrays");
229 else if(a.
id()==ID_typecast)
235 typecast_op.type().id() == ID_array,
236 "unexpected array type cast from " + typecast_op.type().id_string());
238 arrays.make_union(a, typecast_op);
241 else if(a.
id()==ID_index)
245 arrays.make_union(a, array_op);
248 else if(a.
id() == ID_array_comprehension)
253 arrays.make_union(a, let_expr->where());
260 "unexpected array expression (collect_arrays): '" + a.
id_string() +
"'");
299 std::set<std::size_t> roots_to_process, updated_roots;
300 for(std::size_t i=0; i<
arrays.size(); i++)
301 roots_to_process.insert(
arrays.find_number(i));
303 while(!roots_to_process.empty())
305 for(std::size_t i = 0; i <
arrays.size(); i++)
307 if(roots_to_process.count(
arrays.find_number(i)) == 0)
318 updated_roots.insert(
arrays.find_number(u));
322 roots_to_process = std::move(updated_roots);
323 updated_roots.clear();
345 std::cout <<
"arrays.size(): " <<
arrays.size() <<
'\n';
349 for(std::size_t i=0; i<
arrays.size(); i++)
354 std::cout <<
"index_set.size(): " << index_set.size() <<
'\n';
358 for(index_sett::const_iterator
359 i1=index_set.begin();
362 for(index_sett::const_iterator
368 if(i1->is_constant() && i2->is_constant())
379 const typet &subtype =
384 index_expr2.
index()=*i2;
386 equal_exprt values_equal(index_expr1, index_expr2);
395 prop.lcnf(!indices_equal_lit,
convert(values_equal));
405 if(
arrays.is_root_number(i))
408 std::size_t root_number=
arrays.find_number(i);
409 INVARIANT(root_number!=i,
"is_root_number incorrect?");
414 root_index_set.insert(index_set.begin(), index_set.end());
428 for(std::size_t i=0; i<
arrays.size(); i++)
442 for(
const auto &index : index_entry.second)
443 std::cout <<
"Index set (" << index_entry.first <<
" = "
444 <<
arrays.find_number(index_entry.first) <<
" = "
446 <<
"): " <<
format(index) <<
'\n';
447 std::cout <<
"-----\n";
457 for(
const auto &index : index_set)
459 const typet &element_type1 =
461 index_exprt index_expr1(array_equality.
f1, index, element_type1);
463 const typet &element_type2 =
465 index_exprt index_expr2(array_equality.
f2, index, element_type2);
468 index_expr1.
type()==index_expr2.
type(),
469 "array elements should all have same type");
472 equal.
f1 = index_expr1;
473 equal.
f2 = index_expr2;
474 equal.
l = array_equality.
l;
475 equal_exprt equality_expr(index_expr1, index_expr2);
489 if(expr.
id()==ID_with)
491 else if(expr.
id()==ID_update)
493 else if(expr.
id()==ID_if)
495 else if(expr.
id()==ID_array_of)
497 else if(expr.
id() == ID_array)
499 else if(expr.
id() == ID_array_comprehension)
505 expr.
id() == ID_symbol || expr.
id() == ID_nondet_symbol ||
507 expr.
id() == ID_string_constant)
511 expr.
id() == ID_member &&
516 else if(expr.
id()==ID_byte_update_little_endian ||
517 expr.
id()==ID_byte_update_big_endian)
519 INVARIANT(
false,
"byte_update should be removed before arrayst");
521 else if(expr.
id()==ID_typecast)
527 for(
const auto &index : index_set)
530 index_exprt index_expr1(expr, index, element_type);
531 index_exprt index_expr2(expr_typecast_op, index, element_type);
534 index_expr1.
type()==index_expr2.
type(),
535 "array elements should all have same type");
544 else if(expr.
id()==ID_index)
552 exprt where = let_expr->where();
554 for(
const auto &binding :
555 make_range(let_expr->variables()).zip(let_expr->values()))
557 replace_symbol.
insert(binding.first, binding.second);
559 replace_symbol(where);
561 for(
const auto &index : index_set)
578 "unexpected array expression (add_array_constraints): '" +
589 std::unordered_set<exprt, irep_hash> updated_indices;
596 "with-expression operand should match array element type",
604 updated_indices.insert(expr.
where());
609 for(
auto other_index : index_set)
611 if(updated_indices.find(other_index) == updated_indices.end())
615 disjuncts.reserve(updated_indices.size());
616 for(
const auto &index : updated_indices)
627 index_exprt index_expr1(expr, other_index, element_type);
630 equal_exprt equality_expr(index_expr1, index_expr2);
645 bv.push_back(equality_lit);
646 bv.push_back(guard_lit);
663 const exprt &index=expr.where();
664 const exprt &value=expr.new_value();
671 "update operand should match array element type",
680 for(
auto other_index : index_set)
682 if(other_index!=index)
692 const typet &subtype=expr.type().subtype();
693 index_exprt index_expr1(expr, other_index, subtype);
694 index_exprt index_expr2(expr.op0(), other_index, subtype);
696 equal_exprt equality_expr(index_expr1, index_expr2);
703 bv.push_back(equality_lit);
704 bv.push_back(guard_lit);
720 for(
const auto &index : index_set)
727 "array_of operand type should match array element type");
744 for(
const auto &index : index_set)
747 const index_exprt index_expr{expr, index, element_type};
749 if(index.is_constant())
754 const std::optional<std::size_t> i =
757 if(!i.has_value() || *i >= operands.size())
760 const exprt v = operands[*i];
763 "array operand type should match array element type");
779 std::vector<std::pair<std::size_t, std::size_t>> ranges;
781 for(std::size_t i = 0; i < operands.size(); ++i)
783 if(ranges.empty() || operands[i] != operands[ranges.back().first])
784 ranges.emplace_back(i, i);
786 ranges.back().second = i;
789 for(
const auto &range : ranges)
791 exprt index_constraint;
793 if(range.first == range.second)
804 index, ID_le,
from_integer(range.second, index.type())}};
826 for(
const auto &index : index_set)
855 for(
const auto &index : index_set)
858 index_exprt index_expr1(expr, index, element_type);
874 for(
const auto &index : index_set)
877 index_exprt index_expr1(expr, index, element_type);
899 return "arrayAckermann";
907 return "arrayTypecast";
909 return "arrayConstant";
911 return "arrayComprehension";
913 return "arrayEquality";
927 size_t num_constraints = 0;
933 json_array_theory[contraint_type_string] =
936 num_constraints += it->second;
940 json_result[
"numOfConstraints"] =
942 log.status() <<
",\n" << json_result;
Theory of Arrays with Extensionality.
Boolean AND All operands must be boolean, and the result is always boolean.
Expression to define a mapping from an argument (index) to elements.
const symbol_exprt & arg() const
const exprt & body() const
Array constructor from list of elements.
const array_typet & type() const
Array constructor from single element.
const array_typet & type() const
const typet & element_type() const
The type of the elements of the array.
std::list< lazy_constraintt > lazy_array_constraints
std::unordered_set< irep_idt > array_comprehension_args
void add_array_constraints_equality(const index_sett &index_set, const array_equalityt &array_equality)
std::set< std::size_t > update_indices
void add_array_constraints_array_of(const index_sett &index_set, const array_of_exprt &exprt)
void add_array_Ackermann_constraints()
void add_array_constraints_update(const index_sett &index_set, const update_exprt &expr)
void collect_arrays(const exprt &a)
message_handlert & message_handler
void record_array_let_binding(const symbol_exprt &symbol, const exprt &value)
Record that symbol is equal to value for the purposes of the array theory.
union_find< exprt, irep_hash > arrays
literalt record_array_equality(const equal_exprt &expr)
void add_array_constraints_array_constant(const index_sett &index_set, const array_exprt &exprt)
arrayst(const namespacet &_ns, propt &_prop, message_handlert &message_handler, bool get_array_constraints=false)
std::map< exprt, bool > expr_map
void add_array_constraints_if(const index_sett &index_set, const if_exprt &exprt)
std::string enum_to_string(constraint_typet)
bool get_array_constraints
void add_array_constraints()
void add_array_constraints_comprehension(const index_sett &index_set, const array_comprehension_exprt &expr)
virtual bool is_unbounded_array(const typet &type) const =0
void record_array_index(const index_exprt &expr)
array_equalitiest array_equalities
std::set< exprt > index_sett
void add_array_constraint(const lazy_constraintt &lazy, bool refine=true)
adds array constraints (refine=true...lazily for the refinement loop)
array_constraint_countt array_constraint_count
void add_array_constraints_with(const index_sett &index_set, const with_exprt &expr)
void display_array_constraint_count()
void update_index_map(bool update_all)
A base class for expressions that are predicates, i.e., Boolean-typed, and that take exactly two argu...
void set_to_true(const exprt &)
For a Boolean expression expr, add the constraint 'expr'.
equalityt(propt &_prop, message_handlert &message_handler)
virtual literalt equality(const exprt &e1, const exprt &e2)
Base class for all expressions.
std::vector< exprt > operandst
bool is_constant() const
Return whether the expression is a constant.
typet & type()
Return the type of the expression.
The trinary if-then-else operator.
const std::string & id_string() const
const irep_idt & id() const
json_objectt & make_object()
const exprt & struct_op() const
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Boolean OR All operands must be boolean, and the result is always boolean.
literalt convert(const exprt &expr) override
Convert a Boolean expression and return the corresponding literal.
Replace a symbol expression by a given expression.
void insert(const class symbol_exprt &old_expr, const exprt &new_expr)
Sets old_expr to be replaced by new_expr if we don't already have a replacement; otherwise does nothi...
Expression to hold a symbol (variable).
static exprt conditional_cast(const exprt &expr, const typet &type)
The type of an expression, extends irept.
Operator to update elements in structs and arrays.
Operator to update elements in structs and arrays.
auto expr_try_dynamic_cast(TExpr &base) -> typename detail::expr_try_dynamic_cast_return_typet< T, TExpr >::type
Try to cast a reference to a generic exprt to a specific derived class.
auto lazy(funt fun) -> lazyt< decltype(fun())>
Delay the computation of fun to the next time the force method is called.
std::vector< literalt > bvt
literalt const_literal(bool value)
ranget< iteratort > make_range(iteratort begin, iteratort end)
bool replace_expr(const exprt &what, const exprt &by, exprt &dest)
#define UNREACHABLE
This should be used to mark dead code.
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
#define DATA_INVARIANT_WITH_DIAGNOSTICS(CONDITION, REASON,...)
exprt disjunction(const exprt::operandst &op)
1) generates a disjunction for two or more operands 2) for one operand, returns the operand 3) return...
API to expression classes.
const array_of_exprt & to_array_of_expr(const exprt &expr)
Cast an exprt to an array_of_exprt.
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
const array_comprehension_exprt & to_array_comprehension_expr(const exprt &expr)
Cast an exprt to a array_comprehension_exprt.
const array_exprt & to_array_expr(const exprt &expr)
Cast an exprt to an array_exprt.
const typecast_exprt & to_typecast_expr(const exprt &expr)
Cast an exprt to a typecast_exprt.
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
const with_exprt & to_with_expr(const exprt &expr)
Cast an exprt to a with_exprt.
const update_exprt & to_update_expr(const exprt &expr)
Cast an exprt to an update_exprt.
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.