diff options
author | Nathaniel Shead <nathanieloshead@gmail.com> | 2025-08-08 23:23:18 +1000 |
---|---|---|
committer | Nathaniel Shead <nathanieloshead@gmail.com> | 2025-08-17 10:14:14 +1000 |
commit | 7921bb4afcb7a3be8e10e63b10acfc2bfa477cae (patch) | |
tree | aa4dc446d6d55a3029a6a720155ae9d3c37f8349 | |
parent | 38d76a4e6033016b5e2dcaae57df67075a605edd (diff) | |
download | gcc-7921bb4afcb7a3be8e10e63b10acfc2bfa477cae.zip gcc-7921bb4afcb7a3be8e10e63b10acfc2bfa477cae.tar.gz gcc-7921bb4afcb7a3be8e10e63b10acfc2bfa477cae.tar.bz2 |
c++: Implement P2115R0 linkage changes for unnamed unscoped enums [PR120503]
We currently list P2115R0 as implemented, but only the modules changes
had been done. This patch implements the linkage changes so that
unnamed unscoped enums will use the name of the first enumerator for
linkage purposes.
This is (strictly speaking) a breaking change, as code that previously
relied on unnamed enumerations being internal linkage may have overloads
using those types become exposed and clash with other functions in a
different TU that have been similarly exposed. As such this feature is
only implemented for C++20.
No ABI flag warning is provided, partly because C++20 is still an
experimental standard, but also because any affected functions could not
have been part of an ABI until this change anyway.
A number of testcases that are testing for behaviour of no-linkage types
are adjusted to use an enumeration with no values, so that the pre-C++20
and post-C++20 behaviour is equivalently tested.
In terms of implementation, I had originally considered adjusting the
DECL_NAME of the enum, as with 'name_unnamed_type', but this ended up
being more complicated as it had unwanted interactions with the existing
modules streaming and with name lookup and diagnostic messages. This
patch instead uses a new function to derive this case.
The standard says that ([dcl.enum] p11) such an enum "...is denoted, for
linkage purposes, by its underlying type and its first enumerator", so
we need to add a new mangling production as well to handle this.
PR c++/120503
PR c++/120824
gcc/cp/ChangeLog:
* cp-tree.h (TYPE_UNNAMED_P): Adjust for enums with enumerators
for linkage purposes.
(enum_with_enumerator_for_linkage_p): Declare.
* decl.cc (name_unnamed_type): Adjust assertions to handle enums
with enumerators for linkage purposes.
(grokdeclarator): Use a typedef name for enums with enumerators
for linkage purposes.
(enum_with_enumerator_for_linkage_p): New function.
(finish_enum_value_list): Reset type linkage for enums with
enumerators for linkage purposes.
* mangle.cc (write_unnamed_enum_name): New function.
(write_unqualified_name): Handle enums with enumerators for
linkage purposes.
* tree.cc (decl_linkage): Fixup unnamed enums.
gcc/testsuite/ChangeLog:
* g++.dg/abi/mangle32.C: Remove enumerator list.
* g++.dg/cpp0x/linkage2.C: Likewise.
* g++.dg/ext/vector26.C: Likewise.
* g++.dg/other/anon3.C: Likewise.
* g++.dg/abi/mangle83.C: New test.
* g++.dg/modules/enum-15_a.C: New test.
* g++.dg/modules/enum-15_b.C: New test.
include/ChangeLog:
* demangle.h (enum demangle_component_type): Add enumeration
DEMANGLE_COMPONENT_UNNAMED_ENUM.
libiberty/ChangeLog:
* cp-demangle.c (d_unnamed_enum): New function.
(d_unqualified_name): Call it.
(cplus_demangle_type): Handle unscoped unnamed types
(Ue, Ul, etc.)
(d_count_templates_scopes): Handle unnamed enums.
(d_find_pack): Likewise.
(d_print_comp_inner): Print unnamed enums.
* testsuite/demangle-expected: Add tests.
Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
-rw-r--r-- | gcc/cp/cp-tree.h | 4 | ||||
-rw-r--r-- | gcc/cp/decl.cc | 28 | ||||
-rw-r--r-- | gcc/cp/mangle.cc | 16 | ||||
-rw-r--r-- | gcc/cp/tree.cc | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/abi/mangle32.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/abi/mangle83.C | 29 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/linkage2.C | 6 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/vector26.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/modules/enum-15_a.C | 10 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/modules/enum-15_b.C | 18 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/other/anon3.C | 2 | ||||
-rw-r--r-- | include/demangle.h | 2 | ||||
-rw-r--r-- | libiberty/cp-demangle.c | 63 | ||||
-rw-r--r-- | libiberty/testsuite/demangle-expected | 7 |
14 files changed, 174 insertions, 21 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e7749fd..55e8e07 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 48547fb..140cc9b 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 fd69099..f48cb22 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); } +/* <unnamed-enum-name> ::= Ue <underlying type> <enumerator source-name> */ + +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 c66d8bf..e354da0 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 4c5b33b..edb542e 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 0000000..42df1b9 --- /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<auto V> int Frob () { return int (V); } + +enum { A = (unsigned int)12345, B = 0 }; +template int Frob<A> (); +template int Frob<B> (); +// { 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<C> (); +// typedef name 'X' should take precedence +// { dg-final { scan-assembler {_Z4FrobITnDaL1X5EEiv:} } } + +typedef enum : long long { D = 8 }* Y; +template int Frob<D> (); +// 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 34a36c4..0ec2cec 100644 --- a/gcc/testsuite/g++.dg/cpp0x/linkage2.C +++ b/gcc/testsuite/g++.dg/cpp0x/linkage2.C @@ -16,9 +16,9 @@ template <typename T> struct B { template <typename T> T B<T>::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 1d7a1e4..95cae39 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 0000000..e59a73a --- /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 0000000..43d7865 --- /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 d33eb41..36bf2d6 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 34e760a..a22e698 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 2d38148..8b4c6d1 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; } +/* <unnamed-enum-name> ::= Ue <underlying type> <enumerator source-name> */ + +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; +} + /* <clone-suffix> ::= [ . <clone-type-identifier> ] [ . <nonnegative number> ]* */ @@ -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 0f7b97a..e5cd8ec 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}}) |