]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
backport: re PR c++/90947 (Simple lookup table of array of strings is miscompiled)
authorMartin Sebor <msebor@redhat.com>
Wed, 14 Aug 2019 20:36:46 +0000 (20:36 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Wed, 14 Aug 2019 20:36:46 +0000 (14:36 -0600)
Backport from mainline

2019-08-01  Martin Sebor  <msebor@redhat.com>

PR c++/90947
* tree.c (type_initializer_zero_p): Define.
* tree.h (type_initializer_zero_p): New function.

2019-08-05  Martin Sebor  <msebor@redhat.com>

* doc/extend.texi (Common Variable Attributes): Document alias
attribute.

2019-08-01  Martin Sebor  <msebor@redhat.com>

PR c++/90947
* decl.c (reshape_init_array_1): Avoid truncating initializer
lists containing string literals.

2019-08-14  Martin Sebor  <msebor@redhat.com>

PR tree-optimization/91294
* gcc.dg/strlenopt-44.c: Adjust tested result.
* gcc.dg/strlenopt-70.c: Avoid exercising unimplemnted optimization.
* gcc.dg/strlenopt-73.c: New test.
* gcc.dg/strlenopt-74.c: New test.
* gcc.dg/strlenopt-75.c: New test.
* gcc.dg/strlenopt-76.c: New test.
* gcc.dg/strlenopt-77.c: New test.

From-SVN: r274494

gcc/ChangeLog
gcc/cp/ChangeLog
gcc/cp/decl.c
gcc/doc/extend.texi
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/array-1.c [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/mangle73.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/nontype-class23.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/array53.C [new file with mode: 0644]
gcc/tree.c
gcc/tree.h

index 2befb71beaa4bcc20adad50483235047bee051e3..24786ec2c8e756951573a188277760879bce2fd0 100644 (file)
@@ -1,3 +1,18 @@
+2019-08-14  Martin Sebor  <msebor@redhat.com>
+
+       Backport from mainline
+
+       2019-08-01  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/90947
+       * tree.c (type_initializer_zero_p): Define.
+       * tree.h (type_initializer_zero_p): New function.
+
+       2019-08-05  Martin Sebor  <msebor@redhat.com>
+
+       * doc/extend.texi (Common Variable Attributes): Document alias
+       attribute.
+
 2019-08-12  Richard Biener  <rguenther@suse.de>
 
        Backport from mainline
index 307f1e6b032db9dfc413b917fe8126d72bb1fbc5..163017f77cddb3b1e4ad02b96f4139ef27cbd9d0 100644 (file)
@@ -1,3 +1,11 @@
+2019-08-14  Martin Sebor  <msebor@redhat.com>
+
+       2019-08-01  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/90947
+       * decl.c (reshape_init_array_1): Avoid truncating initializer
+       lists containing string literals.
+
 2019-08-14  Jonathan Wakely  <jwakely@redhat.com>
 
        PR c++/91436
@@ -45,7 +53,7 @@
 
        Backported from mainline
        2019-08-02  Marek Polacek  <polacek@redhat.com>
-       
+
        PR c++/91230 - wrong error with __PRETTY_FUNCTION__ and generic lambda.
        * pt.c (value_dependent_expression_p): Consider __PRETTY_FUNCTION__
        inside a template function value-dependent.
index b57ded813b6895909d32dbd3977f6f53f2a4d457..83a7baac4701eb9df8401b763b18332d71747748 100644 (file)
@@ -5836,8 +5836,9 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
       /* Pointers initialized to strings must be treated as non-zero
         even if the string is empty.  */
       tree init_type = TREE_TYPE (elt_init);
-      if ((POINTER_TYPE_P (elt_type) != POINTER_TYPE_P (init_type))
-         || !initializer_zerop (elt_init))
+      if ((POINTER_TYPE_P (elt_type) != POINTER_TYPE_P (init_type)))
+       last_nonzero = index;
+      else if (!type_initializer_zero_p (elt_type, elt_init))
        last_nonzero = index;
 
       /* This can happen with an invalid initializer (c++/54501).  */
index 91679e8b9bae9001645c735cf5078cc928f2b245..5e5e8aa87e5805cea0cd2d3e5f83a02a88bb4c69 100644 (file)
@@ -6713,6 +6713,33 @@ attributes.
 The following attributes are supported on most targets.
 
 @table @code
+
+@item alias ("@var{target}")
+@cindex @code{alias} variable attribute
+The @code{alias} variable attribute causes the declaration to be emitted
+as an alias for another symbol known as an @dfn{alias target}.  Except
+for top-level qualifiers the alias target must have the same type as
+the alias.  For instance, the following
+
+@smallexample
+int var_target;
+extern int __attribute__ ((alias ("var_target"))) var_alias;
+@end smallexample
+
+@noindent
+defines @code{var_alias} to be an alias for the @code{var_target} variable.
+
+It is an error if the alias target is not defined in the same translation
+unit as the alias.
+
+Note that in the absence of the attribute GCC assumes that distinct
+declarations with external linkage denote distinct objects.  Using both
+the alias and the alias target to access the same object is undefined
+in a translation unit without a declaration of the alias with the attribute.
+
+This attribute requires assembler and object file support, and may not be
+available on all targets.
+
 @cindex @code{aligned} variable attribute
 @item aligned
 @itemx aligned (@var{alignment})
index 255786692a0888e8d2304ede7496895431431b79..883aa07bbbdc86346f5a2626ebed74c661145e1e 100644 (file)
@@ -1,3 +1,15 @@
+2019-08-14  Martin Sebor  <msebor@redhat.com>
+
+       Backport from mainline
+
+       2019-08-01  Martin Sebor  <msebor@redhat.com>
+
+       PR c++/90947
+       * c-c++-common/array-1.c: New test.
+       * g++.dg/abi/mangle73.C: New test.
+       * g++.dg/cpp2a/nontype-class23.C: New test.
+       * g++.dg/init/array53.C: New test.
+
 2019-08-14  Jonathan Wakely  <jwakely@redhat.com>
 
        PR c++/91436
@@ -10,7 +22,7 @@
 
        PR fortran/87991
        * gfortran.dg/pr87991.f90: New test.
+
 2013-08-13  Thomas Koenig  <tkoenig@gcc.gnu.org>
 
        Backport from trunk
diff --git a/gcc/testsuite/c-c++-common/array-1.c b/gcc/testsuite/c-c++-common/array-1.c
new file mode 100644 (file)
index 0000000..5de9ade
--- /dev/null
@@ -0,0 +1,247 @@
+// PR c++/90947 - Simple lookup table of array of strings is miscompiled
+// { dg-do compile }
+// { dg-options "-O1 -fdump-tree-optimized" }
+
+#define assert(expr) ((expr) ? (void)0 : __builtin_abort ())
+
+void pr90947 (void)
+{
+  int vecsize = 4;
+  int index = 0;
+  static const char *a[4][4] =
+    {
+     { ".x", ".y", ".z", ".w" },
+     { ".xy", ".yz", ".zw", 0 },
+     { ".xyz", ".yzw", 0, 0 },
+     { "", 0, 0, 0 },
+    };
+
+  assert (vecsize >= 1 && vecsize <= 4);
+  assert (index >= 0 && index < 4);
+  assert (a[vecsize - 1][index]);
+}
+
+void f_a1_1 (void)
+{
+  {
+    const char* a[1][1] = { { 0 } };
+    assert (0 == a[0][0]);
+  }
+  {
+    const char* a[1][1] = { { "" } };
+    assert ('\0' == *a[0][0]);
+  }
+}
+
+void f_a2_1 (void)
+{
+  {
+    const char* a[2][1] = { { "" }, { "" } };
+    assert ('\0' == *a[0][0] && '\0' == *a[1][0]);
+  }
+  {
+    const char* a[2][1] = { { 0 }, { "" } };
+    assert (0 == a[0][0] && '\0' == *a[1][0]);
+  }
+  {
+    const char* a[2][1] = { { }, { "" } };
+    assert (0 == a[0][0] && '\0' == *a[1][0]);
+  }
+}
+
+void f_a2_2 (void)
+{
+  {
+    const char* a[2][2] = { { "", "" }, { "", "" } };
+    assert ('\0' == *a[0][0] && '\0' == *a[0][1]);
+    assert ('\0' == *a[1][0] && '\0' == *a[1][1]);
+  }
+  {
+    const char* a[2][2] = { { "", "" }, { "", 0 } };
+    assert ('\0' == *a[0][0] && '\0' == *a[0][1]);
+    assert ('\0' == *a[1][0] && 0 == a[1][1]);
+  }
+  {
+    const char* a[2][2] = { { "", "" }, { "" } };
+    assert ('\0' == *a[0][0] && '\0' == *a[0][1]);
+    assert ('\0' == *a[1][0] && 0 == a[1][1]);
+  }
+  {
+    const char* a[2][2] = { { "", "" }, { 0, "" } };
+    assert ('\0' == *a[0][0] && '\0' == *a[0][1]);
+    assert (0 == a[1][0] && '\0' == *a[1][1]);
+  }
+  {
+    const char* a[2][2] = { { "", 0 }, { 0, "" } };
+    assert ('\0' == *a[0][0] && 0 == a[0][1]);
+    assert (0 == a[1][0] && '\0' == *a[1][1]);
+  }
+  {
+    const char* a[2][2] = { { 0, 0 }, { 0, "" } };
+    assert (0 == a[0][0] && 0 == a[0][1]);
+    assert (0 == a[1][0] && '\0' == *a[1][1]);
+  }
+  {
+    const char* a[2][2] = { { 0 }, { 0, "" } };
+    assert (0 == a[0][0] && 0 == a[0][1]);
+    assert (0 == a[1][0] && '\0' == *a[1][1]);
+  }
+  {
+    const char* a[2][2] = { { }, { 0, "" } };
+    assert (0 == a[0][0] && 0 == a[0][1]);
+    assert (0 == a[1][0] && '\0' == *a[1][1]);
+  }
+}
+
+void f_a2_2_2 (void)
+{
+  {
+    const char* a[2][2][2] =
+      { { { "", "" }, { "", "" } }, { { "", "" }, { "", "" } } };
+
+    assert ('\0' == *a[0][0][0] && '\0' == *a[0][0][1]);
+    assert ('\0' == *a[0][1][0] && '\0' == *a[0][1][1]);
+    assert ('\0' == *a[1][0][0] && '\0' == *a[1][0][1]);
+    assert ('\0' == *a[1][1][0] && '\0' == *a[1][1][1]);
+  }
+
+  {
+    const char* a[2][2][2] =
+      { { { "", "" }, { "", "" } }, { { "", "" }, { 0, "" } } };
+
+    assert ('\0' == *a[0][0][0] && '\0' == *a[0][0][1]);
+    assert ('\0' == *a[0][1][0] && '\0' == *a[0][1][1]);
+    assert ('\0' == *a[1][0][0] && '\0' == *a[1][0][1]);
+    assert (0 == a[1][1][0] && '\0' == *a[1][1][1]);
+  }
+
+  {
+    const char* a[2][2][2] =
+      { { { "", "" }, { "", "" } }, { { 0, 0 }, { 0, "" } } };
+
+    assert ('\0' == *a[0][0][0] && '\0' == *a[0][0][1]);
+    assert ('\0' == *a[0][1][0] && '\0' == *a[0][1][1]);
+    assert (0 == a[1][0][0] && 0 == a[1][0][1]);
+    assert (0 == a[1][1][0] && '\0' == *a[1][1][1]);
+  }
+
+  {
+    const char* a[2][2][2] =
+      { { { "", "" }, { 0, 0 } }, { { 0, 0 }, { 0, "" } } };
+
+    assert ('\0' == *a[0][0][0] && '\0' == *a[0][0][1]);
+    assert (0 == a[0][1][0] && 0 == a[0][1][1]);
+    assert (0 == a[1][0][0] && 0 == a[1][0][1]);
+    assert (0 == a[1][1][0] && '\0' == *a[1][1][1]);
+  }
+
+  {
+    const char* a[2][2][2] =
+      { { { 0, 0 }, { 0, 0 } }, { { 0, 0 }, { 0, "" } } };
+
+    assert (0 == a[0][0][0] && 0 == a[0][0][1]);
+    assert (0 == a[0][1][0] && 0 == a[0][1][1]);
+    assert (0 == a[1][0][0] && 0 == a[1][0][1]);
+    assert (0 == a[1][1][0] && '\0' == *a[1][1][1]);
+  }
+
+  {
+    const char* a[2][2][2] =
+      { { { }, { } }, { { }, { 0, "" } } };
+
+    assert (0 == a[0][0][0] && 0 == a[0][0][1]);
+    assert (0 == a[0][1][0] && 0 == a[0][1][1]);
+    assert (0 == a[1][0][0] && 0 == a[1][0][1]);
+    assert (0 == a[1][1][0] && '\0' == *a[1][1][1]);
+  }
+}
+
+void f_sa2_2_2 (void)
+{
+  struct S { const char a[2], *s, c; };
+
+  {
+    const struct S a[2][2][2] = {
+      { },
+      {
+        { { }, { "", "" } },
+        { }
+      }
+    };
+
+    assert ('\0' == *a[0][0][0].a && 0 == a[0][0][0].s && 0 == a[0][0][0].c);
+    assert ('\0' == *a[0][0][1].a && 0 == a[0][0][1].s && 0 == a[0][0][1].c);
+    assert ('\0' == *a[0][1][0].a && 0 == a[0][1][0].s && 0 == a[0][1][0].c);
+    assert ('\0' == *a[0][1][1].a && 0 == a[0][1][1].s && 0 == a[0][1][1].c);
+
+    assert ('\0' == *a[1][0][0].a && 0 == a[1][0][0].s && 0 == a[1][0][0].c);
+    assert ('\0' == *a[1][0][1].a && '\0' == *a[1][0][1].s && 0 == a[1][0][1].c);
+    assert ('\0' == *a[1][1][0].a && 0 == a[1][1][0].s && 0 == a[1][1][0].c);
+    assert ('\0' == *a[1][1][1].a && 0 == a[1][1][1].s && 0 == a[1][1][1].c);
+  }
+
+  {
+    const struct S a[2][2][2] = {
+      { },
+      {
+        { { } },
+        { { "", "" } }
+      }
+    };
+
+    assert ('\0' == *a[0][0][0].a && 0 == a[0][0][0].s);
+    assert ('\0' == *a[0][0][1].a && 0 == a[0][0][1].s);
+    assert ('\0' == *a[0][1][0].a && 0 == a[0][1][0].s);
+    assert ('\0' == *a[0][1][1].a && 0 == a[0][1][1].s);
+
+    assert ('\0' == *a[1][0][0].a && 0 == a[1][0][0].s);
+    assert ('\0' == *a[1][0][1].a && 0 == a[1][0][1].s);
+    assert ('\0' == *a[1][1][0].a && '\0' == *a[1][1][0].s);
+    assert ('\0' == *a[1][1][1].a && 0 == a[1][1][1].s);
+  }
+
+  {
+    const struct S a[2][2][2] = {
+      { },
+      {
+        { { }, { } },
+        { { }, { "", "", 0 } }
+      }
+    };
+
+    assert ('\0' == *a[0][0][0].a && 0 == a[0][0][0].s);
+    assert ('\0' == *a[0][0][1].a && 0 == a[0][0][1].s);
+    assert ('\0' == *a[0][1][0].a && 0 == a[0][1][0].s);
+    assert ('\0' == *a[0][1][1].a && 0 == a[0][1][1].s);
+
+    assert ('\0' == *a[1][0][0].a && 0 == a[1][0][0].s);
+    assert ('\0' == *a[1][0][1].a && 0 == a[1][0][1].s);
+    assert ('\0' == *a[1][1][0].a && 0 == a[1][1][0].s);
+    assert ('\0' == *a[1][1][1].a && '\0' == *a[1][1][1].s);
+  }
+
+  {
+    const struct S a[2][2][2] = {
+      {
+       { { { 0 }, 0, 0 }, { { 0 } , 0, 0 } },
+       { { { 0 }, 0, 0 }, { { 0 } , 0, 0 } },
+      },
+      {
+       { { { 0 }, 0, 0 }, { { 0 } , 0, 0 } },
+       { { }, { "", "", 0 } }
+      }
+    };
+
+    assert ('\0' == *a[0][0][0].a && 0 == a[0][0][0].s);
+    assert ('\0' == *a[0][0][1].a && 0 == a[0][0][1].s);
+    assert ('\0' == *a[0][1][0].a && 0 == a[0][1][0].s);
+    assert ('\0' == *a[0][1][1].a && 0 == a[0][1][1].s);
+
+    assert ('\0' == *a[1][0][0].a && 0 == a[1][0][0].s);
+    assert ('\0' == *a[1][0][1].a && 0 == a[1][0][1].s);
+    assert ('\0' == *a[1][1][0].a && 0 == a[1][1][0].s);
+    assert ('\0' == *a[1][1][1].a && '\0' == *a[1][1][1].s);
+  }
+}
+
+// { dg-final { scan-tree-dump-not "abort" "optimized" } }
diff --git a/gcc/testsuite/g++.dg/abi/mangle73.C b/gcc/testsuite/g++.dg/abi/mangle73.C
new file mode 100644 (file)
index 0000000..2a5322a
--- /dev/null
@@ -0,0 +1,96 @@
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  char a[2][2];
+};
+
+template <A> struct B { };
+
+typedef B<A{ { { 0, 0 }, { 0, 0 } } }> AZZZZ;
+typedef B<A{ { { 0, 0 }, { 0 } } }>    AZZZ_;
+typedef B<A{ { { 0, 0 } } }>           AZZ__;
+typedef B<A{ { { 0 } } }>              AZ___;
+typedef B<A{ { { } } }>                A____;
+
+typedef B<A{ { { "" }, { "" } } }>     AS_S_;
+typedef B<A{ { { "" }, { 0, 0 } } }>   AS_ZZ;
+typedef B<A{ { { "" }, { 0 } } }>      AS_Z_;
+typedef B<A{ { { "" } } }>             AS___;
+
+
+// Verify that the types mangle the same.
+void a_zzzz (AZZZZ) { }
+// { dg-final { scan-assembler "_Z6a_zzzz1BIXtl1AEEE" } }
+
+void a_zzz_ (AZZZ_) { }
+// { dg-final { scan-assembler "_Z6a_zzz_1BIXtl1AEEE" } }
+
+void a_zz__ (AZZ__) { }
+// { dg-final { scan-assembler "_Z6a_zz__1BIXtl1AEEE" } }
+
+void a_z___ (AZ___) { }
+// { dg-final { scan-assembler "_Z6a_z___1BIXtl1AEEE" } }
+
+void a_____ (A____) { }
+// { dg-final { scan-assembler "_Z6a_____1BIXtl1AEEE" } }
+
+void a_s_s_ (AS_S_) { }
+// { dg-final { scan-assembler "_Z6a_s_s_1BIXtl1AEEE" } }
+
+void a_s_zz (AS_ZZ) { }
+// { dg-final { scan-assembler "_Z6a_s_zz1BIXtl1AEEE" } }
+
+void a_s_z_ (AS_Z_) { }
+// { dg-final { scan-assembler "_Z6a_s_z_1BIXtl1AEEE" } }
+
+void a_s___ (AS___) { }
+// { dg-final { scan-assembler "_Z6a_s___1BIXtl1AEEE" } }
+
+
+struct C
+{
+  struct { const char a[2][2], *p; } a[2];
+};
+
+template <C> struct D { };
+
+typedef D<C{{{{{ 0, 0 }, { 0, 0 }}, 0 }, {{{ 0, 0 }, { 0, 0 }}, 0 }}}> DZZZZZZZZZZ;
+typedef D<C{{{{{ 0, 0 }, { 0, 0 }}, 0 }, {{{ 0, 0 }, { 0, 0 }}}}}> DZZZZZZZZZ_;
+typedef D<C{{{{{ 0, 0 }, { 0, 0 }}, 0 }, {{{ 0, 0 }, { 0 }}}}}>    DZZZZZZZZ__;
+typedef D<C{{{{{ 0, 0 }, { 0, 0 }}, 0 }, {{{ 0, 0 } }}}}>          DZZZZZZZ___;
+typedef D<C{{{{{ 0, 0 }, { 0, 0 }}, 0 }, {{{ 0 } }}}}>             DZZZZZZ____;
+typedef D<C{{{{{ 0, 0 }, { 0, 0 }}, 0 }}}>                         DZZZZZ_____;
+typedef D<C{{{{{ 0, 0 }, { 0, 0 }}}}}>                             DZZZZ______;
+typedef D<C{{{{{ 0, 0 }, { 0 }}}}}>                                DZZZ_______;
+typedef D<C{{{{{ 0, 0 }}}}}>                                       DZZ________;
+typedef D<C{{{{{ 0 }}}}}>                                          DZ_________;
+typedef D<C{ }>                                                    D__________;
+
+typedef D<C{{{{{ "" }, { "" }}, 0 }, {{{ "" }, { "" }}, 0 }}}>     DS_S_ZS_S_Z;
+
+void d_zzzzzzzzzz (DZZZZZZZZZZ) { }
+// { dg-final { scan-assembler "_Z12d_zzzzzzzzzz1DIXtl1CEEE" } }
+void d_zzzzzzzzz_ (DZZZZZZZZZ_) { }
+// { dg-final { scan-assembler "_Z12d_zzzzzzzzz_1DIXtl1CEEE" } }
+void d_zzzzzzzz__ (DZZZZZZZZ__) { }
+// { dg-final { scan-assembler "_Z12d_zzzzzzzz__1DIXtl1CEEE" } }
+void d_zzzzzzz___ (DZZZZZZZ___) { }
+// { dg-final { scan-assembler "_Z12d_zzzzzzz___1DIXtl1CEEE" } }
+void d_zzzzzz____ (DZZZZZZ____) { }
+// { dg-final { scan-assembler "_Z12d_zzzzzz____1DIXtl1CEEE" } }
+void d_zzzzz_____ (DZZZZZ_____) { }
+// { dg-final { scan-assembler "_Z12d_zzzzz_____1DIXtl1CEEE" } }
+void d_zzzz______ (DZZZZ______) { }
+// { dg-final { scan-assembler "_Z12d_zzzz______1DIXtl1CEEE" } }
+void d_zzz_______ (DZZZ_______) { }
+// { dg-final { scan-assembler "_Z12d_zzz_______1DIXtl1CEEE" } }
+void d_zz________ (DZZ________) { }
+// { dg-final { scan-assembler "_Z12d_zz________1DIXtl1CEEE" } }
+void d_z_________ (DZ_________) { }
+// { dg-final { scan-assembler "_Z12d_z_________1DIXtl1CEEE" } }
+void d___________ (D__________) { }
+// { dg-final { scan-assembler "_Z12d___________1DIXtl1CEEE" } }
+
+void d_s_s_zs_s_z (DS_S_ZS_S_Z) { }
+// { dg-final { scan-assembler "_Z12d_s_s_zs_s_z1DIXtl1CEEE" } }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class23.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class23.C
new file mode 100644 (file)
index 0000000..ab9e80f
--- /dev/null
@@ -0,0 +1,102 @@
+// PR c++/90947 - Simple lookup table of array of strings is miscompiled
+// Test to verify that the same specializations on non-type template
+// parameters of class types are in fact treated as the same.  Unlike
+// nontype-class15.C which involves only one-dimensional arrays this
+// test involves arrays of arrays and arrays of structs.
+// { dg-do compile { target c++2a } }
+
+struct AA3
+{
+  const char a[2][2][2];
+};
+
+template <AA3> struct BAA3 { };
+
+// Redeclare the same variable using different initialization forms
+// of the same constant to verify that they are in fact all recognized
+// as the same.
+extern BAA3<AA3{{{ "", "" }, { "", "" }}}>       baa3;
+extern BAA3<AA3{{{ "", "" }, { "", { 0, 0 } }}}> baa3;
+extern BAA3<AA3{{{ "", "" }, { "", { 0 } }}}>    baa3;
+extern BAA3<AA3{{{ "", "" }, { "", {} }}}>       baa3;
+extern BAA3<AA3{{{ "", "" }, { "" }}}>           baa3;
+extern BAA3<AA3{{{ "", "" }, { { 0, 0 } }}}>     baa3;
+extern BAA3<AA3{{{ "", "" }, { { 0 } }}}>        baa3;
+extern BAA3<AA3{{{ "", "" }, { {} }}}>           baa3;
+extern BAA3<AA3{{{ "", "" }, { }}}>              baa3;
+extern BAA3<AA3{{{ "", "" }}}>                   baa3;
+extern BAA3<AA3{{{ "", { 0, 0 } }}}>             baa3;
+extern BAA3<AA3{{{ "", { 0 } }}}>                baa3;
+extern BAA3<AA3{{{ "", {} }}}>                   baa3;
+extern BAA3<AA3{{{ "" }}}>                       baa3;
+extern BAA3<AA3{{{ { 0, 0 } }}}>                 baa3;
+extern BAA3<AA3{{{ { 0 } }}}>                    baa3;
+extern BAA3<AA3{{{ {} }}}>                       baa3;
+extern BAA3<AA3{{{ }}}>                          baa3;
+extern BAA3<AA3{{ }}>                            baa3;
+extern BAA3<AA3{ }>                              baa3;
+
+extern BAA3<AA3{{{ "", "" }, { "", "1" }}}>        baa3_1;
+extern BAA3<AA3{{{ "", "" }, { "", { '1', 0 } }}}> baa3_1;
+extern BAA3<AA3{{{ "", "" }, { "", { '1' } }}}>    baa3_1;
+
+extern BAA3<AA3{{{ "", "" }, { "1", {} }}}>        baa3_2;
+extern BAA3<AA3{{{ "", "" }, { "1" }}}>            baa3_2;
+extern BAA3<AA3{{{ "", "" }, { { '1', 0 } }}}>     baa3_2;
+extern BAA3<AA3{{{ "", "" }, { { '1' } }}}>        baa3_2;
+
+extern BAA3<AA3{{{ "", "1" }}}>                    baa3_3;
+extern BAA3<AA3{{{ "", { '1', 0 } }}}>             baa3_3;
+extern BAA3<AA3{{{ "", { '1' } }}}>                baa3_3;
+
+extern BAA3<AA3{{{ "1" }}}>                        baa3_4;
+extern BAA3<AA3{{{ { '1', 0 } }}}>                 baa3_4;
+extern BAA3<AA3{{{ { '1' } }}}>                    baa3_4;
+
+struct AS2
+{
+  struct S { const char a[2], *p; } a[2];
+};
+
+template <AS2> struct BAS2 { };
+
+extern BAS2<AS2{{{ "", 0 }, { "", 0 }}}> bas2;
+extern BAS2<AS2{{{ "", 0 }, { {}, 0 }}}> bas2;
+extern BAS2<AS2{{{ "", 0 }, { "" }}}>    bas2;
+extern BAS2<AS2{{{ "", 0 }, { {} }}}>    bas2;
+extern BAS2<AS2{{{ "", 0 }, { }}}>       bas2;
+extern BAS2<AS2{{{ "", 0 }}}>            bas2;
+extern BAS2<AS2{{{ {}, 0 }}}>            bas2;
+extern BAS2<AS2{{{ "" }}}>               bas2;
+extern BAS2<AS2{{{ {} }}}>               bas2;
+extern BAS2<AS2{{{ }}}>                  bas2;
+extern BAS2<AS2{{ }}>                    bas2;
+extern BAS2<AS2{ }>                      bas2;
+
+struct AS2_2
+{
+  struct S { const char a[2], *p; } a[2][2];
+};
+
+template <AS2_2> struct BAS2_2 { };
+
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { "", 0 }, { "", 0 }}}}> b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { "", 0 }, { "" }}}}>    b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { "", 0 }, { {} }}}}>    b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { "", 0 }, { }}}}>       b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { "", 0 } }}}>           b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { "" } }}}>              b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { {} } }}}>              b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { { }}}}>                  b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 } }, { }}}>                     b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "", 0 }}}}>                           b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { "" }}}}>                              b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { {} }}}}>                              b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }, { }}}}>                                 b2_2;
+extern BAS2_2<AS2_2{{{{ "", 0 }}}}>                                      b2_2;
+extern BAS2_2<AS2_2{{{{ "" }}}}>                                         b2_2;
+extern BAS2_2<AS2_2{{{{ {} }}}}>                                         b2_2;
+extern BAS2_2<AS2_2{{{{ }}}}>                                            b2_2;
+extern BAS2_2<AS2_2{{{ }}}>                                              b2_2;
+extern BAS2_2<AS2_2{{ }}>                                                b2_2;
+extern BAS2_2<AS2_2{ }>                                                  b2_2;
diff --git a/gcc/testsuite/g++.dg/init/array53.C b/gcc/testsuite/g++.dg/init/array53.C
new file mode 100644 (file)
index 0000000..2bf4805
--- /dev/null
@@ -0,0 +1,33 @@
+// PR c++/90947 - Simple lookup table of array of strings is miscompiled
+// Verify that initializers for arrays of elements of a class type with
+// "unusual" data members are correctly recognized as non-zero.
+// { dg-do compile }
+// { dg-options "-O1 -fdump-tree-optimized" }
+
+struct S
+{
+  const char *p;
+  static int i;
+  enum { e };
+  typedef int X;
+  int: 1, b:1;
+  union {
+    int c;
+  };
+  const char *q;
+};
+
+void f (void)
+{
+  const struct S a[2] =
+    {
+     { /* .p = */ "", /* .b = */ 0, /* .c = */ 0, /* .q = */ "" },
+     { /* .p = */ "", /* .b = */ 0, /* .c = */ 0, /* .q = */ "" }
+    };
+
+  if (!a[0].p || *a[0].p || !a[0].q || *a[0].q
+      || !a[1].p || *a[1].p || !a[1].q || *a[1].q)
+    __builtin_abort ();
+}
+
+// { dg-final { scan-tree-dump-not "abort" "optimized" } }
index b82d73c8517dca1153786aaa27d3fbf76ad31f37..337b95a85e2ad77e64f29f75f280073b74649183 100644 (file)
@@ -11356,6 +11356,73 @@ initializer_each_zero_or_onep (const_tree expr)
     }
 }
 
+/* Given an initializer INIT for a TYPE, return true if INIT is zero
+   so that it can be replaced by value initialization.  This function
+   distinguishes betwen empty strings as initializers for arrays and
+   for pointers (which make it return false).  */
+
+bool
+type_initializer_zero_p (tree type, tree init)
+{
+  if (type  == error_mark_node || init == error_mark_node)
+    return false;
+
+  STRIP_NOPS (init);
+
+  if (POINTER_TYPE_P (type))
+    return TREE_CODE (init) != STRING_CST && initializer_zerop (init);
+
+  if (TREE_CODE (init) != CONSTRUCTOR)
+    return initializer_zerop (init);
+
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      tree elt_type = TREE_TYPE (type);
+      elt_type = TYPE_MAIN_VARIANT (elt_type);
+      if (elt_type == char_type_node)
+       return initializer_zerop (init);
+
+      tree elt_init;
+      unsigned HOST_WIDE_INT i;
+      FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (init), i, elt_init)
+       if (!type_initializer_zero_p (elt_type, elt_init))
+         return false;
+      return true;
+    }
+
+  if (TREE_CODE (type) != RECORD_TYPE)
+    return initializer_zerop (init);
+
+  tree fld = TYPE_FIELDS (type);
+
+  tree fld_init;
+  unsigned HOST_WIDE_INT i;
+  FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (init), i, fld_init)
+    {
+      /* Advance to the next member, skipping over everything that
+        canot be initialized (including unnamed bit-fields).  */
+      while (TREE_CODE (fld) != FIELD_DECL
+            || DECL_ARTIFICIAL (fld)
+            || (DECL_BIT_FIELD (fld) && !DECL_NAME (fld)))
+       {
+         fld = DECL_CHAIN (fld);
+         if (!fld)
+           return true;
+         continue;
+       }
+
+      tree fldtype = TREE_TYPE (fld);
+      if (!type_initializer_zero_p (fldtype, fld_init))
+       return false;
+
+      fld = DECL_CHAIN (fld);
+      if (!fld)
+       break;
+    }
+
+  return true;
+}
+
 /* Check if vector VEC consists of all the equal elements and
    that the number of elements corresponds to the type of VEC.
    The function returns first element of the vector
index d650511c66f8f9e83645900f11bddc8d9bd6aeb4..331edfcb1c305729f7990d668bf7c2faf0412dd4 100644 (file)
@@ -4525,6 +4525,12 @@ extern tree first_field (const_tree);
 extern bool initializer_zerop (const_tree, bool * = NULL);
 extern bool initializer_each_zero_or_onep (const_tree);
 
+/* Analogous to initializer_zerop but also examines the type for
+   which the initializer is being used.  Unlike initializer_zerop,
+   considers empty strings to be zero initializers for arrays and
+   non-zero for pointers.  */
+extern bool type_initializer_zero_p (tree, tree);
+
 extern wide_int vector_cst_int_elt (const_tree, unsigned int);
 extern tree vector_cst_elt (const_tree, unsigned int);