@@ -1597,42 +1597,23 @@ void goto_check_ct::bounds_check_index(
15971597 std::string name = array_name (expr.array ());
15981598
15991599 const exprt &index = expr.index ();
1600- object_descriptor_exprt ode;
1601- ode.build (expr, ns);
16021600
1603- if (index.type ().id () != ID_unsignedbv)
1601+ // When the array element type has no byte size (e.g., arrays of unbounded
1602+ // arrays used in heap models), object_descriptor_exprt::build cannot compute
1603+ // byte offsets and produces an ID_unknown offset. Fall back to a simple
1604+ // index-based lower-bound check.
1605+ std::optional<object_descriptor_exprt> ode;
1606+ if (!size_of_expr (expr.type (), ns).has_value ())
16041607 {
1605- // we undo typecasts to signedbv
1606- if (
1607- index.id () == ID_typecast &&
1608- to_typecast_expr (index).op ().type ().id () == ID_unsignedbv)
1609- {
1610- // ok
1611- }
1612- else
1608+ if (index.type ().id () != ID_unsignedbv && index.type ().id () != ID_natural)
16131609 {
16141610 const auto i = numeric_cast<mp_integer>(index);
16151611
16161612 if (!i.has_value () || *i < 0 )
16171613 {
1618- exprt effective_offset = ode.offset ();
1619-
1620- if (ode.root_object ().id () == ID_dereference)
1621- {
1622- exprt p_offset =
1623- pointer_offset (to_dereference_expr (ode.root_object ()).pointer ());
1624-
1625- effective_offset = plus_exprt{
1626- p_offset,
1627- typecast_exprt::conditional_cast (
1628- effective_offset, p_offset.type ())};
1629- }
1630-
1631- exprt zero = from_integer (0 , effective_offset.type ());
1614+ exprt zero = from_integer (0 , index.type ());
16321615
1633- // the final offset must not be negative
1634- binary_relation_exprt inequality (
1635- effective_offset, ID_ge, std::move (zero));
1616+ binary_relation_exprt inequality (index, ID_ge, std::move (zero));
16361617
16371618 add_guarded_property (
16381619 inequality,
@@ -1645,15 +1626,66 @@ void goto_check_ct::bounds_check_index(
16451626 }
16461627 }
16471628 }
1629+ else
1630+ {
1631+ ode.emplace ();
1632+ ode->build (expr, ns);
1633+
1634+ if (index.type ().id () != ID_unsignedbv)
1635+ {
1636+ // we undo typecasts to signedbv
1637+ if (
1638+ index.id () == ID_typecast &&
1639+ to_typecast_expr (index).op ().type ().id () == ID_unsignedbv)
1640+ {
1641+ // ok
1642+ }
1643+ else
1644+ {
1645+ const auto i = numeric_cast<mp_integer>(index);
1646+
1647+ if (!i.has_value () || *i < 0 )
1648+ {
1649+ exprt effective_offset = ode->offset ();
1650+
1651+ if (ode->root_object ().id () == ID_dereference)
1652+ {
1653+ exprt p_offset =
1654+ pointer_offset (to_dereference_expr (ode->root_object ()).pointer ());
1655+
1656+ effective_offset = plus_exprt{
1657+ p_offset,
1658+ typecast_exprt::conditional_cast (
1659+ effective_offset, p_offset.type ())};
1660+ }
1661+
1662+ exprt zero = from_integer (0 , effective_offset.type ());
1663+
1664+ // the final offset must not be negative
1665+ binary_relation_exprt inequality (
1666+ effective_offset, ID_ge, std::move (zero));
1667+
1668+ add_guarded_property (
1669+ inequality,
1670+ name + " lower bound" ,
1671+ " array bounds" ,
1672+ true , // fatal
1673+ expr.find_source_location (),
1674+ expr,
1675+ guard);
1676+ }
1677+ }
1678+ }
1679+ }
16481680
1649- if (ode. root_object ().id () == ID_dereference)
1681+ if (ode && ode-> root_object ().id () == ID_dereference)
16501682 {
1651- const exprt &pointer = to_dereference_expr (ode. root_object ()).pointer ();
1683+ const exprt &pointer = to_dereference_expr (ode-> root_object ()).pointer ();
16521684
16531685 const plus_exprt effective_offset{
1654- ode. offset (),
1686+ ode-> offset (),
16551687 typecast_exprt::conditional_cast (
1656- pointer_offset (pointer), ode. offset ().type ())};
1688+ pointer_offset (pointer), ode-> offset ().type ())};
16571689
16581690 binary_relation_exprt inequality{
16591691 effective_offset,
@@ -1664,7 +1696,7 @@ void goto_check_ct::bounds_check_index(
16641696 exprt in_bounds_of_some_explicit_allocation =
16651697 is_in_bounds_of_some_explicit_allocation (
16661698 pointer,
1667- plus_exprt{ode. offset (), from_integer (1 , ode. offset ().type ())});
1699+ plus_exprt{ode-> offset (), from_integer (1 , ode-> offset ().type ())});
16681700
16691701 or_exprt precond (
16701702 std::move (in_bounds_of_some_explicit_allocation), inequality);
@@ -1694,7 +1726,7 @@ void goto_check_ct::bounds_check_index(
16941726 {
16951727 }
16961728 else if (
1697- expr.array ().id () == ID_member &&
1729+ ode && expr.array ().id () == ID_member &&
16981730 (size == 0 || array_type.get_bool (ID_C_flexible_array_member)))
16991731 {
17001732 // a variable sized struct member
@@ -1706,13 +1738,13 @@ void goto_check_ct::bounds_check_index(
17061738 // array (with the same element type) that would not make the structure
17071739 // larger than the object being accessed; [...]
17081740 const auto type_size_opt =
1709- pointer_offset_size (ode. root_object ().type (), ns);
1741+ pointer_offset_size (ode-> root_object ().type (), ns);
17101742 CHECK_RETURN (type_size_opt.has_value ());
17111743
17121744 binary_relation_exprt inequality (
1713- ode. offset (),
1745+ ode-> offset (),
17141746 ID_lt,
1715- from_integer (type_size_opt.value (), ode. offset ().type ()));
1747+ from_integer (type_size_opt.value (), ode-> offset ().type ()));
17161748
17171749 add_guarded_property (
17181750 inequality,
0 commit comments