Skip to content

Commit 12c5f26

Browse files
authored
Merge pull request #828 from allredj/test-gen-string-fix-159
Specification for contains in the case of a constant argument
2 parents 00bb9b1 + 1162377 commit 12c5f26

File tree

2 files changed

+102
-26
lines changed

2 files changed

+102
-26
lines changed

src/solvers/refinement/string_constraint_generator.h

+3
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,14 @@ class string_constraint_generatort
305305
return args;
306306
}
307307

308+
private:
309+
// Helper functions
308310
exprt int_of_hex_char(const exprt &chr) const;
309311
exprt is_high_surrogate(const exprt &chr) const;
310312
exprt is_low_surrogate(const exprt &chr) const;
311313
exprt character_equals_ignore_case(
312314
exprt char1, exprt char2, exprt char_a, exprt char_A, exprt char_Z);
315+
bool is_constant_string(const string_exprt &expr) const;
313316
};
314317

315318
#endif

src/solvers/refinement/string_constraint_generator_testing.cpp

+99-26
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,35 @@ exprt string_constraint_generatort::add_axioms_for_is_suffix(
192192

193193
/*******************************************************************\
194194
195+
Function: string_constraint_generatort::is_constant_string
196+
197+
Inputs:
198+
expr - a string expression
199+
200+
Outputs: a Boolean
201+
202+
Purpose: tells whether the given string is a constant
203+
204+
\*******************************************************************/
205+
206+
bool string_constraint_generatort::is_constant_string(
207+
const string_exprt &expr) const
208+
{
209+
if(expr.id()!=ID_struct ||
210+
expr.operands().size()!=2 ||
211+
expr.length().id()!=ID_constant ||
212+
expr.content().id()!=ID_array)
213+
return false;
214+
for(const auto &element : expr.content().operands())
215+
{
216+
if(element.id()!=ID_constant)
217+
return false;
218+
}
219+
return true;
220+
}
221+
222+
/*******************************************************************\
223+
195224
Function: string_constraint_generatort::add_axioms_for_contains
196225
197226
Inputs: function application with two string arguments
@@ -206,46 +235,90 @@ exprt string_constraint_generatort::add_axioms_for_contains(
206235
const function_application_exprt &f)
207236
{
208237
assert(f.type()==bool_typet() || f.type().id()==ID_c_bool);
209-
symbol_exprt contains=fresh_boolean("contains");
210-
typecast_exprt tc_contains(contains, f.type());
211238
string_exprt s0=get_string_expr(args(f, 2)[0]);
212239
string_exprt s1=get_string_expr(args(f, 2)[1]);
240+
bool constant=is_constant_string(s1);
241+
242+
symbol_exprt contains=fresh_boolean("contains");
213243
const refined_string_typet ref_type=to_refined_string_type(s0.type());
214244
const typet &index_type=ref_type.get_index_type();
215245

216246
// We add axioms:
217-
// a1 : contains => s0.length >= s1.length
218-
// a2 : 0 <= startpos <= s0.length-s1.length
219-
// a3 : forall qvar<s1.length. contains => s1[qvar]=s0[startpos + qvar]
220-
// a4 : !contains => s1.length > s0.length
221-
// || (forall startpos <= s0.length-s1.length.
222-
// exists witness<s1.length &&s1[witness]!=s0[witness + startpos]
247+
// a1 : contains ==> |s0| >= |s1|
248+
// a2 : 0 <= startpos <= |s0|-|s1|
249+
// a3 : forall qvar < |s1|. contains ==> s1[qvar] = s0[startpos + qvar]
250+
// a4 : !contains ==> |s1| > |s0| ||
251+
// (forall startpos <= |s0| - |s1|.
252+
// exists witness < |s1|. s1[witness] != s0[witness + startpos])
223253

224254
implies_exprt a1(contains, s0.axiom_for_is_longer_than(s1));
225255
axioms.push_back(a1);
226256

227257
symbol_exprt startpos=fresh_exist_index("startpos_contains", index_type);
228258
minus_exprt length_diff(s0.length(), s1.length());
229-
and_exprt a2(
259+
and_exprt bounds(
230260
axiom_for_is_positive_index(startpos),
231261
binary_relation_exprt(startpos, ID_le, length_diff));
262+
implies_exprt a2(contains, bounds);
232263
axioms.push_back(a2);
233264

234-
symbol_exprt qvar=fresh_univ_index("QA_contains", index_type);
235-
exprt qvar_shifted=plus_exprt(qvar, startpos);
236-
string_constraintt a3(
237-
qvar, s1.length(), contains, equal_exprt(s1[qvar], s0[qvar_shifted]));
238-
axioms.push_back(a3);
239-
240-
// We rewrite the axiom for !contains as:
241-
// forall startpos <= |s0|-|s1|. (!contains &&|s0| >= |s1| )
242-
// ==> exists witness<|s1|. s1[witness]!=s0[startpos+witness]
243-
string_not_contains_constraintt a4(
244-
from_integer(0, index_type),
245-
plus_exprt(from_integer(1, index_type), length_diff),
246-
and_exprt(not_exprt(contains), s0.axiom_for_is_longer_than(s1)),
247-
from_integer(0, index_type), s1.length(), s0, s1);
248-
axioms.push_back(a4);
249-
250-
return tc_contains;
265+
if(constant)
266+
{
267+
// If the string is constant, we can use a more efficient axiom for a3:
268+
// contains ==> AND_{i < |s1|} s1[i] = s0[startpos + i]
269+
mp_integer s1_length;
270+
assert(!to_integer(s1.length(), s1_length));
271+
exprt::operandst conjuncts;
272+
for(mp_integer i=0; i<s1_length; ++i)
273+
{
274+
exprt expr_i=from_integer(i, index_type);
275+
plus_exprt shifted_i(expr_i, startpos);
276+
conjuncts.push_back(equal_exprt(s1[expr_i], s0[shifted_i]));
277+
}
278+
implies_exprt a3(contains, conjunction(conjuncts));
279+
axioms.push_back(a3);
280+
281+
// The a4 constraint for constant strings translates to:
282+
// !contains ==> |s1| > |s0| ||
283+
// (forall qvar <= |s0| - |s1|.
284+
// !(AND_{i < |s1|} s1[i] == s0[i + qvar])
285+
//
286+
// which we implement as:
287+
// forall qvar <= |s0| - |s1|. (!contains && |s0| >= |s1|)
288+
// ==> !(AND_{i < |s1|} (s1[i] == s0[qvar+i]))
289+
symbol_exprt qvar=fresh_univ_index("QA_contains_constant", index_type);
290+
exprt::operandst conjuncts1;
291+
for(mp_integer i=0; i<s1_length; ++i)
292+
{
293+
exprt expr_i=from_integer(i, index_type);
294+
plus_exprt shifted_i(expr_i, qvar);
295+
conjuncts1.push_back(equal_exprt(s1[expr_i], s0[shifted_i]));
296+
}
297+
298+
string_constraintt a4(
299+
qvar,
300+
plus_exprt(from_integer(1, index_type), length_diff),
301+
and_exprt(not_exprt(contains), s0.axiom_for_is_longer_than(s1)),
302+
not_exprt(conjunction(conjuncts1)));
303+
axioms.push_back(a4);
304+
}
305+
else
306+
{
307+
symbol_exprt qvar=fresh_univ_index("QA_contains", index_type);
308+
exprt qvar_shifted=plus_exprt(qvar, startpos);
309+
string_constraintt a3(
310+
qvar, s1.length(), contains, equal_exprt(s1[qvar], s0[qvar_shifted]));
311+
axioms.push_back(a3);
312+
313+
// We rewrite axiom a4 as:
314+
// forall startpos <= |s0|-|s1|. (!contains && |s0| >= |s1|)
315+
// ==> exists witness < |s1|. s1[witness] != s0[startpos+witness]
316+
string_not_contains_constraintt a4(
317+
from_integer(0, index_type),
318+
plus_exprt(from_integer(1, index_type), length_diff),
319+
and_exprt(not_exprt(contains), s0.axiom_for_is_longer_than(s1)),
320+
from_integer(0, index_type), s1.length(), s0, s1);
321+
axioms.push_back(a4);
322+
}
323+
return typecast_exprt(contains, f.type());
251324
}

0 commit comments

Comments
 (0)