This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] avoid user-constructible types in reshape_init_array (PR 90938)
- From: Martin Sebor <msebor at gmail dot com>
- To: gcc-patches <gcc-patches at gcc dot gnu dot org>, Jason Merrill <jason at redhat dot com>
- Date: Tue, 11 Feb 2020 13:00:05 -0700
- Subject: [PATCH] avoid user-constructible types in reshape_init_array (PR 90938)
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" } }