From 6324c52bba490baa17b6001a6d555ff8bef939d6 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Sat, 4 Jul 2020 05:45:01 -0400 Subject: [PATCH] c++: Support non-type template parms of union type. 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 | 11 ++- gcc/cp/tree.c | 7 -- .../g++.dg/cpp2a/nontype-class-union1.C | 25 ++++++ libiberty/cp-demangle.c | 77 ++++++++++++++++++- libiberty/testsuite/demangle-expected | 9 +++ 5 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index 090fb529a98..ab2d8ecf2f2 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -3189,6 +3189,7 @@ write_expression (tree expr) { vec *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 diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 9effd27f587..1fcba55313a 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -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 index 00000000000..038d46fdac8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class-union1.C @@ -0,0 +1,25 @@ +// { dg-do compile { target c++20 } } + +template struct A {}; +template struct assert_same; +template struct assert_same {}; + +#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) { } +// { dg-final { scan-assembler "_Z1g1AIXtl1Udi1jLi0EEEE" } } +void g(A) { } diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c index cbfb2f937ca..b413ba2be5f 100644 --- a/libiberty/cp-demangle.c +++ b/libiberty/cp-demangle.c @@ -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 ::= sr ::= + + ::= + ::= di # .name = expr + ::= dx # [expr] = expr + ::= dX + # [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)); diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected index d8e50951f84..2da27849835 100644 --- a/libiberty/testsuite/demangle-expected +++ b/libiberty/testsuite/demangle-expected @@ -1456,3 +1456,12 @@ coro1::empty::operator co_await() const _ZNK3FoossERKS_ Foo::operator<=>(Foo const&) const + +_Z1f1AIXtl1Udi1iLi0EEEE +f(A) + +_Z1f1AIXtl1Xdi1adi1bdxLi3ELi1EEEE +f(A) + +_Z1f1AIXtl1Xdi1adi1bdXLi3ELi4ELi1EEEE +f(A) -- 2.30.2