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-
3432static 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 (
0 commit comments