diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e7749fd7839a..55e8e0736272 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2341,7 +2341,8 @@ enum languages { lang_c, lang_cplusplus }; /* Nonzero if NODE, a TYPE, has no name for linkage purposes. */ #define TYPE_UNNAMED_P(NODE) \ (TYPE_ANON_P (NODE) \ - && !IDENTIFIER_LAMBDA_P (TYPE_LINKAGE_IDENTIFIER (NODE))) + && !IDENTIFIER_LAMBDA_P (TYPE_LINKAGE_IDENTIFIER (NODE)) \ + && !enum_with_enumerator_for_linkage_p (NODE)) /* The _DECL for this _TYPE. */ #define TYPE_MAIN_DECL(NODE) (TYPE_STUB_DECL (TYPE_MAIN_VARIANT (NODE))) @@ -7325,6 +7326,7 @@ extern tree xref_tag (tag_types, tree, bool tpl_header_p = false); extern void xref_basetypes (tree, tree); extern tree start_enum (tree, tree, tree, tree, bool, bool *); +extern bool enum_with_enumerator_for_linkage_p (tree); extern void finish_enum_value_list (tree); extern void finish_enum (tree); extern tree build_enumerator (tree, tree, tree, tree, location_t); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 48547fba11c6..140cc9b4699a 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -13106,7 +13106,8 @@ maybe_diagnose_non_c_class_typedef_for_linkage (tree type, tree orig, tree t) void name_unnamed_type (tree type, tree decl) { - gcc_assert (TYPE_UNNAMED_P (type)); + gcc_assert (TYPE_UNNAMED_P (type) + || enum_with_enumerator_for_linkage_p (type)); /* Replace the anonymous decl with the real decl. Be careful not to rename other typedefs (such as the self-reference) of type. */ @@ -13132,7 +13133,8 @@ name_unnamed_type (tree type, tree decl) /* Check that our job is done, and that it would fail if we attempted to do it again. */ - gcc_assert (!TYPE_UNNAMED_P (type)); + gcc_assert (!TYPE_UNNAMED_P (type) + && !enum_with_enumerator_for_linkage_p (type)); } /* Check that decltype(auto) was well-formed: only plain decltype(auto) @@ -15382,7 +15384,10 @@ grokdeclarator (const cp_declarator *declarator, && unqualified_id && TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && TYPE_UNNAMED_P (type) + && (TYPE_UNNAMED_P (type) + /* An enum may have previously used an enumerator for linkage + purposes, but we want the typedef name to take priority. */ + || enum_with_enumerator_for_linkage_p (type)) && declspecs->type_definition_p && attributes_naming_typedef_ok (*attrlist) && cp_type_quals (type) == TYPE_UNQUALIFIED) @@ -18225,6 +18230,18 @@ start_enum (tree name, tree enumtype, tree underlying_type, return enumtype; } +/* Returns true if TYPE is an enum that uses an enumerator name for + linkage purposes. */ + +bool +enum_with_enumerator_for_linkage_p (tree type) +{ + return (cxx_dialect >= cxx20 + && UNSCOPED_ENUM_P (type) + && TYPE_ANON_P (type) + && TYPE_VALUES (type)); +} + /* After processing and defining all the values of an enumeration type, install their decls in the enumeration type. ENUMTYPE is the type object. */ @@ -18455,6 +18472,11 @@ finish_enum_value_list (tree enumtype) fixup_type_variants (current_class_type); } + /* P2115: An unnamed enum uses the name of its first enumerator for + linkage purposes; reset the type linkage if that is the case. */ + if (enum_with_enumerator_for_linkage_p (enumtype)) + reset_type_linkage (enumtype); + /* Finish debugging output for this type. */ rest_of_type_compilation (enumtype, namespace_bindings_p ()); diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index fd69099c8fd5..f48cb22a3c04 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -203,6 +203,7 @@ static void write_conversion_operator_name (const tree); static void write_source_name (tree); static void write_literal_operator_name (tree); static void write_unnamed_type_name (const tree); +static void write_unnamed_enum_name (const tree); static void write_closure_type_name (const tree); static int hwint_to_ascii (unsigned HOST_WIDE_INT, const unsigned int, char *, const unsigned int); @@ -1591,7 +1592,9 @@ write_unqualified_name (tree decl) tree type = TREE_TYPE (decl); if (TREE_CODE (decl) == TYPE_DECL - && TYPE_UNNAMED_P (type)) + && enum_with_enumerator_for_linkage_p (type)) + write_unnamed_enum_name (type); + else if (TREE_CODE (decl) == TYPE_DECL && TYPE_UNNAMED_P (type)) write_unnamed_type_name (type); else if (TREE_CODE (decl) == TYPE_DECL && LAMBDA_TYPE_P (type)) write_closure_type_name (type); @@ -1820,6 +1823,17 @@ write_unnamed_type_name (const tree type) write_compact_number (discriminator); } +/* ::= Ue */ + +static void +write_unnamed_enum_name (const tree type) +{ + MANGLE_TRACE_TREE ("unnamed-enum-name", type); + write_string ("Ue"); + write_type (ENUM_UNDERLYING_TYPE (type)); + write_source_name (DECL_NAME (TREE_VALUE (TYPE_VALUES (type)))); +} + /* ABI issue #47: if a function template parameter is not "natural" for its argument we must mangle the parameter. */ diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index c66d8bfa69c4..e354da0f0185 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -6399,8 +6399,8 @@ decl_linkage (tree decl) if (NAMESPACE_SCOPE_P (decl) && (!DECL_NAME (decl) || IDENTIFIER_ANON_P (DECL_NAME (decl)))) { - if (TREE_CODE (decl) == TYPE_DECL && !TYPE_ANON_P (TREE_TYPE (decl))) - /* This entity has a typedef name for linkage purposes. */; + if (TREE_CODE (decl) == TYPE_DECL && !TYPE_UNNAMED_P (TREE_TYPE (decl))) + /* This entity has a name for linkage purposes. */; else if (DECL_DECOMPOSITION_P (decl) && DECL_DECOMP_IS_BASE (decl)) /* Namespace-scope structured bindings can have linkage. */; else if (TREE_CODE (decl) == NAMESPACE_DECL && cxx_dialect >= cxx11) diff --git a/gcc/testsuite/g++.dg/abi/mangle32.C b/gcc/testsuite/g++.dg/abi/mangle32.C index 4c5b33b8b4d1..edb542e28c1b 100644 --- a/gcc/testsuite/g++.dg/abi/mangle32.C +++ b/gcc/testsuite/g++.dg/abi/mangle32.C @@ -15,7 +15,7 @@ void f(B) { } struct C { typedef struct { }* D; - typedef enum { e }* E; + typedef enum { }* E; }; // { dg-final { scan-assembler "_Z2g1PN1CUt_E" } } @@ -31,7 +31,7 @@ void h2(T t) { } inline void j() { - typedef enum { f }* F; + typedef enum { }* F; // { dg-final { scan-assembler "_Z2h1IPZ1jvEUt_EvT_" } } h1(F()); typedef struct { }* G; diff --git a/gcc/testsuite/g++.dg/abi/mangle83.C b/gcc/testsuite/g++.dg/abi/mangle83.C new file mode 100644 index 000000000000..42df1b979b99 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/mangle83.C @@ -0,0 +1,29 @@ +// PR c++/120503 +// Implement P2115r0 "merging definitions of unnamed unscoped enums" +// { dg-do compile { target c++14 } } + +template int Frob () { return int (V); } + +enum { A = (unsigned int)12345, B = 0 }; +template int Frob (); +template int Frob (); +// { dg-final { scan-assembler {_Z4FrobITnDaLUej1A12345EEiv:} { target c++20 } } } +// { dg-final { scan-assembler {_Z4FrobITnDaL8._anon_012345EEiv:} { target c++17_down } } } +// { dg-final { scan-assembler {_Z4FrobITnDaLUej1A0EEiv:} { target c++20 } } } +// { dg-final { scan-assembler {_Z4FrobITnDaL8._anon_00EEiv:} { target c++17_down } } } + +enum { C = 5 } typedef X; +template int Frob (); +// typedef name 'X' should take precedence +// { dg-final { scan-assembler {_Z4FrobITnDaL1X5EEiv:} } } + +typedef enum : long long { D = 8 }* Y; +template int Frob (); +// but 'Y' is not a typedef name here +// { dg-final { scan-assembler {_Z4FrobITnDaLUex1D8EEiv:} { target c++20 } } } +// { dg-final { scan-assembler {_Z4FrobITnDaL8._anon_28EEiv:} { target c++17_down } } } + +enum : int { E }; +void foo(decltype(E), decltype(E)) {} +// { dg-final { scan-assembler {_Z3fooUei1ES_:} { target c++20 } } } +// { dg-final { scan-assembler {_Z3foo8._anon_3S_:} { target c++17_down } } } diff --git a/gcc/testsuite/g++.dg/cpp0x/linkage2.C b/gcc/testsuite/g++.dg/cpp0x/linkage2.C index 34a36c45a586..0ec2cec8a994 100644 --- a/gcc/testsuite/g++.dg/cpp0x/linkage2.C +++ b/gcc/testsuite/g++.dg/cpp0x/linkage2.C @@ -16,9 +16,9 @@ template struct B { template T B::t2 = { }; -enum { E1 } e1; // OK, defined -extern enum { E2 } e2; // { dg-error "never defined" } -extern "C" enum { E3 } e3; // OK, extern "C" +enum { } e1; // OK, defined +extern enum { } e2; // { dg-error "never defined" } +extern "C" enum { } e3; // OK, extern "C" void f() { struct A { int x; }; // no linkage diff --git a/gcc/testsuite/g++.dg/ext/vector26.C b/gcc/testsuite/g++.dg/ext/vector26.C index 1d7a1e47160d..95cae393fb56 100644 --- a/gcc/testsuite/g++.dg/ext/vector26.C +++ b/gcc/testsuite/g++.dg/ext/vector26.C @@ -3,7 +3,7 @@ // gets internal linkage. // { dg-options "-mmmx" { target { { i?86-*-* x86_64-*-* } && ilp32 } } } */ -typedef enum { e } T __attribute__((vector_size(8))); +typedef enum { } T __attribute__((vector_size(8))); static void foo(T t) {} void bar (T t) {} // { dg-error "no linkage" "" { target { ! c++11 } } } // { dg-final { scan-assembler-not "globl\[ \t]*_Z3bar" { target c++11 } } } diff --git a/gcc/testsuite/g++.dg/modules/enum-15_a.C b/gcc/testsuite/g++.dg/modules/enum-15_a.C new file mode 100644 index 000000000000..e59a73a09e95 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/enum-15_a.C @@ -0,0 +1,10 @@ +// PR c++/120824 +// { dg-additional-options "-fmodules -Wno-global-module -std=c++20" } +// { dg-module-cmi M } + +module; +enum { E }; +enum { F }; +export module M; +export using ::E; +export using ::F; diff --git a/gcc/testsuite/g++.dg/modules/enum-15_b.C b/gcc/testsuite/g++.dg/modules/enum-15_b.C new file mode 100644 index 000000000000..43d7865e7498 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/enum-15_b.C @@ -0,0 +1,18 @@ +// PR c++/120824 +// { dg-additional-options "-fmodules -Wno-global-module -std=c++20" } +// { dg-module-cmi !bad } + +module; +enum { E }; +namespace { + enum { G }; // { dg-message "internal" } +} +export module bad; +import M; +inline void ok() { + auto a = E; + auto b = F; +} +inline void err() { // { dg-error "TU-local" } + auto c = G; +} diff --git a/gcc/testsuite/g++.dg/other/anon3.C b/gcc/testsuite/g++.dg/other/anon3.C index d33eb413ccfa..36bf2d64520c 100644 --- a/gcc/testsuite/g++.dg/other/anon3.C +++ b/gcc/testsuite/g++.dg/other/anon3.C @@ -4,4 +4,4 @@ // { dg-do compile } -enum { a = 3 } x; // { dg-warning "unnamed type" "" { target { ! c++11 } } } +enum { } x; // { dg-warning "unnamed type" "" { target { ! c++11 } } } diff --git a/include/demangle.h b/include/demangle.h index 34e760ab65d6..a22e69858ce6 100644 --- a/include/demangle.h +++ b/include/demangle.h @@ -435,6 +435,8 @@ enum demangle_component_type DEMANGLE_COMPONENT_DEFAULT_ARG, /* An unnamed type. */ DEMANGLE_COMPONENT_UNNAMED_TYPE, + /* An unnamed enum. */ + DEMANGLE_COMPONENT_UNNAMED_ENUM, /* A transactional clone. This has one subtree, the encoding for which it is providing alternative linkage. */ DEMANGLE_COMPONENT_TRANSACTION_CLONE, diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c index 2d38148da931..8b4c6d139a95 100644 --- a/libiberty/cp-demangle.c +++ b/libiberty/cp-demangle.c @@ -499,6 +499,8 @@ static struct demangle_component *d_lambda (struct d_info *); static struct demangle_component *d_unnamed_type (struct d_info *); +static struct demangle_component *d_unnamed_enum (struct d_info *); + static struct demangle_component * d_clone_suffix (struct d_info *, struct demangle_component *); @@ -1799,6 +1801,9 @@ d_unqualified_name (struct d_info *di, struct demangle_component *scope, { switch (d_peek_next_char (di)) { + case 'e': + ret = d_unnamed_enum (di); + break; case 'l': ret = d_lambda (di); break; @@ -2728,13 +2733,20 @@ cplus_demangle_type (struct d_info *di) break; case 'U': - d_advance (di, 1); - ret = d_source_name (di); - if (d_peek_char (di) == 'I') - ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, - d_template_args (di)); - ret = d_make_comp (di, DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL, - cplus_demangle_type (di), ret); + peek = d_peek_next_char (di); + if (IS_DIGIT (peek)) + { + d_advance (di, 1); + ret = d_source_name (di); + if (d_peek_char (di) == 'I') + ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, + d_template_args (di)); + ret = d_make_comp (di, DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL, + cplus_demangle_type (di), ret); + } + else + /* Could be a closure type or an unnamed enum. */ + ret = d_unqualified_name (di, NULL, NULL); break; case 'D': @@ -4090,6 +4102,33 @@ d_unnamed_type (struct d_info *di) return ret; } +/* ::= Ue */ + +static struct demangle_component * +d_unnamed_enum (struct d_info *di) +{ + if (! d_check_char (di, 'U')) + return NULL; + if (! d_check_char (di, 'e')) + return NULL; + + struct demangle_component *underlying = cplus_demangle_type (di); + struct demangle_component *name = d_source_name (di); + + struct demangle_component *ret = d_make_empty (di); + if (ret) + { + ret->type = DEMANGLE_COMPONENT_UNNAMED_ENUM; + d_left (ret) = underlying; + d_right (ret) = name; + } + + if (! d_add_substitution (di, ret)) + return NULL; + + return ret; +} + /* ::= [ . ] [ . ]* */ @@ -4396,6 +4435,7 @@ d_count_templates_scopes (struct d_print_info *dpi, case DEMANGLE_COMPONENT_CHARACTER: case DEMANGLE_COMPONENT_NUMBER: case DEMANGLE_COMPONENT_UNNAMED_TYPE: + case DEMANGLE_COMPONENT_UNNAMED_ENUM: case DEMANGLE_COMPONENT_STRUCTURED_BINDING: case DEMANGLE_COMPONENT_MODULE_NAME: case DEMANGLE_COMPONENT_MODULE_PARTITION: @@ -4780,6 +4820,7 @@ d_find_pack (struct d_print_info *dpi, case DEMANGLE_COMPONENT_CHARACTER: case DEMANGLE_COMPONENT_FUNCTION_PARAM: case DEMANGLE_COMPONENT_UNNAMED_TYPE: + case DEMANGLE_COMPONENT_UNNAMED_ENUM: case DEMANGLE_COMPONENT_DEFAULT_ARG: case DEMANGLE_COMPONENT_NUMBER: return NULL; @@ -6258,6 +6299,14 @@ d_print_comp_inner (struct d_print_info *dpi, int options, d_append_char (dpi, '}'); return; + case DEMANGLE_COMPONENT_UNNAMED_ENUM: + d_append_string (dpi, "{enum:"); + d_print_comp (dpi, options, d_left (dc)); + d_append_string (dpi, "{"); + d_print_comp (dpi, options, d_right (dc)); + d_append_string (dpi, "}}"); + return; + case DEMANGLE_COMPONENT_CLONE: d_print_comp (dpi, options, d_left (dc)); d_append_string (dpi, " [clone "); diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected index 0f7b97a6d0ad..e5cd8ec8a776 100644 --- a/libiberty/testsuite/demangle-expected +++ b/libiberty/testsuite/demangle-expected @@ -1709,3 +1709,10 @@ void S::bar<5, int>(this S, int) _ZNH1S3bazERKS_ S::baz(this S const&) + +_Z3fooUlvE_ +foo({lambda()#1}) + +# P2115R0 unnamed enums +_Z3fooUei1ES_ +foo({enum:int{E}}, {enum:int{E}})