Skip to content

Commit e5b2cc7

Browse files
Merge pull request #1157 from romainbrenguier/bugfix/char-array-if-correction
Improve the handling of if expressions representing character arrays
2 parents 9d08aa6 + cc8c420 commit e5b2cc7

File tree

3 files changed

+107
-70
lines changed

3 files changed

+107
-70
lines changed

src/solvers/refinement/string_constraint_generator.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ class string_constraint_generatort
9494

9595
static constant_exprt constant_char(int i, const typet &char_type);
9696

97-
// Used by string refinement
98-
void add_axioms_for_if_array(const exprt &lhs, const if_exprt &expr);
9997

10098
private:
10199
// The integer with the longest string is Integer.MIN_VALUE which is -2^31,

src/solvers/refinement/string_constraint_generator_main.cpp

Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ Author: Romain Brenguier, [email protected]
2424
#include <util/arith_tools.h>
2525
#include <util/pointer_predicates.h>
2626
#include <util/ssa_expr.h>
27-
#include <iostream>
2827

2928
unsigned string_constraint_generatort::next_symbol_id=1;
3029

@@ -232,6 +231,25 @@ string_exprt string_constraint_generatort::add_axioms_for_refined_string(
232231
else if(string.id()==ID_struct)
233232
{
234233
const string_exprt &s=to_string_expr(string);
234+
INVARIANT(
235+
s.length().id()==ID_symbol || s.length().id()==ID_constant,
236+
"string length should be a symbol or a constant");
237+
irep_idt content_id=s.content().id();
238+
INVARIANT(
239+
content_id==ID_symbol || content_id==ID_array || content_id==ID_if,
240+
"string content should be a symbol, a constant array, or an if");
241+
if(content_id==ID_if)
242+
{
243+
// If the string content is an if expression, we add axioms ensuring
244+
// the content is the same as the content in the 'true' branch when the
245+
// condition holds and the 'false' branch otherwise.
246+
if_exprt if_expr=to_if_expr(s.content());
247+
string_exprt str_true=add_axioms_for_refined_string(
248+
string_exprt(s.length(), if_expr.true_case(), type));
249+
string_exprt str_false=add_axioms_for_refined_string(
250+
string_exprt(s.length(), if_expr.false_case(), type));
251+
return add_axioms_for_if(if_exprt(if_expr.cond(), str_true, str_false));
252+
}
235253
add_default_axioms(s);
236254
return s;
237255
}
@@ -267,56 +285,17 @@ string_exprt string_constraint_generatort::add_axioms_for_if(
267285
implies_exprt(expr.cond(), res.axiom_for_has_same_length_as(t)));
268286
symbol_exprt qvar=fresh_univ_index("QA_string_if_true", index_type);
269287
equal_exprt qequal(res[qvar], t[qvar]);
270-
axioms.push_back(string_constraintt(qvar, t.length(), expr.cond(), qequal));
288+
string_constraintt sc1(qvar, t.length(), implies_exprt(expr.cond(), qequal));
289+
axioms.push_back(sc1);
271290
axioms.push_back(
272291
implies_exprt(not_exprt(expr.cond()), res.axiom_for_has_same_length_as(f)));
273292
symbol_exprt qvar2=fresh_univ_index("QA_string_if_false", index_type);
274293
equal_exprt qequal2(res[qvar2], f[qvar2]);
275-
string_constraintt sc2(qvar2, f.length(), not_exprt(expr.cond()), qequal2);
294+
string_constraintt sc2(qvar2, f.length(), or_exprt(expr.cond(), qequal2));
276295
axioms.push_back(sc2);
277296
return res;
278297
}
279298

280-
/// Add axioms enforcing that the content of the first array is equal to
281-
/// the true case array if the condition is true and
282-
/// the else case array otherwise.
283-
/// \param lhs: an expression
284-
/// \param expr: an if expression of type array
285-
void string_constraint_generatort::add_axioms_for_if_array(
286-
const exprt &lhs, const if_exprt &expr)
287-
{
288-
PRECONDITION(lhs.type()==expr.type());
289-
PRECONDITION(expr.type().id()==ID_array);
290-
exprt t=expr.true_case();
291-
exprt f=expr.false_case();
292-
INVARIANT(t.type()==f.type(), "branches of if_exprt should have same type");
293-
const array_typet &type=to_array_type(t.type());
294-
const typet &index_type=type.size().type();
295-
const exprt max_length=from_integer(max_string_length, index_type);
296-
297-
// We add axioms:
298-
// a1 : forall qvar<max_length, cond => lhs[qvar] = t[qvar]
299-
// a1 : forall qvar2<max_length, !cond => lhs[qvar] = f[qvar]
300-
symbol_exprt qvar=fresh_univ_index("QA_array_if_true", index_type);
301-
equal_exprt qequal(index_exprt(lhs, qvar), index_exprt(t, qvar));
302-
303-
// In case t is a constant array of fixed length is does not make sense
304-
// to talk about indexes exceeding this length
305-
exprt upper_bound_t=
306-
(t.id()==ID_array)?from_integer(t.operands().size(), index_type):max_length;
307-
string_constraintt a1(qvar, upper_bound_t, expr.cond(), qequal);
308-
axioms.push_back(a1);
309-
310-
symbol_exprt qvar2=fresh_univ_index("QA_array_if_false", index_type);
311-
equal_exprt qequal2(index_exprt(lhs, qvar2), index_exprt(f, qvar2));
312-
// In case f is a constant array of fixed length is does not make sense
313-
// to talk about indexes exceeding this length
314-
exprt upper_bound_f=
315-
(f.id()==ID_array)?from_integer(f.operands().size(), index_type):max_length;
316-
string_constraintt a2(qvar2, upper_bound_f, not_exprt(expr.cond()), qequal2);
317-
axioms.push_back(a2);
318-
}
319-
320299
/// if a symbol representing a string is present in the symbol_to_string table,
321300
/// returns the corresponding string, if the symbol is not yet present, creates
322301
/// a new string with the correct type depending on whether the mode is java or

src/solvers/refinement/string_refinement.cpp

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -117,28 +117,71 @@ void string_refinementt::add_instantiations()
117117
}
118118
}
119119

120+
/// List the simple expressions on which the expression depends in the
121+
/// `symbol_resolve` map. A simple expression is either a symbol or a
122+
/// constant array
123+
/// \param expr: an expression
124+
static void depends_in_symbol_map(const exprt &expr, std::vector<exprt> &accu)
125+
{
126+
if(expr.id()==ID_if)
127+
{
128+
if_exprt if_expr=to_if_expr(expr);
129+
depends_in_symbol_map(if_expr.true_case(), accu);
130+
depends_in_symbol_map(if_expr.false_case(), accu);
131+
}
132+
else if(expr.id()==ID_struct)
133+
{
134+
string_exprt str=to_string_expr(expr);
135+
depends_in_symbol_map(str.content(), accu);
136+
}
137+
else
138+
{
139+
INVARIANT(
140+
expr.id()==ID_symbol || expr.id()==ID_array || expr.id()==ID_array_of,
141+
"leaf in symbol resolve should be a symbol or a constant array");
142+
accu.push_back(expr);
143+
}
144+
}
145+
120146
/// keeps a map of symbols to expressions, such as none of the mapped values
121147
/// exist as a key
122148
/// \param lhs: a symbol expression
123-
/// \param rhs: an expression to map it to
149+
/// \param rhs: an expression to map it to, which should be either a symbol
150+
/// a string_exprt, an array_exprt, an array_of_exprt or an
151+
/// if_exprt with branches of the previous kind
124152
void string_refinementt::add_symbol_to_symbol_map(
125153
const exprt &lhs, const exprt &rhs)
126154
{
127155
PRECONDITION(lhs.id()==ID_symbol);
156+
PRECONDITION(rhs.id()==ID_symbol ||
157+
rhs.id()==ID_array ||
158+
rhs.id()==ID_array_of ||
159+
rhs.id()==ID_if ||
160+
(rhs.id()==ID_struct &&
161+
refined_string_typet::is_refined_string_type(rhs.type())));
128162

129163
// We insert the mapped value of the rhs, if it exists.
130164
auto it=symbol_resolve.find(rhs);
131165
const exprt &new_rhs=it!=symbol_resolve.end()?it->second:rhs;
132-
133166
symbol_resolve[lhs]=new_rhs;
134-
reverse_symbol_resolve[new_rhs].push_back(lhs);
135167

136-
const std::list<exprt> &symbols_to_update_with_new_rhs(
137-
reverse_symbol_resolve[lhs]);
138-
for(exprt item : symbols_to_update_with_new_rhs)
168+
// List the leaves of new_rhs
169+
std::vector<exprt> leaves;
170+
depends_in_symbol_map(new_rhs, leaves);
171+
172+
const auto &symbols_to_update_with_new_rhs=reverse_symbol_resolve[lhs];
173+
174+
// We need to update all the symbols which depend on lhs
175+
for(const exprt &item : symbols_to_update_with_new_rhs)
176+
replace_expr(symbol_resolve, symbol_resolve[item]);
177+
178+
// Every time a symbol at the leaves is updated we need to update lhs
179+
// and the symbols that depend on it
180+
for(const auto &leaf : leaves)
139181
{
140-
symbol_resolve[item]=new_rhs;
141-
reverse_symbol_resolve[new_rhs].push_back(item);
182+
reverse_symbol_resolve[leaf].push_back(lhs);
183+
for(const exprt &item : symbols_to_update_with_new_rhs)
184+
reverse_symbol_resolve[leaf].push_back(item);
142185
}
143186
}
144187

@@ -158,9 +201,6 @@ void string_refinementt::set_char_array_equality(
158201
index_exprt arraycell(rhs, from_integer(i, index_type));
159202
equal_exprt arrayeq(arraycell, rhs.operands()[i]);
160203
add_lemma(arrayeq, false);
161-
#if 0
162-
generator.axioms.push_back(arrayeq);
163-
#endif
164204
}
165205
}
166206
// At least for Java (as it is currently pre-processed), we need not consider
@@ -205,7 +245,8 @@ bool string_refinementt::is_char_array(const typet &type) const
205245
/// add lemmas to the solver corresponding to the given equation
206246
/// \param lhs: left hand side of an equality expression
207247
/// \param rhs: right and side of the equality
208-
/// \return false if the lemmas were added successfully, true otherwise
248+
/// \return true if the assignemnt needs to be handled by the parent class
249+
/// via `set_to`
209250
bool string_refinementt::add_axioms_for_string_assigns(
210251
const exprt &lhs, const exprt &rhs)
211252
{
@@ -225,8 +266,8 @@ bool string_refinementt::add_axioms_for_string_assigns(
225266
}
226267
else if(rhs.id()==ID_if)
227268
{
228-
generator.add_axioms_for_if_array(lhs, to_if_expr(rhs));
229-
return false;
269+
add_symbol_to_symbol_map(lhs, rhs);
270+
return true;
230271
}
231272
else
232273
{
@@ -632,7 +673,7 @@ void string_refinementt::add_lemma(
632673
/// \return an array expression or an array_of_exprt
633674
exprt string_refinementt::get_array(const exprt &arr, const exprt &size) const
634675
{
635-
exprt arr_val=get_array(arr);
676+
exprt arr_val=simplify_expr(get_array(arr), ns);
636677
exprt size_val=supert::get(size);
637678
size_val=simplify_expr(size_val, ns);
638679
typet char_type=arr.type().subtype();
@@ -1412,6 +1453,30 @@ void string_refinementt::update_index_set(const std::vector<exprt> &cur)
14121453
update_index_set(axiom);
14131454
}
14141455

1456+
/// An expression representing an array of characters can be in the form of an
1457+
/// if expression for instance `cond?array1:(cond2:array2:array3)`.
1458+
/// We return all the array expressions contained in `array_expr`.
1459+
/// \param array_expr : an expression representing an array
1460+
/// \return a vector containing symbols and constant arrays contained in the
1461+
/// expression
1462+
static std::vector<exprt> sub_arrays(const exprt &array_expr)
1463+
{
1464+
if(array_expr.id()==ID_if)
1465+
{
1466+
std::vector<exprt> res1=sub_arrays(to_if_expr(array_expr).true_case());
1467+
std::vector<exprt> res2=sub_arrays(to_if_expr(array_expr).false_case());
1468+
res1.insert(res1.end(), res2.begin(), res2.end());
1469+
return res1;
1470+
}
1471+
else
1472+
{
1473+
INVARIANT(
1474+
array_expr.id()==ID_symbol || array_expr.id()==ID_array,
1475+
"character arrays should be symbol, constant array, or if expression");
1476+
return std::vector<exprt>(1, array_expr);
1477+
}
1478+
}
1479+
14151480
/// add to the index set all the indices that appear in the formula and the
14161481
/// upper bound minus one
14171482
/// \par parameters: a string constraint
@@ -1423,17 +1488,13 @@ void string_refinementt::add_to_index_set(const exprt &s, exprt i)
14231488
mp_integer mpi;
14241489
to_integer(i, mpi);
14251490
if(mpi<0)
1426-
{
1427-
debug() << "add_to_index_set : ignoring negative number " << mpi << eom;
14281491
return;
1429-
}
1430-
}
1431-
if(index_set[s].insert(i).second)
1432-
{
1433-
debug() << "adding to index set of " << from_expr(ns, "", s)
1434-
<< ": " << from_expr(ns, "", i) << eom;
1435-
current_index_set[s].insert(i);
14361492
}
1493+
1494+
std::vector<exprt> subs=sub_arrays(s);
1495+
for(const auto &sub : subs)
1496+
if(index_set[sub].insert(i).second)
1497+
current_index_set[sub].insert(i);
14371498
}
14381499

14391500
void string_refinementt::initial_index_set(const string_constraintt &axiom)
@@ -1504,7 +1565,6 @@ void string_refinementt::update_index_set(const exprt &formula)
15041565
}
15051566
}
15061567

1507-
15081568
// Will be used to visit an expression and return the index used
15091569
// with the given char array that contains qvar
15101570
class find_index_visitort: public const_expr_visitort

0 commit comments

Comments
 (0)