This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] avoid user-constructible types in reshape_init_array (PR 90938)


On 2/11/20 5:28 PM, Jason Merrill wrote:
On 2/11/20 9:00 PM, Martin Sebor wrote:
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,

Hmm, a type with only a deleted default constructor is not trivial, that should have been OK already.

For Marek's test case:
  struct A { A () == delete; A (int) = delete; };

trivial_type_p() returns true (as does __is_trivial (A) in both GCC
and Clang).

[class.prop] says that

  A trivial class is a class that is trivially copyable and has one
  or more default constructors (10.3.4.1), all of which are either
  trivial or deleted and at least one of which is not deleted.

That sounds like A above is not trivial because it doesn't have
at least one default ctor that's not deleted, but both GCC and
Clang say it is.  What am I missing?  Is there some other default
constructor hiding in there that I don't know about?

and others are eliminated in favor of the defaulted
ctor instead of invoking a user-defined conversion ctor, leading to
wrong code.

This seems like a bug in type_initializer_zero_p; it shouldn't treat 0 as a zero initializer for any class.

That does fix it, and it seems like the right solution to me as well.
Thanks for the suggestion.  I'm a little unsure about the condition
I put in place though.

Attached is an updated patch rested on x86_64-linux.

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.
	* tree.c (type_initializer_zero_p): Fail for structs initialized
	with non-structs.

gcc/testsuite/ChangeLog:

	PR c++/90938
	* g++.dg/init/array55.C: New test.
	* g++.dg/init/array56.C: New test.
	* g++.dg/cpp2a/nontype-class33.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/cp/tree.c b/gcc/cp/tree.c
index eb540f851ee..d79d7b1ddd1 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -5708,7 +5708,15 @@ type_initializer_zero_p (tree type, tree init)
     return TREE_CODE (init) != STRING_CST && initializer_zerop (init);
 
   if (TREE_CODE (init) != CONSTRUCTOR)
-    return initializer_zerop (init);
+    {
+      /* A class can only be initialized by a non-class type if it has
+	 a ctor that converts from that type.  Such classes are excluded
+	 since their semantics are unknown.  */
+      if (RECORD_OR_UNION_TYPE_P (type)
+	  && !RECORD_OR_UNION_TYPE_P (TREE_TYPE (init)))
+	return false;
+      return initializer_zerop (init);
+    }
 
   if (TREE_CODE (type) == ARRAY_TYPE)
     {
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class33.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class33.C
new file mode 100644
index 00000000000..8407f4b4c8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class33.C
@@ -0,0 +1,35 @@
+// PR c++/90938
+// { dg-do compile }
+
+struct A { int i; };
+struct B { A a[2]; };
+
+static const constexpr A a0 = { 0 };
+static const constexpr A a_ = { };
+
+template <B> struct X { };
+
+typedef X<B{ }>             XB;
+typedef X<B{{A{ }}}>        XB;
+typedef X<B{{A{ 0 }}}>      XB;
+typedef X<B{{a_}}>          XB;
+typedef X<B{{a0}}>          XB;
+typedef X<B{{a_, A{ }}}>    XB;
+typedef X<B{{a_, A{ 0 }}}>  XB;
+typedef X<B{{a_, a_}}>      XB;
+typedef X<B{{a_, a0}}>      XB;
+
+
+struct C { constexpr C () = default; };
+struct D { C c[2]; };
+
+static const constexpr C c_ = { };
+
+template <D> struct Y { };
+
+typedef Y<D{ }>             YD;
+typedef Y<D{C { }}>         YD;
+typedef Y<D{{c_}}>          YD;
+typedef Y<D{C{ }, C{ }}>    YD;
+typedef Y<D{C{ }, c_}>      YD;
+typedef Y<D{{c_, c_}}>      YD;
diff --git a/gcc/testsuite/g++.dg/init/array55.C b/gcc/testsuite/g++.dg/init/array55.C
new file mode 100644
index 00000000000..70fb183b897
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/array55.C
@@ -0,0 +1,27 @@
+/* PR c++/90938 - Initializing array with {1} works, but not {0}
+   { dg-do compile { target c++11 } } */
+
+struct A
+{
+  A () = delete;
+  A (int) = delete;
+};
+
+A a_[] = { 0 };            // { dg-error "use of deleted function 'A::A\\\(int\\\)'" }
+
+A a1[1] = { 0 };           // { dg-error "use of deleted function 'A::A\\\(int\\\)'" }
+
+
+struct B
+{
+  B () = delete;
+  B (int) = delete;
+  B (long);
+};
+
+B b_[] = { 0 };            // { dg-error "use of deleted function 'B::B\\\(int\\\)'" }
+
+B b1[1] = { 0 };           // { dg-error "use of deleted function 'B::B\\\(int\\\)'" }
+
+B b2[] = { 0L };
+B b3[1] = { 0L };
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" } }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]