Skip to content

Commit d599dfc

Browse files
Allow duplicate instantiations of the same constant string
We normally don't want the same string to be accessed at two indexes in the same universal formula, but for constant strings this does not really matter, so we can allow it. In particular the previous invariant would have failed if we try to write `cprover_starts_with("foo", "foo", 1)`.
1 parent 6763669 commit d599dfc

File tree

1 file changed

+30
-42
lines changed

1 file changed

+30
-42
lines changed

src/solvers/refinement/string_refinement.cpp

+30-42
Original file line numberDiff line numberDiff line change
@@ -1833,18 +1833,17 @@ static void update_index_set(
18331833
}
18341834
}
18351835

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`.
18401839
/// \param [in] expr: the expression to search
18411840
/// \param [in] str: the string which must be indexed
18421841
/// \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)
18471845
{
1846+
decltype(find_indexes(expr, str, qvar)) result;
18481847
auto index_str_containing_qvar = [&](const exprt &e) {
18491848
if(auto index_expr = expr_try_dynamic_cast<index_exprt>(e))
18501849
{
@@ -1855,28 +1854,11 @@ find_index(const exprt &expr, const exprt &str, const symbol_exprt &qvar)
18551854
return false;
18561855
};
18571856

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;
18801862
}
18811863

18821864
/// Instantiates a string constraint by substituting the quantifiers.
@@ -1897,18 +1879,24 @@ static exprt instantiate(
18971879
const exprt &str,
18981880
const exprt &val)
18991881
{
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);
19121900
}
19131901

19141902
/// Instantiates a quantified formula representing `not_contains` by

0 commit comments

Comments
 (0)