Skip to content

Commit f2fab1a

Browse files
author
Lukasz A.J. Wrona
committed
String.replace for single-character strings
1 parent fade69f commit f2fab1a

File tree

2 files changed

+55
-23
lines changed

2 files changed

+55
-23
lines changed

src/java_bytecode/java_string_library_preprocess.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,9 @@ void java_string_library_preprocesst::initialize_conversion_table()
19571957
cprover_equivalent_to_java_string_returning_function
19581958
["java::java.lang.String.replace:(CC)Ljava/lang/String;"]=
19591959
ID_cprover_string_replace_func;
1960+
cprover_equivalent_to_java_string_returning_function
1961+
["java::java.lang.String.replace:(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;"]= // NOLINT
1962+
ID_cprover_string_replace_func;
19601963
cprover_equivalent_to_java_function
19611964
["java::java.lang.String.startsWith:(Ljava/lang/String;)Z"]=
19621965
ID_cprover_string_startswith_func;

src/solvers/refinement/string_constraint_generator_transformation.cpp

+52-23
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Romain Brenguier, [email protected]
1313

1414
#include <solvers/refinement/string_refinement_invariant.h>
1515
#include <solvers/refinement/string_constraint_generator.h>
16+
#include "expr_cast.h"
1617

1718
/// add axioms to say that the returned string expression `res` has length `k`
1819
/// and characters at position `i` in `res` are equal to the character at
@@ -359,38 +360,66 @@ string_exprt string_constraint_generatort::add_axioms_for_char_set(
359360
return res;
360361
}
361362

363+
/// Convert two expressions to pair of chars
364+
/// If both expressions are characters, return pair of them
365+
/// If both expressions are 1-length strings, return first character of each
366+
/// Otherwise return empty optional
367+
/// \param expr1 First expression
368+
/// \param expr2 Second expression
369+
/// \return Optional pair of two expressions
370+
static optionalt<std::pair<exprt, exprt>> to_char_pair(
371+
exprt expr1, exprt expr2)
372+
{
373+
if((expr1.type().id()==ID_unsignedbv
374+
|| expr1.type().id()==ID_char)
375+
&& (expr2.type().id()==ID_char
376+
|| expr2.type().id()==ID_unsignedbv))
377+
return std::make_pair(expr1, expr2);
378+
const auto expr1_str=to_string_expr(expr1);
379+
const auto expr2_str=to_string_expr(expr2);
380+
const auto expr1_length=expr_cast<size_t>(expr1_str.length());
381+
const auto expr2_length=expr_cast<size_t>(expr2_str.length());
382+
if(expr1_length && expr2_length && *expr1_length==1 && *expr2_length==1)
383+
return std::make_pair(exprt(expr1_str[0]), exprt(expr2_str[0]));
384+
return { };
385+
}
386+
362387
/// add axioms corresponding to the String.replace java function
363388
/// \par parameters: function application with three arguments, the first is a
364389
/// string,
365-
/// the second and the third are characters
390+
/// the second and the third are either pair of characters or a pair of strings
366391
/// \return a new string expression
367392
string_exprt string_constraint_generatort::add_axioms_for_replace(
368393
const function_application_exprt &f)
369394
{
370395
string_exprt str=get_string_expr(args(f, 3)[0]);
371396
const refined_string_typet &ref_type=to_refined_string_type(str.type());
372-
const exprt &old_char=args(f, 3)[1];
373-
const exprt &new_char=args(f, 3)[2];
374-
string_exprt res=fresh_string(ref_type);
375-
376-
// We add axioms:
377-
// a1 : |res| = |str|
378-
// a2 : forall qvar, 0<=qvar<|res|,
379-
// str[qvar]=oldChar => res[qvar]=newChar
380-
// !str[qvar]=oldChar => res[qvar]=str[qvar]
381-
382-
m_axioms.push_back(res.axiom_for_has_same_length_as(str));
383-
384-
symbol_exprt qvar=fresh_univ_index("QA_replace", ref_type.get_index_type());
385-
implies_exprt case1(
386-
equal_exprt(str[qvar], old_char),
387-
equal_exprt(res[qvar], new_char));
388-
implies_exprt case2(
389-
not_exprt(equal_exprt(str[qvar], old_char)),
390-
equal_exprt(res[qvar], str[qvar]));
391-
string_constraintt a2(qvar, res.length(), and_exprt(case1, case2));
392-
m_axioms.push_back(a2);
393-
return res;
397+
if(const auto maybe_chars=to_char_pair(args(f, 3)[1], args(f, 3)[2]))
398+
{
399+
const auto old_char=maybe_chars->first;
400+
const auto new_char=maybe_chars->second;
401+
string_exprt res=fresh_string(ref_type);
402+
403+
// We add axioms:
404+
// a1 : |res| = |str|
405+
// a2 : forall qvar, 0<=qvar<|res|,
406+
// str[qvar]=oldChar => res[qvar]=newChar
407+
// !str[qvar]=oldChar => res[qvar]=str[qvar]
408+
409+
m_axioms.push_back(res.axiom_for_has_same_length_as(str));
410+
411+
symbol_exprt qvar=fresh_univ_index("QA_replace", ref_type.get_index_type());
412+
implies_exprt case1(
413+
equal_exprt(str[qvar], old_char),
414+
equal_exprt(res[qvar], new_char));
415+
implies_exprt case2(
416+
not_exprt(equal_exprt(str[qvar], old_char)),
417+
equal_exprt(res[qvar], str[qvar]));
418+
string_constraintt a2(qvar, res.length(), and_exprt(case1, case2));
419+
m_axioms.push_back(a2);
420+
return res;
421+
}
422+
return str;
394423
}
395424

396425
/// add axioms corresponding to the StringBuilder.deleteCharAt java function

0 commit comments

Comments
 (0)