@@ -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+
195224Function: 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