@@ -29,8 +29,6 @@ Author: Alberto Griggio, alberto.griggio@gmail.com
29
29
#include < java_bytecode/java_types.h>
30
30
#include < unordered_set>
31
31
32
- static exprt substitute_array_with_expr (const exprt &expr, const exprt &index);
33
-
34
32
static bool is_valid_string_constraint (
35
33
messaget::mstreamt &stream,
36
34
const namespacet &ns,
@@ -124,6 +122,12 @@ static optionalt<exprt> get_array(
124
122
messaget::mstreamt &stream,
125
123
const array_string_exprt &arr);
126
124
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
+
127
131
// / Convert index-value map to a vector of values. If a value for an
128
132
// / index is not defined, set it to the value referenced by the next higher
129
133
// / index.
@@ -1218,35 +1222,61 @@ void debug_model(
1218
1222
}
1219
1223
1220
1224
// / 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 :=
1222
1226
// / array_of(12) with {0:=24} with {2:=42}` the constructed expression will be:
1223
1227
// / `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`
1224
1230
// / \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.
1226
1234
// / \param index: An index with which to build the equality condition
1227
1235
// / \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)
1229
1240
{
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);
1249
1278
}
1279
+ return result;
1250
1280
}
1251
1281
1252
1282
// / 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)
1257
1287
// / \param string_max_length: bound on the length of strings
1258
1288
// / \return an array expression with filled in values, or expr if it is simply
1259
1289
// / 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)
1263
1292
{
1264
1293
PRECONDITION (expr.type ().id ()==ID_array);
1265
1294
PRECONDITION (expr.id ()==ID_with || expr.id ()==ID_array_of);
@@ -1325,112 +1354,148 @@ exprt fill_in_array_expr(const array_exprt &expr, std::size_t string_max_length)
1325
1354
return result;
1326
1355
}
1327
1356
1328
- // / create an equivalent expression where array accesses and 'with' expressions
1329
- // / are replaced by 'if' expressions, in particular:
1330
- // / * for an array access `arr[x]`, where:
1331
- // / `arr := {12, 24, 48}` the constructed expression will be:
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:
1332
1360
// / `index==0 ? 12 : index==1 ? 24 : 48`
1333
- // / * for an array access `arr[x]`, where:
1334
- // / `arr := array_of(12) with {0:=24} with {2:=42}` the constructed
1335
- // / expression will be: `index==0 ? 24 : index==2 ? 42 : 12`
1336
- // / * for an array access `(g1?arr1:arr2)[x]` where `arr1 := {12}` and
1337
- // / `arr2 := {34}`, the constructed expression will be: `g1 ? 12 : 34`
1338
- // / * for an access in an empty array `{ }[x]` returns a fresh symbol, this
1339
- // / corresponds to a non-deterministic result
1340
- // / \param expr: an expression containing array accesses
1341
- // / \param symbol_generator: function which given a prefix and a type generates
1342
- // / a fresh symbol of the given type
1343
- // / \return an expression containing no array access
1344
- static void substitute_array_access (
1345
- exprt &expr,
1361
+ static exprt substitute_array_access (
1362
+ const array_exprt &array_expr,
1363
+ const exprt &index,
1346
1364
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1347
1365
&symbol_generator)
1348
1366
{
1349
- for (auto &op : expr.operands ())
1350
- substitute_array_access (op, symbol_generator);
1367
+ const typet &char_type = array_expr.type ().subtype ();
1351
1368
1352
- if (expr. id ()==ID_index )
1353
- {
1354
- index_exprt &index_expr= to_index_expr (expr );
1369
+ // Access to an empty array is undefined (non deterministic result )
1370
+ if (array_expr. operands (). empty ())
1371
+ return symbol_generator ( " out_of_bound_access " , char_type );
1355
1372
1356
- if (index_expr.array ().id ()==ID_symbol)
1357
- {
1358
- expr=index_expr;
1359
- return ;
1360
- }
1373
+ const std::size_t last_index = array_expr.operands ().size () - 1 ;
1374
+ exprt ite = array_expr.operands ().back ();
1361
1375
1362
- if (index_expr.array ().id ()==ID_with)
1363
- {
1364
- expr=substitute_array_with_expr (index_expr.array (), index_expr.index ());
1365
- return ;
1366
- }
1376
+ if (ite.type () != char_type)
1377
+ {
1378
+ // We have to manually set the type for unknown values
1379
+ INVARIANT (
1380
+ ite.id () == ID_unknown,
1381
+ string_refinement_invariantt (
1382
+ " the last element can only have type char "
1383
+ " or unknown, and it is not char type" ));
1384
+ ite.type () = char_type;
1385
+ }
1367
1386
1368
- if (index_expr.array ().id ()==ID_array_of)
1369
- {
1370
- expr=to_array_of_expr (index_expr.array ()).op ();
1371
- return ;
1372
- }
1387
+ auto op_it = ++array_expr.operands ().rbegin ();
1373
1388
1374
- if (index_expr.array ().id ()==ID_if)
1389
+ for (std::size_t i = last_index - 1 ; op_it != array_expr.operands ().rend ();
1390
+ ++op_it, --i)
1391
+ {
1392
+ const equal_exprt equals (index, from_integer (i, java_int_type ()));
1393
+ if (op_it->type () != char_type)
1375
1394
{
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 ;
1395
+ INVARIANT (
1396
+ op_it->id () == ID_unknown,
1397
+ string_refinement_invariantt (
1398
+ " elements can only have type char or "
1399
+ " unknown, and it is not char type" ));
1400
+ ite = if_exprt (equals, exprt (ID_unknown, char_type), ite);
1384
1401
}
1402
+ else
1403
+ ite = if_exprt (equals, *op_it, ite);
1404
+ }
1405
+ return ite;
1406
+ }
1385
1407
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 ();
1408
+ static exprt substitute_array_access (
1409
+ const if_exprt &if_expr,
1410
+ const exprt &index,
1411
+ const std::function<symbol_exprt(const irep_idt &, const typet &)>
1412
+ &symbol_generator,
1413
+ const bool left_propagate)
1414
+ {
1415
+ // Substitute recursively in branches of conditional expressions
1416
+ const exprt true_case = substitute_array_access (
1417
+ index_exprt (if_expr.true_case (), index), symbol_generator, left_propagate);
1418
+ const exprt false_case = substitute_array_access (
1419
+ index_exprt (if_expr.false_case (), index), symbol_generator, left_propagate);
1394
1420
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
- }
1421
+ return if_exprt (if_expr.cond (), true_case, false_case);
1422
+ }
1401
1423
1402
- size_t last_index=array_expr.operands ().size ()-1 ;
1424
+ static exprt substitute_array_access (
1425
+ const index_exprt &index_expr,
1426
+ const std::function<symbol_exprt(const irep_idt &, const typet &)>
1427
+ &symbol_generator,
1428
+ const bool left_propagate)
1429
+ {
1430
+ const exprt &array = index_expr.array ();
1431
+
1432
+ if (array.id () == ID_symbol)
1433
+ return index_expr;
1434
+ if (auto array_of = expr_try_dynamic_cast<array_of_exprt>(array))
1435
+ return array_of->op ();
1436
+ if (auto array_with = expr_try_dynamic_cast<with_exprt>(array))
1437
+ return substitute_array_access (
1438
+ *array_with, index_expr.index (), left_propagate);
1439
+ if (auto array_expr = expr_try_dynamic_cast<array_exprt>(array))
1440
+ return substitute_array_access (
1441
+ *array_expr, index_expr.index (), symbol_generator);
1442
+ if (auto if_expr = expr_try_dynamic_cast<if_exprt>(array))
1443
+ return substitute_array_access (
1444
+ *if_expr, index_expr.index (), symbol_generator, left_propagate);
1445
+
1446
+ UNREACHABLE;
1447
+ }
1403
1448
1404
- exprt ite=array_expr.operands ().back ();
1449
+ // / Create an equivalent expression where array accesses and 'with' expressions
1450
+ // / are replaced by 'if' expressions, in particular:
1451
+ // / * for an array access `arr[index]`, where:
1452
+ // / `arr := {12, 24, 48}` the constructed expression will be:
1453
+ // / `index==0 ? 12 : index==1 ? 24 : 48`
1454
+ // / * for an array access `arr[index]`, where:
1455
+ // / `arr := array_of(12) with {0:=24} with {2:=42}` the constructed
1456
+ // / expression will be: `index==0 ? 24 : index==2 ? 42 : 12`
1457
+ // / * for an array access `(g1?arr1:arr2)[x]` where `arr1 := {12}` and
1458
+ // / `arr2 := {34}`, the constructed expression will be: `g1 ? 12 : 34`
1459
+ // / * for an access in an empty array `{ }[x]` returns a fresh symbol, this
1460
+ // / corresponds to a non-deterministic result
1461
+ // / Note that if left_propagate is set to true, the `with` case will result in
1462
+ // / something like: `index <= 0 ? 24 : index <= 2 ? 42 : 12`
1463
+ // / \param expr: an expression containing array accesses
1464
+ // / \param symbol_generator: function which given a prefix and a type generates
1465
+ // / a fresh symbol of the given type
1466
+ // / \param left_propagate: should values be propagated to the left in with
1467
+ // / expressions
1468
+ // / \return an expression containing no array access, and a Boolean which is
1469
+ // / true if the expression is unchanged
1470
+ std::pair<exprt, bool > substitute_array_access (
1471
+ const exprt &expr,
1472
+ const std::function<symbol_exprt(const irep_idt &, const typet &)>
1473
+ &symbol_generator,
1474
+ const bool left_propagate)
1475
+ {
1476
+ if (const auto index_expr = expr_try_dynamic_cast<index_exprt>(expr))
1477
+ {
1478
+ const exprt substituted =
1479
+ substitute_array_access (*index_expr, symbol_generator, left_propagate);
1480
+ return {substituted, false };
1481
+ }
1405
1482
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
- }
1483
+ exprt::operandst operands;
1484
+ bool unchanged = true ;
1485
+ for ( auto &op : expr. operands ())
1486
+ {
1487
+ std::pair<exprt, bool > pair =
1488
+ substitute_array_access (op, symbol_generator, left_propagate);
1489
+ unchanged = unchanged && pair. second ;
1490
+ operands. push_back (pair. first ) ;
1491
+ }
1415
1492
1416
- auto op_it=++array_expr.operands ().rbegin ();
1493
+ if (unchanged)
1494
+ return {expr, true };
1417
1495
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
- }
1496
+ exprt copy (expr);
1497
+ copy.operands () = std::move (operands);
1498
+ return {copy, false };
1434
1499
}
1435
1500
1436
1501
// / Negates the constraint to be fed to a solver. The intended usage is to find
@@ -1657,12 +1722,10 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
1657
1722
1658
1723
exprt negaxiom=negation_of_constraint (axiom_in_model);
1659
1724
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);
1664
1725
1665
1726
stream << indent << i << " .\n " ;
1727
+ const exprt with_concretized_arrays =
1728
+ substitute_array_access (negaxiom, gen_symbol, true ).first ;
1666
1729
debug_check_axioms_step (
1667
1730
stream, ns, axiom, axiom_in_model, negaxiom, with_concretized_arrays);
1668
1731
@@ -1713,10 +1776,8 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
1713
1776
negation_of_not_contains_constraint (nc_axiom_in_model, univ_var);
1714
1777
1715
1778
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);
1779
+ const exprt with_concrete_arrays =
1780
+ substitute_array_access (negaxiom, gen_symbol, true ).first ;
1720
1781
1721
1782
stream << indent << i << " .\n " ;
1722
1783
debug_check_axioms_step (
0 commit comments