Skip to content

Commit 5e1b377

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 c905b68 commit 5e1b377

File tree

1 file changed

+28
-39
lines changed

1 file changed

+28
-39
lines changed

src/solvers/refinement/string_refinement.cpp

Lines changed: 28 additions & 39 deletions
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,12 @@ 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())
1857+
for(auto it = expr.depth_begin(); it != expr.depth_end(); ++it)
18671858
{
18681859
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;
1860+
result.insert(to_index_expr(*it).index());
18771861
}
1878-
1879-
return index;
1862+
return result;
18801863
}
18811864

18821865
/// Instantiates a string constraint by substituting the quantifiers.
@@ -1897,18 +1880,24 @@ static exprt instantiate(
18971880
const exprt &str,
18981881
const exprt &val)
18991882
{
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;
1883+
const auto indexes = find_indexes(axiom.body, str, axiom.univ_var);
1884+
INVARIANT(
1885+
str.id() == ID_array || indexes.size() <= 1,
1886+
"non constant array should always be accessed at the same index");
1887+
exprt::operandst conjuncts;
1888+
for(const auto &index : indexes)
1889+
{
1890+
const exprt univ_var_value =
1891+
compute_inverse_function(stream, axiom.univ_var, val, index);
1892+
implies_exprt instance(
1893+
and_exprt(
1894+
binary_relation_exprt(axiom.univ_var, ID_ge, axiom.lower_bound),
1895+
binary_relation_exprt(axiom.univ_var, ID_lt, axiom.upper_bound)),
1896+
axiom.body);
1897+
replace_expr(axiom.univ_var, univ_var_value, instance);
1898+
conjuncts.push_back(instance);
1899+
}
1900+
return conjunction(conjuncts);
19121901
}
19131902

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

0 commit comments

Comments
 (0)