diff mbox series

handle strings as template arguments (PR 47488, 89833, 89876)

Message ID 1e94288b-f43d-9e24-40c2-880a36738771@gmail.com
State New
Headers show
Series handle strings as template arguments (PR 47488, 89833, 89876) | expand

Commit Message

Martin Sebor April 1, 2019, 2:17 a.m. UTC
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).  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

Martin

Comments

Jason Merrill April 1, 2019, 5:27 p.m. UTC | #1
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.

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.

> +	  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?

Jason
Martin Sebor April 4, 2019, 2:34 a.m. UTC | #2
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);
+}
Jason Merrill April 4, 2019, 2:57 p.m. UTC | #3
On 4/3/19 10:34 PM, Martin Sebor wrote:
> 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).

Hmm, let me poke at this a bit.

>>> +      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.

Yes, I meant why look at TYPE_SIZE_UNIT at all?  TREE_STRING_LENGTH 
seems like the right starting value of strsize.

Jason
Martin Sebor April 4, 2019, 4:29 p.m. UTC | #4
On 4/4/19 8:57 AM, Jason Merrill wrote:
> On 4/3/19 10:34 PM, Martin Sebor wrote:
>> 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).
> 
> Hmm, let me poke at this a bit.

Here's a test case showing that mangling null member pointers
as zero leads to conflicts:

   struct A { int i; };
   typedef int A::*pam_t;
   struct B { pam_t a[2]; };
   template <B> struct Y { };

   // both mangle as _Z2.31YIXtl1BtlA2_M1AiLS2_0ELS2_0EEEEE
   void f (Y<B{{ 0, 0 }}>) { }
   void g (Y<B{{ 0, &A::i }}>) { }

This happens both with trunk and with my patch.  They probably
need to mangle as negative one, don't you think?  (There's
a comment saying mangling them as zeros is intentional but not
why.)

> 
>>>> +      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.
> 
> Yes, I meant why look at TYPE_SIZE_UNIT at all?  TREE_STRING_LENGTH 
> seems like the right starting value of strsize.

Ah.  Let me see about changing that.

Martin
Jason Merrill April 4, 2019, 4:50 p.m. UTC | #5
On 4/4/19 12:29 PM, Martin Sebor wrote:
> On 4/4/19 8:57 AM, Jason Merrill wrote:
>> On 4/3/19 10:34 PM, Martin Sebor wrote:
>>> 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).
>>
>> Hmm, let me poke at this a bit.
> 
> Here's a test case showing that mangling null member pointers
> as zero leads to conflicts:
> 
>    struct A { int i; };
>    typedef int A::*pam_t;
>    struct B { pam_t a[2]; };
>    template <B> struct Y { };
> 
>    // both mangle as _Z2.31YIXtl1BtlA2_M1AiLS2_0ELS2_0EEEEE
>    void f (Y<B{{ 0, 0 }}>) { }
>    void g (Y<B{{ 0, &A::i }}>) { }
> 
> This happens both with trunk and with my patch.  They probably
> need to mangle as negative one, don't you think?  (There's
> a comment saying mangling them as zeros is intentional but not
> why.)

Hmm.  If we leave out f, then g mangles as 0 and &A::i, which is fine.
Need to figure out why creating a B before the declaration of g breaks that.

The rationale for mangling as 0 would have been to reflect what you 
would write in the source: (int A::*)0 does give a null member pointer. 
But then we really can't use that representation 0 for anything else, we 
have to use a symbolic representation.   This patch doesn't need to fix 
that.

Jason
Martin Sebor April 4, 2019, 6:30 p.m. UTC | #6
On 4/4/19 10:50 AM, Jason Merrill wrote:
> On 4/4/19 12:29 PM, Martin Sebor wrote:
>> On 4/4/19 8:57 AM, Jason Merrill wrote:
>>> On 4/3/19 10:34 PM, Martin Sebor wrote:
>>>> 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).
>>>
>>> Hmm, let me poke at this a bit.
>>
>> Here's a test case showing that mangling null member pointers
>> as zero leads to conflicts:
>>
>>    struct A { int i; };
>>    typedef int A::*pam_t;
>>    struct B { pam_t a[2]; };
>>    template <B> struct Y { };
>>
>>    // both mangle as _Z2.31YIXtl1BtlA2_M1AiLS2_0ELS2_0EEEEE
>>    void f (Y<B{{ 0, 0 }}>) { }
>>    void g (Y<B{{ 0, &A::i }}>) { }
>>
>> This happens both with trunk and with my patch.  They probably
>> need to mangle as negative one, don't you think?  (There's
>> a comment saying mangling them as zeros is intentional but not
>> why.)
> 
> Hmm.  If we leave out f, then g mangles as 0 and &A::i, which is fine.
> Need to figure out why creating a B before the declaration of g breaks 
> that.

Let me know if you want me to start looking into this or if you
are on it yourself.

> 
> The rationale for mangling as 0 would have been to reflect what you 
> would write in the source: (int A::*)0 does give a null member pointer. 
> But then we really can't use that representation 0 for anything else, we 
> have to use a symbolic representation.   This patch doesn't need to fix 
> that.

Okay.  It doesn't look like it would be hard to change but I'm
happy to leave it for later.

Attached is a patch that just simplifies the STRING_CST traversal.
I suspect I got confused into thinking that TREE_STRING_LENGTH is
the length of the string literal as opposed to its size, even
after all this time working with GCC strings. I wonder if
the macro could be renamed to something like TREE_STRING_SIZE.

I also added more tests for member pointers, including those to
member functions and managed to trigger another ICE in the process:

   struct A { void (A::*p)(); };
   template <A> struct X { };
   X<A{ 0 }> x;   // ICE in find_substitution during mangling

I opened bug 89974 for it even though the patch happens to avoid
it because it treats a null member function pointer as an empty
initializer list (i.e., { }).  The added test exposes the mangling
problems with pointers to data members.  I xfailed them.

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..a5fd66fe0de 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,31 @@ 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.  */
+	  for (const char *p = str + n - 1; ; --p)
+	    {
+	      if (*p || p == str)
+		{
+		  n -= str + n - !!*p - p;
+		  break;
+		}
+	    }
+	  tree eltype = TREE_TYPE (valtype);
+	  for (const char *p = str; n--; ++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..656a0cae403
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle72.C
@@ -0,0 +1,106 @@
+// Verify manglinng of class literals with pointers to members.
+// Some of the mangling here is wrong.  Note the FIXME comments below.
+// { 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" } }
+
+
+// Exercise arrays of pointers to data members.
+typedef int (A::*padm_t)[2];
+
+struct B { padm_t a[2]; };
+template <B> struct Y { };
+
+void g__ (Y<B{{ }}>) { }
+// { dg-final { scan-assembler "_Z3g__1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } }
+
+void g0_ (Y<B{{ 0 }}>) { }
+// { dg-final { scan-assembler "_Z3g0_1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } }
+
+void g00 (Y<B{{ 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z3g001YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } }
+
+void g0x (Y<B{{ 0, &A::a }}>) { }
+// FIXME: This needs to mangle differently from g00.  The space at
+// the end is intentional to make the directive fail so that the xfail
+// can be reminder to change this once the mangling is fixed.
+// { dg-final { scan-assembler "_Z3g0x1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE " { xfail *-*-* } } }
+
+void gx_ (Y<B{{ &A::a }}>) { }
+// { dg-final { scan-assembler "_Z3gx_1YIXtl1BtlA2_M1AA2_iLS3_0ELS3_0EEEEE" } }
+
+
+struct C { padm_t a[3]; };
+template <C> struct Z { };
+
+void h___ (Z<C{{ }}>) { }
+// { dg-final { scan-assembler "_Z4h___1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
+
+void h0__ (Z<C{{ 0 }}>) { }
+// { dg-final { scan-assembler "_Z4h0__1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
+
+void h00_ (Z<C{{ 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z4h00_1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
+
+void h000 (Z<C{{ 0, 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z4h0001ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
+
+void h00x (Z<C{{ 0, 0, &A::a }}>) { }
+// FIXME: This needs to mangle differently from hx0_ and hx__.
+// { dg-final { scan-assembler "_Z4h00x1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0EEEEE " { xfail *-*-*} } }
+
+void h0x0 (Z<C{{ 0, &A::a, 0 }}>) { }
+// { dg-final { scan-assembler "_Z4h0x01ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0ELS3_0EEEEE" } }
+
+void h0x_ (Z<C{{ 0, &A::a }}>) { }
+// { dg-final { scan-assembler "_Z4h0x_1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0ELS3_0EEEEE" } }
+
+void hx0_ (Z<C{{ &A::a, 0 }}>) { }
+// FIXME: This needs to mangle differently from h00x and hx__.
+// { dg-final { scan-assembler "_Z4hx0_1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0EEEEE " { xfail *-*-*} } }
+
+void hx__ (Z<C{{ &A::a }}>) { }
+// FIXME: This needs to mangle differently from h00x and hx0_.
+// { dg-final { scan-assembler "_Z4hx__1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0EEEEE " { xfail *-*-* } } }
+
+
+// Exercise arrays of pointers to function members.
+
+struct AF { void f (); };
+typedef void (AF::*pafm_t)();
+
+struct D { pafm_t a[2]; };
+template <D> struct F { };
+
+void k__ (F<D{{ }}>) { }
+// { dg-final { scan-assembler "_Z3k__1FIXtl1DEEE" } }
+
+void k0_ (F<D{{ 0 }}>) { }
+// { dg-final { scan-assembler "_Z3k0_1FIXtl1DEEE" } }
+
+void k00 (F<D{{ 0, 0 }}>) { }
+// { dg-final { scan-assembler "_Z3k001FIXtl1DEEE" } }
+
+void k0x (F<D{{ 0, &AF::f }}>) { }
+// { dg-final { scan-assembler "_Z3k0x1FIXtl1DtlA2_M2AFFvvEtlS3_EtlS3_adL_ZNS1_1fEvEEEEEE" } }
+
+void kx_ (F<D{{ &AF::f }}>) { }
+// { dg-final { scan-assembler "_Z3kx_1FIXtl1DtlA2_M2AFFvvEtlS3_adL_ZNS1_1fEvEEEEEE" } }
+
+void kx0 (F<D{{ &AF::f, 0 }}>) { }
+// { dg-final { scan-assembler "_Z3kx01FIXtl1DtlA2_M2AFFvvEtlS3_adL_ZNS1_1fEvEEEEEE" } }
+
+void kxx (F<D{{ &AF::f, &AF::f }}>) { }
+// { dg-final { scan-assembler "_Z3kxx1FIXtl1DtlA2_M2AFFvvEtlS3_adL_ZNS1_1fEvEEtlS3_adL_ZNS1_1fEvEEEEEE" } }
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);
+}
Jason Merrill April 4, 2019, 7:31 p.m. UTC | #7
On 4/4/19 2:30 PM, Martin Sebor wrote:
> On 4/4/19 10:50 AM, Jason Merrill wrote:
>> On 4/4/19 12:29 PM, Martin Sebor wrote:
>>> On 4/4/19 8:57 AM, Jason Merrill wrote:
>>>> On 4/3/19 10:34 PM, Martin Sebor wrote:
>>>>> 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).
>>>>
>>>> Hmm, let me poke at this a bit.
>>>
>>> Here's a test case showing that mangling null member pointers
>>> as zero leads to conflicts:
>>>
>>>    struct A { int i; };
>>>    typedef int A::*pam_t;
>>>    struct B { pam_t a[2]; };
>>>    template <B> struct Y { };
>>>
>>>    // both mangle as _Z2.31YIXtl1BtlA2_M1AiLS2_0ELS2_0EEEEE
>>>    void f (Y<B{{ 0, 0 }}>) { }
>>>    void g (Y<B{{ 0, &A::i }}>) { }
>>>
>>> This happens both with trunk and with my patch.  They probably
>>> need to mangle as negative one, don't you think?  (There's
>>> a comment saying mangling them as zeros is intentional but not
>>> why.)
>>
>> Hmm.  If we leave out f, then g mangles as 0 and &A::i, which is fine.
>> Need to figure out why creating a B before the declaration of g breaks 
>> that.
> 
> Let me know if you want me to start looking into this or if you
> are on it yourself.

Sure, go ahead.

>> The rationale for mangling as 0 would have been to reflect what you 
>> would write in the source: (int A::*)0 does give a null member 
>> pointer. But then we really can't use that representation 0 for 
>> anything else, we have to use a symbolic representation.   This patch 
>> doesn't need to fix that.
> 
> Okay.  It doesn't look like it would be hard to change but I'm
> happy to leave it for later.

Feel free to fix it in a followup patch, along with investigating the above.

> Attached is a patch that just simplifies the STRING_CST traversal.

OK.

Jason
H.J. Lu April 5, 2019, 4:20 a.m. UTC | #8
On Thu, Apr 4, 2019 at 11:30 AM Martin Sebor <msebor@gmail.com> wrote:
>
> On 4/4/19 10:50 AM, Jason Merrill wrote:
> > On 4/4/19 12:29 PM, Martin Sebor wrote:
> >> On 4/4/19 8:57 AM, Jason Merrill wrote:
> >>> On 4/3/19 10:34 PM, Martin Sebor wrote:
> >>>> 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).
> >>>
> >>> Hmm, let me poke at this a bit.
> >>
> >> Here's a test case showing that mangling null member pointers
> >> as zero leads to conflicts:
> >>
> >>    struct A { int i; };
> >>    typedef int A::*pam_t;
> >>    struct B { pam_t a[2]; };
> >>    template <B> struct Y { };
> >>
> >>    // both mangle as _Z2.31YIXtl1BtlA2_M1AiLS2_0ELS2_0EEEEE
> >>    void f (Y<B{{ 0, 0 }}>) { }
> >>    void g (Y<B{{ 0, &A::i }}>) { }
> >>
> >> This happens both with trunk and with my patch.  They probably
> >> need to mangle as negative one, don't you think?  (There's
> >> a comment saying mangling them as zeros is intentional but not
> >> why.)
> >
> > Hmm.  If we leave out f, then g mangles as 0 and &A::i, which is fine.
> > Need to figure out why creating a B before the declaration of g breaks
> > that.
>
> Let me know if you want me to start looking into this or if you
> are on it yourself.
>
> >
> > The rationale for mangling as 0 would have been to reflect what you
> > would write in the source: (int A::*)0 does give a null member pointer.
> > But then we really can't use that representation 0 for anything else, we
> > have to use a symbolic representation.   This patch doesn't need to fix
> > that.
>
> Okay.  It doesn't look like it would be hard to change but I'm
> happy to leave it for later.
>
> Attached is a patch that just simplifies the STRING_CST traversal.
> I suspect I got confused into thinking that TREE_STRING_LENGTH is
> the length of the string literal as opposed to its size, even
> after all this time working with GCC strings. I wonder if
> the macro could be renamed to something like TREE_STRING_SIZE.
>
> I also added more tests for member pointers, including those to
> member functions and managed to trigger another ICE in the process:
>
>    struct A { void (A::*p)(); };
>    template <A> struct X { };
>    X<A{ 0 }> x;   // ICE in find_substitution during mangling
>
> I opened bug 89974 for it even though the patch happens to avoid
> it because it treats a null member function pointer as an empty
> initializer list (i.e., { }).  The added test exposes the mangling
> problems with pointers to data members.  I xfailed them.
>

This caused:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89980
diff mbox series

Patch

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.

Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 270037)
+++ gcc/cp/decl.c	(working copy)
@@ -5799,6 +5799,9 @@  reshape_init_array_1 (tree elt_type, tree max_inde
 	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,28 @@  reshape_init_array_1 (tree elt_type, tree max_inde
       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 && trivial_type_p (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;
 }
 
Index: gcc/cp/mangle.c
===================================================================
--- gcc/cp/mangle.c	(revision 270037)
+++ gcc/cp/mangle.c	(working copy)
@@ -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);
 	}
-      FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
-	write_expression (val);
+
+      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);
+	    }
+	}
       write_char ('E');
     }
   else if (code == LAMBDA_EXPR)
@@ -3353,9 +3383,15 @@  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.  */
   if (null_member_pointer_value_p (value))
@@ -3397,9 +3433,36 @@  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 ();
       }
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-array19.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array19.C	(nonexistent)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array19.C	(working copy)
@@ -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);
+}
Index: gcc/testsuite/g++.dg/cpp2a/nontype-class15.C
===================================================================
--- gcc/testsuite/g++.dg/cpp2a/nontype-class15.C	(nonexistent)
+++ gcc/testsuite/g++.dg/cpp2a/nontype-class15.C	(working copy)
@@ -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) { }
Index: gcc/testsuite/g++.dg/abi/mangle69.C
===================================================================
--- gcc/testsuite/g++.dg/abi/mangle69.C	(nonexistent)
+++ gcc/testsuite/g++.dg/abi/mangle69.C	(working copy)
@@ -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" } }
Index: gcc/testsuite/g++.dg/abi/mangle70.C
===================================================================
--- gcc/testsuite/g++.dg/abi/mangle70.C	(nonexistent)
+++ gcc/testsuite/g++.dg/abi/mangle70.C	(working copy)
@@ -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" } }
Index: gcc/testsuite/g++.dg/abi/mangle71.C
===================================================================
--- gcc/testsuite/g++.dg/abi/mangle71.C	(nonexistent)
+++ gcc/testsuite/g++.dg/abi/mangle71.C	(working copy)
@@ -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" } }