Skip to content

Commit 0142834

Browse files
authored
Merge pull request #4673 from tautschnig/array-constraints-fix
Record constraints of indexed access to array constants [blocks: #2108]
2 parents 09a5ad9 + 10177c6 commit 0142834

File tree

4 files changed

+129
-2
lines changed

4 files changed

+129
-2
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <assert.h>
2+
3+
// C semantics are that these will be zero
4+
int uninitializedGlobalArray1[2];
5+
int uninitializedGlobalArray2[2];
6+
7+
int main(void)
8+
{
9+
// Variable access
10+
long directUseReadLocation; // Non-det
11+
12+
long x = directUseReadLocation;
13+
if(0 <= directUseReadLocation && directUseReadLocation < 2)
14+
assert(uninitializedGlobalArray1[directUseReadLocation] == 0);
15+
16+
/*** Variable non-redundant update ***/
17+
// No obvious simplifications to writes
18+
19+
long nonRedundantWriteLocation;
20+
21+
if(
22+
0 <= nonRedundantWriteLocation && nonRedundantWriteLocation < 2 &&
23+
nonRedundantWriteLocation != 1)
24+
{
25+
uninitializedGlobalArray2[nonRedundantWriteLocation] = 1;
26+
}
27+
28+
// Constant access
29+
// Again, constant index into a fully known but non-constant array
30+
assert(uninitializedGlobalArray2[1] == 0);
31+
32+
return 0;
33+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
uf-always.c
3+
--arrays-uf-always
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
This test is extracted from main.c to showcase a bug in the array encoding.

src/solvers/flattening/arrays.cpp

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,11 +436,12 @@ void arrayst::add_array_constraints(
436436
return add_array_constraints_if(index_set, to_if_expr(expr));
437437
else if(expr.id()==ID_array_of)
438438
return add_array_constraints_array_of(index_set, to_array_of_expr(expr));
439+
else if(expr.id() == ID_array)
440+
return add_array_constraints_array_constant(index_set, to_array_expr(expr));
439441
else if(expr.id()==ID_symbol ||
440442
expr.id()==ID_nondet_symbol ||
441443
expr.id()==ID_constant ||
442444
expr.id()=="zero_string" ||
443-
expr.id()==ID_array ||
444445
expr.id()==ID_string_constant)
445446
{
446447
}
@@ -647,6 +648,85 @@ void arrayst::add_array_constraints_array_of(
647648
}
648649
}
649650

651+
void arrayst::add_array_constraints_array_constant(
652+
const index_sett &index_set,
653+
const array_exprt &expr)
654+
{
655+
// we got x = { v, ... } - add constraint x[i] = v
656+
const exprt::operandst &operands = expr.operands();
657+
658+
for(const auto &index : index_set)
659+
{
660+
const typet &subtype = expr.type().subtype();
661+
const index_exprt index_expr{expr, index, subtype};
662+
663+
if(index.is_constant())
664+
{
665+
// We have a constant index - just pick the element at that index from the
666+
// array constant.
667+
668+
const std::size_t i =
669+
numeric_cast_v<std::size_t>(to_constant_expr(index));
670+
// if the access is out of bounds, we leave it unconstrained
671+
if(i >= operands.size())
672+
continue;
673+
674+
const exprt v = operands[i];
675+
DATA_INVARIANT(
676+
index_expr.type() == v.type(),
677+
"array operand type should match array element type");
678+
679+
// add constraint
680+
lazy_constraintt lazy{lazy_typet::ARRAY_CONSTANT,
681+
equal_exprt{index_expr, v}};
682+
add_array_constraint(lazy, false); // added immediately
683+
}
684+
else
685+
{
686+
// We have a non-constant index into an array constant. We need to build a
687+
// case statement testing the index against all possible values. Whenever
688+
// neighbouring array elements are the same, we can test the index against
689+
// the range rather than individual elements. This should be particularly
690+
// helpful when we have arrays of zeros, as is the case for initializers.
691+
692+
std::vector<std::pair<std::size_t, std::size_t>> ranges;
693+
694+
for(std::size_t i = 0; i < operands.size(); ++i)
695+
{
696+
if(ranges.empty() || operands[i] != operands[ranges.back().first])
697+
ranges.emplace_back(i, i);
698+
else
699+
ranges.back().second = i;
700+
}
701+
702+
for(const auto &range : ranges)
703+
{
704+
exprt index_constraint;
705+
706+
if(range.first == range.second)
707+
{
708+
index_constraint =
709+
equal_exprt{index, from_integer(range.first, index.type())};
710+
}
711+
else
712+
{
713+
index_constraint = and_exprt{
714+
binary_predicate_exprt{
715+
from_integer(range.first, index.type()), ID_le, index},
716+
binary_predicate_exprt{
717+
index, ID_le, from_integer(range.second, index.type())}};
718+
}
719+
720+
lazy_constraintt lazy{
721+
lazy_typet::ARRAY_CONSTANT,
722+
implies_exprt{index_constraint,
723+
equal_exprt{index_expr, operands[range.first]}}};
724+
add_array_constraint(lazy, true); // added lazily
725+
}
726+
}
727+
}
728+
}
729+
650730
void arrayst::add_array_constraints_if(
651731
const index_sett &index_set,
652732
const if_exprt &expr)

src/solvers/flattening/arrays.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ class arrayst:public equalityt
8383
ARRAY_WITH,
8484
ARRAY_IF,
8585
ARRAY_OF,
86-
ARRAY_TYPECAST
86+
ARRAY_TYPECAST,
87+
ARRAY_CONSTANT
8788
};
8889

8990
struct lazy_constraintt
@@ -119,6 +120,9 @@ class arrayst:public equalityt
119120
const index_sett &index_set, const update_exprt &expr);
120121
void add_array_constraints_array_of(
121122
const index_sett &index_set, const array_of_exprt &exprt);
123+
void add_array_constraints_array_constant(
124+
const index_sett &index_set,
125+
const array_exprt &exprt);
122126

123127
void update_index_map(bool update_all);
124128
void update_index_map(std::size_t i);

0 commit comments

Comments
 (0)