diff mbox series

c++: __builtin_bit_cast To C array target type [PR103140]

Message ID 20211108200307.4192716-1-wjwray@gmail.com
State New
Headers show
Series c++: __builtin_bit_cast To C array target type [PR103140] | expand

Commit Message

will wray Nov. 8, 2021, 8:03 p.m. UTC
This patch allows __builtin_bit_cast to materialize a C array as its To type.

It was developed as part of an implementation of P1997, array copy-semantics,
but is independent, so makes sense to submit, review and merge ahead of it.

gcc/cp/ChangeLog:

	* constexpr.c (check_bit_cast_type): handle ARRAY_TYPE check,
	(cxx_eval_bit_cast): handle ARRAY_TYPE copy.
	* semantics.c (cp_build_bit_cast): warn only on unbounded/VLA.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/bit-cast2.C: update XFAIL tests.
	* g++.dg/cpp2a/bit-cast-to-array1.C: New test.
---
 gcc/cp/constexpr.c                              |  8 ++++-
 gcc/cp/semantics.c                              |  7 ++---
 gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C | 40 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/bit-cast2.C          |  8 ++---
 4 files changed, 53 insertions(+), 10 deletions(-)

Comments

will wray Nov. 15, 2021, 5:12 p.m. UTC | #1
Ping.

One motivation for allowing builtin bit_cast to builtin array is that
it enables direct bitwise constexpr comparisons via memcmp:

    template<class A, class B>
    constexpr int bit_equal(A const& a, B const& b)
    {
      static_assert( sizeof a == sizeof b,
          "bit_equal(a,b) requires same sizeof" );
      using bytes = unsigned char[sizeof(A)];
      return __builtin_memcmp(
             __builtin_bit_cast(bytes,a),
             __builtin_bit_cast(bytes,b),
             sizeof(A)) == 0;
    }


On Mon, Nov 8, 2021 at 3:03 PM Will Wray <wjwray@gmail.com> wrote:
>
> This patch allows __builtin_bit_cast to materialize a C array as its To type.
>
> It was developed as part of an implementation of P1997, array copy-semantics,
> but is independent, so makes sense to submit, review and merge ahead of it.
>
> gcc/cp/ChangeLog:
>
>         * constexpr.c (check_bit_cast_type): handle ARRAY_TYPE check,
>         (cxx_eval_bit_cast): handle ARRAY_TYPE copy.
>         * semantics.c (cp_build_bit_cast): warn only on unbounded/VLA.
>
> gcc/testsuite/ChangeLog:
>
>         * g++.dg/cpp2a/bit-cast2.C: update XFAIL tests.
>         * g++.dg/cpp2a/bit-cast-to-array1.C: New test.
> ---
>  gcc/cp/constexpr.c                              |  8 ++++-
>  gcc/cp/semantics.c                              |  7 ++---
>  gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C | 40 +++++++++++++++++++++++++
>  gcc/testsuite/g++.dg/cpp2a/bit-cast2.C          |  8 ++---
>  4 files changed, 53 insertions(+), 10 deletions(-)
>
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 453007c686b..be1cdada6f8 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -4124,6 +4124,11 @@ static bool
>  check_bit_cast_type (const constexpr_ctx *ctx, location_t loc, tree type,
>                      tree orig_type)
>  {
> +  if (TREE_CODE (type) == ARRAY_TYPE)
> +      return check_bit_cast_type (ctx, loc,
> +                                 TYPE_MAIN_VARIANT (TREE_TYPE (type)),
> +                                 orig_type);
> +
>    if (TREE_CODE (type) == UNION_TYPE)
>      {
>        if (!ctx->quiet)
> @@ -4280,7 +4285,8 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
>    tree r = NULL_TREE;
>    if (can_native_interpret_type_p (TREE_TYPE (t)))
>      r = native_interpret_expr (TREE_TYPE (t), ptr, len);
> -  else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE)
> +  else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE
> +          || TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
>      {
>        r = native_interpret_aggregate (TREE_TYPE (t), ptr, 0, len);
>        if (r != NULL_TREE)
> diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
> index 2443d032749..b3126b12abc 100644
> --- a/gcc/cp/semantics.c
> +++ b/gcc/cp/semantics.c
> @@ -11562,13 +11562,10 @@ cp_build_bit_cast (location_t loc, tree type, tree arg,
>      {
>        if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
>         return error_mark_node;
> -      if (TREE_CODE (type) == ARRAY_TYPE)
> +      if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type))
>         {
> -         /* std::bit_cast for destination ARRAY_TYPE is not possible,
> -            as functions may not return an array, so don't bother trying
> -            to support this (and then deal with VLAs etc.).  */
>           error_at (loc, "%<__builtin_bit_cast%> destination type %qT "
> -                        "is an array type", type);
> +                        "is a VLA variable-length array type", type);
>           return error_mark_node;
>         }
>        if (!trivially_copyable_p (type))
> diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C
> new file mode 100644
> index 00000000000..e6e50c06389
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C
> @@ -0,0 +1,40 @@
> +// { dg-do compile }
> +
> +class S { int s; };
> +S s();
> +class U { int a, b; };
> +U u();
> +
> +void
> +foo (int *q)
> +{
> +  __builtin_bit_cast (int [1], 0);
> +  __builtin_bit_cast (S [1], 0);
> +  __builtin_bit_cast (U [1], u);
> +}
> +
> +template <int N>
> +void
> +bar (int *q)
> +{
> +  int intN[N] = {};
> +  int int2N[2*N] = {};
> +  __builtin_bit_cast (int [N], intN);
> +  __builtin_bit_cast (S [N], intN);
> +  __builtin_bit_cast (U [N], int2N);
> +}
> +
> +template <typename T1, typename T2, typename T3>
> +void
> +baz (T1 ia, T2 sa, T3 ua)
> +{
> +  __builtin_bit_cast (T1, *ia);
> +  __builtin_bit_cast (T2, *sa);
> +  __builtin_bit_cast (T3, *ua);
> +}
> +
> +void
> +qux (S* sp, int *ip, U* up)
> +{
> +  baz <int[1], S[1], U[1]> (ip, sp, up);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
> index 6bb1760e621..7f1836ee4e9 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
> @@ -14,7 +14,7 @@ foo (int *q)
>    __builtin_bit_cast (int, s);         // { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
>    __builtin_bit_cast (S, 0);           // { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
>    __builtin_bit_cast (int &, q);       // { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
> -  __builtin_bit_cast (int [1], 0);     // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
> +  __builtin_bit_cast (S [1], 0);       // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
>    __builtin_bit_cast (V, 0);           // { dg-error "invalid use of incomplete type 'struct V'" }
>    __builtin_bit_cast (int, v);
>    __builtin_bit_cast (int, *p);                // { dg-error "invalid use of incomplete type 'struct V'" }
> @@ -29,7 +29,7 @@ bar (int *q)
>    __builtin_bit_cast (int, s);         // { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
>    __builtin_bit_cast (S, 0);           // { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
>    __builtin_bit_cast (int &, q);       // { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
> -  __builtin_bit_cast (int [1], 0);     // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
> +  __builtin_bit_cast (S [1], 0);       // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
>    __builtin_bit_cast (V, 0);           // { dg-error "invalid use of incomplete type 'struct V'" }
>    __builtin_bit_cast (int, *p);                // { dg-error "invalid use of incomplete type 'struct V'" }
>    __builtin_bit_cast (U, 0);           // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" }
> @@ -43,7 +43,7 @@ baz (T3 s, T4 *p, T1 *q)
>    __builtin_bit_cast (int, s);         // { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
>    __builtin_bit_cast (T3, 0);          // { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
>    __builtin_bit_cast (T1 &, q);                // { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
> -  __builtin_bit_cast (T2, 0);          // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
> +  __builtin_bit_cast (T2, 0);          // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
>    __builtin_bit_cast (T4, 0);          // { dg-error "invalid use of incomplete type 'struct V'" }
>    __builtin_bit_cast (int, *p);                // { dg-error "invalid use of incomplete type 'struct V'" }
>    __builtin_bit_cast (U, (T1) 0);      // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" }
> @@ -53,5 +53,5 @@ baz (T3 s, T4 *p, T1 *q)
>  void
>  qux (int *q)
>  {
> -  baz <int, int [1], S, V> (s, p, q);
> +  baz <int, S [1], S, V> (s, p, q);
>  }
> --
> 2.31.1
>
Jakub Jelinek Nov. 15, 2021, 5:21 p.m. UTC | #2
On Mon, Nov 15, 2021 at 12:12:22PM -0500, will wray via Gcc-patches wrote:
> One motivation for allowing builtin bit_cast to builtin array is that
> it enables direct bitwise constexpr comparisons via memcmp:
> 
>     template<class A, class B>
>     constexpr int bit_equal(A const& a, B const& b)
>     {
>       static_assert( sizeof a == sizeof b,
>           "bit_equal(a,b) requires same sizeof" );
>       using bytes = unsigned char[sizeof(A)];
>       return __builtin_memcmp(
>              __builtin_bit_cast(bytes,a),
>              __builtin_bit_cast(bytes,b),
>              sizeof(A)) == 0;
>     }

IMNSHO people shouldn't use this builtin directly, and we shouldn't
encourage such uses, the standard interface is std::bit_cast.

For the above, I don't see a reason to do it that way, you can
instead portably:
  struct bytes { unsigned char data[sizeof(A)]; };
  bytes ab = std::bit_cast(bytes, a);
  bytes bb = std::bit_cast(bytes, a);
  for (size_t i = 0; i < sizeof(A); ++i)
    if (ab.data[i] != bb.data[i])
      return false;
  return true;
- __builtin_memcmp isn't portable either and memcmp isn't constexpr.

If P1997 is in, it is easy to support it in std::bit_cast and easy to
explain what __builtin_bit_cast does for array types, but otherwise
it is quite unclear what it exactly does...

	Jakub
will wray Nov. 15, 2021, 11:32 p.m. UTC | #3
Yes - direct use of any builtin is not to be encouraged, in user code.

This __builtin_bit_cast patch is intended to encourage experimentation
with array copy semantics now, on truck, in preparation for P1997.

The builtin bit_cast is strictly more powerful than the std::bit_cast
library function that it helps implement, is available in any -std mode
and might also be useful in C, independent of any standardization effort.

The semantics of bit_cast is clear - it's just the resulting rvalue array
itself is unfamiliar and tricky to handle within current language rules.

On Mon, Nov 15, 2021 at 12:21 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Mon, Nov 15, 2021 at 12:12:22PM -0500, will wray via Gcc-patches wrote:
> > One motivation for allowing builtin bit_cast to builtin array is that
> > it enables direct bitwise constexpr comparisons via memcmp:
> >
> >     template<class A, class B>
> >     constexpr int bit_equal(A const& a, B const& b)
> >     {
> >       static_assert( sizeof a == sizeof b,
> >           "bit_equal(a,b) requires same sizeof" );
> >       using bytes = unsigned char[sizeof(A)];
> >       return __builtin_memcmp(
> >              __builtin_bit_cast(bytes,a),
> >              __builtin_bit_cast(bytes,b),
> >              sizeof(A)) == 0;
> >     }
>
> IMNSHO people shouldn't use this builtin directly, and we shouldn't
> encourage such uses, the standard interface is std::bit_cast.
>
> For the above, I don't see a reason to do it that way, you can
> instead portably:
>   struct bytes { unsigned char data[sizeof(A)]; };
>   bytes ab = std::bit_cast(bytes, a);
>   bytes bb = std::bit_cast(bytes, a);
>   for (size_t i = 0; i < sizeof(A); ++i)
>     if (ab.data[i] != bb.data[i])
>       return false;
>   return true;
> - __builtin_memcmp isn't portable either and memcmp isn't constexpr.
>
> If P1997 is in, it is easy to support it in std::bit_cast and easy to
> explain what __builtin_bit_cast does for array types, but otherwise
> it is quite unclear what it exactly does...
>
>         Jakub
>
will wray Nov. 22, 2021, 1:16 p.m. UTC | #4
Ping.

Another use case; casting arrays of char to arrays of unsigned char
(useful in some crypto APIs).

On Mon, Nov 15, 2021 at 6:32 PM will wray <wjwray@gmail.com> wrote:
>
> Yes - direct use of any builtin is not to be encouraged, in user code.
>
> This __builtin_bit_cast patch is intended to encourage experimentation
> with array copy semantics now, on truck, in preparation for P1997.
>
> The builtin bit_cast is strictly more powerful than the std::bit_cast
> library function that it helps implement, is available in any -std mode
> and might also be useful in C, independent of any standardization effort.
>
> The semantics of bit_cast is clear - it's just the resulting rvalue array
> itself is unfamiliar and tricky to handle within current language rules.
>
> On Mon, Nov 15, 2021 at 12:21 PM Jakub Jelinek <jakub@redhat.com> wrote:
> >
> > On Mon, Nov 15, 2021 at 12:12:22PM -0500, will wray via Gcc-patches wrote:
> > > One motivation for allowing builtin bit_cast to builtin array is that
> > > it enables direct bitwise constexpr comparisons via memcmp:
> > >
> > >     template<class A, class B>
> > >     constexpr int bit_equal(A const& a, B const& b)
> > >     {
> > >       static_assert( sizeof a == sizeof b,
> > >           "bit_equal(a,b) requires same sizeof" );
> > >       using bytes = unsigned char[sizeof(A)];
> > >       return __builtin_memcmp(
> > >              __builtin_bit_cast(bytes,a),
> > >              __builtin_bit_cast(bytes,b),
> > >              sizeof(A)) == 0;
> > >     }
> >
> > IMNSHO people shouldn't use this builtin directly, and we shouldn't
> > encourage such uses, the standard interface is std::bit_cast.
> >
> > For the above, I don't see a reason to do it that way, you can
> > instead portably:
> >   struct bytes { unsigned char data[sizeof(A)]; };
> >   bytes ab = std::bit_cast(bytes, a);
> >   bytes bb = std::bit_cast(bytes, a);
> >   for (size_t i = 0; i < sizeof(A); ++i)
> >     if (ab.data[i] != bb.data[i])
> >       return false;
> >   return true;
> > - __builtin_memcmp isn't portable either and memcmp isn't constexpr.
> >
> > If P1997 is in, it is easy to support it in std::bit_cast and easy to
> > explain what __builtin_bit_cast does for array types, but otherwise
> > it is quite unclear what it exactly does...
> >
> >         Jakub
> >
Jason Merrill Nov. 25, 2021, 3:57 p.m. UTC | #5
On 11/8/21 15:03, Will Wray via Gcc-patches wrote:
> This patch allows __builtin_bit_cast to materialize a C array as its To type.
> 
> It was developed as part of an implementation of P1997, array copy-semantics,
> but is independent, so makes sense to submit, review and merge ahead of it.
> 
> gcc/cp/ChangeLog:
> 
> 	* constexpr.c (check_bit_cast_type): handle ARRAY_TYPE check,
> 	(cxx_eval_bit_cast): handle ARRAY_TYPE copy.
> 	* semantics.c (cp_build_bit_cast): warn only on unbounded/VLA.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/bit-cast2.C: update XFAIL tests.
> 	* g++.dg/cpp2a/bit-cast-to-array1.C: New test.
> ---
>   gcc/cp/constexpr.c                              |  8 ++++-
>   gcc/cp/semantics.c                              |  7 ++---
>   gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C | 40 +++++++++++++++++++++++++
>   gcc/testsuite/g++.dg/cpp2a/bit-cast2.C          |  8 ++---
>   4 files changed, 53 insertions(+), 10 deletions(-)
> 
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 453007c686b..be1cdada6f8 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -4124,6 +4124,11 @@ static bool
>   check_bit_cast_type (const constexpr_ctx *ctx, location_t loc, tree type,
>   		     tree orig_type)
>   {
> +  if (TREE_CODE (type) == ARRAY_TYPE)
> +      return check_bit_cast_type (ctx, loc,
> +				  TYPE_MAIN_VARIANT (TREE_TYPE (type)),
> +				  orig_type);
> +
>     if (TREE_CODE (type) == UNION_TYPE)
>       {
>         if (!ctx->quiet)
> @@ -4280,7 +4285,8 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
>     tree r = NULL_TREE;
>     if (can_native_interpret_type_p (TREE_TYPE (t)))
>       r = native_interpret_expr (TREE_TYPE (t), ptr, len);
> -  else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE)
> +  else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE
> +	   || TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
>       {
>         r = native_interpret_aggregate (TREE_TYPE (t), ptr, 0, len);
>         if (r != NULL_TREE)
> diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
> index 2443d032749..b3126b12abc 100644
> --- a/gcc/cp/semantics.c
> +++ b/gcc/cp/semantics.c
> @@ -11562,13 +11562,10 @@ cp_build_bit_cast (location_t loc, tree type, tree arg,
>       {
>         if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
>   	return error_mark_node;
> -      if (TREE_CODE (type) == ARRAY_TYPE)
> +      if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type))
>   	{
> -	  /* std::bit_cast for destination ARRAY_TYPE is not possible,
> -	     as functions may not return an array, so don't bother trying
> -	     to support this (and then deal with VLAs etc.).  */
>   	  error_at (loc, "%<__builtin_bit_cast%> destination type %qT "
> -			 "is an array type", type);
> +			 "is a VLA variable-length array type", type);

Null TYPE_DOMAIN doesn't mean VLA, it means unknown length.

Probably better to check for null or non-constant TYPE_SIZE rather than 
specifically for VLA.

>   	  return error_mark_node;
>   	}
>         if (!trivially_copyable_p (type))
> diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C
> new file mode 100644
> index 00000000000..e6e50c06389
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C
> @@ -0,0 +1,40 @@
> +// { dg-do compile }
> +
> +class S { int s; };
> +S s();
> +class U { int a, b; };
> +U u();
> +
> +void
> +foo (int *q)
> +{
> +  __builtin_bit_cast (int [1], 0);
> +  __builtin_bit_cast (S [1], 0);
> +  __builtin_bit_cast (U [1], u);
> +}
> +
> +template <int N>
> +void
> +bar (int *q)
> +{
> +  int intN[N] = {};
> +  int int2N[2*N] = {};
> +  __builtin_bit_cast (int [N], intN);
> +  __builtin_bit_cast (S [N], intN);
> +  __builtin_bit_cast (U [N], int2N);
> +}
> +
> +template <typename T1, typename T2, typename T3>
> +void
> +baz (T1 ia, T2 sa, T3 ua)
> +{
> +  __builtin_bit_cast (T1, *ia);
> +  __builtin_bit_cast (T2, *sa);
> +  __builtin_bit_cast (T3, *ua);
> +}
> +
> +void
> +qux (S* sp, int *ip, U* up)
> +{
> +  baz <int[1], S[1], U[1]> (ip, sp, up);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
> index 6bb1760e621..7f1836ee4e9 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
> @@ -14,7 +14,7 @@ foo (int *q)
>     __builtin_bit_cast (int, s);		// { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
>     __builtin_bit_cast (S, 0);		// { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
>     __builtin_bit_cast (int &, q);	// { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
> -  __builtin_bit_cast (int [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
> +  __builtin_bit_cast (S [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
>     __builtin_bit_cast (V, 0);		// { dg-error "invalid use of incomplete type 'struct V'" }
>     __builtin_bit_cast (int, v);
>     __builtin_bit_cast (int, *p);		// { dg-error "invalid use of incomplete type 'struct V'" }
> @@ -29,7 +29,7 @@ bar (int *q)
>     __builtin_bit_cast (int, s);		// { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
>     __builtin_bit_cast (S, 0);		// { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
>     __builtin_bit_cast (int &, q);	// { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
> -  __builtin_bit_cast (int [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
> +  __builtin_bit_cast (S [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
>     __builtin_bit_cast (V, 0);		// { dg-error "invalid use of incomplete type 'struct V'" }
>     __builtin_bit_cast (int, *p);		// { dg-error "invalid use of incomplete type 'struct V'" }
>     __builtin_bit_cast (U, 0);		// { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" }
> @@ -43,7 +43,7 @@ baz (T3 s, T4 *p, T1 *q)
>     __builtin_bit_cast (int, s);		// { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
>     __builtin_bit_cast (T3, 0);		// { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
>     __builtin_bit_cast (T1 &, q);		// { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
> -  __builtin_bit_cast (T2, 0);		// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
> +  __builtin_bit_cast (T2, 0);		// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
>     __builtin_bit_cast (T4, 0);		// { dg-error "invalid use of incomplete type 'struct V'" }
>     __builtin_bit_cast (int, *p);		// { dg-error "invalid use of incomplete type 'struct V'" }
>     __builtin_bit_cast (U, (T1) 0);	// { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" }
> @@ -53,5 +53,5 @@ baz (T3 s, T4 *p, T1 *q)
>   void
>   qux (int *q)
>   {
> -  baz <int, int [1], S, V> (s, p, q);
> +  baz <int, S [1], S, V> (s, p, q);
>   }
>
diff mbox series

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 453007c686b..be1cdada6f8 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4124,6 +4124,11 @@  static bool
 check_bit_cast_type (const constexpr_ctx *ctx, location_t loc, tree type,
 		     tree orig_type)
 {
+  if (TREE_CODE (type) == ARRAY_TYPE)
+      return check_bit_cast_type (ctx, loc,
+				  TYPE_MAIN_VARIANT (TREE_TYPE (type)),
+				  orig_type);
+
   if (TREE_CODE (type) == UNION_TYPE)
     {
       if (!ctx->quiet)
@@ -4280,7 +4285,8 @@  cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
   tree r = NULL_TREE;
   if (can_native_interpret_type_p (TREE_TYPE (t)))
     r = native_interpret_expr (TREE_TYPE (t), ptr, len);
-  else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE)
+  else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE
+	   || TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
     {
       r = native_interpret_aggregate (TREE_TYPE (t), ptr, 0, len);
       if (r != NULL_TREE)
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 2443d032749..b3126b12abc 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -11562,13 +11562,10 @@  cp_build_bit_cast (location_t loc, tree type, tree arg,
     {
       if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
 	return error_mark_node;
-      if (TREE_CODE (type) == ARRAY_TYPE)
+      if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type))
 	{
-	  /* std::bit_cast for destination ARRAY_TYPE is not possible,
-	     as functions may not return an array, so don't bother trying
-	     to support this (and then deal with VLAs etc.).  */
 	  error_at (loc, "%<__builtin_bit_cast%> destination type %qT "
-			 "is an array type", type);
+			 "is a VLA variable-length array type", type);
 	  return error_mark_node;
 	}
       if (!trivially_copyable_p (type))
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C
new file mode 100644
index 00000000000..e6e50c06389
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast-to-array1.C
@@ -0,0 +1,40 @@ 
+// { dg-do compile }
+
+class S { int s; };
+S s();
+class U { int a, b; };
+U u();
+
+void
+foo (int *q)
+{
+  __builtin_bit_cast (int [1], 0);
+  __builtin_bit_cast (S [1], 0);
+  __builtin_bit_cast (U [1], u);
+}
+
+template <int N>
+void
+bar (int *q)
+{
+  int intN[N] = {};
+  int int2N[2*N] = {};
+  __builtin_bit_cast (int [N], intN);
+  __builtin_bit_cast (S [N], intN);
+  __builtin_bit_cast (U [N], int2N);
+}
+
+template <typename T1, typename T2, typename T3>
+void
+baz (T1 ia, T2 sa, T3 ua)
+{
+  __builtin_bit_cast (T1, *ia);
+  __builtin_bit_cast (T2, *sa);
+  __builtin_bit_cast (T3, *ua);
+}
+
+void
+qux (S* sp, int *ip, U* up)
+{
+  baz <int[1], S[1], U[1]> (ip, sp, up);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
index 6bb1760e621..7f1836ee4e9 100644
--- a/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C
@@ -14,7 +14,7 @@  foo (int *q)
   __builtin_bit_cast (int, s);		// { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
   __builtin_bit_cast (S, 0);		// { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
   __builtin_bit_cast (int &, q);	// { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
-  __builtin_bit_cast (int [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
+  __builtin_bit_cast (S [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
   __builtin_bit_cast (V, 0);		// { dg-error "invalid use of incomplete type 'struct V'" }
   __builtin_bit_cast (int, v);
   __builtin_bit_cast (int, *p);		// { dg-error "invalid use of incomplete type 'struct V'" }
@@ -29,7 +29,7 @@  bar (int *q)
   __builtin_bit_cast (int, s);		// { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
   __builtin_bit_cast (S, 0);		// { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
   __builtin_bit_cast (int &, q);	// { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
-  __builtin_bit_cast (int [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
+  __builtin_bit_cast (S [1], 0);	// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
   __builtin_bit_cast (V, 0);		// { dg-error "invalid use of incomplete type 'struct V'" }
   __builtin_bit_cast (int, *p);		// { dg-error "invalid use of incomplete type 'struct V'" }
   __builtin_bit_cast (U, 0);		// { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" }
@@ -43,7 +43,7 @@  baz (T3 s, T4 *p, T1 *q)
   __builtin_bit_cast (int, s);		// { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" }
   __builtin_bit_cast (T3, 0);		// { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" }
   __builtin_bit_cast (T1 &, q);		// { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" }
-  __builtin_bit_cast (T2, 0);		// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" }
+  __builtin_bit_cast (T2, 0);		// { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is not trivially copyable" }
   __builtin_bit_cast (T4, 0);		// { dg-error "invalid use of incomplete type 'struct V'" }
   __builtin_bit_cast (int, *p);		// { dg-error "invalid use of incomplete type 'struct V'" }
   __builtin_bit_cast (U, (T1) 0);	// { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" }
@@ -53,5 +53,5 @@  baz (T3 s, T4 *p, T1 *q)
 void
 qux (int *q)
 {
-  baz <int, int [1], S, V> (s, p, q);
+  baz <int, S [1], S, V> (s, p, q);
 }