Changing rules for casting pointers/references to arrays

1,375 views
Skip to first unread message

Myriachan

unread,
Mar 12, 2019, 5:15:28 PM3/12/19
to ISO C++ Standard - Discussion
Right now, static_cast is prohibited between T(&)[] and T (&)[N].  This seems like an unnecessary restriction.

Also, if you have a T (&)[N] that actually refers to a T (&)[M] where M > N, it's still undefined behavior to use that reference.  Similarly, dereferencing a T (&)[1] referring to a T is also undefined behavior.


So here are some of my proposed ideas:

1. Allow T (&)[N] to implicitly convert to T (&)[], and T (*)[N] to T (*)[].  This then automatically makes the opposite direction doable with static_cast.

2. Dereferencing a pointer/reference to an array is legal if at least N objects of type T exist there, and the indexed value X is less than N.  Note that this rule would allow compilers to assume that at least N elements exist because that is the type.


Conversion between T * and T (*)[] or T (*)[N] would then be legal to use following a reinterpret_cast.  I suppose that static_cast could support that as well, but that seems more dangerous to support.


A potential downside here is an ABI break: template classes that handle unsized array references differently from sized array references would break.  Similarly, some existing code might become ill-formed due to overload ambiguity or SFINAE differences.


Melissa
Message has been deleted
Message has been deleted

Richard Smith

unread,
Mar 12, 2019, 6:21:37 PM3/12/19
to std-dis...@isocpp.org
On Tue, 12 Mar 2019 at 14:15, Myriachan <myri...@gmail.com> wrote:
>
> Right now, static_cast is prohibited between T(&)[] and T (&)[N]. This seems like an unnecessary restriction.

See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0388r3.html
for a paper working its way through CWG. That permits, among other
things, implicit conversions that drop an array bound and static_casts
that add one back.

> Also, if you have a T (&)[N] that actually refers to a T (&)[M] where M > N, it's still undefined behavior to use that reference. Similarly, dereferencing a T (&)[1] referring to a T is also undefined behavior.

Those cases (where you don't actually have an array object of the
specified bound) are unaffected by P0388R3.

> So here are some of my proposed ideas:
>
> 1. Allow T (&)[N] to implicitly convert to T (&)[], and T (*)[N] to T (*)[]. This then automatically makes the opposite direction doable with static_cast.

(Will be done by P0388.)

> 2. Dereferencing a pointer/reference to an array is legal if at least N objects of type T exist there, and the indexed value X is less than N. Note that this rule would allow compilers to assume that at least N elements exist because that is the type.

We need to be careful to keep this case undefined:

struct A {
char x[3];
char y[4];
} a;
char (&r)[7] = static_cast<char(&)[7]>(a.x);
char q = r[5];

... since catching these kinds of things with sanitizers and the like
shows promise of being an important security hardening technique, and
giving that defined behavior would prevent a conforming compiler from
mitigating certain classes of buffer overflow bugs.

However, allowing array slicing (forming a reference to a subset of
the elements of a larger array) seems reasonable.

> Conversion between T * and T (*)[] or T (*)[N] would then be legal to use following a reinterpret_cast. I suppose that static_cast could support that as well, but that seems more dangerous to support.
>
>
> A potential downside here is an ABI break: template classes that handle unsized array references differently from sized array references would break. Similarly, some existing code might become ill-formed due to overload ambiguity or SFINAE differences.

Those kinds of things are a risk for any language change.
Reply all
Reply to author
Forward
Message has been deleted
0 new messages