Skip to content

Commit 5ec351b

Browse files
Drop inheritance from constraint on exprt
There is no strong reason for making string_constraintt inherit from exprt. Getting rid of this inheritance makes usage of the class safer by preventing the application of exprt method that do not make sense for string constraints.
1 parent dd30bbb commit 5ec351b

File tree

4 files changed

+84
-145
lines changed

4 files changed

+84
-145
lines changed

jbmc/unit/solvers/refinement/string_constraint_instantiation/instantiate_not_contains.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,8 @@ SCENARIO("instantiate_not_contains",
206206
constraints.end(),
207207
axioms,
208208
[&](const std::string &accu, string_constraintt sc) {
209-
simplify(sc, ns);
210-
std::string s;
211-
java_lang->from_expr(sc, s, ns);
212-
return accu + s + "\n\n";
209+
simplify(sc.body, ns);
210+
return accu + to_string(sc) + "\n\n";
213211
});
214212

215213
const auto nc_contraints = generator.get_not_contains_constraints();

src/solvers/refinement/string_constraint.h

+36-64
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Author: Romain Brenguier, [email protected]
2727
#include <util/format_type.h>
2828
#include <util/refined_string_type.h>
2929
#include <util/string_expr.h>
30+
#include <util/union_find_replace.h>
3031

3132
/// ### Universally quantified string constraint
3233
///
@@ -54,89 +55,60 @@ Author: Romain Brenguier, [email protected]
5455
/// \f$f\f$ [explicitly stated, implied].
5556
///
5657
/// \todo The fact that we follow this grammar is not enforced at the moment.
57-
class string_constraintt : public exprt
58+
class string_constraintt final
5859
{
5960
public:
6061
// String constraints are of the form
61-
// forall univ_var in [lower_bound,upper_bound[. premise => body
62+
// forall univ_var in [lower_bound,upper_bound[. body
63+
symbol_exprt univ_var;
64+
exprt lower_bound;
65+
exprt upper_bound;
66+
exprt body;
6267

63-
const exprt &premise() const
64-
{
65-
return op0();
66-
}
67-
68-
const exprt &body() const
69-
{
70-
return op1();
71-
}
68+
string_constraintt() = delete;
7269

73-
const symbol_exprt &univ_var() const
70+
string_constraintt(
71+
symbol_exprt _univ_var,
72+
exprt lower_bound,
73+
exprt upper_bound,
74+
exprt body)
75+
: univ_var(_univ_var),
76+
lower_bound(lower_bound),
77+
upper_bound(upper_bound),
78+
body(body)
7479
{
75-
return to_symbol_expr(op2());
7680
}
7781

78-
const exprt &upper_bound() const
79-
{
80-
return op3();
81-
}
82-
83-
const exprt &lower_bound() const
82+
// Default bound inferior is 0
83+
string_constraintt(symbol_exprt univ_var, exprt upper_bound, exprt body)
84+
: string_constraintt(
85+
univ_var,
86+
from_integer(0, univ_var.type()),
87+
upper_bound,
88+
body)
8489
{
85-
return operands()[4];
8690
}
8791

88-
private:
89-
string_constraintt();
90-
91-
public:
92-
string_constraintt(
93-
const symbol_exprt &_univ_var,
94-
const exprt &bound_inf,
95-
const exprt &bound_sup,
96-
const exprt &body):
97-
exprt(ID_string_constraint)
92+
exprt univ_within_bounds() const
9893
{
99-
copy_to_operands(true_exprt(), body);
100-
copy_to_operands(_univ_var, bound_sup, bound_inf);
94+
return and_exprt(
95+
binary_relation_exprt(lower_bound, ID_le, univ_var),
96+
binary_relation_exprt(upper_bound, ID_gt, univ_var));
10197
}
10298

103-
// Default bound inferior is 0
104-
string_constraintt(
105-
const symbol_exprt &_univ_var,
106-
const exprt &bound_sup,
107-
const exprt &body):
108-
string_constraintt(
109-
_univ_var,
110-
from_integer(0, _univ_var.type()),
111-
bound_sup,
112-
body)
113-
{}
114-
115-
exprt univ_within_bounds() const
99+
void replace_expr(union_find_replacet &replace_map)
116100
{
117-
return and_exprt(
118-
binary_relation_exprt(lower_bound(), ID_le, univ_var()),
119-
binary_relation_exprt(upper_bound(), ID_gt, univ_var()));
101+
replace_map.replace_expr(lower_bound);
102+
replace_map.replace_expr(upper_bound);
103+
replace_map.replace_expr(body);
120104
}
121105

122106
exprt negation() const
123107
{
124-
return and_exprt(univ_within_bounds(), not_exprt(body()));
108+
return and_exprt(univ_within_bounds(), not_exprt(body));
125109
}
126110
};
127111

128-
extern inline const string_constraintt &to_string_constraint(const exprt &expr)
129-
{
130-
PRECONDITION(expr.id()==ID_string_constraint && expr.operands().size()==5);
131-
return static_cast<const string_constraintt &>(expr);
132-
}
133-
134-
extern inline string_constraintt &to_string_constraint(exprt &expr)
135-
{
136-
PRECONDITION(expr.id()==ID_string_constraint && expr.operands().size()==5);
137-
return static_cast<string_constraintt &>(expr);
138-
}
139-
140112
/// Used for debug printing.
141113
/// \param [in] ns: namespace for `from_expr`
142114
/// \param [in] identifier: identifier for `from_expr`
@@ -145,9 +117,9 @@ extern inline string_constraintt &to_string_constraint(exprt &expr)
145117
inline std::string to_string(const string_constraintt &expr)
146118
{
147119
std::ostringstream out;
148-
out << "forall " << format(expr.univ_var()) << " in ["
149-
<< format(expr.lower_bound()) << ", " << format(expr.upper_bound())
150-
<< "). " << format(expr.premise()) << " => " << format(expr.body());
120+
out << "forall " << format(expr.univ_var) << " in ["
121+
<< format(expr.lower_bound) << ", " << format(expr.upper_bound) << "). "
122+
<< format(expr.body);
151123
return out.str();
152124
}
153125

src/solvers/refinement/string_refinement.cpp

+46-76
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Author: Alberto Griggio, [email protected]
3333
static bool is_valid_string_constraint(
3434
messaget::mstreamt &stream,
3535
const namespacet &ns,
36-
const string_constraintt &expr);
36+
const string_constraintt &constraint);
3737

3838
static optionalt<exprt> find_counter_example(
3939
const namespacet &ns,
@@ -697,7 +697,7 @@ decision_proceduret::resultt string_refinementt::dec_solve()
697697
constraints.end(),
698698
std::back_inserter(axioms.universal),
699699
[&](string_constraintt constraint) {
700-
symbol_resolve.replace_expr(constraint);
700+
constraint.replace_expr(symbol_resolve);
701701
DATA_INVARIANT(
702702
is_valid_string_constraint(error(), ns, constraint),
703703
string_refinement_invariantt(
@@ -1238,34 +1238,23 @@ static exprt negation_of_not_contains_constraint(
12381238

12391239
/// Debugging function which outputs the different steps an axiom goes through
12401240
/// to be checked in check axioms.
1241+
/// \tparam T: can be either string_constraintt or
1242+
/// string_not_contains_constraintt
1243+
template <typename T>
12411244
static void debug_check_axioms_step(
12421245
messaget::mstreamt &stream,
12431246
const namespacet &ns,
1244-
const exprt &axiom,
1245-
const exprt &axiom_in_model,
1247+
const T &axiom,
1248+
const T &axiom_in_model,
12461249
const exprt &negaxiom,
12471250
const exprt &with_concretized_arrays)
12481251
{
12491252
static const std::string indent = " ";
12501253
static const std::string indent2 = " ";
12511254
stream << indent2 << "- axiom:\n" << indent2 << indent;
1252-
1253-
if(axiom.id() == ID_string_constraint)
1254-
stream << to_string(to_string_constraint(axiom));
1255-
else if(axiom.id() == ID_string_not_contains_constraint)
1256-
stream << to_string(to_string_not_contains_constraint(axiom));
1257-
else
1258-
stream << format(axiom);
1255+
stream << to_string(axiom);
12591256
stream << '\n' << indent2 << "- axiom_in_model:\n" << indent2 << indent;
1260-
1261-
if(axiom_in_model.id() == ID_string_constraint)
1262-
stream << to_string(to_string_constraint(axiom_in_model));
1263-
else if(axiom_in_model.id() == ID_string_not_contains_constraint)
1264-
stream << to_string(to_string_not_contains_constraint(axiom_in_model));
1265-
else
1266-
stream << format(axiom_in_model);
1267-
1268-
stream << '\n'
1257+
stream << to_string(axiom_in_model) << '\n'
12691258
<< indent2 << "- negated_axiom:\n"
12701259
<< indent2 << indent << format(negaxiom) << '\n';
12711260
stream << indent2 << "- negated_axiom_with_concretized_arrays:\n"
@@ -1322,16 +1311,11 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
13221311
for(size_t i=0; i<axioms.universal.size(); i++)
13231312
{
13241313
const string_constraintt &axiom=axioms.universal[i];
1325-
const symbol_exprt &univ_var=axiom.univ_var();
1326-
const exprt &bound_inf=axiom.lower_bound();
1327-
const exprt &bound_sup=axiom.upper_bound();
1328-
const exprt &prem=axiom.premise();
1329-
INVARIANT(
1330-
prem == true_exprt(), "string constraint premises are not supported");
1331-
const exprt &body=axiom.body();
1332-
13331314
const string_constraintt axiom_in_model(
1334-
univ_var, get(bound_inf), get(bound_sup), get(body));
1315+
axiom.univ_var,
1316+
get(axiom.lower_bound),
1317+
get(axiom.upper_bound),
1318+
get(axiom.body));
13351319

13361320
exprt negaxiom = axiom_in_model.negation();
13371321
negaxiom = simplify_expr(negaxiom, ns);
@@ -1342,11 +1326,12 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
13421326
debug_check_axioms_step(
13431327
stream, ns, axiom, axiom_in_model, negaxiom, with_concretized_arrays);
13441328

1345-
if(const auto &witness=
1346-
find_counter_example(ns, ui, with_concretized_arrays, univ_var))
1329+
if(
1330+
const auto &witness =
1331+
find_counter_example(ns, ui, with_concretized_arrays, axiom.univ_var))
13471332
{
1348-
stream << indent2 << "- violated_for: " << univ_var.get_identifier()
1349-
<< "=" << format(*witness) << eom;
1333+
stream << indent2 << "- violated_for: " << format(axiom.univ_var) << "="
1334+
<< format(*witness) << eom;
13501335
violated[i]=*witness;
13511336
}
13521337
else
@@ -1402,13 +1387,13 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
14021387
const exprt &val=v.second;
14031388
const string_constraintt &axiom=axioms.universal[v.first];
14041389

1405-
implies_exprt instance(axiom.premise(), axiom.body());
1406-
replace_expr(axiom.univ_var(), val, instance);
1390+
exprt instance(axiom.body);
1391+
replace_expr(axiom.univ_var, val, instance);
14071392
// We are not sure the index set contains only positive numbers
14081393
and_exprt bounds(
14091394
axiom.univ_within_bounds(),
14101395
binary_relation_exprt(from_integer(0, val.type()), ID_le, val));
1411-
replace_expr(axiom.univ_var(), val, bounds);
1396+
replace_expr(axiom.univ_var, val, bounds);
14121397
const implies_exprt counter(bounds, instance);
14131398
lemmas.push_back(counter);
14141399
}
@@ -1766,10 +1751,10 @@ static void initial_index_set(
17661751
const namespacet &ns,
17671752
const string_constraintt &axiom)
17681753
{
1769-
const symbol_exprt &qvar=axiom.univ_var();
1770-
const auto &bound = axiom.upper_bound();
1771-
auto it = axiom.body().depth_begin();
1772-
const auto end = axiom.body().depth_end();
1754+
const symbol_exprt &qvar = axiom.univ_var;
1755+
const auto &bound = axiom.upper_bound;
1756+
auto it = axiom.body.depth_begin();
1757+
const auto end = axiom.body.depth_end();
17731758
while(it != end)
17741759
{
17751760
if(it->id() == ID_index && is_char_type(it->type()))
@@ -1912,18 +1897,17 @@ static exprt instantiate(
19121897
const exprt &str,
19131898
const exprt &val)
19141899
{
1915-
const optionalt<exprt> idx = find_index(axiom.body(), str, axiom.univ_var());
1900+
const optionalt<exprt> idx = find_index(axiom.body, str, axiom.univ_var);
19161901
if(!idx.has_value())
19171902
return true_exprt();
19181903

1919-
const exprt r = compute_inverse_function(stream, axiom.univ_var(), val, *idx);
1904+
const exprt r = compute_inverse_function(stream, axiom.univ_var, val, *idx);
19201905
implies_exprt instance(
19211906
and_exprt(
1922-
binary_relation_exprt(axiom.univ_var(), ID_ge, axiom.lower_bound()),
1923-
binary_relation_exprt(axiom.univ_var(), ID_lt, axiom.upper_bound()),
1924-
axiom.premise()),
1925-
axiom.body());
1926-
replace_expr(axiom.univ_var(), r, instance);
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);
19271911
return instance;
19281912
}
19291913

@@ -2175,11 +2159,11 @@ is_linear_arithmetic_expr(const exprt &expr, const symbol_exprt &var)
21752159
/// \param [in] expr: The string constraint to check
21762160
/// \return true if the universal variable only occurs in index expressions,
21772161
/// false otherwise.
2178-
static bool universal_only_in_index(const string_constraintt &expr)
2162+
static bool universal_only_in_index(const string_constraintt &constr)
21792163
{
2180-
for(auto it = expr.body().depth_begin(); it != expr.body().depth_end();)
2164+
for(auto it = constr.body.depth_begin(); it != constr.body.depth_end();)
21812165
{
2182-
if(*it == expr.univ_var())
2166+
if(*it == constr.univ_var)
21832167
return false;
21842168
if(it->id() == ID_index)
21852169
it.next_sibling_or_parent();
@@ -2193,35 +2177,20 @@ static bool universal_only_in_index(const string_constraintt &expr)
21932177
/// \related string_constraintt
21942178
/// \param stream: message stream
21952179
/// \param ns: namespace
2196-
/// \param [in] expr: the string constraint to check
2180+
/// \param [in] constraint: the string constraint to check
21972181
/// \return whether the constraint satisfies the invariant
21982182
static bool is_valid_string_constraint(
21992183
messaget::mstreamt &stream,
22002184
const namespacet &ns,
2201-
const string_constraintt &expr)
2185+
const string_constraintt &constraint)
22022186
{
22032187
const auto eom=messaget::eom;
2204-
// Condition 1: The premise cannot contain any string indices
2205-
const array_index_mapt premise_indices=gather_indices(expr.premise());
2206-
if(!premise_indices.empty())
2207-
{
2208-
stream << "Premise has indices: " << format(expr) << ", map: {";
2209-
for(const auto &pair : premise_indices)
2210-
{
2211-
stream << format(pair.first) << ": {";
2212-
for(const auto &i : pair.second)
2213-
stream << format(i) << ", ";
2214-
}
2215-
stream << "}}" << eom;
2216-
return false;
2217-
}
2218-
2219-
const array_index_mapt body_indices=gather_indices(expr.body());
2188+
const array_index_mapt body_indices = gather_indices(constraint.body);
22202189
// Must validate for each string. Note that we have an invariant that the
22212190
// second value in the pair is non-empty.
22222191
for(const auto &pair : body_indices)
22232192
{
2224-
// Condition 2: All indices of the same string must be the of the same form
2193+
// Condition 1: All indices of the same string must be the of the same form
22252194
const exprt rep=pair.second.back();
22262195
for(size_t j=0; j<pair.second.size()-1; j++)
22272196
{
@@ -2230,25 +2199,26 @@ static bool is_valid_string_constraint(
22302199
const exprt result=simplify_expr(equals, ns);
22312200
if(result.is_false())
22322201
{
2233-
stream << "Indices not equal: " << format(expr)
2202+
stream << "Indices not equal: " << to_string(constraint)
22342203
<< ", str: " << format(pair.first) << eom;
22352204
return false;
22362205
}
22372206
}
22382207

2239-
// Condition 3: f must be linear in the quantified variable
2240-
if(!is_linear_arithmetic_expr(rep, expr.univ_var()))
2208+
// Condition 2: f must be linear in the quantified variable
2209+
if(!is_linear_arithmetic_expr(rep, constraint.univ_var))
22412210
{
2242-
stream << "f is not linear: " << format(expr)
2211+
stream << "f is not linear: " << to_string(constraint)
22432212
<< ", str: " << format(pair.first) << eom;
22442213
return false;
22452214
}
22462215

2247-
// Condition 4: the quantified variable can only occur in indices in the
2216+
// Condition 3: the quantified variable can only occur in indices in the
22482217
// body
2249-
if(!universal_only_in_index(expr))
2218+
if(!universal_only_in_index(constraint))
22502219
{
2251-
stream << "Universal variable outside of index:" << format(expr) << eom;
2220+
stream << "Universal variable outside of index:" << to_string(constraint)
2221+
<< eom;
22522222
return false;
22532223
}
22542224
}

0 commit comments

Comments
 (0)