Skip to content

Commit d5c6ac6

Browse files
romainbrenguierJoel Allred
authored and
Joel Allred
committed
Added option to the string solver for string length and
printability Added field in string_refinement and string_constraint_generator to bound size of strings in the program and enforce a range on the characters.
1 parent 6c1f700 commit d5c6ac6

5 files changed

+184
-46
lines changed

src/solvers/refinement/string_constraint_generator.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,17 @@ class string_constraint_generatort
2626
// to the axiom list.
2727

2828
string_constraint_generatort():
29-
mode(ID_unknown)
29+
mode(ID_unknown),
30+
max_string_length(-1),
31+
force_printable_characters(false)
3032
{ }
3133

34+
// Constraints on the maximal length of strings
35+
int max_string_length;
36+
37+
// Should we add constraints on the characters
38+
bool force_printable_characters;
39+
3240
void set_mode(irep_idt _mode)
3341
{
3442
// only C and java modes supported
@@ -74,6 +82,8 @@ class string_constraint_generatort
7482
// Maps unresolved symbols to the string_exprt that was created for them
7583
std::map<irep_idt, string_exprt> unresolved_symbols;
7684

85+
// Set of strings that have been created by the generator
86+
std::set<string_exprt> created_strings;
7787

7888
string_exprt find_or_add_string_of_symbol(
7989
const symbol_exprt &sym,
@@ -100,6 +110,7 @@ class string_constraint_generatort
100110

101111
static irep_idt extract_java_string(const symbol_exprt &s);
102112

113+
void add_default_axioms(const string_exprt &s);
103114
exprt axiom_for_is_positive_index(const exprt &x);
104115

105116
// The following functions add axioms for the returned value

src/solvers/refinement/string_constraint_generator_concat.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ string_exprt string_constraint_generatort::add_axioms_for_concat(
3535
// a4 : forall i<|s1|. res[i]=s1[i]
3636
// a5 : forall i<|s2|. res[i+|s1|]=s2[i]
3737

38-
res.length()=plus_exprt_with_overflow_check(s1.length(), s2.length());
38+
equal_exprt a1(res.length(), plus_exprt_with_overflow_check(s1.length(), s2.length()));
39+
axioms.push_back(a1);
3940
axioms.push_back(s1.axiom_for_is_shorter_than(res));
4041
axioms.push_back(s2.axiom_for_is_shorter_than(res));
4142

src/solvers/refinement/string_constraint_generator_main.cpp

+46-10
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,11 @@ Function: string_constraint_generatort::fresh_string
173173
string_exprt string_constraint_generatort::fresh_string(
174174
const refined_string_typet &type)
175175
{
176-
symbol_exprt length=
177-
fresh_symbol("string_length", type.get_index_type());
176+
symbol_exprt length=fresh_symbol("string_length", type.get_index_type());
178177
symbol_exprt content=fresh_symbol("string_content", type.get_content_type());
179-
return string_exprt(length, content, type);
178+
string_exprt str(length, content, type);
179+
created_strings.insert(str);
180+
return str;
180181
}
181182

182183
/*******************************************************************\
@@ -246,6 +247,45 @@ string_exprt string_constraint_generatort::convert_java_string_to_string_exprt(
246247

247248
/*******************************************************************\
248249
250+
Function: string_constraint_generatort::add_default_constraints
251+
252+
Inputs:
253+
s - a string expression
254+
255+
Outputs: a string expression that is linked to the argument through
256+
axioms that are added to the list
257+
258+
Purpose: adds standard axioms about the length of the string and
259+
its content:
260+
* its length should be positive
261+
* it should not exceed max_string_length
262+
* if force_printable_characters is true then all characters
263+
should belong to the range of ASCII characters between ' ' and '~'
264+
265+
266+
\*******************************************************************/
267+
268+
void string_constraint_generatort::add_default_axioms(
269+
const string_exprt &s)
270+
{
271+
s.axiom_for_is_longer_than(from_integer(0, s.length().type()));
272+
if(max_string_length>=0)
273+
axioms.push_back(s.axiom_for_is_shorter_than(max_string_length));
274+
275+
if(force_printable_characters)
276+
{
277+
symbol_exprt qvar=fresh_univ_index("printable", s.length().type());
278+
exprt chr=s[qvar];
279+
and_exprt printable(
280+
binary_relation_exprt(chr, ID_ge, from_integer(' ', chr.type())),
281+
binary_relation_exprt(chr, ID_le, from_integer('~', chr.type())));
282+
string_constraintt sc(qvar, s.length(), printable);
283+
axioms.push_back(sc);
284+
}
285+
}
286+
287+
/*******************************************************************\
288+
249289
Function: string_constraint_generatort::add_axioms_for_refined_string
250290
251291
Inputs: an expression of refined string type
@@ -258,7 +298,6 @@ Function: string_constraint_generatort::add_axioms_for_refined_string
258298
259299
\*******************************************************************/
260300

261-
262301
string_exprt string_constraint_generatort::add_axioms_for_refined_string(
263302
const exprt &string)
264303
{
@@ -272,15 +311,13 @@ string_exprt string_constraint_generatort::add_axioms_for_refined_string(
272311
{
273312
const symbol_exprt &sym=to_symbol_expr(string);
274313
string_exprt s=find_or_add_string_of_symbol(sym, type);
275-
axioms.push_back(
276-
s.axiom_for_is_longer_than(from_integer(0, s.length().type())));
314+
add_default_axioms(s);
277315
return s;
278316
}
279317
else if(string.id()==ID_nondet_symbol)
280318
{
281319
string_exprt s=fresh_string(type);
282-
axioms.push_back(
283-
s.axiom_for_is_longer_than(from_integer(0, s.length().type())));
320+
add_default_axioms(s);
284321
return s;
285322
}
286323
else if(string.id()==ID_if)
@@ -290,8 +327,7 @@ string_exprt string_constraint_generatort::add_axioms_for_refined_string(
290327
else if(string.id()==ID_struct)
291328
{
292329
const string_exprt &s=to_string_expr(string);
293-
axioms.push_back(
294-
s.axiom_for_is_longer_than(from_integer(0, s.length().type())));
330+
add_default_axioms(s);
295331
return s;
296332
}
297333
else

src/solvers/refinement/string_refinement.cpp

+111-33
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ string_refinementt::string_refinementt(
4343
supert(_ns, _prop),
4444
use_counter_example(false),
4545
do_concretizing(false),
46-
initial_loop_bound(refinement_bound)
46+
initial_loop_bound(refinement_bound),
47+
non_empty_string(false)
4748
{ }
4849

4950
/*******************************************************************\
@@ -65,6 +66,52 @@ void string_refinementt::set_mode()
6566

6667
/*******************************************************************\
6768
69+
Function: string_refinementt::set_max_string_length
70+
71+
Inputs:
72+
i - maximum length which is allowed for strings.
73+
negative number means no limit
74+
75+
Purpose: Add constraints on the size of strings used in the
76+
program.
77+
78+
\*******************************************************************/
79+
80+
void string_refinementt::set_max_string_length(int i)
81+
{
82+
generator.max_string_length=i;
83+
}
84+
85+
/*******************************************************************\
86+
87+
Function: string_refinementt::set_max_string_length
88+
89+
Purpose: Add constraints on the size of nondet character arrays
90+
to ensure they have length at least 1
91+
92+
\*******************************************************************/
93+
94+
void string_refinementt::enforce_non_empty_string()
95+
{
96+
non_empty_string=true;
97+
}
98+
99+
/*******************************************************************\
100+
101+
Function: string_refinementt::enforce_printable_characters
102+
103+
Purpose: Add constraints on characters used in the program
104+
to ensure they are printable
105+
106+
\*******************************************************************/
107+
108+
void string_refinementt::enforce_printable_characters()
109+
{
110+
generator.force_printable_characters=true;
111+
}
112+
113+
/*******************************************************************\
114+
68115
Function: string_refinementt::display_index_set
69116
70117
Purpose: display the current index set, for debugging
@@ -283,52 +330,71 @@ bool string_refinementt::add_axioms_for_string_assigns(const exprt &lhs,
283330

284331
/*******************************************************************\
285332
286-
Function: string_refinementt::concretize_results
333+
Function: string_refinementt::concretize_string
287334
288-
Purpose: For each string whose length has been solved, add constants
335+
Input:
336+
expr - an expression
337+
338+
Purpose: If the expression is of type string, then adds constants
289339
to the index set to force the solver to pick concrete values
290340
for each character, and fill the map `found_length`
291341
292342
\*******************************************************************/
293343

294-
void string_refinementt::concretize_results()
344+
void string_refinementt::concretize_string(const exprt &expr)
295345
{
296-
for(const auto& it : symbol_resolve)
346+
if(refined_string_typet::is_refined_string_type(expr.type()))
297347
{
298-
if(refined_string_typet::is_refined_string_type(it.second.type()))
348+
string_exprt str=to_string_expr(expr);
349+
exprt length=get(str.length());
350+
add_lemma(equal_exprt(str.length(), length));
351+
exprt content=str.content();
352+
replace_expr(symbol_resolve, content);
353+
found_length[content]=length;
354+
mp_integer found_length;
355+
if(!to_integer(length, found_length))
299356
{
300-
string_exprt str=to_string_expr(it.second);
301-
exprt length=current_model[str.length()];
302-
exprt content=str.content();
303-
replace_expr(symbol_resolve, content);
304-
found_length[content]=length;
305-
mp_integer found_length;
306-
if(!to_integer(length, found_length))
357+
assert(found_length.is_long());
358+
if(found_length < 0)
307359
{
308-
assert(found_length.is_long());
309-
if(found_length < 0)
310-
{
311-
debug() << "concretize_results: WARNING found length is negative"
312-
<< eom;
313-
}
314-
else
360+
debug() << "concretize_results: WARNING found length is negative"
361+
<< eom;
362+
}
363+
else
364+
{
365+
size_t concretize_limit=found_length.to_long();
366+
concretize_limit=concretize_limit>MAX_CONCRETE_STRING_SIZE?
367+
MAX_CONCRETE_STRING_SIZE:concretize_limit;
368+
exprt content_expr=str.content();
369+
for(size_t i=0; i<concretize_limit; ++i)
315370
{
316-
size_t concretize_limit=found_length.to_long();
317-
concretize_limit=concretize_limit>MAX_CONCRETE_STRING_SIZE?
318-
MAX_CONCRETE_STRING_SIZE:concretize_limit;
319-
exprt content_expr=str.content();
320-
replace_expr(current_model, content_expr);
321-
for(size_t i=0; i<concretize_limit; ++i)
322-
{
323-
auto i_expr=from_integer(i, str.length().type());
324-
debug() << "Concretizing " << from_expr(content_expr)
325-
<< " / " << i << eom;
326-
current_index_set[str.content()].insert(i_expr);
327-
}
371+
auto i_expr=from_integer(i, str.length().type());
372+
debug() << "Concretizing " << from_expr(content_expr)
373+
<< " / " << i << eom;
374+
current_index_set[str.content()].insert(i_expr);
375+
index_set[str.content()].insert(i_expr);
328376
}
329377
}
330378
}
331379
}
380+
}
381+
382+
/*******************************************************************\
383+
384+
Function: string_refinementt::concretize_results
385+
386+
Purpose: For each string whose length has been solved, add constants
387+
to the index set to force the solver to pick concrete values
388+
for each character, and fill the map `found_length`
389+
390+
\*******************************************************************/
391+
392+
void string_refinementt::concretize_results()
393+
{
394+
for(const auto& it : symbol_resolve)
395+
concretize_string(it.second);
396+
for(const auto& it : generator.created_strings)
397+
concretize_string(it);
332398
add_instantiations();
333399
}
334400

@@ -348,7 +414,18 @@ void string_refinementt::concretize_lengths()
348414
if(refined_string_typet::is_refined_string_type(it.second.type()))
349415
{
350416
string_exprt str=to_string_expr(it.second);
351-
exprt length=current_model[str.length()];
417+
exprt length=get(str.length());
418+
exprt content=str.content();
419+
replace_expr(symbol_resolve, content);
420+
found_length[content]=length;
421+
}
422+
}
423+
for(const auto& it : generator.created_strings)
424+
{
425+
if(refined_string_typet::is_refined_string_type(it.type()))
426+
{
427+
string_exprt str=to_string_expr(it);
428+
exprt length=get(str.length());
352429
exprt content=str.content();
353430
replace_expr(symbol_resolve, content);
354431
found_length[content]=length;
@@ -869,6 +946,7 @@ void string_refinementt::fill_model()
869946
}
870947
}
871948

949+
872950
/*******************************************************************\
873951
874952
Function: string_refinementt::add_negation_of_constraint_to_solver

src/solvers/refinement/string_refinement.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
Module: String support via creating string constraints and progressively
44
instantiating the universal constraints as needed.
5-
The procedure is described in the PASS paper at HVC'13:
5+
The procedure is described in the PASS paper at HVC'13:
66
"PASS: String Solving with Parameterized Array and Interval Automaton"
77
by Guodong Li and Indradeep Ghosh
88
@@ -38,8 +38,13 @@ class string_refinementt: public bv_refinementt
3838
// Should we use counter examples at each iteration?
3939
bool use_counter_example;
4040

41+
// Should we concretize strings when the solver finished
4142
bool do_concretizing;
4243

44+
void set_max_string_length(int i);
45+
void enforce_non_empty_string();
46+
void enforce_printable_characters();
47+
4348
virtual std::string decision_procedure_text() const override
4449
{
4550
return "string refinement loop with "+prop.solver_text();
@@ -65,6 +70,9 @@ class string_refinementt: public bv_refinementt
6570

6671
string_constraint_generatort generator;
6772

73+
bool non_empty_string;
74+
expr_sett nondet_arrays;
75+
6876
// Simple constraints that have been given to the solver
6977
expr_sett seen_instances;
7078

@@ -98,6 +106,8 @@ class string_refinementt: public bv_refinementt
98106
exprt substitute_function_applications(exprt expr);
99107
typet substitute_java_string_types(typet type);
100108
exprt substitute_java_strings(exprt expr);
109+
exprt substitute_array_with_expr(exprt &expr, exprt &index) const;
110+
exprt substitute_array_access(exprt &expr) const;
101111
void add_symbol_to_symbol_map(const exprt &lhs, const exprt &rhs);
102112
bool is_char_array(const typet &type) const;
103113
bool add_axioms_for_string_assigns(const exprt &lhs, const exprt &rhs);
@@ -134,8 +144,10 @@ class string_refinementt: public bv_refinementt
134144

135145
exprt simplify_sum(const exprt &f) const;
136146

147+
void concretize_string(const exprt &expr);
137148
void concretize_results();
138149
void concretize_lengths();
150+
139151
// Length of char arrays found during concretization
140152
std::map<exprt, exprt> found_length;
141153

0 commit comments

Comments
 (0)