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

Reply via email to