Skip to content

Commit 730b3e2

Browse files
Merge pull request diffblue#2655 from romainbrenguier/feature/extend-builtin-functions
[TG-2892] Add builtin function class for string_of_int
2 parents e0f954d + 3a1593b commit 730b3e2

14 files changed

+261
-32
lines changed
Binary file not shown.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
public class Test
2+
{
3+
public static String checkDet()
4+
{
5+
String tmp = String.valueOf(1000000);
6+
tmp = String.valueOf(1000001);
7+
tmp = String.valueOf(1000002);
8+
tmp = String.valueOf(1000003);
9+
tmp = String.valueOf(1000004);
10+
tmp = String.valueOf(1000005);
11+
tmp = String.valueOf(-1000001);
12+
tmp = String.valueOf(-1000002);
13+
tmp = String.valueOf(-1000003);
14+
tmp = String.valueOf(1000004);
15+
tmp = String.valueOf(1000005);
16+
tmp = String.valueOf(-1000001);
17+
tmp = String.valueOf(-1000002);
18+
tmp = String.valueOf(1000003);
19+
tmp = String.valueOf(1000004);
20+
tmp = String.valueOf(1000005);
21+
tmp = String.valueOf(1000001);
22+
tmp = String.valueOf(-1000002);
23+
tmp = String.valueOf(-1000003);
24+
tmp = String.valueOf(1000004);
25+
tmp = String.valueOf(1000005);
26+
return tmp;
27+
}
28+
29+
public static String checkNonDet(int i)
30+
{
31+
String tmp = String.valueOf(1000000);
32+
tmp = String.valueOf(i + 1);
33+
tmp = String.valueOf(i + 2);
34+
tmp = String.valueOf(i + 3);
35+
tmp = String.valueOf(i + 4);
36+
tmp = String.valueOf(i + 5);
37+
tmp = String.valueOf(i - 1);
38+
tmp = String.valueOf(i - 2);
39+
tmp = String.valueOf(i - 3);
40+
tmp = String.valueOf(i - 4);
41+
tmp = String.valueOf(i - 5);
42+
tmp += " ";
43+
tmp += String.valueOf(i);
44+
tmp += " ";
45+
tmp += String.valueOf(i + 2);
46+
tmp += " ";
47+
tmp += String.valueOf(i + 3);
48+
tmp += " ";
49+
tmp += String.valueOf(-i);
50+
tmp += " ";
51+
tmp += String.valueOf(-i + 1);
52+
tmp += " ";
53+
tmp += String.valueOf(-i - 2);
54+
return tmp;
55+
}
56+
57+
public static void checkWithDependency(boolean b)
58+
{
59+
String s = String.valueOf(12);
60+
if (b) {
61+
assert(s.startsWith("12"));
62+
}
63+
else {
64+
assert(!s.startsWith("12"));
65+
}
66+
}
67+
68+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
Test.class
3+
--function Test.checkWithDependency --depth 10000
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
assertion at file Test.java line 61 .*: SUCCESS
7+
assertion at file Test.java line 64 .*: FAILURE
8+
--
9+
--
10+
Check that when a dependency is present, the correct constraints are added
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
Test.class
3+
--function Test.checkDet --verbosity 10 --cover location
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
coverage.* file Test.java line 25 .*: SATISFIED
7+
--
8+
adding lemma .*nondet_infinite_array
9+
--
10+
Check that using the string dependence informations, no lemma involving arrays is added
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
Test.class
3+
--function Test.checkNonDet --verbosity 10 --cover location
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
coverage.* file Test.java line 53 .*: SATISFIED
7+
--
8+
adding lemma .*nondet_infinite_array
9+
--
10+
Check that using the string dependence informations, no lemma involving arrays is added

src/solvers/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ allocates a new string before calling a primitive.
281281
\link string_constraint_generatort::add_axioms_from_literal(const function_application_exprt &f) More... \endlink
282282
* `cprover_string_of_int` :
283283
\copybrief string_constraint_generatort::add_axioms_from_int(const function_application_exprt &f)
284-
\link string_constraint_generatort::add_axioms_from_int(const function_application_exprt &f) More... \endlink
284+
\link string_constraint_generatort::add_axioms_for_string_of_int(const function_application_exprt &f) More... \endlink
285285
* `cprover_string_of_float` :
286286
\copybrief string_constraint_generatort::add_axioms_for_string_of_float(const function_application_exprt &f)
287287
\link string_constraint_generatort::add_axioms_for_string_of_float(const function_application_exprt &f) More... \endlink

src/solvers/refinement/string_builtin_function.cpp

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,19 +122,25 @@ optionalt<std::vector<mp_integer>> eval_string(
122122
return result;
123123
}
124124

125-
array_string_exprt
126-
make_string(const std::vector<mp_integer> &array, const array_typet &array_type)
125+
template <typename Iter>
126+
static array_string_exprt
127+
make_string(Iter begin, Iter end, const array_typet &array_type)
127128
{
128129
const typet &char_type = array_type.subtype();
129130
array_exprt array_expr(array_type);
130131
const auto &insert = std::back_inserter(array_expr.operands());
131-
std::transform(
132-
array.begin(), array.end(), insert, [&](const mp_integer &i) { // NOLINT
133-
return from_integer(i, char_type);
134-
});
132+
std::transform(begin, end, insert, [&](const mp_integer &i) {
133+
return from_integer(i, char_type);
134+
});
135135
return to_array_string_expr(array_expr);
136136
}
137137

138+
static array_string_exprt
139+
make_string(const std::vector<mp_integer> &array, const array_typet &array_type)
140+
{
141+
return make_string(array.begin(), array.end(), array_type);
142+
}
143+
138144
std::vector<mp_integer> string_concatenation_builtin_functiont::eval(
139145
const std::vector<mp_integer> &input1_value,
140146
const std::vector<mp_integer> &input2_value,
@@ -216,6 +222,84 @@ optionalt<exprt> string_insertion_builtin_functiont::eval(
216222
return make_string(result_value, type);
217223
}
218224

225+
/// Constructor from arguments of a function application.
226+
/// The arguments in `fun_args` should be in order:
227+
/// an integer `result.length`, a character pointer `&result[0]`,
228+
/// an expression `arg` which is to be converted to a string.
229+
/// Other arguments are ignored by this constructor.
230+
string_creation_builtin_functiont::string_creation_builtin_functiont(
231+
const exprt &return_code,
232+
const std::vector<exprt> &fun_args,
233+
array_poolt &array_pool)
234+
: string_builtin_functiont(return_code)
235+
{
236+
PRECONDITION(fun_args.size() >= 3);
237+
result = array_pool.find(fun_args[1], fun_args[0]);
238+
arg = fun_args[2];
239+
}
240+
241+
optionalt<exprt> string_of_int_builtin_functiont::eval(
242+
const std::function<exprt(const exprt &)> &get_value) const
243+
{
244+
const auto arg_value = numeric_cast<mp_integer>(get_value(arg));
245+
if(!arg_value)
246+
return {};
247+
static std::string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
248+
const auto radix_value = numeric_cast<mp_integer>(get_value(radix));
249+
if(!radix_value || *radix_value > digits.length())
250+
return {};
251+
252+
mp_integer current_value = *arg_value;
253+
std::vector<mp_integer> right_to_left_characters;
254+
if(current_value < 0)
255+
current_value = -current_value;
256+
if(current_value == 0)
257+
right_to_left_characters.emplace_back('0');
258+
while(current_value > 0)
259+
{
260+
const auto digit_value = (current_value % *radix_value).to_ulong();
261+
right_to_left_characters.emplace_back(digits.at(digit_value));
262+
current_value /= *radix_value;
263+
}
264+
if(*arg_value < 0)
265+
right_to_left_characters.emplace_back('-');
266+
267+
const auto length = right_to_left_characters.size();
268+
const auto length_expr = from_integer(length, result.length().type());
269+
const array_typet type(result.type().subtype(), length_expr);
270+
return make_string(
271+
right_to_left_characters.rbegin(), right_to_left_characters.rend(), type);
272+
}
273+
274+
exprt string_of_int_builtin_functiont::length_constraint() const
275+
{
276+
const typet &type = result.length().type();
277+
const auto radix_opt = numeric_cast<std::size_t>(radix);
278+
const auto radix_value = radix_opt.has_value() ? radix_opt.value() : 2;
279+
const std::size_t upper_bound =
280+
max_printed_string_length(arg.type(), radix_value);
281+
const exprt negative_arg =
282+
binary_relation_exprt(arg, ID_le, from_integer(0, type));
283+
const exprt absolute_arg =
284+
if_exprt(negative_arg, unary_minus_exprt(arg), arg);
285+
286+
exprt size_expr = from_integer(1, type);
287+
exprt min_int_with_current_size = from_integer(1, radix.type());
288+
for(std::size_t current_size = 2; current_size <= upper_bound + 1;
289+
++current_size)
290+
{
291+
min_int_with_current_size = mult_exprt(radix, min_int_with_current_size);
292+
const exprt at_least_current_size =
293+
binary_relation_exprt(absolute_arg, ID_ge, min_int_with_current_size);
294+
size_expr = if_exprt(
295+
at_least_current_size, from_integer(current_size, type), size_expr);
296+
}
297+
298+
const exprt size_expr_with_sign = if_exprt(
299+
negative_arg, plus_exprt(size_expr, from_integer(1, type)), size_expr);
300+
return equal_exprt(result.length(), size_expr_with_sign);
301+
}
302+
219303
string_builtin_function_with_no_evalt::string_builtin_function_with_no_evalt(
220304
const exprt &return_code,
221305
const function_application_exprt &f,

src/solvers/refinement/string_builtin_function.h

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,12 @@ class string_creation_builtin_functiont : public string_builtin_functiont
264264
{
265265
public:
266266
array_string_exprt result;
267-
std::vector<exprt> args;
268-
exprt return_code;
267+
exprt arg;
268+
269+
string_creation_builtin_functiont(
270+
const exprt &return_code,
271+
const std::vector<exprt> &fun_args,
272+
array_poolt &array_pool);
269273

270274
optionalt<array_string_exprt> string_result() const override
271275
{
@@ -278,6 +282,43 @@ class string_creation_builtin_functiont : public string_builtin_functiont
278282
}
279283
};
280284

285+
/// String creation from integer types
286+
class string_of_int_builtin_functiont : public string_creation_builtin_functiont
287+
{
288+
public:
289+
string_of_int_builtin_functiont(
290+
const exprt &return_code,
291+
const std::vector<exprt> &fun_args,
292+
array_poolt &array_pool)
293+
: string_creation_builtin_functiont(return_code, fun_args, array_pool)
294+
{
295+
PRECONDITION(fun_args.size() <= 4);
296+
if(fun_args.size() == 4)
297+
radix = fun_args[3];
298+
else
299+
radix = from_integer(10, arg.type());
300+
};
301+
302+
optionalt<exprt>
303+
eval(const std::function<exprt(const exprt &)> &get_value) const override;
304+
305+
std::string name() const override
306+
{
307+
return "string_of_int";
308+
}
309+
310+
exprt add_constraints(string_constraint_generatort &generator) const override
311+
{
312+
return generator.add_axioms_for_string_of_int_with_radix(
313+
result, arg, radix);
314+
}
315+
316+
exprt length_constraint() const override;
317+
318+
private:
319+
exprt radix;
320+
};
321+
281322
/// String test
282323
class string_test_builtin_functiont : public string_builtin_functiont
283324
{

src/solvers/refinement/string_constraint_generator.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ class string_constraint_generatort final
165165
const array_string_exprt &s1,
166166
const array_string_exprt &s2,
167167
const exprt &offset);
168+
exprt add_axioms_for_string_of_int_with_radix(
169+
const array_string_exprt &res,
170+
const exprt &input_int,
171+
const exprt &radix,
172+
size_t max_size = 0);
168173

169174
private:
170175
symbol_exprt fresh_boolean(const irep_idt &prefix);
@@ -258,15 +263,10 @@ class string_constraint_generatort final
258263
const exprt &guard);
259264
exprt add_axioms_from_literal(const function_application_exprt &f);
260265
exprt add_axioms_from_int(const function_application_exprt &f);
261-
exprt add_axioms_from_int(
266+
exprt add_axioms_for_string_of_int(
262267
const array_string_exprt &res,
263268
const exprt &input_int,
264269
size_t max_size = 0);
265-
exprt add_axioms_from_int_with_radix(
266-
const array_string_exprt &res,
267-
const exprt &input_int,
268-
const exprt &radix,
269-
size_t max_size = 0);
270270
exprt add_axioms_from_int_hex(const array_string_exprt &res, const exprt &i);
271271
exprt add_axioms_from_int_hex(const function_application_exprt &f);
272272
exprt add_axioms_from_long(const function_application_exprt &f);

src/solvers/refinement/string_constraint_generator_float.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ exprt string_constraint_generatort::add_axioms_for_string_of_float(
214214
const array_string_exprt integer_part_str =
215215
fresh_string(index_type, char_type);
216216
const exprt return_code2 =
217-
add_axioms_from_int(integer_part_str, integer_part, 8);
217+
add_axioms_for_string_of_int(integer_part_str, integer_part, 8);
218218

219219
return add_axioms_for_concat(res, integer_part_str, fractional_part_str);
220220
}
@@ -423,8 +423,8 @@ exprt string_constraint_generatort::add_axioms_from_float_scientific_notation(
423423

424424
array_string_exprt string_expr_integer_part =
425425
fresh_string(index_type, char_type);
426-
exprt return_code1 =
427-
add_axioms_from_int(string_expr_integer_part, dec_significand_int, 3);
426+
exprt return_code1 = add_axioms_for_string_of_int(
427+
string_expr_integer_part, dec_significand_int, 3);
428428
minus_exprt fractional_part(
429429
dec_significand, floatbv_of_int_expr(dec_significand_int, float_spec));
430430

@@ -467,7 +467,7 @@ exprt string_constraint_generatort::add_axioms_from_float_scientific_notation(
467467
const array_string_exprt exponent_string =
468468
fresh_string(index_type, char_type);
469469
const exprt return_code6 =
470-
add_axioms_from_int(exponent_string, decimal_exponent, 3);
470+
add_axioms_for_string_of_int(exponent_string, decimal_exponent, 3);
471471

472472
// string_expr = concat(string_expr_with_E, exponent_string)
473473
return add_axioms_for_concat(res, string_expr_with_E, exponent_string);

src/solvers/refinement/string_constraint_generator_format.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ string_constraint_generatort::add_axioms_for_format_specifier(
269269
{
270270
case format_specifiert::DECIMAL_INTEGER:
271271
return_code =
272-
add_axioms_from_int(res, get_component_in_struct(arg, ID_int));
272+
add_axioms_for_string_of_int(res, get_component_in_struct(arg, ID_int));
273273
return res;
274274
case format_specifiert::HEXADECIMAL_INTEGER:
275275
return_code =
@@ -293,8 +293,8 @@ string_constraint_generatort::add_axioms_for_format_specifier(
293293
case format_specifiert::STRING:
294294
return get_string_expr(get_component_in_struct(arg, "string_expr"));
295295
case format_specifiert::HASHCODE:
296-
return_code =
297-
add_axioms_from_int(res, get_component_in_struct(arg, "hashcode"));
296+
return_code = add_axioms_for_string_of_int(
297+
res, get_component_in_struct(arg, "hashcode"));
298298
return res;
299299
case format_specifiert::LINE_SEPARATOR:
300300
// TODO: the constant should depend on the system: System.lineSeparator()

src/solvers/refinement/string_constraint_generator_insert.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ exprt string_constraint_generatort::add_axioms_for_insert_int(
144144
const typet &index_type = s1.length().type();
145145
const typet &char_type = s1.content().type().subtype();
146146
array_string_exprt s2 = fresh_string(index_type, char_type);
147-
exprt return_code = add_axioms_from_int(s2, f.arguments()[4]);
147+
exprt return_code = add_axioms_for_string_of_int(s2, f.arguments()[4]);
148148
return add_axioms_for_insert(res, s1, s2, offset);
149149
}
150150

0 commit comments

Comments
 (0)