Skip to content

Commit c8597a4

Browse files
Merge pull request diffblue#2174 from romainbrenguier/bugfix/not_contains#TG-3150
[TG-3150] Refactor negation of not contains constraints
2 parents b08ef94 + 386faa8 commit c8597a4

File tree

4 files changed

+62
-64
lines changed

4 files changed

+62
-64
lines changed
779 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
public class Test {
2+
public static String check(String s) {
3+
// Filter
4+
if (s == null) {
5+
return "Null string";
6+
}
7+
if (s.length() < 16_000_000) {
8+
return "Too short";
9+
}
10+
11+
// Act
12+
boolean b1 = s.contains("foobar");
13+
14+
// Filter output
15+
if (b1) {
16+
return "Contained";
17+
}
18+
19+
// Assert
20+
assert(false);
21+
return "Unreachable";
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
Test.class
3+
--refine-strings --function Test.check
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
file Test.java line 20 .*: FAILURE$
8+
--
9+
--
10+
--string-max-length is not used on purpose, because this tests the behaviour
11+
of string refinement on very large strings.

src/solvers/refinement/string_refinement.cpp

+28-64
Original file line numberDiff line numberDiff line change
@@ -1202,51 +1202,38 @@ exprt substitute_array_access(
12021202
/// have been replaced by their valuation in the current model.
12031203
/// \pre Symbols other than the universal variable should have been replaced by
12041204
/// their valuation in the current model.
1205-
/// \param axiom: the not_contains constraint to add the negation of
1205+
/// \param constraint: the not_contains constraint to add the negation of
12061206
/// \param univ_var: the universal variable for the negation of the axiom
1207+
/// \param get: valuation function, the result should have been simplified
12071208
/// \return: the negation of the axiom under the current evaluation
12081209
static exprt negation_of_not_contains_constraint(
1209-
const string_not_contains_constraintt &axiom,
1210-
const symbol_exprt &univ_var)
1210+
const string_not_contains_constraintt &constraint,
1211+
const symbol_exprt &univ_var,
1212+
const std::function<exprt(const exprt &)> &get)
12111213
{
12121214
// If the for all is vacuously true, the negation is false.
1213-
const exprt &lbu=axiom.univ_lower_bound();
1214-
const exprt &ubu=axiom.univ_upper_bound();
1215-
if(lbu.id()==ID_constant && ubu.id()==ID_constant)
1216-
{
1217-
const auto lb_int = numeric_cast<mp_integer>(lbu);
1218-
const auto ub_int = numeric_cast<mp_integer>(ubu);
1219-
if(!lb_int || !ub_int || *ub_int<=*lb_int)
1220-
return false_exprt();
1221-
}
1222-
1223-
const auto lbe = numeric_cast_v<mp_integer>(axiom.exists_lower_bound());
1224-
const auto ube = numeric_cast_v<mp_integer>(axiom.exists_upper_bound());
1225-
1226-
// If the premise is false, the implication is trivially true, so the
1227-
// negation is false.
1228-
if(axiom.premise()==false_exprt())
1229-
return false_exprt();
1230-
1231-
and_exprt univ_bounds(
1232-
binary_relation_exprt(lbu, ID_le, univ_var),
1233-
binary_relation_exprt(ubu, ID_gt, univ_var));
1215+
const auto lbe =
1216+
numeric_cast_v<mp_integer>(get(constraint.exists_lower_bound()));
1217+
const auto ube =
1218+
numeric_cast_v<mp_integer>(get(constraint.exists_upper_bound()));
1219+
const auto univ_bounds = and_exprt(
1220+
binary_relation_exprt(get(constraint.univ_lower_bound()), ID_le, univ_var),
1221+
binary_relation_exprt(get(constraint.univ_upper_bound()), ID_gt, univ_var));
12341222

12351223
// The negated existential becomes an universal, and this is the unrolling of
12361224
// that universal quantifier.
12371225
std::vector<exprt> conjuncts;
1226+
conjuncts.reserve(numeric_cast_v<std::size_t>(ube - lbe));
12381227
for(mp_integer i=lbe; i<ube; ++i)
12391228
{
1240-
const constant_exprt i_exprt=from_integer(i, univ_var.type());
1241-
const equal_exprt equal_chars(
1242-
axiom.s0()[plus_exprt(univ_var, i_exprt)],
1243-
axiom.s1()[i_exprt]);
1244-
conjuncts.push_back(equal_chars);
1229+
const constant_exprt i_expr = from_integer(i, univ_var.type());
1230+
const exprt s0_char =
1231+
get(index_exprt(constraint.s0(), plus_exprt(univ_var, i_expr)));
1232+
const exprt s1_char = get(index_exprt(constraint.s1(), i_expr));
1233+
conjuncts.push_back(equal_exprt(s0_char, s1_char));
12451234
}
1246-
exprt equal_strings=conjunction(conjuncts);
1247-
and_exprt negaxiom(univ_bounds, axiom.premise(), equal_strings);
1248-
1249-
return negaxiom;
1235+
const exprt equal_strings = conjunction(conjuncts);
1236+
return and_exprt(univ_bounds, get(constraint.premise()), equal_strings);
12501237
}
12511238

12521239
/// Negates the constraint to be fed to a solver. The intended usage is to find
@@ -1406,42 +1393,19 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
14061393
for(std::size_t i = 0; i < axioms.not_contains.size(); i++)
14071394
{
14081395
const string_not_contains_constraintt &nc_axiom=axioms.not_contains[i];
1409-
const exprt &univ_bound_inf=nc_axiom.univ_lower_bound();
1410-
const exprt &univ_bound_sup=nc_axiom.univ_upper_bound();
1411-
const exprt &prem=nc_axiom.premise();
1412-
const exprt &exists_bound_inf=nc_axiom.exists_lower_bound();
1413-
const exprt &exists_bound_sup=nc_axiom.exists_upper_bound();
1414-
const array_string_exprt &s0 = nc_axiom.s0();
1415-
const array_string_exprt &s1 = nc_axiom.s1();
1416-
1417-
symbol_exprt univ_var=generator.fresh_univ_index(
1396+
const symbol_exprt univ_var = generator.fresh_univ_index(
14181397
"not_contains_univ_var", nc_axiom.s0().length().type());
1419-
string_not_contains_constraintt nc_axiom_in_model(
1420-
get(univ_bound_inf),
1421-
get(univ_bound_sup),
1422-
get(prem),
1423-
get(exists_bound_inf),
1424-
get(exists_bound_sup),
1425-
to_array_string_expr(get(s0)),
1426-
to_array_string_expr(get(s1)));
1427-
1428-
// necessary so that expressions such as `1 + (3 - (TRUE ? 0 : 0))` do not
1429-
// appear in bounds
1430-
nc_axiom_in_model =
1431-
to_string_not_contains_constraint(simplify_expr(nc_axiom_in_model, ns));
1432-
1433-
exprt negaxiom =
1434-
negation_of_not_contains_constraint(nc_axiom_in_model, univ_var);
1435-
1436-
negaxiom = simplify_expr(negaxiom, ns);
1437-
const exprt with_concrete_arrays =
1438-
substitute_array_access(negaxiom, gen_symbol, true);
1398+
const exprt negated_axiom = negation_of_not_contains_constraint(
1399+
nc_axiom, univ_var, [&](const exprt &expr) {
1400+
return simplify_expr(get(expr), ns); });
14391401

14401402
stream << indent << i << ".\n";
14411403
debug_check_axioms_step(
1442-
stream, ns, nc_axiom, nc_axiom_in_model, negaxiom, with_concrete_arrays);
1404+
stream, ns, nc_axiom, nc_axiom, negated_axiom, negated_axiom);
14431405

1444-
if(const auto witness = find_counter_example(ns, ui, negaxiom, univ_var))
1406+
if(
1407+
const auto witness =
1408+
find_counter_example(ns, ui, negated_axiom, univ_var))
14451409
{
14461410
stream << indent2 << "- violated_for: " << univ_var.get_identifier()
14471411
<< "=" << format(*witness) << eom;

0 commit comments

Comments
 (0)