r270155, committed in GCC 9, introduced a transformation that strips redundant trailing zero initializers from array initializer lists in order to support string literals as template arguments.
The transformation neglected to consider the case of array elements of trivial class types with user-defined conversion ctors and either defaulted or deleted default ctors. (It didn't occur to me that those qualify as trivial types despite the user-defined ctors.) As a result, some valid initialization expressions are rejected when the explicit zero-initializers are dropped in favor of the (deleted) default ctor, and others are eliminated in favor of the defaulted ctor instead of invoking a user-defined conversion ctor, leading to wrong code. The attached patch fixes that but avoiding this transformation for such types. Tested on x86_64-linux. I'd like to commit the patch to both trunk and to GCC 9 (with testsuite adjustments if necessary). Martin
PR c++/90938 - Initializing array with {1} works but not {0} gcc/cp/ChangeLog: PR c++/90938 * decl.c (reshape_init_array_1): Avoid types with non-trivial user-defined ctors. gcc/testsuite/ChangeLog: PR c++/90938 * g++.dg/init/array55.C: New test. * g++.dg/init/array56.C: New test. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 31a556a0a1f..60731cb3f9d 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6051,11 +6051,14 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d, break; } - if (sized_array_p && trivial_type_p (elt_type)) + if (sized_array_p + && trivial_type_p (elt_type) + && !TYPE_NEEDS_CONSTRUCTING (elt_type)) { /* Strip trailing zero-initializers from an array of a trivial - type of known size. They are redundant and get in the way - of telling them apart from those with implicit zero value. */ + type with no user-defined ctor of known size. They are + redundant and get in the way of telling them apart from those + with implicit zero value. */ unsigned HOST_WIDE_INT nelts = CONSTRUCTOR_NELTS (new_init); if (last_nonzero > nelts) nelts = 0; diff --git a/gcc/testsuite/g++.dg/init/array55.C b/gcc/testsuite/g++.dg/init/array55.C new file mode 100644 index 00000000000..00a4cf6c616 --- /dev/null +++ b/gcc/testsuite/g++.dg/init/array55.C @@ -0,0 +1,12 @@ +/* PR c++/90938 - Initializing array with {1} works, but not {0} + { dg-do compile { target c++11 } } */ + +struct X +{ + X () = delete; + X (int) { } +}; + +X x0[1] { 1 }; +X x1[1] { 0 }; +X x2[1] { }; // { dg-error "use of deleted function" } diff --git a/gcc/testsuite/g++.dg/init/array56.C b/gcc/testsuite/g++.dg/init/array56.C new file mode 100644 index 00000000000..63e16663ec1 --- /dev/null +++ b/gcc/testsuite/g++.dg/init/array56.C @@ -0,0 +1,107 @@ +/* PR c++/90938 - Initializing array with {1} works, but not {0} + { dg-do compile { target c++11 } } + { dg-options "-O -Wall -fdump-tree-optimized" } */ + +#define assert(e) \ + ((e) ? (void)0 \ + : (__builtin_printf ("assertion failed on line %i: %s\n", \ + __LINE__, #e), \ + __builtin_abort ())) + +namespace A { + +struct X +{ + X () = default; + X (int n) : n (n + 1) { } + int n; +}; + +static_assert (__is_trivial (X), "X is trivial"); + +static void test () +{ + { + X x[] { 0 }; + assert (1 == x->n); + } + + { + X x[1] { 0 }; + assert (1 == x->n); // fails + } + + { + X x[2] { 0 }; + assert (1 == x[0].n && 0 == x[1].n); // fails + } + + { + X x[] { 1, 0 }; + assert (2 == x[0].n && 1 == x[1].n); // passes + } + + { + X x[2] { 1, 0 }; + assert (2 == x[0].n && 1 == x[1].n); // fails + } +} + +} + +namespace B { + +struct X +{ + X () = default; + X (int *p) : p (p ? p : new int (1)) { } + int *p; +}; + +static_assert (__is_trivial (X), "X is trivial"); + +static void test () +{ + X x[1] { nullptr }; + assert (*x->p == 1); // fails + + X y[1] { 0 }; + assert (*y->p == 1); // fails +} + +} + +namespace C { + +static const char *vector_swizzle (int vecsize, int index) +{ + static const char *swizzle[4][4] = + { + { ".x", ".y", ".z", ".w" }, + { ".xy", ".yz", ".zw", nullptr }, + { ".xyz", ".yzw", nullptr, nullptr }, + { "", nullptr, nullptr, nullptr }, + }; + + assert (vecsize >= 1 && vecsize <= 4); + assert (index >= 0 && index < 4); + assert (swizzle[vecsize - 1][index]); + + return swizzle[vecsize - 1][index]; +} + +static void test () +{ + assert (!*vector_swizzle(4, 0)); +} + +} + +int main () +{ + A::test (); + B::test (); + C::test (); +} + +// { dg-final { scan-tree-dump-not "abort" "optimized" } }