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,129 @@ 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
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
1329
1453
// / are replaced by 'if' expressions, in particular:
1330
- // / * for an array access `arr[x ]`, where:
1454
+ // / * for an array access `arr[index ]`, where:
1331
1455
// / `arr := {12, 24, 48}` the constructed expression will be:
1332
1456
// / `index==0 ? 12 : index==1 ? 24 : 48`
1333
- // / * for an array access `arr[x ]`, where:
1457
+ // / * for an array access `arr[index ]`, where:
1334
1458
// / `arr := array_of(12) with {0:=24} with {2:=42}` the constructed
1335
1459
// / expression will be: `index==0 ? 24 : index==2 ? 42 : 12`
1336
1460
// / * for an array access `(g1?arr1:arr2)[x]` where `arr1 := {12}` and
1337
1461
// / `arr2 := {34}`, the constructed expression will be: `g1 ? 12 : 34`
1338
1462
// / * for an access in an empty array `{ }[x]` returns a fresh symbol, this
1339
1463
// / 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`
1340
1466
// / \param expr: an expression containing array accesses
1341
1467
// / \param symbol_generator: function which given a prefix and a type generates
1342
1468
// / a fresh symbol of the given type
1469
+ // / \param left_propagate: should values be propagated to the left in with
1470
+ // / expressions
1343
1471
// / \return an expression containing no array access
1344
- static void substitute_array_access (
1345
- exprt & expr,
1472
+ exprt substitute_array_access (
1473
+ exprt expr,
1346
1474
const std::function<symbol_exprt(const irep_idt &, const typet &)>
1347
- &symbol_generator)
1475
+ &symbol_generator,
1476
+ const bool left_propagate)
1348
1477
{
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;
1434
1480
}
1435
1481
1436
1482
// / 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(
1657
1703
1658
1704
exprt negaxiom=negation_of_constraint (axiom_in_model);
1659
1705
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
1706
1665
1707
stream << indent << i << " .\n " ;
1708
+ const exprt with_concretized_arrays =
1709
+ substitute_array_access (negaxiom, gen_symbol, true );
1666
1710
debug_check_axioms_step (
1667
1711
stream, ns, axiom, axiom_in_model, negaxiom, with_concretized_arrays);
1668
1712
@@ -1713,10 +1757,8 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
1713
1757
negation_of_not_contains_constraint (nc_axiom_in_model, univ_var);
1714
1758
1715
1759
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 );
1720
1762
1721
1763
stream << indent << i << " .\n " ;
1722
1764
debug_check_axioms_step (
0 commit comments