@@ -1833,18 +1833,17 @@ static void update_index_set(
1833
1833
}
1834
1834
}
1835
1835
1836
- // / Finds an index on `str` used in `expr` that contains `qvar`, for instance
1837
- // / with arguments ``(str[k]=='a')``, `str`, and `k`, the function should
1838
- // / return `k`.
1839
- // / If two different such indexes exist, an invariant will fail.
1836
+ // / Find indexes of `str` used in `expr` that contains `qvar`, for instance
1837
+ // / with arguments ``(str[k+1]=='a')``, `str`, and `k`, the function should
1838
+ // / return `k+1`.
1840
1839
// / \param [in] expr: the expression to search
1841
1840
// / \param [in] str: the string which must be indexed
1842
1841
// / \param [in] qvar: the universal variable that must be in the index
1843
- // / \return an index expression in `expr` on `str` containing `qvar`. Returns
1844
- // / an empty optional when `expr` does not contain `str`.
1845
- static optionalt<exprt>
1846
- find_index (const exprt &expr, const exprt &str, const symbol_exprt &qvar)
1842
+ // / \return index expressions in `expr` on `str` containing `qvar`.
1843
+ static std::unordered_set<exprt, irep_hash>
1844
+ find_indexes (const exprt &expr, const exprt &str, const symbol_exprt &qvar)
1847
1845
{
1846
+ decltype (find_indexes (expr, str, qvar)) result;
1848
1847
auto index_str_containing_qvar = [&](const exprt &e) {
1849
1848
if (auto index_expr = expr_try_dynamic_cast<index_exprt>(e))
1850
1849
{
@@ -1855,28 +1854,11 @@ find_index(const exprt &expr, const exprt &str, const symbol_exprt &qvar)
1855
1854
return false ;
1856
1855
};
1857
1856
1858
- auto it = std::find_if (
1859
- expr.depth_begin (), expr.depth_end (), index_str_containing_qvar);
1860
- if (it == expr.depth_end ())
1861
- return {};
1862
- const exprt &index = to_index_expr (*it).index ();
1863
-
1864
- // Check that there are no two such indexes
1865
- it.next_sibling_or_parent ();
1866
- while (it != expr.depth_end ())
1867
- {
1868
- if (index_str_containing_qvar (*it))
1869
- {
1870
- INVARIANT (
1871
- to_index_expr (*it).index () == index ,
1872
- " string should always be indexed by same value in a given formula" );
1873
- it.next_sibling_or_parent ();
1874
- }
1875
- else
1876
- ++it;
1877
- }
1878
-
1879
- return index ;
1857
+ std::for_each (expr.depth_begin (), expr.depth_end (), [&](const exprt &e) {
1858
+ if (index_str_containing_qvar (e))
1859
+ result.insert (to_index_expr (e).index ());
1860
+ });
1861
+ return result;
1880
1862
}
1881
1863
1882
1864
// / Instantiates a string constraint by substituting the quantifiers.
@@ -1897,18 +1879,24 @@ static exprt instantiate(
1897
1879
const exprt &str,
1898
1880
const exprt &val)
1899
1881
{
1900
- const optionalt<exprt> idx = find_index (axiom.body , str, axiom.univ_var );
1901
- if (!idx.has_value ())
1902
- return true_exprt ();
1903
-
1904
- const exprt r = compute_inverse_function (stream, axiom.univ_var , val, *idx);
1905
- implies_exprt instance (
1906
- and_exprt (
1907
- binary_relation_exprt (axiom.univ_var , ID_ge, axiom.lower_bound ),
1908
- binary_relation_exprt (axiom.univ_var , ID_lt, axiom.upper_bound )),
1909
- axiom.body );
1910
- replace_expr (axiom.univ_var , r, instance);
1911
- return instance;
1882
+ const auto indexes = find_indexes (axiom.body , str, axiom.univ_var );
1883
+ INVARIANT (
1884
+ str.id () == ID_array || indexes.size () <= 1 ,
1885
+ " non constant array should always be accessed at the same index" );
1886
+ exprt::operandst conjuncts;
1887
+ for (const auto &index : indexes)
1888
+ {
1889
+ const exprt univ_var_value =
1890
+ compute_inverse_function (stream, axiom.univ_var , val, index );
1891
+ implies_exprt instance (
1892
+ and_exprt (
1893
+ binary_relation_exprt (axiom.univ_var , ID_ge, axiom.lower_bound ),
1894
+ binary_relation_exprt (axiom.univ_var , ID_lt, axiom.upper_bound )),
1895
+ axiom.body );
1896
+ replace_expr (axiom.univ_var , univ_var_value, instance);
1897
+ conjuncts.push_back (instance);
1898
+ }
1899
+ return conjunction (conjuncts);
1912
1900
}
1913
1901
1914
1902
// / Instantiates a quantified formula representing `not_contains` by
0 commit comments