Skip to content

Commit 5b6eb00

Browse files
author
Joel Allred
authored
Merge pull request #1668 from romainbrenguier/bugfix/string-index-of#TG-1846
Fix bugs in String.indexOf(c) TG-1846
2 parents 7b06a00 + f3b4c9b commit 5b6eb00

File tree

7 files changed

+106
-10
lines changed

7 files changed

+106
-10
lines changed
1.15 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
public class Test {
2+
3+
public boolean check(String input_String, char input_char, int input_int) {
4+
// Verify indexOf is conform to its specification
5+
int i = input_String.indexOf(input_char, input_int);
6+
7+
assert i < input_String.length();
8+
9+
int lower_bound;
10+
if (input_int < 0)
11+
lower_bound = 0;
12+
else
13+
lower_bound = input_int;
14+
15+
if (i == -1) {
16+
for (int j = lower_bound; j < input_String.length(); j++)
17+
assert input_String.charAt(j) != input_char;
18+
} else {
19+
assert i >= lower_bound;
20+
assert input_String.charAt(i) == input_char;
21+
22+
for (int j = lower_bound; j < i; j++)
23+
assert input_String.charAt(j) != input_char;
24+
}
25+
return true;
26+
}
27+
28+
public boolean check2() {
29+
// Verification should fail, this is to check the solver does
30+
// not get a contradiction
31+
int i = "hello".indexOf('o', 1);
32+
assert i == 4;
33+
i = "hello".indexOf('h', 1);
34+
assert i == -1;
35+
i = "hello".indexOf('e', 4);
36+
assert i == -1;
37+
i = "hello".indexOf('e', 8);
38+
assert i == -1;
39+
i = "hello".indexOf('x', 0);
40+
assert i == -1;
41+
i = "hello".indexOf('h', -1000);
42+
assert i == 0;
43+
assert false;
44+
return true;
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
Test.class
3+
--refine-strings --function Test.check --unwind 4 --string-max-length 3 --java-assume-inputs-non-null
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
Test.class
3+
--refine-strings --function Test.check2 --unwind 10 --string-max-length 10 --java-assume-inputs-non-null
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
assertion at file Test.java line 32 .* SUCCESS
7+
assertion at file Test.java line 34 .* SUCCESS
8+
assertion at file Test.java line 36 .* SUCCESS
9+
assertion at file Test.java line 38 .* SUCCESS
10+
assertion at file Test.java line 40 .* SUCCESS
11+
assertion at file Test.java line 42 .* SUCCESS
12+
assertion at file Test.java line 43 .* FAILURE
13+
^VERIFICATION FAILED$
14+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
THOROUGH
2+
Test.class
3+
--refine-strings --function Test.check --unwind 10 --string-max-length 10 --java-assume-inputs-non-null
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--

src/solvers/refinement/string_constraint_generator_indexof.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,19 @@ exprt string_constraint_generatort::add_axioms_for_index_of(
5959
equal_exprt(str[index], c)));
6060
axioms.push_back(a3);
6161

62+
const auto zero = from_integer(0, index_type);
63+
const if_exprt lower_bound(
64+
binary_relation_exprt(from_index, ID_le, zero), zero, from_index);
65+
6266
symbol_exprt n=fresh_univ_index("QA_index_of", index_type);
6367
string_constraintt a4(
64-
n, from_index, index, contains, not_exprt(equal_exprt(str[n], c)));
68+
n, lower_bound, index, contains, not_exprt(equal_exprt(str[n], c)));
6569
axioms.push_back(a4);
6670

6771
symbol_exprt m=fresh_univ_index("QA_index_of", index_type);
6872
string_constraintt a5(
6973
m,
70-
from_index,
74+
lower_bound,
7175
str.length(),
7276
not_exprt(contains),
7377
not_exprt(equal_exprt(str[m], c)));

src/solvers/refinement/string_refinement.cpp

+26-8
Original file line numberDiff line numberDiff line change
@@ -1076,12 +1076,19 @@ exprt fill_in_array_expr(const array_exprt &expr, std::size_t string_max_length)
10761076
/// expression will be: `index==0 ? 24 : index==2 ? 42 : 12`
10771077
/// * for an array access `(g1?arr1:arr2)[x]` where `arr1 := {12}` and
10781078
/// `arr2 := {34}`, the constructed expression will be: `g1 ? 12 : 34`
1079+
/// * for an access in an empty array `{ }[x]` returns a fresh symbol, this
1080+
/// corresponds to a non-deterministic result
10791081
/// \param expr: an expression containing array accesses
1082+
/// \param symbol_generator: function which given a prefix and a type generates
1083+
/// a fresh symbol of the given type
10801084
/// \return an expression containing no array access
1081-
static void substitute_array_access(exprt &expr)
1085+
static void substitute_array_access(
1086+
exprt &expr,
1087+
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1088+
&symbol_generator)
10821089
{
10831090
for(auto &op : expr.operands())
1084-
substitute_array_access(op);
1091+
substitute_array_access(op, symbol_generator);
10851092

10861093
if(expr.id()==ID_index)
10871094
{
@@ -1110,9 +1117,9 @@ static void substitute_array_access(exprt &expr)
11101117
// Substitute recursively in branches of conditional expressions
11111118
if_exprt if_expr=to_if_expr(index_expr.array());
11121119
exprt true_case=index_exprt(if_expr.true_case(), index_expr.index());
1113-
substitute_array_access(true_case);
1120+
substitute_array_access(true_case, symbol_generator);
11141121
exprt false_case=index_exprt(if_expr.false_case(), index_expr.index());
1115-
substitute_array_access(false_case);
1122+
substitute_array_access(false_case, symbol_generator);
11161123
expr=if_exprt(if_expr.cond(), true_case, false_case);
11171124
return;
11181125
}
@@ -1124,13 +1131,17 @@ static void substitute_array_access(exprt &expr)
11241131
"above"));
11251132
array_exprt &array_expr=to_array_expr(index_expr.array());
11261133

1127-
// Empty arrays do not need to be substituted.
1134+
const typet &char_type = index_expr.array().type().subtype();
1135+
1136+
// Access to an empty array is undefined (non deterministic result)
11281137
if(array_expr.operands().empty())
1138+
{
1139+
expr = symbol_generator("out_of_bound_access", char_type);
11291140
return;
1141+
}
11301142

11311143
size_t last_index=array_expr.operands().size()-1;
11321144

1133-
const typet &char_type=index_expr.array().type().subtype();
11341145
exprt ite=array_expr.operands().back();
11351146

11361147
if(ite.type()!=char_type)
@@ -1342,6 +1353,12 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
13421353
const auto eom=messaget::eom;
13431354
static const std::string indent = " ";
13441355
static const std::string indent2 = " ";
1356+
// clang-format off
1357+
const auto gen_symbol = [&](const irep_idt &id, const typet &type)
1358+
{
1359+
return generator.fresh_symbol(id, type);
1360+
};
1361+
// clang-format on
13451362

13461363
stream << "string_refinementt::check_axioms:" << eom;
13471364

@@ -1383,7 +1400,8 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
13831400
negaxiom = simplify_expr(negaxiom, ns);
13841401
exprt with_concretized_arrays =
13851402
concretize_arrays_in_expression(negaxiom, max_string_length, ns);
1386-
substitute_array_access(with_concretized_arrays);
1403+
1404+
substitute_array_access(with_concretized_arrays, gen_symbol);
13871405

13881406
stream << indent << i << ".\n";
13891407
debug_check_axioms_step(
@@ -1439,7 +1457,7 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
14391457
exprt with_concrete_arrays =
14401458
concretize_arrays_in_expression(negaxiom, max_string_length, ns);
14411459

1442-
substitute_array_access(with_concrete_arrays);
1460+
substitute_array_access(with_concrete_arrays, gen_symbol);
14431461

14441462
stream << indent << i << ".\n";
14451463
debug_check_axioms_step(

0 commit comments

Comments
 (0)