@@ -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