On 4/1/19 11:27 AM, Jason Merrill wrote:
On 3/31/19 10:17 PM, Martin Sebor wrote:
To fix PR 89833, a P1 regression, the attached patch tries to
handle string literals as C++ 2a non-type template arguments
by treating them the same as brace enclosed initializer lists
(where the latter are handled correctly). The solution makes
sure equivalent forms of array initializers such as for char[5]:
"\0\1\2"
"\0\1\2\0"
{ 0, 1, 2 }
{ 0, 1, 2, 0 }
{ 0, 1, 2, 0, 0 }
are treated as the same, both for overloading and mangling.
Ditto for the following equivalent forms:
""
"\0"
"\0\0\0\0"
{ }
{ 0 }
{ 0, 0, 0, 0, 0 }
and for these of struct { char a[5], b[5], c[5]; }:
{ {0,0,0,0,0}, {0,0,0,0}, {0,0,0} }
{ { 0 }, { } }
{ "" }
Since this is not handled correctly by the current code (see PR
89876 for a test case) the patch also fixes that.
I'm not at all confident the handling of classes with user-defined
constexpr ctors is 100% correct. (I use triviality to constrain
the solution for strings but that was more of an off-the-cuff guess
than a carefully considered decision).
You could use TYPE_HAS_TRIVIAL_DFLT since we don't care about the
triviality of other operations.
Done (I think).
I wouldn't worry about trying to omit user-defined constexpr ctors.
The g++.dg/abi/mangle71.C
test is all I've got in terms of verifying it works correctly.
I'm quite sure the C++ 2a testing could stand to be beefed up.
The patch passes x86_64-linux bootstrap and regression tests.
There are a few failures in check-c++-all tests that don't look
related to the changes (I see them with an unpatched GCC as well):
g++.dg/spellcheck-macro-ordering-2.C
g++.dg/cpp0x/auto52.C
g++.dg/cpp1y/auto-neg1.C
g++.dg/other/crash-9.C
You probably need to check zero_init_p to properly handle pointers to
data members, where a null value is integer -1; given
struct A { int i; };
constexpr A::* pm = &A::i;
int A::* pma[] = { pm, pm };
we don't want to discard the initializers because they look like zeros,
as then digest_init will add back -1s.
I added it but it wasn't doing the right thing. It mangled { } and
{ 0 } differently:
typedef int A::*pam_t;
struct B { pam_t a[2]; };
template <B> struct Y { };
void f (Y<B{{ }}>) { } // _Z1f1YIXtl1BtlA2_M1AiLS2_0EEEEE
void f (Y<B{{ 0 }}>) { } // _Z1f1YIXtl1BtlA2_M1AiLS2_0ELS2_0EEEEE
because the zeros didn't get removed. They need to be mangled the same,
don't they?
I've added three tests to exercise this (array51.C, nontype-class16.C,
and mangle72.C). They pass without the zero_init_p() calls but some
fail with it (depending on where it's added). Please check to see
that the tests really do exercise what they should.
If you think a zero_init_p() check really is needed I will need some
guidance where (a test case would be great).
+ unsigned n = TREE_STRING_LENGTH (value);
+ const char *str = TREE_STRING_POINTER (value);
+ /* Count the number of trailing nuls and subtract them from
+ STRSIZE because they don't need to be mangled. */
+ tree strsizenode = TYPE_SIZE_UNIT (TREE_TYPE (value));
+ unsigned strsize = tree_to_uhwi (strsizenode);
+ if (strsize > n)
+ strsize = n;
Why not just use TREE_STRING_LENGTH?
The length reflects the size of the string literal, including
any trailing nuls (like in "x\0y\0\0"). We only want to mangle
the leading part up to (but not including) the first trailing
nul.
Attached is a patch adjusted to use TYPE_HAS_TRIVIAL_DFLT in
decl.c and with the three additional tests.
Martin
PR c++/89833 - sorry, unimplemented: string literal in function template signature
gcc/cp/ChangeLog:
PR c++/89833
* decl.c (reshape_init_array_1): Strip trailing zero-initializers
from arrays of trivial type and known size.
* mangle.c (write_expression): Convert braced initializer lists
to STRING_CSTs.
(write_expression): Trim trailing zero-initializers from arrays
of trivial type.
(write_template_arg_literal): Mangle strings the same as braced
initializer lists.
gcc/testsuite/ChangeLog:
* gcc/testsuite/g++.dg/abi/mangle69.C: New test.
* gcc/testsuite/g++.dg/abi/mangle70.C: New test.
* gcc/testsuite/g++.dg/abi/mangle71.C: New test.
* gcc/testsuite/g++.dg/abi/mangle72.C: New test.
* gcc/testsuite/g++.dg/cpp0x/constexpr-array19.C: New test.
* gcc/testsuite/g++.dg/cpp2a/nontype-class15.C: New test.
* gcc/testsuite/g++.dg/cpp2a/nontype-class16.C: New test.
* gcc/testsuite/g++.dg/init/array51.C: New test.
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c46a39665bd..400e1a274aa 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5799,6 +5799,9 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
max_index_cst = tree_to_uhwi (fold_convert (size_type_node, max_index));
}
+ /* Set to the index of the last element with a non-zero initializer.
+ Initializers for elements past this one can be dropped. */
+ unsigned HOST_WIDE_INT last_nonzero = -1;
/* Loop until there are no more initializers. */
for (index = 0;
d->cur != d->end && (!sized_array_p || index <= max_index_cst);
@@ -5817,11 +5820,30 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
if (!TREE_CONSTANT (elt_init))
TREE_CONSTANT (new_init) = false;
+ if (!initializer_zerop (elt_init))
+ last_nonzero = index;
+
/* This can happen with an invalid initializer (c++/54501). */
if (d->cur == old_cur && !sized_array_p)
break;
}
+ if (sized_array_p
+ && (!CLASS_TYPE_P (elt_type)
+ || TYPE_HAS_TRIVIAL_DFLT (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. */
+ unsigned HOST_WIDE_INT nelts = CONSTRUCTOR_NELTS (new_init);
+ if (last_nonzero > nelts)
+ nelts = 0;
+ else if (last_nonzero < nelts - 1)
+ nelts = last_nonzero + 1;
+
+ vec_safe_truncate (CONSTRUCTOR_ELTS (new_init), nelts);
+ }
+
return new_init;
}
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index f40c3e16c5d..02f0c07f33c 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -3136,18 +3136,48 @@ write_expression (tree expr)
}
else if (code == CONSTRUCTOR)
{
- vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (expr);
- unsigned i; tree val;
+ bool braced_init = BRACE_ENCLOSED_INITIALIZER_P (expr);
+ tree etype = TREE_TYPE (expr);
- if (BRACE_ENCLOSED_INITIALIZER_P (expr))
+ if (braced_init)
write_string ("il");
else
{
write_string ("tl");
- write_type (TREE_TYPE (expr));
+ write_type (etype);
+ }
+
+ if (!initializer_zerop (expr) || !trivial_type_p (etype))
+ {
+ /* Convert braced initializer lists to STRING_CSTs so that
+ A<"Foo"> mangles the same as A<{'F', 'o', 'o', 0}> while
+ still using the latter mangling for strings that
+ originated as braced initializer lists. */
+ expr = braced_lists_to_strings (etype, expr);
+
+ if (TREE_CODE (expr) == CONSTRUCTOR)
+ {
+ vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (expr);
+ unsigned last_nonzero = -1, i;
+ tree val;
+
+ FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
+ if (!initializer_zerop (val))
+ last_nonzero = i;
+
+ FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
+ {
+ if (i > last_nonzero)
+ break;
+ write_expression (val);
+ }
+ }
+ else
+ {
+ gcc_assert (TREE_CODE (expr) == STRING_CST);
+ write_expression (expr);
+ }
}
- FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
- write_expression (val);
write_char ('E');
}
else if (code == LAMBDA_EXPR)
@@ -3353,8 +3383,14 @@ write_expression (tree expr)
static void
write_template_arg_literal (const tree value)
{
- write_char ('L');
- write_type (TREE_TYPE (value));
+ if (TREE_CODE (value) == STRING_CST)
+ /* Temporarily mangle strings as braced initializer lists. */
+ write_string ("tl");
+ else
+ write_char ('L');
+
+ tree valtype = TREE_TYPE (value);
+ write_type (valtype);
/* Write a null member pointer value as (type)0, regardless of its
real representation. */
@@ -3397,8 +3433,35 @@ write_template_arg_literal (const tree value)
break;
case STRING_CST:
- sorry ("string literal in function template signature");
- break;
+ {
+ /* Mangle strings the same as braced initializer lists. */
+ unsigned n = TREE_STRING_LENGTH (value);
+ const char *str = TREE_STRING_POINTER (value);
+
+ /* Count the number of trailing nuls and subtract them from
+ STRSIZE because they don't need to be mangled. */
+ tree strsizenode = TYPE_SIZE_UNIT (TREE_TYPE (value));
+ unsigned strsize = tree_to_uhwi (strsizenode);
+ if (strsize > n)
+ strsize = n;
+ for (const char *p = str + strsize - 1; ; --p)
+ {
+ if (*p || p == str)
+ {
+ strsize -= str + strsize - !!*p - p;
+ break;
+ }
+ }
+ tree eltype = TREE_TYPE (valtype);
+ for (const char *p = str; strsize--; ++p)
+ {
+ write_char ('L');
+ write_type (eltype);
+ write_unsigned_number (*(const unsigned char*)p);
+ write_string ("E");
+ }
+ break;
+ }
default:
gcc_unreachable ();
diff --git a/gcc/testsuite/g++.dg/abi/mangle69.C b/gcc/testsuite/g++.dg/abi/mangle69.C
new file mode 100644
index 00000000000..dea3eeca022
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle69.C
@@ -0,0 +1,164 @@
+// { dg-do compile { target c++2a } }
+
+struct A1 { char c[5]; };
+
+template <A1> struct B { };
+
+// All of the following name the same type.
+typedef B<A1{ }> A______;
+typedef B<A1{ { 0 } }> A_Z____;
+typedef B<A1{ { 0, 0 } }> A_ZZ___;
+typedef B<A1{ { 0, 0, 0 } }> A_ZZZ__;
+typedef B<A1{ { 0, 0, 0, 0 } }> A_ZZZZ_;
+typedef B<A1{ { 0, 0, 0, 0, 0 } }> A_ZZZZZ;
+
+// Verify that the types mangle the same.
+void a______ (A______) { }
+// { dg-final { scan-assembler "_Z7a______1BIXtl2A1EEE" } }
+
+void a_z____ (A_Z____) { }
+// { dg-final { scan-assembler "_Z7a_z____1BIXtl2A1EEE" } }
+
+void a_zz___ (A_ZZ___) { }
+// { dg-final { scan-assembler "_Z7a_zz___1BIXtl2A1EEE" } }
+
+void a_zzz__ (A_ZZZ__) { }
+// { dg-final { scan-assembler "_Z7a_zzz__1BIXtl2A1EEE" } }
+
+void a_zzzz_ (A_ZZZZ_) { }
+// { dg-final { scan-assembler "_Z7a_zzzz_1BIXtl2A1EEE" } }
+
+void a_zzzzz (A_ZZZZZ) { }
+// { dg-final { scan-assembler "_Z7a_zzzzz1BIXtl2A1EEE" } }
+
+
+// All of the following use a string to initialize the array but
+// also name the same type as the above.
+typedef B<A1{ "" }> S_z____;
+typedef B<A1{ "\0" }> S_Zz___;
+typedef B<A1{ "\0\0" }> S_ZZz__;
+typedef B<A1{ "\0\0\0" }> S_ZZZz_;
+typedef B<A1{ "\0\0\0\0" }> S_ZZZZz;
+
+// Verify that the types mangle the same.
+void s_z____ (S_z____) { }
+// { dg-final { scan-assembler "_Z7s_z____1BIXtl2A1EEE" } }
+
+void s_Zz___ (S_Zz___) { }
+// { dg-final { scan-assembler "_Z7s_Zz___1BIXtl2A1EEE" } }
+
+void s_ZZz__ (S_ZZz__) { }
+// { dg-final { scan-assembler "_Z7s_ZZz__1BIXtl2A1EEE" } }
+
+void s_ZZZz_ (S_ZZZz_) { }
+// { dg-final { scan-assembler "_Z7s_ZZZz_1BIXtl2A1EEE" } }
+
+void s_ZZZZz (S_ZZZZz) { }
+// { dg-final { scan-assembler "_Z7s_ZZZZz1BIXtl2A1EEE" } }
+
+
+// All of the following also name the same type (distinct from
+// the above).
+typedef B<A1{ { 'A' } }> A_A____;
+typedef B<A1{ { 'A', 0 } }> A_AZ___;
+typedef B<A1{ { 'A', 0, 0 } }> A_AZZ__;
+typedef B<A1{ { 'A', 0, 0, 0 } }> A_AZZZ_;
+typedef B<A1{ { 'A', 0, 0, 0, 0 } }> A_AZZZZ;
+
+void a_A____ (A_A____) { }
+// { dg-final { scan-assembler "_Z7a_A____1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+void a_AZ___ (A_AZ___) { }
+// { dg-final { scan-assembler "_Z7a_AZ___1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+void a_AZZ__ (A_AZZ__) { }
+// { dg-final { scan-assembler "_Z7a_AZZ__1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+void a_AZZZ_ (A_AZZZ_) { }
+// { dg-final { scan-assembler "_Z7a_AZZZ_1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+void a_AZZZZ (A_AZZZZ) { }
+// { dg-final { scan-assembler "_Z7a_AZZZZ1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+
+typedef B<A1{ "A" }> S_Az___;
+typedef B<A1{ "A\0" }> S_AZz__;
+typedef B<A1{ "A\0\0" }> S_AZZz_;
+typedef B<A1{ "A\0\0\0" }> S_AZZZz;
+
+void s_Az___ (S_Az___) { }
+// { dg-final { scan-assembler "_Z7s_Az___1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+void s_AZz__ (S_AZz__) { }
+// { dg-final { scan-assembler "_Z7s_AZz__1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+void s_AZZz_ (S_AZZz_) { }
+// { dg-final { scan-assembler "_Z7s_AZZz_1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+void s_AZZZz (S_AZZZz) { }
+// { dg-final { scan-assembler "_Z7s_AZZZz1BIXtl2A1tlA5_cLc65EEEEE" } }
+
+
+typedef B<A1{ 'A', 0, 0, 'D', 0 }> A_AZZDZ;
+typedef B<A1{ 'A', 0, 0, 'D' }> A_AZZD_;
+
+void a_AZZDZ (A_AZZDZ) { }
+// { dg-final { scan-assembler "_Z7a_AZZD_1BIXtl2A1tlA5_cLc65ELc0ELc0ELc68EEEEE" } }
+
+void a_AZZD_ (A_AZZD_) { }
+// { dg-final { scan-assembler "_Z7a_AZZDZ1BIXtl2A1tlA5_cLc65ELc0ELc0ELc68EEEEE" } }
+
+
+typedef B<A1{ { "AB\0D" } }> S_ABZD_;
+typedef B<A1{ { "AB\0\0" } }> S_ABZZ_;
+typedef B<A1{ { "AB\0" } }> S_ABZ__;
+typedef B<A1{ { "AB" } }> S_AB___;
+
+void s_abzd_ (S_ABZD_) { }
+// { dg-final { scan-assembler "_Z7s_abzd_1BIXtl2A1tlA5_cLc65ELc66ELc0ELc68EEEEE" } }
+
+void s_abzz_ (S_ABZZ_) { }
+// { dg-final { scan-assembler "_Z7s_abzz_1BIXtl2A1tlA5_cLc65ELc66EEEEE" } }
+
+void s_abz__ (S_ABZ__) { }
+// { dg-final { scan-assembler "_Z7s_abz__1BIXtl2A1tlA5_cLc65ELc66EEEEE" } }
+
+void s_ab___ (S_AB___) { }
+// { dg-final { scan-assembler "_Z7s_ab___1BIXtl2A1tlA5_cLc65ELc66EEEEE" } }
+
+
+struct A3 { char a[5], b[5], c[5]; };
+template <A3> struct B3 { };
+
+/* These all name the same type. */
+typedef B3<A3{ "\1\2", { }, "\3\4\5\6" }> T_123z_______3456z;
+typedef B3<A3{ "\1\2", { 0 }, "\3\4\5\6" }> T_123z__Z____3456z;
+typedef B3<A3{ "\1\2", { 0, 0 }, "\3\4\5\6" }> T_123z__ZZ___3456z;
+typedef B3<A3{ "\1\2", { 0, 0, 0 }, "\3\4\5\6" }> T_123z__ZZZ__3456z;
+typedef B3<A3{ "\1\2", { 0, 0, 0, 0 }, "\3\4\5\6" }> T_123z__ZZZZ_3456z;
+typedef B3<A3{ "\1\2", "", "\3\4\5\6" }> T_123z__Z____3456z;
+typedef B3<A3{ "\1\2", "\0", "\3\4\5\6" }> T_123z__ZZ___3456z;
+typedef B3<A3{ "\1\2", "\0\0", "\3\4\5\6" }> T_123z__ZZZ__3456z;
+typedef B3<A3{ "\1\2", "\0\0\0", "\3\4\5\6" }> T_123z__ZZZZ_3456z;
+typedef B3<A3{ "\1\2", "\0\0\0\0", "\3\4\5\6" }> T_123z__ZZZZZ3456z;
+typedef B3<A3{ "\1\2\0", "\0\0\0\0", "\3\4\5\6" }> T_123Zz_ZZZZZ3456z;
+typedef B3<A3{ "\1\2\0\0", "\0\0\0\0", "\3\4\5\6" }> T_123ZZzZZZZZ3456z;
+
+
+void ft0 (T_123z_______3456z) { }
+// { dg-final { scan-assembler "_Z3ft02B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
+
+void ft1 (T_123z__Z____3456z) { }
+// { dg-final { scan-assembler "_Z3ft12B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
+void ft2 (T_123z__ZZ___3456z) { }
+// { dg-final { scan-assembler "_Z3ft22B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
+void ft3 (T_123z__ZZZ__3456z) { }
+// { dg-final { scan-assembler "_Z3ft32B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
+void ft4 (T_123z__ZZZZ_3456z) { }
+// { dg-final { scan-assembler "_Z3ft42B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
+void ft9 (T_123z__ZZZZZ3456z) { }
+// { dg-final { scan-assembler "_Z3ft92B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
+void fta (T_123Zz_ZZZZZ3456z) { }
+// { dg-final { scan-assembler "_Z3fta2B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
+void ftb (T_123ZZzZZZZZ3456z) { }
+// { dg-final { scan-assembler "_Z3ftb2B3IXtl2A3tlA5_cLc1ELc2EEtlS1_EtlS1_Lc3ELc4ELc5ELc6EEEEE" } }
diff --git a/gcc/testsuite/g++.dg/abi/mangle70.C b/gcc/testsuite/g++.dg/abi/mangle70.C
new file mode 100644
index 00000000000..39c987d73c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle70.C
@@ -0,0 +1,29 @@
+// Verify that class literals are mangled the same way regardless
+// of the underlying type.
+// { dg-do compile { target c++2a } }
+
+struct I { int a[5], b[5], c[5]; };
+template <I> struct X { };
+
+typedef X<I{ {1,2}, {}, {11,12,13,14} }> Ti;
+void f (Ti) { }
+// { dg-final { scan-assembler "_Z1f1XIXtl1ItlA5_iLi1ELi2EEtlS1_EtlS1_Li11ELi12ELi13ELi14EEEEE" } }
+
+struct C { char a[5], b[5], c[5]; };
+template <C> struct Y { };
+
+typedef Y<C{ {1,2}, {}, {11,12,13,14} }> Tca;
+void g (Tca) { }
+// { dg-final { scan-assembler "_Z1g1YIXtl1CtlA5_cLc1ELc2EEtlS1_EtlS1_Lc11ELc12ELc13ELc14EEEEE" } }
+
+typedef Y<C{ "\1\2", "", {11,12,13,14} }> Tcs;
+void h (Tcs) { }
+// { dg-final { scan-assembler "_Z1h1YIXtl1CtlA5_cLc1ELc2EEtlS1_EtlS1_Lc11ELc12ELc13ELc14EEEEE" } }
+
+struct S { signed char a[5], b[5], c[5]; };
+template <S> struct Z { };
+
+typedef Z<S{ {1,2}, {}, {11,12,13,14} }> Tsc;
+
+void i (Tsc) { }
+// { dg-final { scan-assembler "_Z1i1ZIXtl1StlA5_aLa1ELa2EEtlS1_EtlS1_La11ELa12ELa13ELa14EEEEE" } }
diff --git a/gcc/testsuite/g++.dg/abi/mangle71.C b/gcc/testsuite/g++.dg/abi/mangle71.C
new file mode 100644
index 00000000000..cb9d7d3a1d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle71.C
@@ -0,0 +1,28 @@
+// Verify manglinng of class literals of types with ctors.
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+ char i;
+ constexpr A (): i (1) { }
+ constexpr A (int i): i (i) { }
+};
+
+struct B { A a[3]; };
+
+template <B> struct X { };
+
+void f___ (X<B{{ }}>) { }
+// { dg-final { scan-assembler "_Z4f___1XIXtl1BtlA3_1AtlS1_Lc1EEEEEE" } }
+
+void f0__ (X<B{{ 0 }}>) { }
+// { dg-final { scan-assembler "_Z4f0__1XIXtl1BtlA3_1AtlS1_Lc0EEtlS1_Lc1EEEEEE" } }
+
+void f00_ (X<B{{ 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z4f00_1XIXtl1BtlA3_1AtlS1_Lc0EEtlS1_Lc0EEtlS1_Lc1EEEEEE" } }
+
+void f000 (X<B{{ 0, 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z4f0001XIXtl1BtlA3_1AtlS1_Lc0EEtlS1_Lc0EEtlS1_Lc0EEEEEE" } }
+
+void f1__ (X<B{{ 1 }}>) { }
+// { dg-final { scan-assembler "_Z4f1__1XIXtl1BtlA3_1AtlS1_Lc1EEtlS1_Lc1EEEEEE" } }
diff --git a/gcc/testsuite/g++.dg/abi/mangle72.C b/gcc/testsuite/g++.dg/abi/mangle72.C
new file mode 100644
index 00000000000..bdceead65f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle72.C
@@ -0,0 +1,46 @@
+// Verify manglinng of class literals with pointers to members.
+// { dg-do compile { target c++2a } }
+
+struct A { int a[2]; };
+
+template <A> struct X { };
+
+// Let's mangle some non-member pointer literals for comparison.
+void f__ (X<A{{ }}>) { }
+// { dg-final { scan-assembler "_Z3f001XIXtl1AEEE" } }
+
+void f0_ (X<A{{ 0 }}>) { }
+// { dg-final { scan-assembler "_Z3f0_1XIXtl1AEEE" } }
+
+void f00 (X<A{{ 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z3f__1XIXtl1AEEE" } }
+
+
+typedef int A::*pam_t;
+struct B { pam_t a[2]; };
+template <B> struct Y { };
+
+void g__ (Y<B{{ }}>) { }
+// { dg-final { scan-assembler "_Z3g__1YIYtl1BtlA2_M1AiLS2_0EEEEE" } }
+
+void g0_ (Y<B{{ 0 }}>) { }
+// { dg-final { scan-assembler "_Z3g0_1YIYtl1BtlA2_M1AiLS2_0EEEEE" } }
+
+void g00 (Y<B{{ 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z3g001YIYtl1BtlA2_M1AiLS2_0EEEEE" } }
+
+
+struct C { pam_t a[3]; };
+template <C> struct Z { };
+
+void h___ (Z<C{{ }}>) { }
+// { dg-final { scan-assembler "_Z4h___1ZIXtl1CtlA3_M1AiLS2_0EEEEE" } }
+
+void h0__ (Z<C{{ 0 }}>) { }
+// { dg-final { scan-assembler "_Z4h0__1ZIXtl1CtlA3_M1AiLS2_0EEEEE" } }
+
+void h00_ (Z<C{{ 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z4h00_1ZIXtl1CtlA3_M1AiLS2_0EEEEE" } }
+
+void h000 (Z<C{{ 0, 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z4h0001ZIXtl1CtlA3_M1AiLS2_0EEEEE" } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array19.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array19.C
new file mode 100644
index 00000000000..f7e5d352d88
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array19.C
@@ -0,0 +1,137 @@
+// PR c++/89833
+// Test to verify that constant array elements initialized to zero
+// evaluate to zero regardless of the form of their initilizer,
+// and irrespective whether it's explicit or implicit.
+
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wall" }
+
+static const char all_zero[1024] = { };
+
+namespace test_int
+{
+constexpr int a[][3] = { { 0, 0 }, { 0 }, { } };
+
+static_assert (sizeof a == sizeof (int) * 3 * 3);
+
+static_assert ( a[0][0] == 0 && a[0][1] == 0 && a[0][2] == 0
+ && a[1][0] == 0 && a[1][1] == 0 && a[1][2] == 0
+ && a[2][0] == 0 && a[2][1] == 0 && a[2][2] == 0);
+
+constexpr int b[3][3] = { { 0, 0 }, { 0 } };
+
+static_assert (sizeof b == sizeof (int) * 3 * 3);
+
+static_assert ( b[0][0] == 0 && b[0][1] == 0 && b[0][2] == 0
+ && b[1][0] == 0 && b[1][1] == 0 && b[1][2] == 0
+ && b[2][0] == 0 && b[2][1] == 0 && b[2][2] == 0);
+
+constexpr int c[3][3] = { { } };
+
+static_assert (sizeof c == sizeof (int) * 3 * 3);
+
+static_assert ( c[0][0] == 0 && c[0][1] == 0 && c[0][2] == 0
+ && c[1][0] == 0 && c[1][1] == 0 && c[1][2] == 0
+ && c[2][0] == 0 && c[2][1] == 0 && c[2][2] == 0);
+
+}
+
+namespace test_char
+{
+constexpr char a[][3] = { { 0, 0 }, { 0 }, { } };
+
+static_assert (sizeof a == sizeof (char) * 3 * 3);
+
+static_assert ( a[0][0] == 0 && a[0][1] == 0 && a[0][2] == 0
+ && a[1][0] == 0 && a[1][1] == 0 && a[1][2] == 0
+ && a[2][0] == 0 && a[2][1] == 0 && a[2][2] == 0);
+
+constexpr char b[3][3] = { { 0, 0 }, { 0 } };
+
+static_assert (sizeof b == sizeof (char) * 3 * 3);
+
+static_assert ( b[0][0] == 0 && b[0][1] == 0 && b[0][2] == 0
+ && b[1][0] == 0 && b[1][1] == 0 && b[1][2] == 0
+ && b[2][0] == 0 && b[2][1] == 0 && b[2][2] == 0);
+
+constexpr char c[3][3] = { { } };
+
+static_assert (sizeof c == sizeof (char) * 3 * 3);
+
+static_assert ( c[0][0] == 0 && c[0][1] == 0 && c[0][2] == 0
+ && c[1][0] == 0 && c[1][1] == 0 && c[1][2] == 0
+ && c[2][0] == 0 && c[2][1] == 0 && c[2][2] == 0);
+}
+
+namespace test_string
+{
+constexpr char a[][3] = { "\0", "", { } };
+
+static_assert (sizeof a == sizeof (char) * 3 * 3);
+
+static_assert ( a[0][0] == 0 && a[0][1] == 0 && a[0][2] == 0
+ && a[1][0] == 0 && a[1][1] == 0 && a[1][2] == 0
+ && a[2][0] == 0 && a[2][1] == 0 && a[2][2] == 0);
+
+constexpr char b[3][3] = { "\0", "" };
+
+static_assert (sizeof b == sizeof (char) * 3 * 3);
+
+static_assert ( b[0][0] == 0 && b[0][1] == 0 && b[0][2] == 0
+ && b[1][0] == 0 && b[1][1] == 0 && b[1][2] == 0
+ && b[2][0] == 0 && b[2][1] == 0 && b[2][2] == 0);
+
+constexpr char c[3][3] = { };
+
+static_assert (sizeof c == sizeof (char) * 3 * 3);
+
+static_assert ( c[0][0] == 0 && c[0][1] == 0 && c[0][2] == 0
+ && c[1][0] == 0 && c[1][1] == 0 && c[1][2] == 0
+ && c[2][0] == 0 && c[2][1] == 0 && c[2][2] == 0);
+}
+
+namespace test_string_member
+{
+struct B { struct A { char a[5]; } a[2]; };
+
+constexpr B b[3] =
+ {
+ /* [0] */
+ {
+ /* a = */
+ {
+ /* a[0] */ { { 0, 0, 0, 0, 0 } },
+ /* a[1] */ { { 0, 0 } }
+ }
+ },
+ /* [1] */
+ {
+ /* a */
+ {
+ /* a[0] */ { "\0\0\0\0" },
+ /* a[0] */ { "" }
+ }
+ },
+ };
+
+static_assert ( b[0].a[0].a[0] == 0
+ && b[0].a[0].a[1] == 0
+ && b[0].a[0].a[2] == 0
+ && b[0].a[0].a[3] == 0
+ && b[0].a[0].a[4] == 0
+ && b[0].a[1].a[0] == 0
+ && b[0].a[1].a[1] == 0
+ && b[0].a[1].a[2] == 0
+ && b[0].a[1].a[3] == 0
+ && b[0].a[1].a[4] == 0
+ && b[1].a[0].a[0] == 0
+ && b[1].a[0].a[1] == 0
+ && b[1].a[0].a[2] == 0
+ && b[1].a[0].a[3] == 0
+ && b[1].a[0].a[4] == 0
+ && b[2].a[0].a[0] == 0
+ && b[2].a[0].a[1] == 0
+ && b[2].a[0].a[2] == 0
+ && b[2].a[0].a[3] == 0
+ && b[2].a[0].a[4] == 0);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class15.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class15.C
new file mode 100644
index 00000000000..d684785a77f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class15.C
@@ -0,0 +1,222 @@
+// PR c++/89833
+// Test to verify that the same specializations on non-type template
+// parameters of class types are in fact treated as the same.
+// { dg-do compile { target c++2a } }
+
+struct A1 { char c[5]; };
+
+template <A1> struct B { };
+
+// All of the following name the same type.
+typedef B<A1{ }> A______;
+typedef B<A1{ { 0 } }> A_Z____;
+typedef B<A1{ { 0, 0 } }> A_ZZ___;
+typedef B<A1{ { 0, 0, 0 } }> A_ZZZ__;
+typedef B<A1{ { 0, 0, 0, 0 } }> A_ZZZZ_;
+typedef B<A1{ { 0, 0, 0, 0, 0 } }> A_ZZZZZ;
+
+// Verify the types are indeed the same by redeclaring the same identifier
+// of each of them.
+extern A______ same_type_B_A1;
+extern A_Z____ same_type_B_A1;
+extern A_ZZ___ same_type_B_A1;
+extern A_ZZZ__ same_type_B_A1;
+extern A_ZZZZ_ same_type_B_A1;
+extern A_ZZZZZ same_type_B_A1;
+
+
+// All of the following use a string to initialize the array but
+// also name the same type as the above.
+typedef B<A1{ "" }> S_z____;
+typedef B<A1{ "\0" }> S_Zz___;
+typedef B<A1{ "\0\0" }> S_ZZz__;
+typedef B<A1{ "\0\0\0" }> S_ZZZz_;
+typedef B<A1{ "\0\0\0\0" }> S_ZZZZz;
+
+// Verify the types are indeed the same by redeclaring the same identifier
+// of each of them.
+extern S_z____ same_type_B_A1;
+extern S_Zz___ same_type_B_A1;
+extern S_Zz___ same_type_B_A1;
+extern S_ZZz__ same_type_B_A1;
+extern S_ZZZz_ same_type_B_A1;
+extern S_ZZZZz same_type_B_A1;
+
+
+// All of the following also name the same type (distinct from
+// the above).
+typedef B<A1{ { 'A' } }> A_A____;
+typedef B<A1{ { 'A', 0 } }> A_AZ___;
+typedef B<A1{ { 'A', 0, 0 } }> A_AZZ__;
+typedef B<A1{ { 'A', 0, 0, 0 } }> A_AZZZ_;
+typedef B<A1{ { 'A', 0, 0, 0, 0 } }> A_AZZZZ;
+
+extern A_A____ same_type_B_A1_A;
+extern A_AZ___ same_type_B_A1_A;
+extern A_AZZ__ same_type_B_A1_A;
+extern A_AZZZ_ same_type_B_A1_A;
+extern A_AZZZZ same_type_B_A1_A;
+
+
+struct A3 { char a[5], b[5], c[5]; };
+template <A3> struct B3 { };
+
+// These all name the same type.
+typedef B3<A3{ }> B3_A3________________;
+typedef B3<A3{ { } }> B3_A3________________;
+typedef B3<A3{ { }, { } }> B3_A3________________;
+typedef B3<A3{ { }, { }, { } }> B3_A3________________;
+typedef B3<A3{ { 0 }, { }, { } }> B3_A3________________;
+typedef B3<A3{ { 0 }, { 0 }, { } }> B3_A3________________;
+typedef B3<A3{ { 0 }, { 0 }, { 0 } }> B3_A3________________;
+typedef B3<A3{ { 0, 0 }, { 0 }, { 0 } }> B3_A3________________;
+typedef B3<A3{ { 0, 0 }, { 0, 0 }, { 0 } }> B3_A3________________;
+typedef B3<A3{ { 0, 0 }, { 0, 0 }, { 0, 0 } }> B3_A3________________;
+
+// These all name the same type.
+typedef B3<A3{ "AB", { }, "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", { 0 }, "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", { 0, 0 }, "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", { 0, 0, 0 }, "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", { 0, 0, 0, 0 }, "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", "", "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", "\0", "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", "\0\0", "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", "\0\0\0", "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB", "\0\0\0\0", "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB\0", "\0\0\0\0", "IJKL" }> B3_A3_AB________IJKL_;
+typedef B3<A3{ "AB\0\0", "\0\0\0\0", "IJKL" }> B3_A3_AB________IJKL_;
+
+// Types with the same name must be the same (and so redefinitions
+// must be accepted). Likewise, overloads on distinct types must
+// be accepted.
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0} }> B3_A3________________;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,0}, {0} }> B3_A3________________;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,0}, {} }> B3_A3________________;
+typedef B3<A3{ {0,0,0,0,0}, {0}, {} }> B3_A3________________;
+typedef B3<A3{ {0,0,0,0,0}, {}, {} }> B3_A3________________;
+typedef B3<A3{ {0}, {0}, {0} }> B3_A3________________;
+typedef B3<A3{ {0}, {0}, {} }> B3_A3________________;
+typedef B3<A3{ {0}, {}, {0} }> B3_A3________________;
+typedef B3<A3{ {}, {}, {} }> B3_A3________________;
+typedef B3<A3{ {}, {} }> B3_A3________________;
+typedef B3<A3{ {} }> B3_A3________________;
+typedef B3<A3{ }> B3_A3________________;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0,0,0}, {0,0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0,0,0}, {0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0,0,0}, {}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0}, {0,0,0,0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0}, {0,0,0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0}, {0,0,}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {}, {0}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {}, {}, {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ {0,0,0}, {0,0}, {0,0,0,1,0} }> B3_A3______________1_;
+typedef B3<A3{ {0,0,0}, {0,0}, {0,0,0,1} }> B3_A3______________1_;
+typedef B3<A3{ {0}, {}, {0,0,1,0} }> B3_A3_____________1__;
+typedef B3<A3{ {0}, {}, {0,0,1} }> B3_A3_____________1__;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,0}, {0,1,0} }> B3_A3____________1___;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,0}, {0,1} }> B3_A3____________1___;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,0}, {1} }> B3_A3___________1____;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,1}, {0,0,0,0,0} }> B3_A3__________1_____;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,1}, {} }> B3_A3__________1_____;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,0,1} }> B3_A3__________1_____;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,0,1,0}, {0,0,0,0,0} }> B3_A3_________1______;
+typedef B3<A3{ {0,0,0,0,0}, {0,0,1,0,0}, {0,0,0,0,0} }> B3_A3________1_______;
+typedef B3<A3{ {0,0,0,0,0}, {0,1,0,0,0}, {0,0,0,0,0} }> B3_A3_______1________;
+typedef B3<A3{ {0,0,0,0,0}, {1,0,0,0,0}, {0,0,0,0,0} }> B3_A3______1_________;
+typedef B3<A3{ {0,0,0,0,1}, {0,0,0,0,0}, {0,0,0,0,0} }> B3_A3_____1__________;
+typedef B3<A3{ {0,0,0,0,1}, {0,0,0,0,0} }> B3_A3_____1__________;
+typedef B3<A3{ {0,0,0,0,1} }> B3_A3_____1__________;
+typedef B3<A3{ {0,0,0,1,0}, {0,0,0,0,0}, {0,0,0,0,0} }> B3_A3____1___________;
+typedef B3<A3{ {0,0,1,0,0}, {0,0,0,0,0}, {0,0,0,0,0} }> B3_A3___1____________;
+typedef B3<A3{ {0,1,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0} }> B3_A3__1_____________;
+typedef B3<A3{ {1,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0} }> B3_A3_1______________;
+typedef B3<A3{ {1,0,0,0,0}, {0,0,0,0,0} }> B3_A3_1______________;
+typedef B3<A3{ {1,0,0,0,0} }> B3_A3_1______________;
+typedef B3<A3{ {1,0,0,0} }> B3_A3_1______________;
+typedef B3<A3{ {1,0,0} }> B3_A3_1______________;
+typedef B3<A3{ {1,0} }> B3_A3_1______________;
+typedef B3<A3{ {1} }> B3_A3_1______________;
+
+typedef B3<A3{ {1,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,1} }> B3_A3_1_____________1;
+typedef B3<A3{ {1}, {0}, {0,0,0,0,1} }> B3_A3_1_____________1;
+typedef B3<A3{ {1}, {}, {0,0,0,0,1} }> B3_A3_1_____________1;
+typedef B3<A3{ {0,1,0,0,0}, {0,0,0,0,0}, {0,0,0,1} }> B3_A3__1___________1_;
+typedef B3<A3{ {0,1}, {0}, {0,0,0,1} }> B3_A3__1___________1_;
+typedef B3<A3{ {0,1}, {}, {0,0,0,1} }> B3_A3__1___________1_;
+
+// Same as above.
+typedef B3<A3{ "\0\0\0\0", "\0\0\0\0", "\0\0\0\0" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0\0", "\0\0\0" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0\0", "\0\0" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0\0", "\0" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0\0", "" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0", "" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0\0", "" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0", "" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "", "" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0", "", "" }> B3_A3________________;
+typedef B3<A3{ "\0\0", "", "" }> B3_A3________________;
+typedef B3<A3{ "\0", "", "" }> B3_A3________________;
+typedef B3<A3{ "", "", "" }> B3_A3________________;
+typedef B3<A3{ "", "" }> B3_A3________________;
+typedef B3<A3{ "" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0", { 0 } }> B3_A3________________;
+typedef B3<A3{ "\0\0", { 0 }, "\0" }> B3_A3________________;
+typedef B3<A3{ { 0 }, "", "\0\0\0\0" }> B3_A3________________;
+typedef B3<A3{ "\0\0\0", "\0\0", { } }> B3_A3________________;
+typedef B3<A3{ "\0", { }, "" }> B3_A3________________;
+typedef B3<A3{ { }, "\0\0\0\0", "\0\0\0" }> B3_A3________________;
+
+typedef B3<A3{ "\0\0\0\0", "\0\0\0", {0,0,0,0,1} }> B3_A3_______________1;
+typedef B3<A3{ "\0\0", "\0", "\0\0\0\1" }> B3_A3______________1_;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0", "\0\0\1" }> B3_A3_____________1__;
+typedef B3<A3{ "\0\0", "\0", "\0\1" }> B3_A3____________1___;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0", "\1" }> B3_A3___________1____;
+typedef B3<A3{ "\0\0", {0,0,0,0,1}, "\0" }> B3_A3__________1_____;
+typedef B3<A3{ "\0\0\0\0", "\0\0\0\1", "\0\0\0" }> B3_A3_________1______;
+typedef B3<A3{ "\0\0", "\0\0\1", "\0" }> B3_A3________1_______;
+typedef B3<A3{ "\0\0\0\0", "\0\1", "\0\0\0" }> B3_A3_______1________;
+typedef B3<A3{ "\0\0", "\1", "\0" }> B3_A3______1_________;
+typedef B3<A3{ {0,0,0,0,1}, "\0\0\0\0", "\0\0\0" }> B3_A3_____1__________;
+typedef B3<A3{ "\0\0\0\1", "\0\0", "\0" }> B3_A3____1___________;
+typedef B3<A3{ "\0\0\1", "\0\0\0\0", "\0\0\0" }> B3_A3___1____________;
+typedef B3<A3{ "\0\1", "\0\0", "\0" }> B3_A3__1_____________;
+typedef B3<A3{ "\1", "", "\0\0\0\0" }> B3_A3_1______________;
+
+typedef B3<A3{ "\1", {}, {0,0,0,0,1} }> B3_A3_1_____________1;
+typedef B3<A3{ "\1", "", {0,0,0,0,1} }> B3_A3_1_____________1;
+typedef B3<A3{ "\0\1", {}, {0,0,0,1} }> B3_A3__1___________1_;
+typedef B3<A3{ "\0\1", "", "\0\0\0\1" }> B3_A3__1___________1_;
+typedef B3<A3{ "\0\1\0", "\0", "\0\0\0\1" }> B3_A3__1___________1_;
+
+void f_b3_a3 (B3_A3________________) { }
+void f_b3_a3 (B3_A3_______________1) { }
+void f_b3_a3 (B3_A3______________1_) { }
+void f_b3_a3 (B3_A3_____________1__) { }
+void f_b3_a3 (B3_A3____________1___) { }
+void f_b3_a3 (B3_A3___________1____) { }
+void f_b3_a3 (B3_A3__________1_____) { }
+void f_b3_a3 (B3_A3_________1______) { }
+void f_b3_a3 (B3_A3________1_______) { }
+void f_b3_a3 (B3_A3_______1________) { }
+void f_b3_a3 (B3_A3______1_________) { }
+void f_b3_a3 (B3_A3_____1__________) { }
+void f_b3_a3 (B3_A3____1___________) { }
+void f_b3_a3 (B3_A3___1____________) { }
+void f_b3_a3 (B3_A3__1_____________) { }
+void f_b3_a3 (B3_A3_1______________) { }
+void f_b3_a3 (B3_A3_1_____________1) { }
+void f_b3_a3 (B3_A3__1___________1_) { }
+
+typedef B3<A3{ "AB\0D", { }, "IJKL" }> B3_A3_ABZDZZZZZZIJKLZ;
+typedef B3<A3{ "AB\0D", { 0, 0, 1 }, "IJKL" }> B3_A3_ABZDZZZ1ZZIJKLZ;
+typedef B3<A3{ "AB\0D", { 0, 1 }, "IJKL" }> B3_A3_ABZDZZ1ZZZIJKLZ;
+
+void f (B3_A3_ABZDZZZZZZIJKLZ) { }
+void f (B3_A3_ABZDZZZ1ZZIJKLZ) { }
+void f (B3_A3_ABZDZZ1ZZZIJKLZ) { }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class16.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class16.C
new file mode 100644
index 00000000000..3afb5d24261
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class16.C
@@ -0,0 +1,57 @@
+// PR c++/89833
+// Test to verify that arrays of null pointer to members used as
+// non-type template arguments are interprested as null regardless
+// of the form of their initialization.
+// { dg-do compile { target c++2a } }
+// { dg-options "-O2 -Wall -fdump-tree-optimized" }
+
+struct A { int i; };
+
+typedef int A::*pam_t;
+
+struct B { pam_t a[2]; };
+template <B x> struct C { static constexpr B b = x; };
+
+B f__ () { return B{ }; }
+B f0_ () { return B{ 0 }; }
+B f00 () { return B{ 0, 0 }; }
+
+typedef C<B{ }> X__;
+typedef C<B{ 0 }> X0_;
+typedef C<B{ 0, 0 }> X00;
+
+B g__ () { return X__::b; }
+B g0_ () { return X0_::b; }
+B g00 () { return X00::b; }
+
+const B b__{ };
+const B b0_{ 0 };
+const B b00{ 0, 0 };
+
+const pam_t apam__[2] = { };
+const pam_t apam0_[2] = { 0 };
+const pam_t apam00[2] = { 0, 0 };
+
+#define assert(expr) \
+ (expr) ? (void)0 : __builtin_abort ()
+
+void test ()
+{
+ assert (f__ ().a[0] == nullptr && f__ ().a[1] == nullptr);
+ assert (f0_ ().a[0] == nullptr && f0_ ().a[1] == nullptr);
+ assert (f00 ().a[0] == nullptr && f00 ().a[1] == nullptr);
+
+ assert (g__ ().a[0] == nullptr && g__ ().a[1] == nullptr);
+ assert (g0_ ().a[0] == nullptr && g0_ ().a[1] == nullptr);
+ assert (g00 ().a[0] == nullptr && g00 ().a[1] == nullptr);
+
+ assert (b__.a[0] == nullptr && b__.a[1] == nullptr);
+ assert (b0_.a[0] == nullptr && b0_.a[1] == nullptr);
+ assert (b00.a[0] == nullptr && b00.a[1] == nullptr);
+
+ assert (apam__[0] == nullptr && apam__[1] == nullptr);
+ assert (apam0_[0] == nullptr && apam0_[1] == nullptr);
+ assert (apam00[0] == nullptr && apam00[1] == nullptr);
+}
+
+// { dg-final { scan-tree-dump-not "abort" "optimized" } }
diff --git a/gcc/testsuite/g++.dg/init/array51.C b/gcc/testsuite/g++.dg/init/array51.C
new file mode 100644
index 00000000000..2a90088953c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/array51.C
@@ -0,0 +1,86 @@
+// PR c++/89833
+// Anal test to verify that arrays of null pointer to members are
+// treated as null regardless of the form of their initialization,
+// and have all bits set in their representation.
+// { dg-do run { target c++11 } }
+// { dg-options "-O2 -Wall" }
+
+#define NOIPA __attribute__ ((noipa))
+
+struct A { int i; };
+
+typedef int A::*pam_t;
+
+pam_t apam__[2] = { };
+pam_t apam0_[2] = { 0 };
+pam_t apam00[2] = { 0, 0 };
+
+struct B { pam_t a[2]; };
+
+NOIPA B f__ () { return B{ }; }
+NOIPA B f0_ () { return B{ 0 }; }
+NOIPA B f00 () { return B{ 0, 0 }; }
+
+const B c__{ };
+const B c0_{ 0 };
+const B c00{ 0, 0 };
+
+B b__{ };
+B b0_{ 0 };
+B b00{ 0, 0 };
+
+#define assert(expr) \
+ (expr) ? (void)0 : __builtin_abort ()
+
+signed char allones[2 * sizeof (pam_t)];
+
+#define assert_rep(mp, n) \
+ assert (!test_allones (mp, n))
+
+NOIPA void init_allones ()
+{
+ __builtin_memset (allones, -1, sizeof allones);
+}
+
+NOIPA int test_allones (const pam_t *p, unsigned n)
+{
+ return __builtin_memcmp (allones, p, sizeof *p * n);
+}
+
+int main ()
+{
+ init_allones ();
+
+ assert (apam__[0] == nullptr && apam__[1] == nullptr);
+ assert (apam0_[0] == nullptr && apam0_[1] == nullptr);
+ assert (apam00[0] == nullptr && apam00[1] == nullptr);
+
+ assert (f__ ().a[0] == nullptr && f__ ().a[1] == nullptr);
+ assert (f0_ ().a[0] == nullptr && f0_ ().a[1] == nullptr);
+ assert (f00 ().a[0] == nullptr && f00 ().a[1] == nullptr);
+
+ assert (b__.a[0] == nullptr && b__.a[1] == nullptr);
+ assert (b0_.a[0] == nullptr && b0_.a[1] == nullptr);
+ assert (b00.a[0] == nullptr && b00.a[1] == nullptr);
+
+ assert (c__.a[0] == nullptr && c__.a[1] == nullptr);
+ assert (c0_.a[0] == nullptr && c0_.a[1] == nullptr);
+ assert (c00.a[0] == nullptr && c00.a[1] == nullptr);
+
+ assert_rep (apam__, 2);
+ assert_rep (apam0_, 2);
+ assert_rep (apam00, 2);
+
+ assert_rep (f__ ().a, 2);
+ assert_rep (f0_ ().a, 2);
+ assert_rep (f0_ ().a, 2);
+ assert_rep (f00 ().a, 2);
+
+ assert_rep (b__.a, 2);
+ assert_rep (b0_.a, 2);
+ assert_rep (b00.a, 2);
+
+ assert_rep (c__.a, 2);
+ assert_rep (c0_.a, 2);
+ assert_rep (c00.a, 2);
+}