On Wed, Feb 25, 2026 at 08:50:40PM +0100, Jakub Jelinek wrote: > Will change next week.
Here is an updated patch. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2026-03-04 Jakub Jelinek <[email protected]> PR c++/123810 * cp-tree.h (TYPE_DECL_FOR_LINKAGE_PURPOSES_P): Define. (TYPE_DECL_WAS_UNNAMED): Likewise. (TYPE_WAS_UNNAMED): Also check TYPE_DECL_WAS_UNNAMED. * decl.cc (start_decl): Use TYPE_DECL_FOR_LINKAGE_PURPOSES_P. (maybe_diagnose_non_c_class_typedef_for_l): If t == type, use DECL_SOURCE_LOCATION (orig) instead of DECL_SOURCE_LOCATION (TYPE_NAME (t)). (name_unnamed_type): Set TYPE_DECL_FOR_LINKAGE_PURPOSES_P on decl. For -freflection don't change TYPE_NAME from orig to decl, but instead change DECL_NAME (orig) to DECL_NAME (decl) and set TYPE_DECL_FOR_LINKAGE_PURPOSES_P on orig too. * decl2.cc (grokfield): Use TYPE_DECL_FOR_LINKAGE_PURPOSES_P. * name-lookup.cc (fields_linear_search): Ignore TYPE_DECL_WAS_UNNAMED decls. (count_class_fields): Likewise. (member_vec_append_class_fields): Likewise. (pop_local_binding): Likewise. * reflect.cc (namespace_members_of): For TYPE_DECL with TYPE_DECL_FOR_LINKAGE_PURPOSES_P set also append reflection of strip_typedefs (m). * class.cc (find_flexarrays): Handle TYPE_DECLs with TYPE_DECL_FOR_LINKAGE_PURPOSES_P like the ones with IDENTIFIER_ANON_P name. * g++.dg/reflect/members_of10.C: New test. * g++.dg/cpp2a/typedef1.C: Expect one message on a different line. --- gcc/cp/cp-tree.h.jj 2026-03-02 07:43:12.342788130 +0100 +++ gcc/cp/cp-tree.h 2026-03-03 17:22:57.917038151 +0100 @@ -591,6 +591,7 @@ extern GTY(()) tree cp_global_trees[CPTI 7: DECL_THUNK_P (in a member FUNCTION_DECL) DECL_NORMAL_CAPTURE_P (in FIELD_DECL) DECL_DECLARED_CONSTINIT_P (in VAR_DECL) + TYPE_DECL_FOR_LINKAGE_PURPOSES_P (in TYPE_DECL) 8: DECL_DECLARED_CONSTEXPR_P (in VAR_DECL, FUNCTION_DECL) Usage of language-independent fields in a language-dependent manner: @@ -4000,6 +4001,20 @@ struct GTY(()) lang_decl { && TREE_CODE (TYPE_NAME (NODE)) == TYPE_DECL \ && TYPE_DECL_ALIAS_P (TYPE_NAME (NODE))) +/* Nonzero for typedef name for linkage purposes. For -freflection + set also on the originally unnamed TYPE_DECL. */ +#define TYPE_DECL_FOR_LINKAGE_PURPOSES_P(NODE) \ + DECL_LANG_FLAG_7 (TYPE_DECL_CHECK (NODE)) + +/* Nonzero for TYPE_DECL originally with IDENTIFIER_ANON_P DECL_NAME + later on named by a typedef name for linkage purposes in the + -freflection case (otherwise the TYPE_DECL keeps IDENTIFIER_ANON_P + DECL_NAME). */ +#define TYPE_DECL_WAS_UNNAMED(NODE) \ + (TREE_CODE (NODE) == TYPE_DECL \ + && TYPE_DECL_FOR_LINKAGE_PURPOSES_P (NODE) \ + && DECL_IMPLICIT_TYPEDEF_P (NODE)) + /* If non-NULL for a VAR_DECL, FUNCTION_DECL, TYPE_DECL, TEMPLATE_DECL, or CONCEPT_DECL, the entity is either a template specialization (if DECL_USE_TEMPLATE is nonzero) or the abstract instance of the @@ -5372,10 +5387,13 @@ get_vec_init_expr (tree t) /* True if TYPE is an unnamed structured type with a typedef for linkage purposes. In that case TYPE_NAME and TYPE_STUB_DECL of the - MAIN-VARIANT are different. */ + MAIN-VARIANT are different or TYPE_DECL_WAS_UNNAMED + is true for the TYPE_NAME. */ #define TYPE_WAS_UNNAMED(NODE) \ (TYPE_NAME (TYPE_MAIN_VARIANT (NODE)) \ - != TYPE_STUB_DECL (TYPE_MAIN_VARIANT (NODE))) + != TYPE_STUB_DECL (TYPE_MAIN_VARIANT (NODE)) \ + || TYPE_DECL_WAS_UNNAMED \ + (TYPE_NAME (TYPE_MAIN_VARIANT (NODE)))) /* C++: all of these are overloaded! These apply only to TYPE_DECLs. */ --- gcc/cp/decl.cc.jj 2026-02-17 15:56:52.071376438 +0100 +++ gcc/cp/decl.cc 2026-03-03 17:22:57.919239897 +0100 @@ -6618,8 +6618,7 @@ start_decl (const cp_declarator *declara /* If this is a typedef that names the class for linkage purposes (7.1.3p8), apply any attributes directly to the type. */ if (TREE_CODE (decl) == TYPE_DECL - && OVERLOAD_TYPE_P (TREE_TYPE (decl)) - && decl == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (decl)))) + && TYPE_DECL_FOR_LINKAGE_PURPOSES_P (decl)) flags = ATTR_FLAG_TYPE_IN_PLACE; else flags = 0; @@ -13731,7 +13730,8 @@ maybe_diagnose_non_c_class_typedef_for_l { auto_diagnostic_group d; if (diagnose_non_c_class_typedef_for_linkage (type, orig)) - inform (DECL_SOURCE_LOCATION (TYPE_NAME (t)), + inform (type == t ? DECL_SOURCE_LOCATION (orig) + : DECL_SOURCE_LOCATION (TYPE_NAME (t)), "type is not C-compatible because it has a base class"); return true; } @@ -13797,12 +13797,22 @@ name_unnamed_type (tree type, tree decl) 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. */ tree orig = TYPE_NAME (type); - for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t)) - if (TYPE_NAME (t) == orig) - TYPE_NAME (t) = decl; + if (flag_reflection) + { + /* For -freflection for typedef struct { ... } S; ^^S needs to be + a reflection of a type alias. So, TREE_TYPE (DECL) can't be + TYPE. Instead of what we do below, override DECL_NAME (orig). */ + DECL_NAME (orig) = DECL_NAME (decl); + TYPE_DECL_FOR_LINKAGE_PURPOSES_P (orig) = 1; + } + else + /* Replace the anonymous decl with the real decl. Be careful not to + rename other typedefs (such as the self-reference) of type. */ + for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t)) + if (TYPE_NAME (t) == orig) + TYPE_NAME (t) = decl; + TYPE_DECL_FOR_LINKAGE_PURPOSES_P (decl) = 1; /* If this is a typedef within a template class, the nested type is a (non-primary) template. The name for the --- gcc/cp/decl2.cc.jj 2026-02-12 17:51:31.805334303 +0100 +++ gcc/cp/decl2.cc 2026-03-03 17:22:57.920518198 +0100 @@ -1303,8 +1303,7 @@ grokfield (const cp_declarator *declarat /* If this is a typedef that names the class for linkage purposes (7.1.3p8), apply any attributes directly to the type. */ - if (OVERLOAD_TYPE_P (TREE_TYPE (value)) - && value == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (value)))) + if (TYPE_DECL_FOR_LINKAGE_PURPOSES_P (value)) attrflags = ATTR_FLAG_TYPE_IN_PLACE; cplus_decl_attributes (&value, attrlist, attrflags); --- gcc/cp/name-lookup.cc.jj 2026-03-02 07:43:12.343788113 +0100 +++ gcc/cp/name-lookup.cc 2026-03-03 17:57:41.387644318 +0100 @@ -1869,6 +1869,11 @@ fields_linear_search (tree klass, tree n continue; } + if (TYPE_DECL_WAS_UNNAMED (decl)) + /* Ignore DECL_NAME given to unnamed TYPE_DECLs named for linkage + purposes. */ + continue; + if (DECL_DECLARES_FUNCTION_P (decl)) /* Functions are found separately. */ continue; @@ -2345,7 +2350,13 @@ count_class_fields (tree klass) && ANON_AGGR_TYPE_P (TREE_TYPE (fields))) n_fields += count_class_fields (TREE_TYPE (fields)); else if (DECL_NAME (fields)) - n_fields += 1; + { + if (TYPE_DECL_WAS_UNNAMED (fields)) + /* Ignore DECL_NAME given to unnamed TYPE_DECLs named for linkage + purposes. */ + continue; + n_fields += 1; + } return n_fields; } @@ -2369,6 +2380,10 @@ member_vec_append_class_fields (vec<tree if (TREE_CODE (field) == USING_DECL && IDENTIFIER_CONV_OP_P (DECL_NAME (field))) field = ovl_make (conv_op_marker, field); + else if (TYPE_DECL_WAS_UNNAMED (field)) + /* Ignore DECL_NAME given to unnamed TYPE_DECLs named for linkage + purposes. */ + continue; member_vec->quick_push (field); } } @@ -2700,7 +2715,9 @@ pop_local_binding (tree id, tree decl) binding->value = NULL_TREE; else if (binding->type == decl) binding->type = NULL_TREE; - else + /* Ignore DECL_NAME given to unnamed TYPE_DECLs named for linkage + purposes. */ + else if (!TYPE_DECL_WAS_UNNAMED (decl)) { /* Name-independent variable was found after at least one declaration with the same name. */ --- gcc/cp/reflect.cc.jj 2026-03-03 16:42:26.904622279 +0100 +++ gcc/cp/reflect.cc 2026-03-03 17:22:57.921903017 +0100 @@ -6545,6 +6545,14 @@ namespace_members_of (location_t loc, tr so don't bother calling it here. */ CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, get_reflection_raw (loc, m)); + /* For typedef struct { ... } S; include both the S type + alias (added above) and dealias of that for the originally + unnamed type (added below). */ + if (TREE_CODE (b) == TYPE_DECL + && TYPE_DECL_FOR_LINKAGE_PURPOSES_P (b)) + CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, + get_reflection_raw (loc, + strip_typedefs (m))); } } delete seen; --- gcc/cp/class.cc.jj 2026-02-12 17:51:31.801334370 +0100 +++ gcc/cp/class.cc 2026-03-03 17:22:57.922677708 +0100 @@ -7608,7 +7608,8 @@ find_flexarrays (tree t, flexmems_t *fme if (TREE_CODE (fld) == TYPE_DECL && DECL_IMPLICIT_TYPEDEF_P (fld) && CLASS_TYPE_P (TREE_TYPE (fld)) - && IDENTIFIER_ANON_P (DECL_NAME (fld))) + && (IDENTIFIER_ANON_P (DECL_NAME (fld)) + || TYPE_DECL_FOR_LINKAGE_PURPOSES_P (fld))) { /* Check the nested unnamed type referenced via a typedef independently of FMEM (since it's not a data member of --- gcc/testsuite/g++.dg/reflect/members_of10.C.jj 2026-03-03 17:29:58.003069946 +0100 +++ gcc/testsuite/g++.dg/reflect/members_of10.C 2026-03-03 17:29:29.739538767 +0100 @@ -0,0 +1,56 @@ +// PR c++/123810 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <meta> + +namespace A { + typedef struct { int b; } B; +} +struct C { + typedef struct { int d; } D; +}; +struct F { + typedef struct { int g; } G; + typedef struct { int h; } H; + typedef struct { int i; } I; + typedef struct { int j; } J; + typedef struct { int k; } K; + typedef struct { int l; } L; + typedef struct { int m; } M; + typedef struct { int n; } N; +}; +void +foo () +{ + typedef struct { int e; } E; + static_assert (is_type_alias (^^E)); +} + +static_assert (is_type_alias (^^A::B)); +static_assert (is_type_alias (^^C::D)); +static_assert (is_type_alias (^^F::G)); +static_assert (is_type_alias (^^F::N)); +constexpr auto uctx = std::meta::access_context::unchecked (); +static_assert (members_of (^^C, uctx)[0] == dealias (^^C::D)); +static_assert (members_of (^^C, uctx)[1] == ^^C::D); +static_assert (members_of (^^F, uctx)[0] == dealias (^^F::G)); +static_assert (members_of (^^F, uctx)[1] == ^^F::G); +static_assert (members_of (^^F, uctx)[2] == dealias (^^F::H)); +static_assert (members_of (^^F, uctx)[3] == ^^F::H); +static_assert (members_of (^^F, uctx)[4] == dealias (^^F::I)); +static_assert (members_of (^^F, uctx)[5] == ^^F::I); +static_assert (members_of (^^F, uctx)[6] == dealias (^^F::J)); +static_assert (members_of (^^F, uctx)[7] == ^^F::J); +static_assert (members_of (^^F, uctx)[8] == dealias (^^F::K)); +static_assert (members_of (^^F, uctx)[9] == ^^F::K); +static_assert (members_of (^^F, uctx)[10] == dealias (^^F::L)); +static_assert (members_of (^^F, uctx)[11] == ^^F::L); +static_assert (members_of (^^F, uctx)[12] == dealias (^^F::M)); +static_assert (members_of (^^F, uctx)[13] == ^^F::M); +static_assert (members_of (^^F, uctx)[14] == dealias (^^F::N)); +static_assert (members_of (^^F, uctx)[15] == ^^F::N); +static_assert ((members_of (^^A, uctx)[0] == dealias (^^A::B) + && members_of (^^A, uctx)[1] == ^^A::B) + || ((members_of (^^A, uctx)[0] == ^^A::B) + && members_of (^^A, uctx)[1] == dealias (^^A::B))); --- gcc/testsuite/g++.dg/cpp2a/typedef1.C.jj 2026-02-12 17:51:31.811334202 +0100 +++ gcc/testsuite/g++.dg/cpp2a/typedef1.C 2026-03-03 17:22:57.923438020 +0100 @@ -33,8 +33,8 @@ typedef struct { // { dg-message "un static int a; // { dg-error "static data member '<unnamed struct>::a' in unnamed class" } } B; typedef struct : public A { // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" } - int a; -} C; // { dg-message "type is not C-compatible because it has a base class" } + int a; // { dg-message "type is not C-compatible because it has a base class" "" { target *-*-* } .-1 } +} C; #if __cplusplus >= 201103L typedef struct { // { dg-error "anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration" "" { target c++11 } } int b = 42; // { dg-message "type is not C-compatible because 'D::b' has default member initializer" "" { target c++11 } } Jakub
