Skip to content

Commit 18478e9

Browse files
Merge pull request #1947 from romainbrenguier/dependency-graph/stop-adding-unecessary-constraints#TG-2608
[TG-2608] Use string dependencies to reduce the number of constraints
2 parents be66b35 + 60bfbd0 commit 18478e9

15 files changed

+639
-262
lines changed
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Test {
2+
void test(String s) {
3+
// Filter
4+
if(s == null)
5+
return;
6+
7+
// Act
8+
9+
// this matters for the final test
10+
String t = s.concat("_foo");
11+
12+
// these should be ignored by the solver as they
13+
// do not matter for the final test
14+
String u = s.concat("_bar");
15+
String v = s.concat("_baz");
16+
String w = s.concat("_fiz");
17+
String x = s.concat("_buz");
18+
19+
// Assert
20+
assert t.endsWith("foo");
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
Test.class
3+
--refine-strings --string-max-length 1000 --string-max-input-length 100 --verbosity 10 Test.class --function Test.test
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
assertion at file Test.java line 20 .*: SUCCESS
7+
string_refinement::check_axioms: 3 universal axioms
8+
--
9+
--
10+
We check there are exactly 3 universal formulas considered by the solver (2 for
11+
`t = s.concat("_foo")` and 1 for `t.endsWith("foo")`).
12+
The other concatenations should be ignored.

regression/jbmc-strings/StringEquals/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
CORE
22
Test.class
3-
--refine-strings --string-max-length 1000 --function Test.check
3+
--refine-strings --string-max-length 40 --function Test.check
44
^EXIT=10$
55
^SIGNAL=0$
66
assertion at file Test.java line 6 .* SUCCESS

src/solvers/refinement/string_builtin_function.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ static array_string_exprt make_string(
1818

1919
string_transformation_builtin_functiont::
2020
string_transformation_builtin_functiont(
21+
const exprt &return_code,
2122
const std::vector<exprt> &fun_args,
2223
array_poolt &array_pool)
24+
: string_builtin_functiont(return_code)
2325
{
2426
PRECONDITION(fun_args.size() > 2);
2527
const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]);
@@ -55,8 +57,10 @@ optionalt<exprt> string_transformation_builtin_functiont::eval(
5557
}
5658

5759
string_insertion_builtin_functiont::string_insertion_builtin_functiont(
60+
const exprt &return_code,
5861
const std::vector<exprt> &fun_args,
5962
array_poolt &array_pool)
63+
: string_builtin_functiont(return_code)
6064
{
6165
PRECONDITION(fun_args.size() > 4);
6266
const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]);
@@ -69,8 +73,10 @@ string_insertion_builtin_functiont::string_insertion_builtin_functiont(
6973
}
7074

7175
string_concatenation_builtin_functiont::string_concatenation_builtin_functiont(
76+
const exprt &return_code,
7277
const std::vector<exprt> &fun_args,
7378
array_poolt &array_pool)
79+
: string_insertion_builtin_functiont(return_code)
7480
{
7581
PRECONDITION(fun_args.size() >= 4 && fun_args.size() <= 6);
7682
const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]);
@@ -207,3 +213,33 @@ optionalt<exprt> string_insertion_builtin_functiont::eval(
207213
const array_typet type(result.type().subtype(), length);
208214
return make_string(result_value, type);
209215
}
216+
217+
string_builtin_function_with_no_evalt::string_builtin_function_with_no_evalt(
218+
const exprt &return_code,
219+
const function_application_exprt &f,
220+
array_poolt &array_pool)
221+
: string_builtin_functiont(return_code), function_application(f)
222+
{
223+
const std::vector<exprt> &fun_args = f.arguments();
224+
std::size_t i = 0;
225+
if(fun_args.size() >= 2 && fun_args[1].type().id() == ID_pointer)
226+
{
227+
string_res = array_pool.find(fun_args[1], fun_args[0]);
228+
i = 2;
229+
}
230+
231+
for(; i < fun_args.size(); ++i)
232+
{
233+
const auto arg = expr_try_dynamic_cast<struct_exprt>(fun_args[i]);
234+
// TODO: use is_refined_string_type ?
235+
if(
236+
arg && arg->operands().size() == 2 &&
237+
arg->op1().type().id() == ID_pointer)
238+
{
239+
INVARIANT(is_refined_string_type(arg->type()), "should be a string");
240+
string_args.push_back(array_pool.find(arg->op1(), arg->op0()));
241+
}
242+
else
243+
args.push_back(fun_args[i]);
244+
}
245+
}

src/solvers/refinement/string_builtin_function.h

+148-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <vector>
88
#include <util/optional.h>
99
#include <util/string_expr.h>
10+
#include "string_constraint_generator.h"
1011

1112
class array_poolt;
1213

@@ -32,8 +33,33 @@ class string_builtin_functiont
3233

3334
virtual std::string name() const = 0;
3435

35-
protected:
36+
/// Add constraints ensuring that the value of result expression of the
37+
/// builtin function corresponds to the value of the function call.
38+
virtual exprt
39+
add_constraints(string_constraint_generatort &constraint_generator) const = 0;
40+
41+
/// Constraint ensuring that the length of the strings are coherent with
42+
/// the function call.
43+
virtual exprt length_constraint() const = 0;
44+
45+
exprt return_code;
46+
47+
/// Tells whether the builtin function can be a testing function, that is a
48+
/// function that does not return a string, for instance like `equals`,
49+
/// `indexOf` or `compare`.
50+
virtual bool maybe_testing_function() const
51+
{
52+
return true;
53+
}
54+
55+
private:
3656
string_builtin_functiont() = default;
57+
58+
protected:
59+
explicit string_builtin_functiont(const exprt &return_code)
60+
: return_code(return_code)
61+
{
62+
}
3763
};
3864

3965
/// String builtin_function transforming one string into another
@@ -43,14 +69,14 @@ class string_transformation_builtin_functiont : public string_builtin_functiont
4369
array_string_exprt result;
4470
array_string_exprt input;
4571
std::vector<exprt> args;
46-
exprt return_code;
4772

4873
/// Constructor from arguments of a function application.
4974
/// The arguments in `fun_args` should be in order:
5075
/// an integer `result.length`, a character pointer `&result[0]`,
5176
/// a string `arg1` of type refined_string_typet, and potentially some
5277
/// arguments of primitive types.
5378
string_transformation_builtin_functiont(
79+
const exprt &return_code,
5480
const std::vector<exprt> &fun_args,
5581
array_poolt &array_pool);
5682

@@ -70,6 +96,11 @@ class string_transformation_builtin_functiont : public string_builtin_functiont
7096

7197
optionalt<exprt>
7298
eval(const std::function<exprt(const exprt &)> &get_value) const override;
99+
100+
bool maybe_testing_function() const override
101+
{
102+
return false;
103+
}
73104
};
74105

75106
/// Adding a character at the end of a string
@@ -82,9 +113,10 @@ class string_concat_char_builtin_functiont
82113
/// an integer `result.length`, a character pointer `&result[0]`,
83114
/// a string `arg1` of type refined_string_typet, and a character.
84115
string_concat_char_builtin_functiont(
116+
const exprt &return_code,
85117
const std::vector<exprt> &fun_args,
86118
array_poolt &array_pool)
87-
: string_transformation_builtin_functiont(fun_args, array_pool)
119+
: string_transformation_builtin_functiont(return_code, fun_args, array_pool)
88120
{
89121
}
90122

@@ -96,6 +128,16 @@ class string_concat_char_builtin_functiont
96128
{
97129
return "concat_char";
98130
}
131+
132+
exprt add_constraints(string_constraint_generatort &generator) const override
133+
{
134+
return generator.add_axioms_for_concat_char(result, input, args[0]);
135+
}
136+
137+
exprt length_constraint() const override
138+
{
139+
return length_constraint_for_concat_char(result, input);
140+
}
99141
};
100142

101143
/// String inserting a string into another one
@@ -106,7 +148,6 @@ class string_insertion_builtin_functiont : public string_builtin_functiont
106148
array_string_exprt input1;
107149
array_string_exprt input2;
108150
std::vector<exprt> args;
109-
exprt return_code;
110151

111152
/// Constructor from arguments of a function application.
112153
/// The arguments in `fun_args` should be in order:
@@ -115,6 +156,7 @@ class string_insertion_builtin_functiont : public string_builtin_functiont
115156
/// a string `arg2` of type refined_string_typet,
116157
/// and potentially some arguments of primitive types.
117158
string_insertion_builtin_functiont(
159+
const exprt &return_code,
118160
const std::vector<exprt> &fun_args,
119161
array_poolt &array_pool);
120162

@@ -141,8 +183,34 @@ class string_insertion_builtin_functiont : public string_builtin_functiont
141183
return "insert";
142184
}
143185

186+
exprt add_constraints(string_constraint_generatort &generator) const override
187+
{
188+
if(args.size() == 1)
189+
return generator.add_axioms_for_insert(result, input1, input2, args[0]);
190+
if(args.size() == 3)
191+
UNIMPLEMENTED;
192+
UNREACHABLE;
193+
};
194+
195+
exprt length_constraint() const override
196+
{
197+
if(args.size() == 1)
198+
return length_constraint_for_insert(result, input1, input2, args[0]);
199+
if(args.size() == 3)
200+
UNIMPLEMENTED;
201+
UNREACHABLE;
202+
};
203+
204+
bool maybe_testing_function() const override
205+
{
206+
return false;
207+
}
208+
144209
protected:
145-
string_insertion_builtin_functiont() = default;
210+
explicit string_insertion_builtin_functiont(const exprt &return_code)
211+
: string_builtin_functiont(return_code)
212+
{
213+
}
146214
};
147215

148216
class string_concatenation_builtin_functiont final
@@ -156,6 +224,7 @@ class string_concatenation_builtin_functiont final
156224
/// a string `arg2` of type refined_string_typet,
157225
/// optionally followed by an integer `start` and an integer `end`.
158226
string_concatenation_builtin_functiont(
227+
const exprt &return_code,
159228
const std::vector<exprt> &fun_args,
160229
array_poolt &array_pool);
161230

@@ -168,6 +237,26 @@ class string_concatenation_builtin_functiont final
168237
{
169238
return "concat";
170239
}
240+
241+
exprt add_constraints(string_constraint_generatort &generator) const override
242+
{
243+
if(args.size() == 0)
244+
return generator.add_axioms_for_concat(result, input1, input2);
245+
if(args.size() == 2)
246+
return generator.add_axioms_for_concat_substr(
247+
result, input1, input2, args[0], args[1]);
248+
UNREACHABLE;
249+
};
250+
251+
exprt length_constraint() const override
252+
{
253+
if(args.size() == 0)
254+
return length_constraint_for_concat(result, input1, input2);
255+
if(args.size() == 2)
256+
return length_constraint_for_concat_substr(
257+
result, input1, input2, args[0], args[1]);
258+
UNREACHABLE;
259+
}
171260
};
172261

173262
/// String creation from other types
@@ -182,6 +271,11 @@ class string_creation_builtin_functiont : public string_builtin_functiont
182271
{
183272
return result;
184273
}
274+
275+
bool maybe_testing_function() const override
276+
{
277+
return false;
278+
}
185279
};
186280

187281
/// String test
@@ -197,4 +291,53 @@ class string_test_builtin_functiont : public string_builtin_functiont
197291
}
198292
};
199293

294+
/// Functions that are not yet supported in this class but are supported by
295+
/// string_constraint_generatort.
296+
/// \note Ultimately this should be disappear, once all builtin function have
297+
/// a corresponding string_builtin_functiont class.
298+
class string_builtin_function_with_no_evalt : public string_builtin_functiont
299+
{
300+
public:
301+
function_application_exprt function_application;
302+
optionalt<array_string_exprt> string_res;
303+
std::vector<array_string_exprt> string_args;
304+
std::vector<exprt> args;
305+
306+
string_builtin_function_with_no_evalt(
307+
const exprt &return_code,
308+
const function_application_exprt &f,
309+
array_poolt &array_pool);
310+
311+
std::string name() const override
312+
{
313+
return id2string(function_application.function().get_identifier());
314+
}
315+
std::vector<array_string_exprt> string_arguments() const override
316+
{
317+
return std::vector<array_string_exprt>(string_args);
318+
}
319+
optionalt<array_string_exprt> string_result() const override
320+
{
321+
return string_res;
322+
}
323+
324+
optionalt<exprt>
325+
eval(const std::function<exprt(const exprt &)> &get_value) const override
326+
{
327+
return {};
328+
}
329+
330+
exprt add_constraints(string_constraint_generatort &generator) const override
331+
{
332+
return generator.add_axioms_for_function_application(function_application);
333+
};
334+
335+
exprt length_constraint() const override
336+
{
337+
// For now, there is no need for implementing that as `add_constraints`
338+
// should always be called on these functions
339+
UNIMPLEMENTED;
340+
}
341+
};
342+
200343
#endif // CPROVER_SOLVERS_REFINEMENT_STRING_BUILTIN_FUNCTION_H

0 commit comments

Comments
 (0)