[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