On Thu, May 21, 2026 at 04:24:14PM +0200, Jakub Jelinek wrote:
> https://wg21.link/CWG3130 added
> "All objects of such an unnamed type shall be such an unnamed object."
> sentence which should prevent reusing anonymous union types (and by
> extension anonymous struct types too) as types of some other declarations,
> which in some cases ICEs, in other cases is just really weird and could
> misbehave when trying to look things up etc.
> In addition to that, I've added an error trying to make derived types from
> anonymous struct types (one can't make derived types from union types, so
> that case isn't a problem).
> Without reflection, none of this was a problem, one couldn't expose the
> anonymous union or struct types like that.
> 
> So far lightly tested, ok for trunk if it passes full bootstrap/regtest?

Full testing found this regressed g++.dg/template/anonunion3.C test.

So, if we really want to treat this as a DR, we need to adjust that test.

This passed full bootstrap/regtest on x86_64-linux and i686-linux.

That said, I'm struggling with follow-up patches, will post them just
as a RFC.

2026-05-21  Jakub Jelinek  <[email protected]>

        * decl.cc: Implement part of CWG3130 - Naming function members of
        anonymous unions.
        (cp_finish_decl): Diagnose named vars with anonymous union or struct
        type.
        (grokdeclarator): Diagnose parameters with anonymous union or struct
        type.
        (xref_basetypes): Diagnose anonymous structs as bases.
        * semantics.cc (finish_member_declaration): Diagnose named members
        with anonymous union or struct type.

        * g++.dg/reflect/anon6.C: New test.
        * g++.dg/reflect/anon7.C: New test.
        * g++.dg/template/anonunion3.C: Expect an error.

--- gcc/cp/decl.cc.jj   2026-05-21 16:24:36.091652254 +0200
+++ gcc/cp/decl.cc      2026-05-21 16:41:30.931798427 +0200
@@ -9615,6 +9615,31 @@ cp_finish_decl (tree decl, tree init, bo
        }
     }
 
+  if (ANON_AGGR_TYPE_P (type) && VAR_P (decl) && DECL_NAME (decl))
+    {
+      /* [class.union.anon]/1: Each object of such an unnamed type shall be
+        such an unnamed object.  */
+      auto_diagnostic_group d;
+      location_t aloc
+       = DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (type)));
+      if (ANON_UNION_TYPE_P (type))
+       {
+         error_at (location_of (decl),
+                   "declaration of variable %qD with anonymous union type "
+                   "%qT", decl, type);
+         inform (aloc, "anonymous union declared here");
+       }
+      else
+       {
+         error_at (location_of (decl),
+                   "declaration of variable %qD with anonymous struct type "
+                   "%qT", decl, type);
+         inform (aloc, "anonymous struct declared here");
+       }
+      TREE_TYPE (decl) = error_mark_node;
+      return;
+    }
+
   if (ensure_literal_type_for_constexpr_object (decl) == error_mark_node)
     {
       DECL_DECLARED_CONSTEXPR_P (decl) = 0;
@@ -16380,6 +16405,29 @@ grokdeclarator (const cp_declarator *dec
 
     if (decl_context == PARM)
       {
+       if (ANON_AGGR_TYPE_P (type))
+         {
+           tree adecl = TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (type));
+           auto_diagnostic_group d;
+           /* [class.union.anon]/1: Each object of such an unnamed type shall
+              be such an unnamed object.  */
+           if (ANON_UNION_TYPE_P (type))
+             {
+               error_at (id_loc, "declaration of a parameter with anonymous "
+                         "union type %qT", type);
+               inform (DECL_SOURCE_LOCATION (adecl),
+                       "anonymous union declared here");
+             }
+           else
+             {
+               error_at (id_loc, "declaration of a parameter with anonymous "
+                         "struct type %qT", type);
+               inform (DECL_SOURCE_LOCATION (adecl),
+                       "anonymous struct declared here");
+             }
+           type = error_mark_node;
+         }
+
        decl = cp_build_parm_decl (NULL_TREE, unqualified_id, type);
        DECL_ARRAY_PARAMETER_P (decl) = array_parameter_p;
 
@@ -18726,6 +18774,11 @@ xref_basetypes (tree ref, tree base_list
                 basetype);
          goto dropped_base;
        }
+      else if (ANON_AGGR_TYPE_P (basetype))
+       {
+         error ("base type %qT is anonymous struct type", basetype);
+         goto dropped_base;
+       }
 
       base_binfo = NULL_TREE;
       if (CLASS_TYPE_P (basetype) && !dependent_scope_p (basetype))
--- gcc/cp/semantics.cc.jj      2026-05-21 16:24:36.092652237 +0200
+++ gcc/cp/semantics.cc 2026-05-21 16:44:50.744494581 +0200
@@ -4279,12 +4279,40 @@ finish_member_declaration (tree decl)
   if (TREE_CODE (decl) != CONST_DECL)
     DECL_CONTEXT (decl) = current_class_type;
 
-  /* Remember the single FIELD_DECL an anonymous aggregate type is used for.  
*/
-  if (TREE_CODE (decl) == FIELD_DECL
-      && ANON_AGGR_TYPE_P (TREE_TYPE (decl)))
+  if (TREE_TYPE (decl)
+      && ANON_AGGR_TYPE_P (TREE_TYPE (decl))
+      && TREE_CODE (decl) != TYPE_DECL)
     {
-      gcc_assert (!ANON_AGGR_TYPE_FIELD (TYPE_MAIN_VARIANT (TREE_TYPE 
(decl))));
-      SET_ANON_AGGR_TYPE_FIELD (TYPE_MAIN_VARIANT (TREE_TYPE (decl)), decl);
+      /* Remember the single FIELD_DECL an anonymous aggregate type is used
+        for.  */
+      if (TREE_CODE (decl) == FIELD_DECL && DECL_NAME (decl) == NULL_TREE)
+       {
+         tree type = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
+         gcc_assert (!ANON_AGGR_TYPE_FIELD (type));
+         SET_ANON_AGGR_TYPE_FIELD (type, decl);
+       }
+      /* [class.union.anon]/1: Each object of such an unnamed type shall
+        be such an unnamed object.  */
+      else if (ANON_UNION_TYPE_P (TREE_TYPE (decl)))
+       {
+         tree adecl = TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+         auto_diagnostic_group d;
+         error_at (location_of (decl),
+                   "declaration of member %qD with anonymous union type %qT",
+                   decl, TREE_TYPE (decl));
+         inform (DECL_SOURCE_LOCATION (adecl),
+                 "anonymous union declared here");
+       }
+      else
+       {
+         tree adecl = TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+         auto_diagnostic_group d;
+         error_at (location_of (decl),
+                   "declaration of member %qD with anonymous struct type %qT",
+                   decl, TREE_TYPE (decl));
+         inform (DECL_SOURCE_LOCATION (adecl),
+                 "anonymous union declared here");
+       }
     }
 
   if (TREE_CODE (decl) == USING_DECL)
--- gcc/testsuite/g++.dg/reflect/anon6.C.jj     2026-05-21 16:34:35.832738431 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon6.C        2026-05-21 16:46:52.421482685 
+0200
@@ -0,0 +1,43 @@
+// CWG3130 - Naming function members of anonymous unions
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct A { union { int a; long b; }; };        // { dg-message "anonymous 
union declared here" }
+using U = typename [: parent_of (^^A::a) :];
+U b;                                   // { dg-error "declaration of variable 
'b' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+struct B { U b; };                     // { dg-error "declaration of member 
'B::b' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+auto c = U { .a = 1 };                 // { dg-error "declaration of variable 
'c' with anonymous union type 'A::<unnamed union>'" }
+static union { int d; long e; };       // { dg-message "anonymous union 
declared here" }
+using V = typename [: parent_of (^^e) :];
+auto f = V { .e = 42 };                        // { dg-error "declaration of 
variable 'f' with anonymous union type '<unnamed union>'" }
+V g;                                   // { dg-error "declaration of variable 
'g' with anonymous union type 'V' {aka '<unnamed union>'}" }
+struct C { static auto c = V { .e = 42 }; }; // { dg-error "declaration of 
variable 'C::c' with anonymous union type '<unnamed union>'" }
+struct D { V d; };                     // { dg-error "declaration of member 
'D::d' with anonymous union type 'V' {aka '<unnamed union>'}" }
+
+void
+foo (U x)                              // { dg-error "declaration of a 
parameter with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+{
+}
+
+void
+bar ()
+{
+  U y;                                 // { dg-error "declaration of variable 
'y' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  union W { U u; } v;                  // { dg-error "declaration of member 
'bar\\\(\\\)::W::u' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  try
+    {
+    }
+  catch (U z)                          // { dg-error "declaration of variable 
'z' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+    {
+    }
+}
+
+void
+baz (U)                                        // { dg-error "declaration of a 
parameter with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+{
+}
+
+void qux (U);                          // { dg-error "declaration of a 
parameter with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+void fred (V x);                       // { dg-error "declaration of a 
parameter with anonymous union type 'V' {aka '<unnamed union>'}" }
--- gcc/testsuite/g++.dg/reflect/anon7.C.jj     2026-05-21 16:34:35.832828254 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon7.C        2026-05-21 16:47:29.394871338 
+0200
@@ -0,0 +1,40 @@
+// { dg-do compile { target c++26 } }
+// { dg-options "-freflection" }
+
+#include <meta>
+
+struct A { struct { int a; long b; }; }; // { dg-message "anonymous struct 
declared here" }
+using U = typename [: parent_of (^^A::a) :];
+U b;                                   // { dg-error "declaration of variable 
'b' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+struct B { U b; };                     // { dg-error "declaration of member 
'B::b' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+auto c = U { .a = 1 };                 // { dg-error "declaration of variable 
'c' with anonymous struct type 'A::<unnamed struct>'" }
+U g;                                   // { dg-error "declaration of variable 
'g' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+struct C { static auto c = U { .a = 42 }; }; // { dg-error "declaration of 
variable 'C::c' with anonymous struct type 'A::<unnamed struct>'" }
+
+void
+foo (U x)                              // { dg-error "declaration of a 
parameter with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+{
+}
+
+void
+bar ()
+{
+  U y;                                 // { dg-error "declaration of variable 
'y' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+  union W { U u; } v;                  // { dg-error "declaration of member 
'bar\\\(\\\)::W::u' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" 
}
+  try
+    {
+    }
+  catch (U z)                          // { dg-error "declaration of variable 
'z' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+    {
+    }
+}
+
+void
+baz (U)                                        // { dg-error "declaration of a 
parameter with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+{
+}
+
+struct D : public U {};                        // { dg-error "base type 'U' 
{aka 'A::<unnamed struct>'} is anonymous struct type" }
+
+void qux (U);                          // { dg-error "declaration of a 
parameter with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+void fred (U x);                       // { dg-error "declaration of a 
parameter with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
--- gcc/testsuite/g++.dg/template/anonunion3.C.jj       2026-03-27 
10:17:16.155297760 +0100
+++ gcc/testsuite/g++.dg/template/anonunion3.C  2026-05-22 22:57:02.924323048 
+0200
@@ -4,7 +4,7 @@
 extern "C" int printf (const char *, ...);
 
 template<typename T> static char const * f(T *t) {
- T u(*t);
+ T u(*t);                      // { dg-error "declaration of variable 'u' with 
anonymous union type 'main\\\(\\\)::<unnamed union>'" }
  u.x = "hello world";
  printf("%s\n", u.x);
  return "initialized";


        Jakub

Reply via email to