Skip to content

Commit 768e8a6

Browse files
Merge pull request diffblue#2009 from romainbrenguier/solvers/sparse-arrays-in-get
[TG-2721] Use sparse arrays in string_refinementt::get
2 parents 69f6e7b + 64b336a commit 768e8a6

18 files changed

+408
-375
lines changed
985 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import org.cprover.CProverString;
2+
3+
public class Test {
4+
public static void check(String s, String t) {
5+
// Filter
6+
if(s == null || t == null)
7+
return;
8+
9+
// Act
10+
String u = s.concat(t);
11+
12+
// Filter out
13+
if(u.length() < 3_000_000)
14+
return;
15+
if(CProverString.charAt(u, 500_000) != 'a')
16+
return;
17+
if(CProverString.charAt(u, 2_000_000) != 'b')
18+
return;
19+
20+
// Assert
21+
assert(false);
22+
}
23+
24+
public static void checkAbort(String s, String t) {
25+
// Filter
26+
if(s == null || t == null)
27+
return;
28+
29+
// Act
30+
String u = s.concat(t);
31+
32+
// Filter out
33+
if(u.length() < 67_108_864)
34+
return;
35+
if(CProverString.charAt(u, 2_000_000) != 'b')
36+
return;
37+
38+
// Assert
39+
assert(false);
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
Test.class
3+
--refine-strings --function Test.check --string-max-input-length 2000000
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
assertion at file Test.java line 21 .* FAILURE
7+
--
8+
--
9+
This checks that the solver manage to violate assertions even when this requires
10+
some very long strings, as long as they don't exceed the arbitrary limit that
11+
is set by the solver (64M characters).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--refine-strings --function Test.checkAbort
4+
^EXIT=6$
5+
^SIGNAL=0$
6+
--
7+
--
8+
This tests should abort, because concretizing a string of the required
9+
length may take to much memory.

src/solvers/refinement/string_constraint_generator.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,6 @@ class string_constraint_generatort final
177177
array_string_exprt
178178
fresh_string(const typet &index_type, const typet &char_type);
179179
array_string_exprt get_string_expr(const exprt &expr);
180-
plus_exprt plus_exprt_with_overflow_check(const exprt &op1, const exprt &op2);
181-
182180

183181
static constant_exprt constant_char(int i, const typet &char_type);
184182

@@ -447,6 +445,9 @@ exprt minimum(const exprt &a, const exprt &b);
447445
/// \return expression representing the maximum of two expressions
448446
exprt maximum(const exprt &a, const exprt &b);
449447

448+
/// \return Boolean true when the sum of the two expressions overflows
449+
exprt sum_overflows(const plus_exprt &sum);
450+
450451
exprt length_constraint_for_concat_char(
451452
const array_string_exprt &res,
452453
const array_string_exprt &s1);

src/solvers/refinement/string_constraint_generator_code_points.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,11 @@ exprt string_constraint_generatort::add_axioms_for_code_point_at(
128128
const symbol_exprt result = fresh_symbol("char", return_type);
129129
const exprt index1 = from_integer(1, str.length().type());
130130
const exprt &char1=str[pos];
131-
const exprt &char2=str[plus_exprt_with_overflow_check(pos, index1)];
131+
const exprt &char2 = str[plus_exprt(pos, index1)];
132132
const typecast_exprt char1_as_int(char1, return_type);
133133
const typecast_exprt char2_as_int(char2, return_type);
134134
const exprt pair = pair_value(char1_as_int, char2_as_int, return_type);
135-
const exprt is_low =
136-
is_low_surrogate(str[plus_exprt_with_overflow_check(pos, index1)]);
135+
const exprt is_low = is_low_surrogate(str[plus_exprt(pos, index1)]);
137136
const and_exprt return_pair(is_high_surrogate(str[pos]), is_low);
138137

139138
lemmas.push_back(implies_exprt(return_pair, equal_exprt(result, pair)));
@@ -210,8 +209,8 @@ exprt string_constraint_generatort::add_axioms_for_offset_by_code_point(
210209
const typet &return_type=f.type();
211210
const symbol_exprt result = fresh_symbol("offset_by_code_point", return_type);
212211

213-
const exprt minimum = plus_exprt_with_overflow_check(index, offset);
214-
const exprt maximum = plus_exprt_with_overflow_check(minimum, offset);
212+
const exprt minimum = plus_exprt(index, offset);
213+
const exprt maximum = plus_exprt(minimum, offset);
215214
lemmas.push_back(binary_relation_exprt(result, ID_le, maximum));
216215
lemmas.push_back(binary_relation_exprt(result, ID_ge, minimum));
217216

src/solvers/refinement/string_constraint_generator_concat.cpp

+12-5
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ Author: Romain Brenguier, [email protected]
1818
/// at index `end_index'`.
1919
/// Where start_index' is max(0, start_index) and end_index' is
2020
/// max(min(end_index, s2.length), start_index')
21+
/// If s1.length + end_index' - start_index' is greater than the maximal integer
22+
/// of the type of res.length, then the result gets truncated to the size
23+
/// of this maximal integer.
2124
///
2225
/// These axioms are:
23-
/// 1. \f$|res| = |s_1| + end\_index' - start\_index' \f$
26+
/// 1. \f$|res| = overflow ? |s_1| + end\_index' - start\_index'
27+
/// : max_int \f$
2428
/// 2. \f$\forall i<|s_1|. res[i]=s_1[i] \f$
25-
/// 3. \f$\forall i< end\_index' - start\_index'.\ res[i+|s_1|]
26-
/// = s_2[start\_index'+i]\f$
29+
/// 3. \f$\forall i< |res| - |s_1|.\ res[i+|s_1|] = s_2[start\_index'+i]\f$
2730
///
2831
/// \param res: an array of characters expression
2932
/// \param s1: an array of characters expression
@@ -59,7 +62,8 @@ exprt string_constraint_generatort::add_axioms_for_concat_substr(
5962
fresh_univ_index("QA_index_concat2", res.length().type());
6063
const equal_exprt res_eq(
6164
res[plus_exprt(idx2, s1.length())], s2[plus_exprt(start1, idx2)]);
62-
return string_constraintt(idx2, minus_exprt(end1, start1), res_eq);
65+
const minus_exprt upper_bound(res.length(), s1.length());
66+
return string_constraintt(idx2, upper_bound, res_eq);
6367
}());
6468

6569
return from_integer(0, get_return_code_type());
@@ -77,10 +81,13 @@ exprt length_constraint_for_concat_substr(
7781
const exprt &start,
7882
const exprt &end)
7983
{
84+
PRECONDITION(res.length().type().id() == ID_signedbv);
8085
const exprt start1 = maximum(start, from_integer(0, start.type()));
8186
const exprt end1 = maximum(minimum(end, s2.length()), start1);
8287
const plus_exprt res_length(s1.length(), minus_exprt(end1, start1));
83-
return equal_exprt(res.length(), res_length);
88+
const exprt overflow = sum_overflows(res_length);
89+
const exprt max_int = to_signedbv_type(res.length().type()).largest_expr();
90+
return equal_exprt(res.length(), if_exprt(overflow, max_int, res_length));
8491
}
8592

8693
/// Add axioms enforcing that the length of `res` is that of the concatenation

src/solvers/refinement/string_constraint_generator_format.cpp

+24-10
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ exprt string_constraint_generatort::add_axioms_for_format(
358358
const typet &index_type = res.length().type();
359359

360360
for(const format_elementt &fe : format_strings)
361+
{
361362
if(fe.is_format_specifier())
362363
{
363364
const format_specifiert &fs=fe.get_format_specifier();
@@ -392,24 +393,37 @@ exprt string_constraint_generatort::add_axioms_for_format(
392393
add_axioms_for_constant(str, fe.get_format_text().get_content());
393394
intermediary_strings.push_back(str);
394395
}
396+
}
397+
398+
exprt return_code = from_integer(0, get_return_code_type());
395399

396400
if(intermediary_strings.empty())
397-
return to_array_string_expr(
398-
array_exprt(array_typet(char_type, from_integer(0, index_type))));
401+
{
402+
lemmas.push_back(equal_exprt(res.length(), from_integer(0, index_type)));
403+
return return_code;
404+
}
399405

400-
auto it=intermediary_strings.begin();
401-
array_string_exprt str = *(it++);
402-
exprt return_code = from_integer(0, signedbv_typet(32));
403-
for(; it!=intermediary_strings.end(); ++it)
406+
array_string_exprt str = intermediary_strings[0];
407+
408+
if(intermediary_strings.size() == 1)
404409
{
410+
// Copy the first string
411+
return add_axioms_for_substring(
412+
res, str, from_integer(0, index_type), str.length());
413+
}
414+
415+
// start after the first string and stop before the last
416+
for(std::size_t i = 1; i < intermediary_strings.size() - 1; ++i)
417+
{
418+
const array_string_exprt &intermediary = intermediary_strings[i];
405419
const array_string_exprt fresh = fresh_string(index_type, char_type);
406420
return_code =
407-
bitor_exprt(return_code, add_axioms_for_concat(fresh, str, *it));
421+
bitor_exprt(return_code, add_axioms_for_concat(fresh, str, intermediary));
408422
str = fresh;
409423
}
410-
// Copy
411-
add_axioms_for_substring(res, str, from_integer(0, index_type), str.length());
412-
return return_code;
424+
425+
return bitor_exprt(
426+
return_code, add_axioms_for_concat(res, str, intermediary_strings.back()));
413427
}
414428

415429
/// Construct a string from a constant array.

src/solvers/refinement/string_constraint_generator_indexof.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,7 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of(
352352
lemmas.push_back(a3);
353353

354354
const exprt index1 = from_integer(1, index_type);
355-
const exprt from_index_plus_one =
356-
plus_exprt_with_overflow_check(from_index, index1);
355+
const plus_exprt from_index_plus_one(from_index, index1);
357356
const if_exprt end_index(
358357
binary_relation_exprt(from_index_plus_one, ID_le, str.length()),
359358
from_index_plus_one,

src/solvers/refinement/string_constraint_generator_main.cpp

+13-25
Original file line numberDiff line numberDiff line change
@@ -130,31 +130,19 @@ symbol_exprt string_constraint_generatort::fresh_boolean(
130130
return b;
131131
}
132132

133-
/// Create a plus expression while adding extra constraints to axioms in order
134-
/// to prevent overflows.
135-
/// \param op1: First term of the sum
136-
/// \param op2: Second term of the sum
137-
/// \return A plus expression representing the sum of the arguments
138-
plus_exprt string_constraint_generatort::plus_exprt_with_overflow_check(
139-
const exprt &op1, const exprt &op2)
140-
{
141-
plus_exprt sum(plus_exprt(op1, op2));
142-
143-
exprt zero=from_integer(0, op1.type());
144-
145-
binary_relation_exprt neg1(op1, ID_lt, zero);
146-
binary_relation_exprt neg2(op2, ID_lt, zero);
147-
binary_relation_exprt neg_sum(sum, ID_lt, zero);
148-
149-
// We prevent overflows by adding the following constraint:
150-
// If the signs of the two operands are the same, then the sign of the sum
151-
// should also be the same.
152-
implies_exprt no_overflow(equal_exprt(neg1, neg2),
153-
equal_exprt(neg1, neg_sum));
154-
155-
lemmas.push_back(no_overflow);
156-
157-
return sum;
133+
exprt sum_overflows(const plus_exprt &sum)
134+
{
135+
PRECONDITION(sum.operands().size() == 2);
136+
const exprt zero = from_integer(0, sum.op0().type());
137+
const binary_relation_exprt op0_negative(sum.op0(), ID_lt, zero);
138+
const binary_relation_exprt op1_negative(sum.op1(), ID_lt, zero);
139+
const binary_relation_exprt sum_negative(sum, ID_lt, zero);
140+
141+
// overflow happens when we add two values of same sign but their sum has a
142+
// different sign
143+
return and_exprt(
144+
equal_exprt(op0_negative, op1_negative),
145+
notequal_exprt(op1_negative, sum_negative));
158146
}
159147

160148
/// Associate an actual finite length to infinite arrays

src/solvers/refinement/string_constraint_generator_testing.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@ exprt string_constraint_generatort::add_axioms_for_is_prefix(
3636
symbol_exprt isprefix=fresh_boolean("isprefix");
3737
const typet &index_type=str.length().type();
3838

39-
implies_exprt a1(
40-
isprefix,
41-
str.axiom_for_length_ge(plus_exprt_with_overflow_check(
42-
prefix.length(), offset)));
43-
lemmas.push_back(a1);
39+
// Axiom 1.
40+
lemmas.push_back(
41+
implies_exprt(
42+
isprefix, str.axiom_for_length_ge(plus_exprt(prefix.length(), offset))));
4443

4544
symbol_exprt qvar=fresh_univ_index("QA_isprefix", index_type);
4645
string_constraintt a2(

src/solvers/refinement/string_constraint_generator_transformation.cpp

+22-27
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,8 @@ exprt string_constraint_generatort::add_axioms_for_trim(
175175
const symbol_exprt idx = fresh_exist_index("index_trim", index_type);
176176
const exprt space_char = from_integer(' ', char_type);
177177

178-
exprt a1=str.axiom_for_length_ge(
179-
plus_exprt_with_overflow_check(idx, res.length()));
180-
lemmas.push_back(a1);
178+
// Axiom 1.
179+
lemmas.push_back(str.axiom_for_length_ge(plus_exprt(idx, res.length())));
181180

182181
binary_relation_exprt a2(idx, ID_ge, from_integer(0, index_type));
183182
lemmas.push_back(a2);
@@ -197,32 +196,31 @@ exprt string_constraint_generatort::add_axioms_for_trim(
197196
string_constraintt a6(n, idx, non_print);
198197
constraints.push_back(a6);
199198

200-
symbol_exprt n2=fresh_univ_index("QA_index_trim2", index_type);
201-
minus_exprt bound(str.length(), plus_exprt_with_overflow_check(idx,
202-
res.length()));
203-
binary_relation_exprt eqn2(
204-
str[plus_exprt(idx, plus_exprt(res.length(), n2))],
205-
ID_le,
206-
space_char);
207-
208-
string_constraintt a7(n2, bound, eqn2);
209-
constraints.push_back(a7);
199+
// Axiom 7.
200+
constraints.push_back([&] { // NOLINT
201+
const symbol_exprt n2 = fresh_univ_index("QA_index_trim2", index_type);
202+
const minus_exprt bound(minus_exprt(str.length(), idx), res.length());
203+
const binary_relation_exprt eqn2(
204+
str[plus_exprt(idx, plus_exprt(res.length(), n2))], ID_le, space_char);
205+
return string_constraintt(n2, bound, eqn2);
206+
}());
210207

211208
symbol_exprt n3=fresh_univ_index("QA_index_trim3", index_type);
212209
equal_exprt eqn3(res[n3], str[plus_exprt(n3, idx)]);
213210
string_constraintt a8(n3, res.length(), eqn3);
214211
constraints.push_back(a8);
215212

216-
minus_exprt index_before(
217-
plus_exprt_with_overflow_check(idx, res.length()),
218-
from_integer(1, index_type));
219-
binary_relation_exprt no_space_before(str[index_before], ID_gt, space_char);
220-
or_exprt a9(
221-
equal_exprt(idx, str.length()),
222-
and_exprt(
223-
binary_relation_exprt(str[idx], ID_gt, space_char),
224-
no_space_before));
225-
lemmas.push_back(a9);
213+
// Axiom 9.
214+
lemmas.push_back([&] {
215+
const plus_exprt index_before(
216+
idx, minus_exprt(res.length(), from_integer(1, index_type)));
217+
const binary_relation_exprt no_space_before(
218+
str[index_before], ID_gt, space_char);
219+
return or_exprt(
220+
equal_exprt(idx, str.length()),
221+
and_exprt(
222+
binary_relation_exprt(str[idx], ID_gt, space_char), no_space_before));
223+
}());
226224
return from_integer(0, f.type());
227225
}
228226

@@ -511,10 +509,7 @@ exprt string_constraint_generatort::add_axioms_for_delete_char_at(
511509
const array_string_exprt str = get_string_expr(f.arguments()[2]);
512510
exprt index_one=from_integer(1, str.length().type());
513511
return add_axioms_for_delete(
514-
res,
515-
str,
516-
f.arguments()[3],
517-
plus_exprt_with_overflow_check(f.arguments()[3], index_one));
512+
res, str, f.arguments()[3], plus_exprt(f.arguments()[3], index_one));
518513
}
519514

520515
/// Add axioms stating that `res` corresponds to the input `str`

0 commit comments

Comments
 (0)