@@ -262,6 +262,115 @@ static simplify_exprt::resultt<> simplify_string_compare_to(
262
262
first_shorter ? char1 - char2 : char2 - char1, expr.type ());
263
263
}
264
264
265
+ // / Simplify String.indexOf function when arguments are constant
266
+ // /
267
+ // / \param expr: the expression to simplify
268
+ // / \param ns: namespace
269
+ // / \return: the modified expression or an unchanged expression
270
+ static simplify_exprt::resultt<> simplify_string_index_of (
271
+ const function_application_exprt &expr,
272
+ const namespacet &ns)
273
+ {
274
+ std::size_t starting_index = 0 ;
275
+
276
+ // Determine starting index for the comparison (if given)
277
+ if (expr.arguments ().size () == 3 )
278
+ {
279
+ auto &starting_index_expr = expr.arguments ().at (2 );
280
+
281
+ if (starting_index_expr.id () != ID_constant)
282
+ {
283
+ return simplify_exprt::unchanged (expr);
284
+ }
285
+
286
+ const mp_integer idx =
287
+ numeric_cast_v<mp_integer>(to_constant_expr (starting_index_expr));
288
+
289
+ // Negative indices are treated like 0
290
+ if (idx > 0 )
291
+ {
292
+ starting_index = numeric_cast_v<std::size_t >(idx);
293
+ }
294
+ }
295
+
296
+ const refined_string_exprt &s1 = to_string_expr (expr.arguments ().at (0 ));
297
+
298
+ const auto s1_data_opt = try_get_string_data_array (s1, ns);
299
+
300
+ if (!s1_data_opt)
301
+ {
302
+ return simplify_exprt::unchanged (expr);
303
+ }
304
+
305
+ const array_exprt &s1_data = s1_data_opt->get ();
306
+
307
+ if (starting_index >= s1_data.operands ().size ())
308
+ {
309
+ return from_integer (-1 , expr.type ());
310
+ }
311
+
312
+ // Iterator pointing to the character in the first string at which the second
313
+ // string or character was found
314
+ exprt::operandst::const_iterator it;
315
+
316
+ if (can_cast_expr<refined_string_exprt>(expr.arguments ().at (1 )))
317
+ {
318
+ // Second argument is a string
319
+
320
+ const refined_string_exprt &s2 =
321
+ to_string_expr (expr.arguments ().at (1 ));
322
+
323
+ const auto s2_data_opt = try_get_string_data_array (s2, ns);
324
+
325
+ if (!s2_data_opt)
326
+ {
327
+ return simplify_exprt::unchanged (expr);
328
+ }
329
+
330
+ const array_exprt &s2_data = s2_data_opt->get ();
331
+
332
+ it = std::search (
333
+ std::next (s1_data.operands ().begin (), starting_index),
334
+ s1_data.operands ().end (),
335
+ s2_data.operands ().begin (),
336
+ s2_data.operands ().end ());
337
+ }
338
+ else if (expr.arguments ().at (1 ).id () == ID_constant)
339
+ {
340
+ // Second argument is a constant character
341
+
342
+ const constant_exprt &c1 = to_constant_expr (expr.arguments ().at (1 ));
343
+ const auto c1_val = numeric_cast_v<mp_integer>(c1);
344
+
345
+ auto pred = [&](const exprt &c2)
346
+ {
347
+ const auto c2_val = numeric_cast_v<mp_integer>(to_constant_expr (c2));
348
+
349
+ return c1_val == c2_val;
350
+ };
351
+
352
+ it = std::find_if (
353
+ std::next (s1_data.operands ().begin (), starting_index),
354
+ s1_data.operands ().end (),
355
+ pred);
356
+ }
357
+ else
358
+ {
359
+ return simplify_exprt::unchanged (expr);
360
+ }
361
+
362
+ if (it == s1_data.operands ().end ())
363
+ {
364
+ return from_integer (-1 , expr.type ());
365
+ }
366
+ else
367
+ {
368
+ const std::size_t idx = std::distance (s1_data.operands ().begin (), it);
369
+
370
+ return from_integer (idx, expr.type ());
371
+ }
372
+ }
373
+
265
374
simplify_exprt::resultt<> simplify_exprt::simplify_function_application (
266
375
const function_application_exprt &expr)
267
376
{
@@ -345,6 +454,10 @@ simplify_exprt::resultt<> simplify_exprt::simplify_function_application(
345
454
{
346
455
return simplify_string_compare_to (expr, ns);
347
456
}
457
+ else if (func_id == ID_cprover_string_index_of_func)
458
+ {
459
+ return simplify_string_index_of (expr, ns);
460
+ }
348
461
else if (func_id == ID_cprover_string_char_at_func)
349
462
{
350
463
if (expr.arguments ().at (1 ).id () != ID_constant)
0 commit comments