[PATCH 2/3] c++: P1997 array-copy extensions: Initialization [PR103238]

Jason Merrill jason@redhat.com
Sat Nov 27 22:56:24 GMT 2021


On 11/21/21 21:51, Will Wray via Gcc-patches wrote:
> This patch implements initializations of arrays from array values.
> 
> The first of two 'array-copy' patches, it adds the option -farray-copy
> (flag_array_copy) to enable all features of P1997 (copy related or not),
> documented as experimental extensions.
> 
> It deals with initialization of array variables and member array fields.
> 
> Initialization of an array variable from an array of the same type performs
> array copy-initialization; elementwise move or copy from an rvalue or lvalue
> array respectively, in index order from begin to end. The existing code path
> for a structured binding declaration with array initializer, auto[e...]{a};
> performs the same array copy-initialization (as a special case superpower).
> Borrowing from that, this was a relatively quick and easy change.
> 
> Initialization of member arrays proved much more difficult to do in general.
> I resorted to trial and error, running gcc in gdb with test cases to work out
> where and what to change, until eventually converging on this set of changes.
> 
> One starting point was the C special case of char array initialization from
> string literals (as char array lvalue constants). However, a long-standing
> bug in designated initialization of char arrays by string literals blocked
> the task of extending this special case to general array type initializers.
> A bugfix patch was separated out, to be merged ahead of these patches:
> 
>      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55227
>      https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584897.html
> 
> Other cases to consider, array initializations:
> 
>      * by optionally brace-enclosed or paren-enclosed array values
>      * by possibly-designated array-valued aggregate initializers
>        (within possibly-elided braced init-lists)
>      * by brace or paren-enclosed array values in member initialization lists
>      * by array-valued member initializers
> 
> The patch adds tests for these cases, and for inner initializations of nested
> array elements of array type.
> 
> The work has diverged in details from the P1997 wording, including catching
> up with C++20 changes such as parenthesised initialization of aggregates.
> The paper will be revised to reflect the implementation experience.
> 
> It is likely that there are omissions, errors in the conditions or that changed
> code is inappropriate. For example, I inserted a new call to build_array_copy
> in typeck2.c:digest_init_r which may not be correct for move-enabled elements.
> Please review carefully with this in mind and suggest test cases to exercise.

You would want a testcase with an array of move-only type, e.g. (untested)

struct A { A(int); A(A&&); int i; };
using Ar = A[2];
struct B { A a[2]; };
B b = { Ar{1,2} }; // should move

I don't think the new call to build_array_copy needs to change, but it's 
good to test.

> 	PR c++/103238
> 
> gcc/c-family/ChangeLog:
> 
> 	* c-common.c (complete_array_type): Accept array type initial_value.
> 	* c.opt: New option -farray-copy "experimental extensions for P1997".
> 
> gcc/cp/ChangeLog:
> 
> 	* decl.c (do_aggregate_paren_init): Accept single array type init.
> 	(maybe_deduce_size_from_array_init): Include same-type array inits,
> 	or complain for not same-type arrays.
> 	(reshape_init_r): Extend string-literal handling to all array types.
> 	* init.c (build_aggr_init): Follow existing path for array rhs.
> 	* typeck.c (cp_build_modify_expr): Follow path for synthetic op=.
> 	* typeck2.c (digest_init_r): Add call to build_array_copy for
> 	same-type arrays ('copy' feels wrong for move-eligible rhs).
> 
> gcc/ChangeLog:
> 
> 	* doc/invoke.texi: -farray-copy help info documentation.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/init/array-copy1.C: New test. Variable init 'before' XFAILs
> 	* g++.dg/init/array-copy2.C: New test. Variable init 'after' PASSes
> 	* g++.dg/init/array-copy3.C: New test. Member init 'before' XFAILs
> 	* g++.dg/init/array-copy4.C: New test. Member init 'after' PASSes
> 	* g++.dg/init/array-copy5.C: New test. Member nsdmi & desig XFAILs
> 	* g++.dg/init/array-copy6.C: New test. Member nsdmi & desig PASSes
> ---
>   gcc/c-family/c-common.c                 |  5 +++
>   gcc/c-family/c.opt                      |  4 ++
>   gcc/cp/decl.c                           | 61 ++++++++++++++++++++---------
>   gcc/cp/init.c                           |  6 ++-
>   gcc/cp/typeck.c                         |  9 +++--
>   gcc/cp/typeck2.c                        | 30 +++++++++++----
>   gcc/doc/invoke.texi                     |  6 +++
>   gcc/testsuite/g++.dg/init/array-copy1.C | 66 ++++++++++++++++++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy2.C | 68 +++++++++++++++++++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy3.C | 41 ++++++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy4.C | 42 ++++++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy5.C | 36 +++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy6.C | 51 +++++++++++++++++++++++++
>   13 files changed, 395 insertions(+), 30 deletions(-)
> 
> diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
> index 86c007f53de..fb0b1ef294f 100644
> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c
> @@ -6796,6 +6796,11 @@ complete_array_type (tree *ptype, tree initial_value, bool do_default)
>   	    = int_size_in_bytes (TREE_TYPE (TREE_TYPE (initial_value)));
>   	  maxindex = size_int (TREE_STRING_LENGTH (initial_value)/eltsize - 1);
>   	}
> +      else if (flag_array_copy
> +	       && TREE_CODE (TREE_TYPE (initial_value)) == ARRAY_TYPE)
> +	{
> +	  maxindex = array_type_nelts (TREE_TYPE (initial_value));
> +	}
>         else if (TREE_CODE (initial_value) == CONSTRUCTOR)
>   	{
>   	  vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (initial_value);
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 3976fc368db..9d2a6ad8e1c 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1579,6 +1579,10 @@ fcilkplus
>   C ObjC C++ ObjC++ LTO Undocumented Ignore
>   Removed in GCC 8.  This switch has no effect.
>   
> +farray-copy
> +C ObjC C++ ObjC++ Var(flag_array_copy)
> +Enable experimental extension for P1997; C array copy semantics.
> +
>   fconcepts
>   C++ ObjC++ Var(flag_concepts)
>   Enable support for C++ concepts.
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 83a2d3bf8f1..1010fa2c53f 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -5834,15 +5834,19 @@ tree
>   do_aggregate_paren_init (tree init, tree type)
>   {
>     tree val = TREE_VALUE (init);
> +  tree stripped_val = tree_strip_any_location_wrapper (val);
>   
>     if (TREE_CHAIN (init) == NULL_TREE)
>       {
>         /* If the list has a single element and it's a string literal,
>   	 then it's the initializer for the array as a whole.  */
> -      if (TREE_CODE (type) == ARRAY_TYPE
> -	  && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
> -	  && TREE_CODE (tree_strip_any_location_wrapper (val))
> -	     == STRING_CST)
> +      if ((TREE_CODE (type) == ARRAY_TYPE
> +	   && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
> +	   && TREE_CODE (stripped_val) == STRING_CST)
> +	  || (flag_array_copy
> +	      && TREE_CODE (TREE_TYPE (stripped_val)) == ARRAY_TYPE
> +	      && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (type),
> +					TREE_TYPE (TREE_TYPE (stripped_val)))))
>   	return val;
>         /* Handle non-standard extensions like compound literals.  This also
>   	 prevents triggering aggregate parenthesized-initialization in
> @@ -6007,9 +6011,10 @@ maybe_deduce_size_from_array_init (tree decl, tree init)
>   {
>     tree type = TREE_TYPE (decl);
>   
> -  if (TREE_CODE (type) == ARRAY_TYPE
> -      && TYPE_DOMAIN (type) == NULL_TREE
> -      && TREE_CODE (decl) != TYPE_DECL)
> +  if (TREE_CODE (type) != ARRAY_TYPE
> +      || TREE_CODE (decl) == TYPE_DECL)
> +    ;
> +  else if (TYPE_DOMAIN (type) == NULL_TREE)
>       {
>         /* do_default is really a C-ism to deal with tentative definitions.
>   	 But let's leave it here to ease the eventual merge.  */
> @@ -6072,6 +6077,17 @@ maybe_deduce_size_from_array_init (tree decl, tree init)
>   
>         relayout_decl (decl);
>       }
> +  else if (flag_array_copy && init
> +	   && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE
> +	   && TREE_CODE (tree_strip_any_location_wrapper (init)) != STRING_CST
> +	   && !same_type_ignoring_top_level_qualifiers_p (type,
> +							  TREE_TYPE (init)))
> +    {
> +      error_at (DECL_SOURCE_LOCATION (decl),
> +		"array of type %qT cannot be initialized with an "
> +		"array of type %qT in %qD", type, TREE_TYPE (init), decl);
> +      TREE_TYPE (decl) = error_mark_node;
> +    }
>   }
>   
>   /* Set DECL_SIZE, DECL_ALIGN, etc. for DECL (a VAR_DECL), and issue
> @@ -6815,21 +6831,23 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
>         return init;
>       }
>   
> -  /* [dcl.init.string]
> +  /* [dcl.init.string] or flag_array_copy extension P1997
>   
>         A char array (whether plain char, signed char, or unsigned char)
>         can be initialized by a string-literal (optionally enclosed in
>         braces); a wchar_t array can be initialized by a wide
> -      string-literal (optionally enclosed in braces).  */
> -  if (TREE_CODE (type) == ARRAY_TYPE
> -      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type))))
> +      string-literal (optionally enclosed in braces).
> +      With flag_array_copy an array can be initialized by an array value
> +      (optionally enclosed in braces).  */
> +  if (TREE_CODE (type) == ARRAY_TYPE)
>       {
>         tree arr_init = init;
>         tree stripped_arr_init = stripped_init;

The renaming belongs in this patch.

>         reshape_iter stripd = {};
>   
> -      /* Strip one level of braces if and only if they enclose a single
> -	 element (as allowed by [dcl.init.string]).  */
> +      /* Strip one level of braces if they enclose a single element,
> +	 provisionally for elison in the optionally-braced case if the
> +	 initializer is of compatible array type, as checked next. */
>         if (!first_initializer_p
>   	  && TREE_CODE (stripped_arr_init) == CONSTRUCTOR
>   	  && CONSTRUCTOR_NELTS (stripped_arr_init) == 1)
> @@ -6839,10 +6857,19 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
>   	  stripped_arr_init = tree_strip_any_location_wrapper (arr_init);
>   	}
>   
> -      /* If it's a string literal, then it's the initializer for the array
> -	 as a whole. Otherwise, continue with normal initialization for
> -	 array types (one value per array element).  */
> -      if (TREE_CODE (stripped_arr_init) == STRING_CST)
> +      tree init_type = TREE_TYPE (stripped_arr_init);
> +
> +      /* If it's a string literal initializer for a char array target,
> +	 or, with flag_array_copy, an array value of same element type,
> +	 then it's the initializer for the array as a whole.
> +	 Else, continue with element by element array initialization.  */
> +      if ((TREE_CODE (stripped_arr_init) == STRING_CST
> +	   && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type))))
> +	  || (flag_array_copy
> +	      && TREE_CODE (init_type) == ARRAY_TYPE
> +	      && TYPE_DOMAIN (init_type) != NULL_TREE
> +	      && same_type_ignoring_top_level_qualifiers_p (
> +				TREE_TYPE (type), TREE_TYPE (init_type))))
>   	{
>   	  if ((first_initializer_p && has_designator_problem (d, complain))
>   	      || (stripd.cur && has_designator_problem (&stripd, complain)))
> diff --git a/gcc/cp/init.c b/gcc/cp/init.c
> index 3ba2e3bbe04..fd9a065ed95 100644
> --- a/gcc/cp/init.c
> +++ b/gcc/cp/init.c
> @@ -1787,7 +1787,11 @@ build_aggr_init (tree exp, tree init, int flags, tsubst_flags_t complain)
>         tree itype = init ? TREE_TYPE (init) : NULL_TREE;
>         int from_array = 0;
>   
> -      if (VAR_P (exp) && DECL_DECOMPOSITION_P (exp))
> +      if (VAR_P (exp)
> +	  && (DECL_DECOMPOSITION_P (exp)
> +	      || (flag_array_copy
> +		  && TREE_CODE (tree_strip_any_location_wrapper (init))
> +		     != STRING_CST)))
>   	{
>   	  from_array = 1;
>   	  init = mark_rvalue_use (init);
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index cb20329ceb5..8e96a925186 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -9144,11 +9144,12 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
>   	}
>   
>         /* Allow array assignment in compiler-generated code.  */
> -      else if (!current_function_decl
> -	       || !DECL_DEFAULTED_FN (current_function_decl))
> +      else if ((!current_function_decl
> +		|| !DECL_DEFAULTED_FN (current_function_decl))
> +	       && !flag_array_copy)
>   	{
> -          /* This routine is used for both initialization and assignment.
> -             Make sure the diagnostic message differentiates the context.  */
> +	  /* This routine is used for both initialization and assignment.
> +	     Make sure the diagnostic message differentiates the context.  */
>   	  if (complain & tf_error)
>   	    {
>   	      if (modifycode == INIT_EXPR)
> diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
> index e98fbf7f5fa..20d46716279 100644
> --- a/gcc/cp/typeck2.c
> +++ b/gcc/cp/typeck2.c
> @@ -1175,6 +1175,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
>   	    }
>   	  return init;
>   	}
> +      if (flag_array_copy
> +	  && same_type_ignoring_top_level_qualifiers_p(type, TREE_TYPE (init)))
> +	return build_array_copy(init);

Missing space before ( on two lines.

>       }
>   
>     /* Handle scalar types (including conversions) and references.  */
> @@ -1244,13 +1247,18 @@ digest_init_r (tree type, tree init, int nested, int flags,
>   				     complain);
>     else
>       {
> -      if (COMPOUND_LITERAL_P (stripped_init) && code == ARRAY_TYPE)
> +      if (COMPOUND_LITERAL_P (stripped_init) && code == ARRAY_TYPE
> +	  && (complain & tf_error))
>   	{
> -	  if (complain & tf_error)
> -	    error_at (loc, "cannot initialize aggregate of type %qT with "
> -		      "a compound literal", type);
> -
> -	  return error_mark_node;
> +	  if (flag_array_copy)
> +	    pedwarn (loc, OPT_Wpedantic, "ISO C++ doesn%'t allow initialization"
> +		     "of an array of type %qT with a compound literal", type);
> +	  else
> +	    {
> +	      error_at (loc, "cannot initialize aggregate of type %qT with "
> +			     "a compound literal", type);
> +	      return error_mark_node;
> +	    }

This changes the function to only return error_mark_node when we're 
diagnosing errors, which seems wrong; we need it to fail in SFINAE 
context as well.

>   	}
>   
>         if (code == ARRAY_TYPE
> @@ -1265,8 +1273,14 @@ digest_init_r (tree type, tree init, int nested, int flags,
>   	    return init;
>   
>   	  if (complain & tf_error)
> -	    error_at (loc, "array must be initialized with a brace-enclosed"
> -		      " initializer");
> +	  {
> +	    if (!flag_array_copy)
> +	      error_at (loc, "array must be initialized with a brace-enclosed"
> +			     " initializer");
> +	    else
> +	      error_at (loc, "array must be initialized with a brace-enclosed"
> +			     " initializer or an array value of the same type");
> +	    }
>   	  return error_mark_node;
>   	}
>   
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index d62ec08150e..32a34ca74bb 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -213,6 +213,7 @@ in the following sections.
>   @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
>   @gccoptlist{-fabi-version=@var{n}  -fno-access-control @gol
>   -faligned-new=@var{n}  -fargs-in-order=@var{n}  -fchar8_t  -fcheck-new @gol
> +-farray-copy @gol
>   -fconstexpr-depth=@var{n}  -fconstexpr-cache-depth=@var{n} @gol
>   -fconstexpr-loop-limit=@var{n}  -fconstexpr-ops-limit=@var{n} @gol
>   -fno-elide-constructors @gol
> @@ -3001,6 +3002,11 @@ return value even without this option.  In all other cases, when
>   exhaustion is signalled by throwing @code{std::bad_alloc}.  See also
>   @samp{new (nothrow)}.
>   
> +@item -farray-copy
> +@opindex farray-copy
> +Enables experimental support for P1997; C array copy semantics, pseudo-
> +destructors and auto placeholder for array element type.
> +
>   @item -fconcepts
>   @itemx -fconcepts-ts
>   @opindex fconcepts
> diff --git a/gcc/testsuite/g++.dg/init/array-copy1.C b/gcc/testsuite/g++.dg/init/array-copy1.C
> new file mode 100644
> index 00000000000..15986a00d1b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy1.C
> @@ -0,0 +1,66 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array variable initialization nocompile tests -
> +// should give same errors before and after the array-copy patch.
> +
> +// { dg-do compile { target c++11 } } c++11 for uniform init
> +
> +typedef int int2[2];
> +int2 u = {11,66};
> +
> +int* p = new int2{u}; // { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." }
> +
> +// Initializations from from rvalue array
> +int2 g = int2{11,66};
> +int2 j ( int2{11,66} );  // takes P0960 paren-init path in c++20
> +int2 k = { int2{11,66} };
> +int2 l { int2{11,66} };
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-4 }
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-4 }
> +// { dg-error "taking address of temporary array" "address of temporary" { target *-*-* } .-4 }
> +// { dg-error "taking address of temporary array" "address of temporary" { target *-*-* } .-4 }
> +
> +// Initializations from from lvalue array
> +int2 gc = u;
> +int2 jc (u);  // takes P0960 paren-init path in c++20
> +int2 kc = {u};
> +int2 lc {u};
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-4 }
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-4 }
> +// { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." "bad conv" { target *-*-* } .-4 }
> +// { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." "bad conv" { target *-*-* } .-4 }
> +
> +char str[] = "str";
> +
> +char cpy[] = str;
> +char cpp[] (str); // takes P0960 paren-init path in c++20
> +char cpu[] = {str};
> +char cpw[] {str};
> +// { dg-error "initializer fails to determine size of .cpy." "deduce size" { target *-*-*  } .-4 }
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-5 }
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target c++17_down } .-5 }
> +// { dg-error "initializer fails to determine size of .cpp." "deduce size" { target c++17_down  } .-6 }
> +// { dg-error "invalid conversion from .char\\*. to .char. .\\-fpermissive." "bad conv" { target c++20 } .-7 }
> +// { dg-error "invalid conversion from .char\\*. to .char. .\\-fpermissive." "bad conv" { target *-*-* } .-7 }
> +// { dg-error "invalid conversion from .char\\*. to .char. .\\-fpermissive." "bad conv" { target *-*-* } .-7 }
> +
> +char cp5[5] = str;
> +char cp3[3] = str;
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-2 }
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-2 }
> +
> +int2 s[2] = { u };
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-1 }
> +int2 x[2] = { u
> +	    , u };
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-2 }
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-2 }
> +int2 y[2] = { 1,2
> +	    , {u} };
> +// { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." "bad conv" { target *-*-* } .-1 }
> +int2 z[2] = { {u},
> +	       3,{4} };
> +// { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." "bad conv" { target *-*-* } .-2 }
> +
> +__UINT8_TYPE__ m222[2][2][2] {0,1,2,3,4,5,6,7};
> +__UINT8_TYPE__ c222[2][2][2] { m222[0], m222[1][0], 6,7 };
> +// { dg-error "array must be initialized with a brace-enclosed initializer" "array init" { target *-*-* } .-1 }
> diff --git a/gcc/testsuite/g++.dg/init/array-copy2.C b/gcc/testsuite/g++.dg/init/array-copy2.C
> new file mode 100644
> index 00000000000..7e1ad23592b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy2.C
> @@ -0,0 +1,68 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array variable initialization compile tests, -farray-copy enabled
> +// (c.f. array-copy1.C for compile fail tests)
> +
> +// { dg-do compile { target c++11 } }  c++11 for uniform init
> +// { dg-additional-options "-farray-copy" }
> +
> +typedef int int2[2];
> +int2 u = {11,66};
> +
> +int* p = new int2{u};
> +
> +// Initializations from from rvalue array
> +int2 g = int2{11,66};
> +int2 j ( int2{11,66} );  // takes P0960 paren-init path in c++20
> +int2 k = { int2{11,66} };
> +int2 l { int2{11,66} };
> +
> +// Initializations from from lvalue array
> +int2 gc = u;
> +int2 jc (u);  // takes P0960 paren-init path in c++20
> +int2 kc = {u};
> +int2 lc {u};
> +
> +char str[] = "str";
> +
> +char cpy[] = str;
> +char cpp[] (str);  // takes P0960 paren-init path in c++20
> +char cpu[] = {str};
> +char cpw[] {str};
> +
> +char cp5[5] = str;
> +char cp3[3] = str;
> +// { dg-error "array of type .char .5.. cannot be initialized with an array of type .char .4.." "" { target *-*-* } .-2 }
> +// { dg-error "array of type .char .3.. cannot be initialized with an array of type .char .4.." "" { target *-*-* } .-2 }
> +
> +constexpr char cstr[] = "str";
> +constexpr char ccpy[] = cstr;
> +static_assert(
> +      __builtin_bit_cast(__UINT32_TYPE__, ccpy)
> +   == __builtin_bit_cast(__UINT32_TYPE__, "str")
> +, ""
> +);
> +
> +constexpr __UINT32_TYPE__ ci[] = {11,66};
> +constexpr __UINT32_TYPE__ cc[] = ci;
> +static_assert(
> +      __builtin_bit_cast(__UINT64_TYPE__, ci)
> +   == __builtin_bit_cast(__UINT64_TYPE__, cc)
> +, ""
> +);
> +
> +int2 s[2] = { u };
> +int2 x[2] = { u
> +	    , u };
> +int2 y[2] = { 1,2
> +	    , {u} };
> +int2 z[2] = { {u}
> +	   , 3,{4} };
> +
> +__UINT8_TYPE__ m222[2][2][2] {0,1,2,3,4,5,6,7};
> +__UINT8_TYPE__ c222[2][2][2] { m222[0], m222[1][0], 6,7 };
> +
> +int main() {
> +  if (__builtin_bit_cast(__UINT64_TYPE__, m222)
> +   != __builtin_bit_cast(__UINT64_TYPE__, c222))
> +      __builtin_abort ();
> +}
> diff --git a/gcc/testsuite/g++.dg/init/array-copy3.C b/gcc/testsuite/g++.dg/init/array-copy3.C
> new file mode 100644
> index 00000000000..bec78cb8435
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy3.C
> @@ -0,0 +1,41 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array member initialization nocompile tests -
> +// should give same errors before and after the array-copy patch.
> +
> +// { dg-do compile { target c++11 } } c++11 for uniform init
> +//
> +
> +typedef int int2[2];
> +
> +struct A { int2 a; };
> +
> +A ad { int2{11,66} }; // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +A ai = { int2{11,66} }; // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +
> +struct B { int2 a, b, c, d; };
> +
> +constexpr
> +B b {
> +    {11,66}
> +  , {b.a} // { dg-error "invalid conversion from .const int\\*. to .int. .\\-fpermissive." }
> +  , (b.b) // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +  ,  b.c
> +};
> +
> +struct C {
> +  int2 a, b, c;
> +  C(const int2& r = {11,66})
> +   : a{r} // { dg-error "invalid conversion from .const int\\*. to .int. .\\-fpermissive." }
> +   , b(a) // { dg-error "array used as initializer" }
> +   , c{b} // { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." }
> +     {}
> +};
> +
> +// Not a test; a workaround to construct a single array member from array
> +struct D {
> +  int2 d;
> +  constexpr D(const int2& r = {})
> +   : D{__builtin_bit_cast(D,r)} {}
> +};
> +constexpr D d{{11,66}};
> +constexpr D e{b.a};
> diff --git a/gcc/testsuite/g++.dg/init/array-copy4.C b/gcc/testsuite/g++.dg/init/array-copy4.C
> new file mode 100644
> index 00000000000..8db2852011a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy4.C
> @@ -0,0 +1,42 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array member initialization compile tests -
> +// (c.f. array-copy3.C for compile fail tests)
> +
> +// { dg-do compile { target c++11 } } c++11 for uniform init
> +// { dg-additional-options "-farray-copy" }
> +
> +typedef int int2[2];
> +
> +struct A { int2 a; };
> +
> +A ad { int2{11,66} };
> +A ai = { int2{11,66} };
> +
> +struct B { int2 a, b, c, d; };
> +
> +constexpr
> +B b {
> +    {11,66}
> +  , {b.a}
> +  , (b.b)
> +  ,  b.c
> +};
> +
> +struct C {
> +  int2 a, b, c;
> +  C(const int2& r = {11,66})
> +   : a{r}
> +   , b(a)
> +   , c{b}
> +     {}
> +};
> +
> +template<class T>
> +constexpr int equal(T const& a, T const& b) {
> +  using bytes = unsigned char[sizeof(T)];
> +  return __builtin_memcmp(
> +         __builtin_bit_cast(bytes,a),
> +         __builtin_bit_cast(bytes,b),
> +         sizeof(T)) == 0;
> +}
> +static_assert( equal(b, B{{11,66},{11,66},{11,66},{11,66}}), "");
> diff --git a/gcc/testsuite/g++.dg/init/array-copy5.C b/gcc/testsuite/g++.dg/init/array-copy5.C
> new file mode 100644
> index 00000000000..a3b6c275f2b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy5.C
> @@ -0,0 +1,36 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array member initialization nocompile tests, nsdmi and designated init
> +// should give same errors before and after the array-copy patch.
> +
> +// { dg-do compile { target c++14 } } c++14 for aggregate with member inits
> +// { dg-options "" }
> +//
> +
> +typedef int int2[2];
> +
> +struct A { int2 a = int2{11,66}; }; // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +
> +constexpr
> +A a { .a = int2{11,66} }; // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +A y = { .a{int2{11,66}} }; // { dg-error "taking address of temporary array" }
> +
> +struct B {
> +  int2 a = {11,66};
> +  int2 b = a; // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +  int2 c = {b}; // { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." }
> +  int2 d {c}; // { dg-error "invalid conversion from .int\\*. to .int. .\\-fpermissive." }
> +};
> +
> +constexpr
> +B b {
> +    {11,66}
> +  , {b.a} // { dg-error "invalid conversion from .const int\\*. to .int. .\\-fpermissive." }
> +  , (b.b) // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +  ,  b.c
> +};
> +
> +B bd { .a{11,66}, .b = bd.a, .c {bd.b}, .d = {bd.c} }; // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +
> +struct D : A {
> +  B b {.a{11,66}, .b = b.a, .c {b.b}, .d = {b.c}}; // { dg-error "array must be initialized with a brace-enclosed initializer" }
> +};
> diff --git a/gcc/testsuite/g++.dg/init/array-copy6.C b/gcc/testsuite/g++.dg/init/array-copy6.C
> new file mode 100644
> index 00000000000..09b7478e07f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy6.C
> @@ -0,0 +1,51 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array member initialization compile tests, nsdmi and designated init
> +// (c.f. array-copy5.C for compile fail tests)
> +
> +// { dg-do compile { target c++20 } }
> +//
> +// { dg-additional-options "-farray-copy" }
> +
> +typedef int int2[2];
> +
> +struct A { int2 a = int2{11,66}; };
> +
> +constexpr
> +A a { .a = int2{11,66} };
> +A y = { .a{int2{11,66}} };
> +
> +struct B {
> +  int2 a = {11,66};
> +  int2 b = a;
> +  int2 c = {b};
> +  int2 d {c};
> +};
> +
> +constexpr
> +B b {
> +    {11,66}
> +  , {b.a}
> +  , (b.b)
> +  ,  b.c
> +};
> +
> +B bd { .a{11,66}, .b = bd.a, .c {bd.b}, .d = {bd.c} };
> +
> +struct D : A {
> +  B b {.a{11,66}, .b = b.a, .c {b.b}, .d = {b.c}};
> +};
> +
> +constexpr
> +D d;
> +
> +template<class T>
> +constexpr int equal(T const& a, T const& b) {
> +  using bytes = unsigned char[sizeof(T)];
> +  return __builtin_memcmp(
> +         __builtin_bit_cast(bytes,a),
> +         __builtin_bit_cast(bytes,b),
> +         sizeof(T)) == 0;
> +}
> +static_assert( equal(b, B{}), "");
> +static_assert( equal(d, D{}), "");
> +static_assert( equal(d, D{a,b}), "");
> 



More information about the Gcc-patches mailing list