Skip to content

Commit 53b5c0b

Browse files
author
Joel Allred
authored
Merge pull request #4626 from allredj/string-stop-using-array-size
String solver: stop relying on array size
2 parents 5f97381 + cd61e81 commit 53b5c0b

23 files changed

+906
-441
lines changed
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
test.class
3+
--function test.main --trace
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[.*assertion.1\].* line 6.* FAILURE$
7+
abc_constarray=\{ 'a', 'b', 'c' \}
8+
--
9+
--
10+
Check that the string literal used in the constructor is properly concretized
11+
in the trace even if it is not part of the equation.
12+
This is necessary for test generation.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
public class test
2+
{
3+
public static void main()
4+
{
5+
String x = new String("abc");
6+
assert false;
7+
}
8+
}

jbmc/unit/solvers/strings/string_constraint_instantiation/instantiate_not_contains.cpp

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,25 @@ exprt get_data_pointer(const array_string_exprt &arr)
8888

8989
/// Creates a `string_exprt` of the proper string type.
9090
/// \param [in] str: string to convert
91+
/// \param array_pool: pool of arrays representing strings
9192
/// \return corresponding `string_exprt`
92-
refined_string_exprt make_refined_string_exprt(const array_string_exprt &arr)
93+
refined_string_exprt make_refined_string_exprt(
94+
const array_string_exprt &arr,
95+
array_poolt &array_pool)
9396
{
94-
return refined_string_exprt(arr.length(), get_data_pointer(arr));
97+
return refined_string_exprt(
98+
array_pool.get_or_create_length(arr), get_data_pointer(arr));
9599
}
96100

97101
/// For a constant `string_exprt`, creates a full index set.
98102
/// \param [in] s: `string_exprt` to create index set for
103+
/// \param array_pool: pool of arrays representing strings
99104
/// \return the corresponding index set
100-
std::set<exprt> full_index_set(const array_string_exprt &s)
105+
std::set<exprt>
106+
full_index_set(const array_string_exprt &s, array_poolt &array_pool)
101107
{
102-
const mp_integer n = numeric_cast_v<mp_integer>(to_constant_expr(s.length()));
108+
const mp_integer n = numeric_cast_v<mp_integer>(
109+
to_constant_expr(to_array_type(s.type()).size()));
103110
std::set<exprt> ret;
104111
for(mp_integer i = 0; i < n; ++i)
105112
ret.insert(from_integer(i));
@@ -179,17 +186,20 @@ SCENARIO(
179186
// initialize architecture with sensible default values
180187
config.set_arch("none");
181188

189+
symbol_generatort symbol_generator;
190+
array_poolt array_pool(symbol_generator);
191+
182192
// Creating strings
183193
const auto ab_array = make_string_exprt("ab");
184194
const auto b_array = make_string_exprt("b");
185195
const auto a_array = make_string_exprt("a");
186196
const auto empty_array = make_string_exprt("");
187197
const auto cd_array = make_string_exprt("cd");
188-
const auto ab = make_refined_string_exprt(ab_array);
189-
const auto b = make_refined_string_exprt(b_array);
190-
const auto a = make_refined_string_exprt(a_array);
191-
const auto empty = make_refined_string_exprt(empty_array);
192-
const auto cd = make_refined_string_exprt(cd_array);
198+
const auto ab = make_refined_string_exprt(ab_array, array_pool);
199+
const auto b = make_refined_string_exprt(b_array, array_pool);
200+
const auto a = make_refined_string_exprt(a_array, array_pool);
201+
const auto empty = make_refined_string_exprt(empty_array, array_pool);
202+
const auto cd = make_refined_string_exprt(cd_array, array_pool);
193203

194204
GIVEN("The not_contains axioms of String.lastIndexOf(String, Int)")
195205
{
@@ -247,8 +257,8 @@ SCENARIO(
247257
WHEN("we instantiate and simplify")
248258
{
249259
// Making index sets
250-
const std::set<exprt> index_set_ab = full_index_set(ab_array);
251-
const std::set<exprt> index_set_b = full_index_set(b_array);
260+
const std::set<exprt> index_set_ab = full_index_set(ab_array, array_pool);
261+
const std::set<exprt> index_set_b = full_index_set(b_array, array_pool);
252262

253263
// List of new lemmas to be returned
254264
std::vector<exprt> lemmas;
@@ -305,7 +315,7 @@ SCENARIO(
305315
WHEN("we instantiate and simplify")
306316
{
307317
// Making index sets
308-
const std::set<exprt> index_set_a = full_index_set(a_array);
318+
const std::set<exprt> index_set_a = full_index_set(a_array, array_pool);
309319

310320
// Instantiate the lemmas
311321
std::vector<exprt> lemmas = instantiate_not_contains(
@@ -355,8 +365,8 @@ SCENARIO(
355365
WHEN("we instantiate and simplify")
356366
{
357367
// Making index sets
358-
const std::set<exprt> index_set_a = full_index_set(a_array);
359-
const std::set<exprt> index_set_b = full_index_set(b_array);
368+
const std::set<exprt> index_set_a = full_index_set(a_array, array_pool);
369+
const std::set<exprt> index_set_b = full_index_set(b_array, array_pool);
360370

361371
// Instantiate the lemmas
362372
std::vector<exprt> lemmas = instantiate_not_contains(
@@ -406,7 +416,7 @@ SCENARIO(
406416
WHEN("we instantiate and simplify")
407417
{
408418
// Making index sets
409-
const std::set<exprt> index_set_a = full_index_set(a_array);
419+
const std::set<exprt> index_set_a = full_index_set(a_array, array_pool);
410420
const std::set<exprt> index_set_empty = {
411421
generator.fresh_symbol("z", t.length_type())};
412422

@@ -459,7 +469,7 @@ SCENARIO(
459469
WHEN("we instantiate and simplify")
460470
{
461471
// Making index sets
462-
const std::set<exprt> index_set_ab = full_index_set(ab_array);
472+
const std::set<exprt> index_set_ab = full_index_set(ab_array, array_pool);
463473

464474
// Instantiate the lemmas
465475
std::vector<exprt> lemmas = instantiate_not_contains(
@@ -510,8 +520,8 @@ SCENARIO(
510520
WHEN("we instantiate and simplify")
511521
{
512522
// Making index sets
513-
const std::set<exprt> index_set_ab = full_index_set(ab_array);
514-
const std::set<exprt> index_set_cd = full_index_set(cd_array);
523+
const std::set<exprt> index_set_ab = full_index_set(ab_array, array_pool);
524+
const std::set<exprt> index_set_cd = full_index_set(cd_array, array_pool);
515525

516526
// Instantiate the lemmas
517527
std::vector<exprt> lemmas = instantiate_not_contains(

src/solvers/strings/array_pool.cpp

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,48 @@ operator()(const irep_idt &prefix, const typet &type)
2121
return result;
2222
}
2323

24-
exprt array_poolt::get_length(const array_string_exprt &s) const
24+
exprt array_poolt::get_or_create_length(const array_string_exprt &s)
2525
{
26-
if(s.length() == infinity_exprt(s.length().type()))
26+
if(const auto &if_expr = expr_try_dynamic_cast<if_exprt>((exprt)s))
2727
{
28-
auto it = length_of_array.find(s);
29-
if(it != length_of_array.end())
30-
return it->second;
28+
return if_exprt{
29+
if_expr->cond(),
30+
get_or_create_length(to_array_string_expr(if_expr->true_case())),
31+
get_or_create_length(to_array_string_expr(if_expr->false_case()))};
3132
}
32-
return s.length();
33+
34+
auto emplace_result =
35+
length_of_array.emplace(s, symbol_exprt(s.length_type()));
36+
if(emplace_result.second)
37+
{
38+
emplace_result.first->second =
39+
fresh_symbol("string_length", s.length_type());
40+
}
41+
42+
return emplace_result.first->second;
43+
}
44+
45+
optionalt<exprt>
46+
array_poolt::get_length_if_exists(const array_string_exprt &s) const
47+
{
48+
auto find_result = length_of_array.find(s);
49+
if(find_result != length_of_array.end())
50+
return find_result->second;
51+
return {};
3352
}
3453

3554
array_string_exprt
3655
array_poolt::fresh_string(const typet &index_type, const typet &char_type)
3756
{
38-
symbol_exprt length = fresh_symbol("string_length", index_type);
39-
array_typet array_type(char_type, length);
57+
array_typet array_type{char_type, infinity_exprt(index_type)};
4058
symbol_exprt content = fresh_symbol("string_content", array_type);
4159
array_string_exprt str = to_array_string_expr(content);
42-
created_strings_value.insert(str);
60+
arrays_of_pointers.emplace(
61+
address_of_exprt(index_exprt(str, from_integer(0, index_type))), str);
62+
63+
// add length to length_of_array map
64+
get_or_create_length(str);
65+
4366
return str;
4467
}
4568

@@ -87,40 +110,73 @@ array_string_exprt array_poolt::make_char_array_for_char_pointer(
87110
to_array_string_expr(fresh_symbol(symbol_name, char_array_type));
88111
const auto insert_result =
89112
arrays_of_pointers.insert({char_pointer, array_sym});
113+
114+
// add length to length_of_array map
115+
get_or_create_length(array_sym);
116+
90117
return to_array_string_expr(insert_result.first->second);
91118
}
92119

120+
/// Given an array_string_exprt, get the size of the underlying array. If that
121+
/// size is undefined, create a new symbol for the size.
122+
/// Then add an entry from `array_expr` to that size in the `length_of_array`
123+
/// map.
124+
///
125+
/// This function should only be used at the creation of the
126+
/// `array_string_exprt`s, as it is the only place where we can reliably refer
127+
/// to the size in the type of the array.
128+
static void attempt_assign_length_from_type(
129+
const array_string_exprt &array_expr,
130+
std::unordered_map<array_string_exprt, exprt, irep_hash> &length_of_array,
131+
symbol_generatort &symbol_generator)
132+
{
133+
DATA_INVARIANT(
134+
array_expr.id() != ID_if,
135+
"attempt_assign_length_from_type does not handle if exprts");
136+
// This invariant seems always true, but I don't know why.
137+
// If we find a case where this is violated, try calling
138+
// attempt_assign_length_from_type on the true and false cases.
139+
const exprt &size_from_type = to_array_type(array_expr.type()).size();
140+
const exprt &size_to_assign =
141+
size_from_type != infinity_exprt(size_from_type.type())
142+
? size_from_type
143+
: symbol_generator("string_length", array_expr.length_type());
144+
145+
const auto emplace_result =
146+
length_of_array.emplace(array_expr, size_to_assign);
147+
INVARIANT(
148+
emplace_result.second,
149+
"attempt_assign_length_from_type should only be called when no entry"
150+
"for the array_string_exprt exists in the length_of_array map");
151+
}
152+
93153
void array_poolt::insert(
94154
const exprt &pointer_expr,
95-
array_string_exprt &array_expr)
155+
const array_string_exprt &array_expr)
96156
{
97-
const exprt &length = array_expr.length();
98-
if(length == infinity_exprt(length.type()))
99-
{
100-
auto pair = length_of_array.insert(
101-
std::make_pair(array_expr, fresh_symbol("string_length", length.type())));
102-
array_expr.length() = pair.first->second;
103-
}
104-
105157
const auto it_bool =
106158
arrays_of_pointers.insert(std::make_pair(pointer_expr, array_expr));
107-
created_strings_value.insert(array_expr);
159+
108160
INVARIANT(
109161
it_bool.second, "should not associate two arrays to the same pointer");
162+
163+
attempt_assign_length_from_type(array_expr, length_of_array, fresh_symbol);
110164
}
111165

112-
const std::set<array_string_exprt> &array_poolt::created_strings() const
166+
/// Return a map mapping all array_string_exprt of the array_pool to their
167+
/// length.
168+
const std::unordered_map<array_string_exprt, exprt, irep_hash> &
169+
array_poolt::created_strings() const
113170
{
114-
return created_strings_value;
171+
return length_of_array;
115172
}
116173

117-
const array_string_exprt &
118-
array_poolt::find(const exprt &pointer, const exprt &length)
174+
array_string_exprt array_poolt::find(const exprt &pointer, const exprt &length)
119175
{
120176
const array_typet array_type(pointer.type().subtype(), length);
121177
const array_string_exprt array =
122178
make_char_array_for_char_pointer(pointer, array_type);
123-
return *created_strings_value.insert(array).first;
179+
return array;
124180
}
125181

126182
array_string_exprt of_argument(array_poolt &array_pool, const exprt &arg)

src/solvers/strings/array_pool.h

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,31 @@ class array_poolt final
5353
return arrays_of_pointers;
5454
}
5555

56-
/// Associate an actual finite length to infinite arrays
56+
/// Get the length of an array_string_exprt from the array_pool. If the length
57+
/// does not yet exist, create a new symbol and add it to the pool.
58+
///
59+
/// If the array_string_exprt is an `if` expression, the true and false parts
60+
/// are handled independently (and recursively). That is, no new length symbol
61+
/// is added to the pool for `if` expressions, but symbols may be added for
62+
/// each of the parts.
5763
/// \param s: array expression representing a string
5864
/// \return expression for the length of `s`
59-
exprt get_length(const array_string_exprt &s) const;
65+
exprt get_or_create_length(const array_string_exprt &s);
6066

61-
void insert(const exprt &pointer_expr, array_string_exprt &array);
67+
/// As opposed to get_length(), do not create a new symbol if the length
68+
/// of the array_string_exprt does not have one in the array_pool, but instead
69+
/// return an empty optional.
70+
/// \param s: array expression representing a string
71+
/// \return expression for the length of `s`, or empty optional
72+
optionalt<exprt> get_length_if_exists(const array_string_exprt &s) const;
73+
74+
void insert(const exprt &pointer_expr, const array_string_exprt &array);
6275

6376
/// Creates a new array if the pointer is not pointing to an array
64-
const array_string_exprt &find(const exprt &pointer, const exprt &length);
77+
array_string_exprt find(const exprt &pointer, const exprt &length);
6578

66-
const std::set<array_string_exprt> &created_strings() const;
79+
const std::unordered_map<array_string_exprt, exprt, irep_hash> &
80+
created_strings() const;
6781

6882
/// Construct a string expression whose length and content are new variables
6983
/// \param index_type: type used to index characters of the strings
@@ -74,18 +88,19 @@ class array_poolt final
7488

7589
private:
7690
/// Associate arrays to char pointers
91+
/// Invariant: Each array_string_exprt in this map has a corresponding entry
92+
/// in length_of_array.
93+
/// This is enforced whenever we add an element to
94+
/// `arrays_of_pointers`, i.e. fresh_string()
95+
/// and make_char_array_for_char_pointer().
7796
std::unordered_map<exprt, array_string_exprt, irep_hash> arrays_of_pointers;
7897

7998
/// Associate length to arrays of infinite size
80-
std::unordered_map<array_string_exprt, symbol_exprt, irep_hash>
81-
length_of_array;
99+
std::unordered_map<array_string_exprt, exprt, irep_hash> length_of_array;
82100

83101
/// Generate fresh symbols
84102
symbol_generatort &fresh_symbol;
85103

86-
/// Strings created in the pool
87-
std::set<array_string_exprt> created_strings_value;
88-
89104
array_string_exprt make_char_array_for_char_pointer(
90105
const exprt &char_pointer,
91106
const typet &char_array_type);

0 commit comments

Comments
 (0)