@@ -192,6 +192,35 @@ exprt string_constraint_generatort::add_axioms_for_is_suffix(
192
192
193
193
/* ******************************************************************\
194
194
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
+
195
224
Function: string_constraint_generatort::add_axioms_for_contains
196
225
197
226
Inputs: function application with two string arguments
@@ -206,46 +235,90 @@ exprt string_constraint_generatort::add_axioms_for_contains(
206
235
const function_application_exprt &f)
207
236
{
208
237
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 ());
211
238
string_exprt s0=get_string_expr (args (f, 2 )[0 ]);
212
239
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" );
213
243
const refined_string_typet ref_type=to_refined_string_type (s0.type ());
214
244
const typet &index_type=ref_type.get_index_type ();
215
245
216
246
// 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])
223
253
224
254
implies_exprt a1 (contains, s0.axiom_for_is_longer_than (s1));
225
255
axioms.push_back (a1);
226
256
227
257
symbol_exprt startpos=fresh_exist_index (" startpos_contains" , index_type);
228
258
minus_exprt length_diff (s0.length (), s1.length ());
229
- and_exprt a2 (
259
+ and_exprt bounds (
230
260
axiom_for_is_positive_index (startpos),
231
261
binary_relation_exprt (startpos, ID_le, length_diff));
262
+ implies_exprt a2 (contains, bounds);
232
263
axioms.push_back (a2);
233
264
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 ());
251
324
}
0 commit comments