Skip to content

Commit fe1edcc

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 8f88e4b commit fe1edcc

File tree

3 files changed

+83
-144
lines changed

3 files changed

+83
-144
lines changed

src/solvers/refinement/string_constraint.h

Lines changed: 36 additions & 64 deletions
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

Lines changed: 45 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Author: Alberto Griggio, [email protected]
3232
static bool is_valid_string_constraint(
3333
messaget::mstreamt &stream,
3434
const namespacet &ns,
35-
const string_constraintt &expr);
35+
const string_constraintt &constraint);
3636

3737
static optionalt<exprt> find_counter_example(
3838
const namespacet &ns,
@@ -693,7 +693,7 @@ decision_proceduret::resultt string_refinementt::dec_solve()
693693
constraints.end(),
694694
std::back_inserter(axioms.universal),
695695
[&](string_constraintt constraint) { // NOLINT
696-
symbol_resolve.replace_expr(constraint);
696+
constraint.replace_expr(symbol_resolve);
697697
DATA_INVARIANT(
698698
is_valid_string_constraint(error(), ns, constraint),
699699
string_refinement_invariantt(
@@ -1243,34 +1243,22 @@ static exprt negation_of_not_contains_constraint(
12431243

12441244
/// Debugging function which outputs the different steps an axiom goes through
12451245
/// to be checked in check axioms.
1246+
/// \tparam T: can be either string_constraintt or string_not_contains_constraintt
1247+
template <typename T>
12461248
static void debug_check_axioms_step(
12471249
messaget::mstreamt &stream,
12481250
const namespacet &ns,
1249-
const exprt &axiom,
1250-
const exprt &axiom_in_model,
1251+
const T &axiom,
1252+
const T &axiom_in_model,
12511253
const exprt &negaxiom,
12521254
const exprt &with_concretized_arrays)
12531255
{
12541256
static const std::string indent = " ";
12551257
static const std::string indent2 = " ";
12561258
stream << indent2 << "- axiom:\n" << indent2 << indent;
1257-
1258-
if(axiom.id() == ID_string_constraint)
1259-
stream << to_string(to_string_constraint(axiom));
1260-
else if(axiom.id() == ID_string_not_contains_constraint)
1261-
stream << to_string(to_string_not_contains_constraint(axiom));
1262-
else
1263-
stream << format(axiom);
1259+
stream << to_string(axiom);
12641260
stream << '\n' << indent2 << "- axiom_in_model:\n" << indent2 << indent;
1265-
1266-
if(axiom_in_model.id() == ID_string_constraint)
1267-
stream << to_string(to_string_constraint(axiom_in_model));
1268-
else if(axiom_in_model.id() == ID_string_not_contains_constraint)
1269-
stream << to_string(to_string_not_contains_constraint(axiom_in_model));
1270-
else
1271-
stream << format(axiom_in_model);
1272-
1273-
stream << '\n'
1261+
stream << to_string(axiom_in_model) << '\n'
12741262
<< indent2 << "- negated_axiom:\n"
12751263
<< indent2 << indent << format(negaxiom) << '\n';
12761264
stream << indent2 << "- negated_axiom_with_concretized_arrays:\n"
@@ -1327,16 +1315,11 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
13271315
for(size_t i=0; i<axioms.universal.size(); i++)
13281316
{
13291317
const string_constraintt &axiom=axioms.universal[i];
1330-
const symbol_exprt &univ_var=axiom.univ_var();
1331-
const exprt &bound_inf=axiom.lower_bound();
1332-
const exprt &bound_sup=axiom.upper_bound();
1333-
const exprt &prem=axiom.premise();
1334-
INVARIANT(
1335-
prem == true_exprt(), "string constraint premises are not supported");
1336-
const exprt &body=axiom.body();
1337-
13381318
const string_constraintt axiom_in_model(
1339-
univ_var, get(bound_inf), get(bound_sup), get(body));
1319+
axiom.univ_var,
1320+
get(axiom.lower_bound),
1321+
get(axiom.upper_bound),
1322+
get(axiom.body));
13401323

13411324
exprt negaxiom = axiom_in_model.negation();
13421325
negaxiom = simplify_expr(negaxiom, ns);
@@ -1347,11 +1330,12 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
13471330
debug_check_axioms_step(
13481331
stream, ns, axiom, axiom_in_model, negaxiom, with_concretized_arrays);
13491332

1350-
if(const auto &witness=
1351-
find_counter_example(ns, ui, with_concretized_arrays, univ_var))
1333+
if(
1334+
const auto &witness =
1335+
find_counter_example(ns, ui, with_concretized_arrays, axiom.univ_var))
13521336
{
1353-
stream << indent2 << "- violated_for: " << univ_var.get_identifier()
1354-
<< "=" << format(*witness) << eom;
1337+
stream << indent2 << "- violated_for: " << format(axiom.univ_var) << "="
1338+
<< format(*witness) << eom;
13551339
violated[i]=*witness;
13561340
}
13571341
else
@@ -1430,13 +1414,13 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
14301414
const exprt &val=v.second;
14311415
const string_constraintt &axiom=axioms.universal[v.first];
14321416

1433-
implies_exprt instance(axiom.premise(), axiom.body());
1434-
replace_expr(axiom.univ_var(), val, instance);
1417+
exprt instance(axiom.body);
1418+
replace_expr(axiom.univ_var, val, instance);
14351419
// We are not sure the index set contains only positive numbers
14361420
and_exprt bounds(
14371421
axiom.univ_within_bounds(),
14381422
binary_relation_exprt(from_integer(0, val.type()), ID_le, val));
1439-
replace_expr(axiom.univ_var(), val, bounds);
1423+
replace_expr(axiom.univ_var, val, bounds);
14401424
const implies_exprt counter(bounds, instance);
14411425
lemmas.push_back(counter);
14421426
}
@@ -1794,10 +1778,10 @@ static void initial_index_set(
17941778
const namespacet &ns,
17951779
const string_constraintt &axiom)
17961780
{
1797-
const symbol_exprt &qvar=axiom.univ_var();
1798-
const auto &bound = axiom.upper_bound();
1799-
auto it = axiom.body().depth_begin();
1800-
const auto end = axiom.body().depth_end();
1781+
const symbol_exprt &qvar = axiom.univ_var;
1782+
const auto &bound = axiom.upper_bound;
1783+
auto it = axiom.body.depth_begin();
1784+
const auto end = axiom.body.depth_end();
18011785
while(it != end)
18021786
{
18031787
if(it->id() == ID_index && is_char_type(it->type()))
@@ -1940,18 +1924,17 @@ static exprt instantiate(
19401924
const exprt &str,
19411925
const exprt &val)
19421926
{
1943-
const optionalt<exprt> idx = find_index(axiom.body(), str, axiom.univ_var());
1927+
const optionalt<exprt> idx = find_index(axiom.body, str, axiom.univ_var);
19441928
if(!idx.has_value())
19451929
return true_exprt();
19461930

1947-
const exprt r = compute_inverse_function(stream, axiom.univ_var(), val, *idx);
1931+
const exprt r = compute_inverse_function(stream, axiom.univ_var, val, *idx);
19481932
implies_exprt instance(
19491933
and_exprt(
1950-
binary_relation_exprt(axiom.univ_var(), ID_ge, axiom.lower_bound()),
1951-
binary_relation_exprt(axiom.univ_var(), ID_lt, axiom.upper_bound()),
1952-
axiom.premise()),
1953-
axiom.body());
1954-
replace_expr(axiom.univ_var(), r, instance);
1934+
binary_relation_exprt(axiom.univ_var, ID_ge, axiom.lower_bound),
1935+
binary_relation_exprt(axiom.univ_var, ID_lt, axiom.upper_bound)),
1936+
axiom.body);
1937+
replace_expr(axiom.univ_var, r, instance);
19551938
return instance;
19561939
}
19571940

@@ -2208,11 +2191,11 @@ is_linear_arithmetic_expr(const exprt &expr, const symbol_exprt &var)
22082191
/// \param [in] expr: The string constraint to check
22092192
/// \return true if the universal variable only occurs in index expressions,
22102193
/// false otherwise.
2211-
static bool universal_only_in_index(const string_constraintt &expr)
2194+
static bool universal_only_in_index(const string_constraintt &constr)
22122195
{
2213-
for(auto it = expr.body().depth_begin(); it != expr.body().depth_end();)
2196+
for(auto it = constr.body.depth_begin(); it != constr.body.depth_end();)
22142197
{
2215-
if(*it == expr.univ_var())
2198+
if(*it == constr.univ_var)
22162199
return false;
22172200
if(it->id() == ID_index)
22182201
it.next_sibling_or_parent();
@@ -2226,35 +2209,20 @@ static bool universal_only_in_index(const string_constraintt &expr)
22262209
/// \related string_constraintt
22272210
/// \param stream: message stream
22282211
/// \param ns: namespace
2229-
/// \param [in] expr: the string constraint to check
2212+
/// \param [in] constraint: the string constraint to check
22302213
/// \return whether the constraint satisfies the invariant
22312214
static bool is_valid_string_constraint(
22322215
messaget::mstreamt &stream,
22332216
const namespacet &ns,
2234-
const string_constraintt &expr)
2217+
const string_constraintt &constraint)
22352218
{
22362219
const auto eom=messaget::eom;
2237-
// Condition 1: The premise cannot contain any string indices
2238-
const array_index_mapt premise_indices=gather_indices(expr.premise());
2239-
if(!premise_indices.empty())
2240-
{
2241-
stream << "Premise has indices: " << format(expr) << ", map: {";
2242-
for(const auto &pair : premise_indices)
2243-
{
2244-
stream << format(pair.first) << ": {";
2245-
for(const auto &i : pair.second)
2246-
stream << format(i) << ", ";
2247-
}
2248-
stream << "}}" << eom;
2249-
return false;
2250-
}
2251-
2252-
const array_index_mapt body_indices=gather_indices(expr.body());
2220+
const array_index_mapt body_indices = gather_indices(constraint.body);
22532221
// Must validate for each string. Note that we have an invariant that the
22542222
// second value in the pair is non-empty.
22552223
for(const auto &pair : body_indices)
22562224
{
2257-
// Condition 2: All indices of the same string must be the of the same form
2225+
// Condition 1: All indices of the same string must be the of the same form
22582226
const exprt rep=pair.second.back();
22592227
for(size_t j=0; j<pair.second.size()-1; j++)
22602228
{
@@ -2263,25 +2231,26 @@ static bool is_valid_string_constraint(
22632231
const exprt result=simplify_expr(equals, ns);
22642232
if(result.is_false())
22652233
{
2266-
stream << "Indices not equal: " << format(expr)
2234+
stream << "Indices not equal: " << to_string(constraint)
22672235
<< ", str: " << format(pair.first) << eom;
22682236
return false;
22692237
}
22702238
}
22712239

2272-
// Condition 3: f must be linear in the quantified variable
2273-
if(!is_linear_arithmetic_expr(rep, expr.univ_var()))
2240+
// Condition 2: f must be linear in the quantified variable
2241+
if(!is_linear_arithmetic_expr(rep, constraint.univ_var))
22742242
{
2275-
stream << "f is not linear: " << format(expr)
2243+
stream << "f is not linear: " << to_string(constraint)
22762244
<< ", str: " << format(pair.first) << eom;
22772245
return false;
22782246
}
22792247

2280-
// Condition 4: the quantified variable can only occur in indices in the
2248+
// Condition 3: the quantified variable can only occur in indices in the
22812249
// body
2282-
if(!universal_only_in_index(expr))
2250+
if(!universal_only_in_index(constraint))
22832251
{
2284-
stream << "Universal variable outside of index:" << format(expr) << eom;
2252+
stream << "Universal variable outside of index:" << to_string(constraint)
2253+
<< eom;
22852254
return false;
22862255
}
22872256
}

unit/solvers/refinement/string_constraint_instantiation/instantiate_not_contains.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,8 @@ SCENARIO("instantiate_not_contains",
207207
constraints.end(),
208208
axioms,
209209
[&](const std::string &accu, string_constraintt sc) { // NOLINT
210-
simplify(sc, ns);
211-
std::string s;
212-
java_lang->from_expr(sc, s, ns);
213-
return accu + s + "\n\n";
210+
simplify(sc.body, ns);
211+
return accu + to_string(sc) + "\n\n";
214212
});
215213

216214
const auto nc_contraints = generator.get_not_contains_constraints();

0 commit comments

Comments
 (0)