aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2019-08-01 23:45:36 +0000
committerMartin Sebor <msebor@redhat.com>2019-08-01 23:45:36 +0000
commit2f65414f43f5fe3fddea13f1c0995b45b01c6f81 (patch)
tree7c4a96774ea3cef422ba9596824ceb090bf0ff21 /gcc
parent6988a2ec402eb06621fe660227ae57838a3662a1 (diff)
PR c++/90947 - Simple lookup table of array of strings is miscompiled
gcc/cp/ChangeLog: PR c++/90947 * decl.c (reshape_init_array_1): Avoid truncating initializer lists containing string literals. gcc/testsuite/ChangeLog: 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. gcc/ChangeLog: PR c++/90947 * tree.c (type_initializer_zero_p): Define. * tree.h (type_initializer_zero_p): New function. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@273989 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog6
-rw-r--r--gcc/cp/ChangeLog6
-rw-r--r--gcc/cp/decl.c5
-rw-r--r--gcc/testsuite/ChangeLog8
-rw-r--r--gcc/testsuite/c-c++-common/array-1.c247
-rw-r--r--gcc/testsuite/g++.dg/abi/mangle73.C96
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/nontype-class23.C102
-rw-r--r--gcc/testsuite/g++.dg/init/array53.C33
-rw-r--r--gcc/tree.c67
-rw-r--r--gcc/tree.h6
10 files changed, 574 insertions, 2 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 1186e07c188..e5c3a59cbb5 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+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-01 Eric Botcazou <ebotcazou@adacore.com>
* cfgrtl.c (relink_block_chain): Add line returns in dump file.
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 9a6c2e30e7f..02992ae9b47 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,9 @@
+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-01 Marek Polacek <polacek@redhat.com>
PR c++/90805 - detect narrowing in case values.
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c8b9e3b8fb9..a1ab5ca8193 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5899,8 +5899,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). */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 0b4334a8cbd..028f978e8b1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+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-01 Uroš Bizjak <ubizjak@gmail.com>
PR target/85693
diff --git a/gcc/testsuite/c-c++-common/array-1.c b/gcc/testsuite/c-c++-common/array-1.c
new file mode 100644
index 00000000000..5de9ade4d43
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/array-1.c
@@ -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
index 00000000000..2a5322a37c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle73.C
@@ -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
index 00000000000..ab9e80fd335
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class23.C
@@ -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
index 00000000000..2bf480561fe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/array53.C
@@ -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" } }
diff --git a/gcc/tree.c b/gcc/tree.c
index 8cf75f22220..efa49e99d65 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -11376,6 +11376,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
diff --git a/gcc/tree.h b/gcc/tree.h
index 4aa2c4a8739..d5fb3b2bd59 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4623,6 +4623,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);