Skip to content

Commit 773721b

Browse files
Refactoring of substitute array access
We make substitute_array return an expression. We add an argument to left-propagate values in the substitution. With this, we can avoid having to first replace the array_with with an array and then propagating to overwrite unknown values in the array. This function is now implemented using expr_try_dynamic_cast, which is better than checking ids: it can perform additional checks than just the id, it also does the check and convert in one function call. We split the function in several parts each responsible to handle a specific type, which makes the global structure clearer.
1 parent dc5ffc9 commit 773721b

File tree

2 files changed

+175
-126
lines changed

2 files changed

+175
-126
lines changed

src/solvers/refinement/string_refinement.cpp

+168-126
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ Author: Alberto Griggio, [email protected]
2929
#include <java_bytecode/java_types.h>
3030
#include <unordered_set>
3131

32-
static exprt substitute_array_with_expr(const exprt &expr, const exprt &index);
33-
3432
static bool is_valid_string_constraint(
3533
messaget::mstreamt &stream,
3634
const namespacet &ns,
@@ -124,6 +122,12 @@ static optionalt<exprt> get_array(
124122
messaget::mstreamt &stream,
125123
const array_string_exprt &arr);
126124

125+
static exprt substitute_array_access(
126+
const index_exprt &index_expr,
127+
const std::function<symbol_exprt(const irep_idt &, const typet &)>
128+
&symbol_generator,
129+
const bool left_propagate);
130+
127131
/// Convert index-value map to a vector of values. If a value for an
128132
/// index is not defined, set it to the value referenced by the next higher
129133
/// index.
@@ -1218,35 +1222,61 @@ void debug_model(
12181222
}
12191223

12201224
/// Create a new expression where 'with' expressions on arrays are replaced by
1221-
/// 'if' expressions. e.g. for an array access arr[x], where: `arr :=
1225+
/// 'if' expressions. e.g. for an array access arr[index], where: `arr :=
12221226
/// array_of(12) with {0:=24} with {2:=42}` the constructed expression will be:
12231227
/// `index==0 ? 24 : index==2 ? 42 : 12`
1228+
/// If `left_propagate` is set to true, the expression will look like
1229+
/// `index<=0 ? 24 : index<=2 ? 42 : 12`
12241230
/// \param expr: A (possibly nested) 'with' expression on an `array_of`
1225-
/// expression
1231+
/// expression. The function checks that the expression is of the form
1232+
/// `with_expr(with_expr(...(array_of(...)))`. This is the form in which
1233+
/// array valuations coming from the underlying solver are given.
12261234
/// \param index: An index with which to build the equality condition
12271235
/// \return An expression containing no 'with' expression
1228-
static exprt substitute_array_with_expr(const exprt &expr, const exprt &index)
1236+
static exprt substitute_array_access(
1237+
const with_exprt &expr,
1238+
const exprt &index,
1239+
const bool left_propagate)
12291240
{
1230-
if(expr.id()==ID_with)
1231-
{
1232-
const with_exprt &with_expr=to_with_expr(expr);
1233-
const exprt &then_expr=with_expr.new_value();
1234-
exprt else_expr=substitute_array_with_expr(with_expr.old(), index);
1235-
const typet &type=then_expr.type();
1236-
CHECK_RETURN(else_expr.type()==type);
1237-
CHECK_RETURN(index.type()==with_expr.where().type());
1238-
return if_exprt(
1239-
equal_exprt(index, with_expr.where()), then_expr, else_expr, type);
1240-
}
1241-
else
1242-
{
1243-
// Only handle 'with' expressions and 'array_of' expressions.
1244-
INVARIANT(
1245-
expr.id()==ID_array_of,
1246-
string_refinement_invariantt("only handles 'with' and 'array_of' "
1247-
"expressions, and expr is 'with' is handled above"));
1248-
return to_array_of_expr(expr).what();
1241+
std::vector<std::pair<std::size_t, exprt>> entries;
1242+
std::reference_wrapper<const exprt> ref =
1243+
std::ref(static_cast<const exprt &>(expr));
1244+
while(can_cast_expr<with_exprt>(ref.get()))
1245+
{
1246+
const auto &with_expr = expr_dynamic_cast<with_exprt>(ref.get());
1247+
auto current_index = numeric_cast_v<std::size_t>(with_expr.where());
1248+
entries.push_back(std::make_pair(current_index, with_expr.new_value()));
1249+
ref = with_expr.old();
1250+
}
1251+
1252+
// This function only handles 'with' and 'array_of' expressions
1253+
PRECONDITION(ref.get().id() == ID_array_of);
1254+
1255+
// sort entries by increasing index
1256+
std::sort(
1257+
entries.begin(),
1258+
entries.end(),
1259+
[](
1260+
const std::pair<std::size_t, exprt> &a,
1261+
const std::pair<std::size_t, exprt> &b) { return a.first < b.first; });
1262+
1263+
exprt result = expr_dynamic_cast<array_of_exprt>(ref.get()).what();
1264+
for(const auto &entry : entries)
1265+
{
1266+
const exprt &then_expr = entry.second;
1267+
const typet &type = then_expr.type();
1268+
CHECK_RETURN(type == result.type());
1269+
const exprt entry_index = from_integer(entry.first, index.type());
1270+
if(left_propagate)
1271+
{
1272+
const binary_relation_exprt index_small_eq(index, ID_le, entry_index);
1273+
result = if_exprt(index_small_eq, then_expr, result, type);
1274+
}
1275+
else
1276+
result =
1277+
if_exprt(equal_exprt(index, entry_index), then_expr, result, type);
12491278
}
1279+
return result;
12501280
}
12511281

12521282
/// Fill an array represented by a list of with_expr by propagating values to
@@ -1257,9 +1287,8 @@ static exprt substitute_array_with_expr(const exprt &expr, const exprt &index)
12571287
/// \param string_max_length: bound on the length of strings
12581288
/// \return an array expression with filled in values, or expr if it is simply
12591289
/// an `ARRAY_OF(x)` expression
1260-
exprt fill_in_array_with_expr(
1261-
const exprt &expr,
1262-
const std::size_t string_max_length)
1290+
static array_exprt
1291+
fill_in_array_with_expr(const exprt &expr, const std::size_t string_max_length)
12631292
{
12641293
PRECONDITION(expr.type().id()==ID_array);
12651294
PRECONDITION(expr.id()==ID_with || expr.id()==ID_array_of);
@@ -1325,112 +1354,129 @@ exprt fill_in_array_expr(const array_exprt &expr, std::size_t string_max_length)
13251354
return result;
13261355
}
13271356

1328-
/// create an equivalent expression where array accesses and 'with' expressions
1357+
/// Create an equivalent expression where array accesses are replaced by 'if'
1358+
/// expressions: for instance in array access `arr[index]`, where:
1359+
/// `arr := {12, 24, 48}` the constructed expression will be:
1360+
/// `index==0 ? 12 : index==1 ? 24 : 48`
1361+
static exprt substitute_array_access(
1362+
const array_exprt &array_expr,
1363+
const exprt &index,
1364+
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1365+
&symbol_generator)
1366+
{
1367+
const typet &char_type = array_expr.type().subtype();
1368+
const std::vector<exprt> &operands = array_expr.operands();
1369+
1370+
exprt result = symbol_generator("out_of_bound_access", char_type);
1371+
1372+
for(std::size_t i = 0; i < operands.size(); ++i)
1373+
{
1374+
// Go in reverse order so that smaller indexes appear first in the result
1375+
const std::size_t pos = operands.size() - 1 - i;
1376+
const equal_exprt equals(index, from_integer(pos, java_int_type()));
1377+
if(operands[pos].type() != char_type)
1378+
{
1379+
INVARIANT(
1380+
operands[pos].id() == ID_unknown,
1381+
string_refinement_invariantt(
1382+
"elements can only have type char or "
1383+
"unknown, and it is not char type"));
1384+
result = if_exprt(equals, exprt(ID_unknown, char_type), result);
1385+
}
1386+
else
1387+
result = if_exprt(equals, operands[pos], result);
1388+
}
1389+
return result;
1390+
}
1391+
1392+
static exprt substitute_array_access(
1393+
const if_exprt &if_expr,
1394+
const exprt &index,
1395+
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1396+
&symbol_generator,
1397+
const bool left_propagate)
1398+
{
1399+
// Substitute recursively in branches of conditional expressions
1400+
const exprt true_case = substitute_array_access(
1401+
index_exprt(if_expr.true_case(), index), symbol_generator, left_propagate);
1402+
const exprt false_case = substitute_array_access(
1403+
index_exprt(if_expr.false_case(), index), symbol_generator, left_propagate);
1404+
1405+
return if_exprt(if_expr.cond(), true_case, false_case);
1406+
}
1407+
1408+
static exprt substitute_array_access(
1409+
const index_exprt &index_expr,
1410+
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1411+
&symbol_generator,
1412+
const bool left_propagate)
1413+
{
1414+
const exprt &array = index_expr.array();
1415+
1416+
if(array.id() == ID_symbol)
1417+
return index_expr;
1418+
if(auto array_of = expr_try_dynamic_cast<array_of_exprt>(array))
1419+
return array_of->op();
1420+
if(auto array_with = expr_try_dynamic_cast<with_exprt>(array))
1421+
return substitute_array_access(
1422+
*array_with, index_expr.index(), left_propagate);
1423+
if(auto array_expr = expr_try_dynamic_cast<array_exprt>(array))
1424+
return substitute_array_access(
1425+
*array_expr, index_expr.index(), symbol_generator);
1426+
if(auto if_expr = expr_try_dynamic_cast<if_exprt>(array))
1427+
return substitute_array_access(
1428+
*if_expr, index_expr.index(), symbol_generator, left_propagate);
1429+
1430+
UNREACHABLE;
1431+
}
1432+
1433+
/// Auxiliary function for substitute_array_access
1434+
/// Performs the same operation but modifies the argument instead of returning
1435+
/// the resulting expression.
1436+
static void substitute_array_access_in_place(
1437+
exprt &expr,
1438+
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1439+
&symbol_generator,
1440+
const bool left_propagate)
1441+
{
1442+
if(const auto index_expr = expr_try_dynamic_cast<index_exprt>(expr))
1443+
{
1444+
expr =
1445+
substitute_array_access(*index_expr, symbol_generator, left_propagate);
1446+
}
1447+
1448+
for(auto &op : expr.operands())
1449+
substitute_array_access_in_place(op, symbol_generator, left_propagate);
1450+
}
1451+
1452+
/// Create an equivalent expression where array accesses and 'with' expressions
13291453
/// are replaced by 'if' expressions, in particular:
1330-
/// * for an array access `arr[x]`, where:
1454+
/// * for an array access `arr[index]`, where:
13311455
/// `arr := {12, 24, 48}` the constructed expression will be:
13321456
/// `index==0 ? 12 : index==1 ? 24 : 48`
1333-
/// * for an array access `arr[x]`, where:
1457+
/// * for an array access `arr[index]`, where:
13341458
/// `arr := array_of(12) with {0:=24} with {2:=42}` the constructed
13351459
/// expression will be: `index==0 ? 24 : index==2 ? 42 : 12`
13361460
/// * for an array access `(g1?arr1:arr2)[x]` where `arr1 := {12}` and
13371461
/// `arr2 := {34}`, the constructed expression will be: `g1 ? 12 : 34`
13381462
/// * for an access in an empty array `{ }[x]` returns a fresh symbol, this
13391463
/// corresponds to a non-deterministic result
1464+
/// Note that if left_propagate is set to true, the `with` case will result in
1465+
/// something like: `index <= 0 ? 24 : index <= 2 ? 42 : 12`
13401466
/// \param expr: an expression containing array accesses
13411467
/// \param symbol_generator: function which given a prefix and a type generates
13421468
/// a fresh symbol of the given type
1469+
/// \param left_propagate: should values be propagated to the left in with
1470+
/// expressions
13431471
/// \return an expression containing no array access
1344-
static void substitute_array_access(
1345-
exprt &expr,
1472+
exprt substitute_array_access(
1473+
exprt expr,
13461474
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1347-
&symbol_generator)
1475+
&symbol_generator,
1476+
const bool left_propagate)
13481477
{
1349-
for(auto &op : expr.operands())
1350-
substitute_array_access(op, symbol_generator);
1351-
1352-
if(expr.id()==ID_index)
1353-
{
1354-
index_exprt &index_expr=to_index_expr(expr);
1355-
1356-
if(index_expr.array().id()==ID_symbol)
1357-
{
1358-
expr=index_expr;
1359-
return;
1360-
}
1361-
1362-
if(index_expr.array().id()==ID_with)
1363-
{
1364-
expr=substitute_array_with_expr(index_expr.array(), index_expr.index());
1365-
return;
1366-
}
1367-
1368-
if(index_expr.array().id()==ID_array_of)
1369-
{
1370-
expr=to_array_of_expr(index_expr.array()).op();
1371-
return;
1372-
}
1373-
1374-
if(index_expr.array().id()==ID_if)
1375-
{
1376-
// Substitute recursively in branches of conditional expressions
1377-
if_exprt if_expr=to_if_expr(index_expr.array());
1378-
exprt true_case=index_exprt(if_expr.true_case(), index_expr.index());
1379-
substitute_array_access(true_case, symbol_generator);
1380-
exprt false_case=index_exprt(if_expr.false_case(), index_expr.index());
1381-
substitute_array_access(false_case, symbol_generator);
1382-
expr=if_exprt(if_expr.cond(), true_case, false_case);
1383-
return;
1384-
}
1385-
1386-
DATA_INVARIANT(
1387-
index_expr.array().id()==ID_array,
1388-
string_refinement_invariantt("and index expression must be on a symbol, "
1389-
"with, array_of, if, or array, and all cases besides array are handled "
1390-
"above"));
1391-
array_exprt &array_expr=to_array_expr(index_expr.array());
1392-
1393-
const typet &char_type = index_expr.array().type().subtype();
1394-
1395-
// Access to an empty array is undefined (non deterministic result)
1396-
if(array_expr.operands().empty())
1397-
{
1398-
expr = symbol_generator("out_of_bound_access", char_type);
1399-
return;
1400-
}
1401-
1402-
size_t last_index=array_expr.operands().size()-1;
1403-
1404-
exprt ite=array_expr.operands().back();
1405-
1406-
if(ite.type()!=char_type)
1407-
{
1408-
// We have to manually set the type for unknown values
1409-
INVARIANT(
1410-
ite.id()==ID_unknown,
1411-
string_refinement_invariantt("the last element can only have type char "
1412-
"or unknown, and it is not char type"));
1413-
ite.type()=char_type;
1414-
}
1415-
1416-
auto op_it=++array_expr.operands().rbegin();
1417-
1418-
for(size_t i=last_index-1;
1419-
op_it!=array_expr.operands().rend(); ++op_it, --i)
1420-
{
1421-
equal_exprt equals(index_expr.index(), from_integer(i, java_int_type()));
1422-
if(op_it->type()!=char_type)
1423-
{
1424-
INVARIANT(
1425-
op_it->id()==ID_unknown,
1426-
string_refinement_invariantt("elements can only have type char or "
1427-
"unknown, and it is not char type"));
1428-
op_it->type()=char_type;
1429-
}
1430-
ite=if_exprt(equals, *op_it, ite);
1431-
}
1432-
expr=ite;
1433-
}
1478+
substitute_array_access_in_place(expr, symbol_generator, left_propagate);
1479+
return expr;
14341480
}
14351481

14361482
/// Negates the constraint to be fed to a solver. The intended usage is to find
@@ -1657,12 +1703,10 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
16571703

16581704
exprt negaxiom=negation_of_constraint(axiom_in_model);
16591705
negaxiom = simplify_expr(negaxiom, ns);
1660-
exprt with_concretized_arrays =
1661-
concretize_arrays_in_expression(negaxiom, max_string_length, ns);
1662-
1663-
substitute_array_access(with_concretized_arrays, gen_symbol);
16641706

16651707
stream << indent << i << ".\n";
1708+
const exprt with_concretized_arrays =
1709+
substitute_array_access(negaxiom, gen_symbol, true);
16661710
debug_check_axioms_step(
16671711
stream, ns, axiom, axiom_in_model, negaxiom, with_concretized_arrays);
16681712

@@ -1713,10 +1757,8 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
17131757
negation_of_not_contains_constraint(nc_axiom_in_model, univ_var);
17141758

17151759
negaxiom = simplify_expr(negaxiom, ns);
1716-
exprt with_concrete_arrays =
1717-
concretize_arrays_in_expression(negaxiom, max_string_length, ns);
1718-
1719-
substitute_array_access(with_concrete_arrays, gen_symbol);
1760+
const exprt with_concrete_arrays =
1761+
substitute_array_access(negaxiom, gen_symbol, true);
17201762

17211763
stream << indent << i << ".\n";
17221764
debug_check_axioms_step(

src/solvers/refinement/string_refinement.h

+7
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,11 @@ union_find_replacet string_identifiers_resolution_from_equations(
116116
const namespacet &ns,
117117
messaget::mstreamt &stream);
118118

119+
// Declaration required for unit-test:
120+
exprt substitute_array_access(
121+
exprt expr,
122+
const std::function<symbol_exprt(const irep_idt &, const typet &)>
123+
&symbol_generator,
124+
const bool left_propagate);
125+
119126
#endif

0 commit comments

Comments
 (0)