c++: Support non-type template parms of union type.
authorJason Merrill <jason@redhat.com>
Sat, 4 Jul 2020 09:45:01 +0000 (05:45 -0400)
committerJason Merrill <jason@redhat.com>
Fri, 10 Jul 2020 12:36:50 +0000 (08:36 -0400)
Another thing newly allowed by P1907R1.  The ABI group has discussed
representing unions with designated initializers, and has separately
specified how to represent designators; this patch implements both.

gcc/cp/ChangeLog:

* tree.c (structural_type_p): Allow unions.
* mangle.c (write_expression): Express unions with a designator.

libiberty/ChangeLog:

* cp-demangle.c (cplus_demangle_operators): Add di, dx, dX.
(d_expression_1): Handle di and dX.
(is_designated_init, d_maybe_print_designated_init): New.
(d_print_comp_inner): Use d_maybe_print_designated_init.
* testsuite/demangle-expected: Add designator tests.

gcc/testsuite/ChangeLog:

* g++.dg/cpp2a/nontype-class-union1.C: New test.

gcc/cp/mangle.c
gcc/cp/tree.c
gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C [new file with mode: 0644]
libiberty/cp-demangle.c
libiberty/testsuite/demangle-expected

index 090fb529a98507ce24f8d1b02de456f3dcc4b8d8..ab2d8ecf2f27727c5fd820dac041c677a0ea2e14 100644 (file)
@@ -3189,6 +3189,7 @@ write_expression (tree expr)
            {
              vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (expr);
              unsigned last_nonzero = UINT_MAX, i;
+             constructor_elt *ce;
              tree val;
 
              if (!nontriv)
@@ -3197,12 +3198,18 @@ write_expression (tree expr)
                    last_nonzero = i;
 
              if (nontriv || last_nonzero != UINT_MAX)
-               FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
+               for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
                  {
                    if (i > last_nonzero)
                      break;
                    /* FIXME handle RANGE_EXPR */
-                   write_expression (val);
+                   if (TREE_CODE (etype) == UNION_TYPE)
+                     {
+                       /* Express the active member as a designator.  */
+                       write_string ("di");
+                       write_unqualified_name (ce->index);
+                     }
+                   write_expression (ce->value);
                  }
            }
          else
index 9effd27f58789572344999a3054ac81b6de87ce0..1fcba55313a7a1a08e3c0ad35b2ac4bf2b137b65 100644 (file)
@@ -4534,13 +4534,6 @@ structural_type_p (tree t, bool explain)
        structural types or (possibly multi-dimensional) array thereof.  */
   if (!CLASS_TYPE_P (t))
     return false;
-  if (TREE_CODE (t) == UNION_TYPE)
-    {
-      /* FIXME allow (and mangle) unions.  */
-      if (explain)
-       inform (location_of (t), "%qT is a union", t);
-      return false;
-    }
   if (!literal_type_p (t))
     {
       if (explain)
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C
new file mode 100644 (file)
index 0000000..038d46f
--- /dev/null
@@ -0,0 +1,25 @@
+// { dg-do compile { target c++20 } }
+
+template <auto N> struct A {};
+template <class,class> struct assert_same;
+template <class T> struct assert_same<T,T> {};
+
+#define TEQ(X,Y) static_assert(__is_same(A<(X)>,A<(Y)>))
+#define TNEQ(X,Y) static_assert(!__is_same(A<(X)>,A<(Y)>))
+
+union U {
+  int i; int j;
+  constexpr U(int i): i(i) {}
+  constexpr U(unsigned u): j(u) {}
+};
+
+TEQ(U(0),U(0));
+
+// Calling the other constructor initializes a different member with the same
+// value.  We need to distinguish these.
+TNEQ(U(0),U(0u));
+
+// { dg-final { scan-assembler "_Z1f1AIXtl1Udi1iLi0EEEE" } }
+void f(A<U(0)>) { }
+// { dg-final { scan-assembler "_Z1g1AIXtl1Udi1jLi0EEEE" } }
+void g(A<U(0u)>) { }
index cbfb2f937ca970bb57bf7983a466ac7fcf1cb478..b413ba2be5fe6e142e86978ba1391003bdc6bf1c 100644 (file)
@@ -1809,13 +1809,16 @@ const struct demangle_operator_info cplus_demangle_operators[] =
   { "cm", NL (","),         2 },
   { "co", NL ("~"),         1 },
   { "dV", NL ("/="),        2 },
+  { "dX", NL ("[...]="),     3 }, /* [expr...expr] = expr */
   { "da", NL ("delete[] "), 1 },
   { "dc", NL ("dynamic_cast"), 2 },
   { "de", NL ("*"),         1 },
+  { "di", NL ("="),         2 }, /* .name = expr */
   { "dl", NL ("delete "),   1 },
   { "ds", NL (".*"),        2 },
   { "dt", NL ("."),         2 },
   { "dv", NL ("/"),         2 },
+  { "dx", NL ("]="),        2 }, /* [expr] = expr */
   { "eO", NL ("^="),        2 },
   { "eo", NL ("^"),         2 },
   { "eq", NL ("=="),        2 },
@@ -3291,6 +3294,12 @@ op_is_new_cast (struct demangle_component *op)
                 ::= sr <type> <unqualified-name>
                 ::= sr <type> <unqualified-name> <template-args>
                 ::= <expr-primary>
+
+  <braced-expression> ::= <expression>
+                     ::= di <field source-name> <braced-expression>    # .name = expr
+                     ::= dx <index expression> <braced-expression>     # [expr] = expr
+                     ::= dX <range begin expression> <range end expression> <braced-expression>
+                                                                       # [expr ... expr] = expr
 */
 
 static inline struct demangle_component *
@@ -3453,6 +3462,8 @@ d_expression_1 (struct d_info *di)
            else if (code[0] == 'f')
              /* fold-expression.  */
              left = d_operator_name (di);
+           else if (!strcmp (code, "di"))
+             left = d_unqualified_name (di);
            else
              left = d_expression_1 (di);
            if (!strcmp (code, "cl"))
@@ -3480,7 +3491,8 @@ d_expression_1 (struct d_info *di)
 
            if (code == NULL)
              return NULL;
-           else if (!strcmp (code, "qu"))
+           else if (!strcmp (code, "qu")
+                    || !strcmp (code, "dX"))
              {
                /* ?: expression.  */
                first = d_expression_1 (di);
@@ -4675,6 +4687,64 @@ d_maybe_print_fold_expression (struct d_print_info *dpi, int options,
   return 1;
 }
 
+/* True iff DC represents a C99-style designated initializer.  */
+
+static int
+is_designated_init (struct demangle_component *dc)
+{
+  if (dc->type != DEMANGLE_COMPONENT_BINARY
+      && dc->type != DEMANGLE_COMPONENT_TRINARY)
+    return 0;
+
+  struct demangle_component *op = d_left (dc);
+  const char *code = op->u.s_operator.op->code;
+  return (code[0] == 'd'
+         && (code[1] == 'i' || code[1] == 'x' || code[1] == 'X'));
+}
+
+/* If DC represents a C99-style designated initializer, print it and return
+   true; otherwise, return false.  */
+
+static int
+d_maybe_print_designated_init (struct d_print_info *dpi, int options,
+                              struct demangle_component *dc)
+{
+  if (!is_designated_init (dc))
+    return 0;
+
+  const char *code = d_left (dc)->u.s_operator.op->code;
+
+  struct demangle_component *operands = d_right (dc);
+  struct demangle_component *op1 = d_left (operands);
+  struct demangle_component *op2 = d_right (operands);
+
+  if (code[1] == 'i')
+    d_append_char (dpi, '.');
+  else
+    d_append_char (dpi, '[');
+
+  d_print_comp (dpi, options, op1);
+  if (code[1] == 'X')
+    {
+      d_append_string (dpi, " ... ");
+      d_print_comp (dpi, options, d_left (op2));
+      op2 = d_right (op2);
+    }
+  if (code[1] != 'i')
+    d_append_char (dpi, ']');
+  if (is_designated_init (op2))
+    {
+      /* Don't put '=' or '(' between chained designators.  */
+      d_print_comp (dpi, options, op2);
+    }
+  else
+    {
+      d_append_char (dpi, '=');
+      d_print_subexpr (dpi, options, op2);
+    }
+  return 1;
+}
+
 /* Subroutine to handle components.  */
 
 static void
@@ -5491,6 +5561,9 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
       if (d_maybe_print_fold_expression (dpi, options, dc))
        return;
 
+      if (d_maybe_print_designated_init (dpi, options, dc))
+       return;
+
       /* We wrap an expression which uses the greater-than operator in
         an extra layer of parens so that it does not get confused
         with the '>' which ends the template parameters.  */
@@ -5548,6 +5621,8 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
        }
       if (d_maybe_print_fold_expression (dpi, options, dc))
        return;
+      if (d_maybe_print_designated_init (dpi, options, dc))
+       return;
       {
        struct demangle_component *op = d_left (dc);
        struct demangle_component *first = d_left (d_right (dc));
index d8e50951f844fc8da64ef597e6dd1e20b7809984..2da27849835b3aead85c7d6739ac88f0735d4eb2 100644 (file)
@@ -1456,3 +1456,12 @@ coro1::empty::operator co_await() const
 
 _ZNK3FoossERKS_
 Foo::operator<=>(Foo const&) const
+
+_Z1f1AIXtl1Udi1iLi0EEEE
+f(A<U{.i=(0)}>)
+
+_Z1f1AIXtl1Xdi1adi1bdxLi3ELi1EEEE
+f(A<X{.a.b[3]=(1)}>)
+
+_Z1f1AIXtl1Xdi1adi1bdXLi3ELi4ELi1EEEE
+f(A<X{.a.b[3 ... 4]=(1)}>)