Skip to content

Commit 7085f9c

Browse files
Correct find_index function for if-expressions
find_index would give up if the array in the index expression does not correspond exactly to the given `str`, however it could be an expression that contains str. For instance `(cond?str1:str2)[i+1]` is equivalent to `cond?(str1[i+1]):(str2[i+1])`, so in both cases `find_index(e, str1, i)` should return `i+1`.
1 parent a2a3140 commit 7085f9c

File tree

1 file changed

+38
-17
lines changed

1 file changed

+38
-17
lines changed

src/solvers/refinement/string_refinement.cpp

+38-17
Original file line numberDiff line numberDiff line change
@@ -2088,26 +2088,47 @@ static void update_index_set(
20882088
/// Finds an index on `str` used in `expr` that contains `qvar`, for instance
20892089
/// with arguments ``(str[k]=='a')``, `str`, and `k`, the function should
20902090
/// return `k`.
2091+
/// If two different such indexes exist, an invariant will fail.
20912092
/// \param [in] expr: the expression to search
20922093
/// \param [in] str: the string which must be indexed
20932094
/// \param [in] qvar: the universal variable that must be in the index
2094-
/// \return an index expression in `expr` on `str` containing `qvar`
2095-
static exprt find_index(
2096-
const exprt &expr, const exprt &str, const symbol_exprt &qvar)
2095+
/// \return an index expression in `expr` on `str` containing `qvar`. Returns
2096+
/// an empty optional when `expr` does not contain `str`.
2097+
static optionalt<exprt>
2098+
find_index(const exprt &expr, const exprt &str, const symbol_exprt &qvar)
20972099
{
2098-
const auto it=std::find_if(
2099-
expr.depth_begin(),
2100-
expr.depth_end(),
2101-
[&] (const exprt &e) -> bool
2100+
auto index_str_containing_qvar = [&](const exprt &e) { // NOLINT
2101+
if(auto index_expr = expr_try_dynamic_cast<index_exprt>(e))
21022102
{
2103-
return e.id()==ID_index
2104-
&& to_index_expr(e).array()==str
2105-
&& find_qvar(to_index_expr(e).index(), qvar);
2106-
});
2103+
const auto &arr = index_expr->array();
2104+
const auto str_it = std::find(arr.depth_begin(), arr.depth_end(), str);
2105+
return str_it != arr.depth_end() && find_qvar(index_expr->index(), qvar);
2106+
}
2107+
return false;
2108+
};
2109+
2110+
auto it = std::find_if(
2111+
expr.depth_begin(), expr.depth_end(), index_str_containing_qvar);
2112+
if(it == expr.depth_end())
2113+
return {};
2114+
const exprt &index = to_index_expr(*it).index();
2115+
2116+
// Check that there are no two such indexes
2117+
it.next_sibling_or_parent();
2118+
while(it != expr.depth_end())
2119+
{
2120+
if(index_str_containing_qvar(*it))
2121+
{
2122+
INVARIANT(
2123+
to_index_expr(*it).index() == index,
2124+
"string should always be indexed by same value in a given formula");
2125+
it.next_sibling_or_parent();
2126+
}
2127+
else
2128+
++it;
2129+
}
21072130

2108-
return it==expr.depth_end()
2109-
?nil_exprt()
2110-
:to_index_expr(*it).index();
2131+
return index;
21112132
}
21122133

21132134
/// Instantiates a string constraint by substituting the quantifiers.
@@ -2128,11 +2149,11 @@ static exprt instantiate(
21282149
const exprt &str,
21292150
const exprt &val)
21302151
{
2131-
const exprt idx = find_index(axiom.body(), str, axiom.univ_var());
2132-
if(idx.is_nil())
2152+
const optionalt<exprt> idx = find_index(axiom.body(), str, axiom.univ_var());
2153+
if(!idx.has_value())
21332154
return true_exprt();
21342155

2135-
const exprt r = compute_inverse_function(stream, axiom.univ_var(), val, idx);
2156+
const exprt r = compute_inverse_function(stream, axiom.univ_var(), val, *idx);
21362157
implies_exprt instance(
21372158
and_exprt(
21382159
binary_relation_exprt(axiom.univ_var(), ID_ge, axiom.lower_bound()),

0 commit comments

Comments
 (0)