Hi!

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?

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.

--- gcc/cp/decl.cc.jj   2026-05-20 22:33:18.132865846 +0200
+++ gcc/cp/decl.cc      2026-05-21 16:08:39.796596869 +0200
@@ -9615,6 +9615,22 @@ 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.  */
+      if (ANON_UNION_TYPE_P (type))
+       error_at (location_of (decl),
+                 "declaration of variable %qD with anonymous union type %qT",
+                 decl, type);
+      else
+       error_at (location_of (decl),
+                 "declaration of variable %qD with anonymous struct type %qT",
+                 decl, type);
+      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 +16396,19 @@ grokdeclarator (const cp_declarator *dec
 
     if (decl_context == PARM)
       {
+        if (ANON_AGGR_TYPE_P (type))
+         {
+           /* [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);
+           else
+             error_at (id_loc, "declaration of a parameter with anonymous "
+                       "struct type %qT", type);
+           type = error_mark_node;
+         }
+
        decl = cp_build_parm_decl (NULL_TREE, unqualified_id, type);
        DECL_ARRAY_PARAMETER_P (decl) = array_parameter_p;
 
@@ -18726,6 +18755,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-20 22:33:18.133865830 +0200
+++ gcc/cp/semantics.cc 2026-05-21 16:05:32.300723086 +0200
@@ -4279,12 +4279,28 @@ 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)))
+       error_at (location_of (decl),
+                 "declaration of member %qD with anonymous union type %qT",
+                 decl, TREE_TYPE (decl));
+      else
+       error_at (location_of (decl),
+                 "declaration of member %qD with anonymous struct type %qT",
+                 decl, TREE_TYPE (decl));
     }
 
   if (TREE_CODE (decl) == USING_DECL)
--- gcc/testsuite/g++.dg/reflect/anon6.C.jj     2026-05-21 15:41:14.858037235 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon6.C        2026-05-21 16:16:15.852992809 
+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; }; };
+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; };
+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 15:58:07.165145714 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon7.C        2026-05-21 16:15:46.191487369 
+0200
@@ -0,0 +1,40 @@
+// { dg-do compile { target c++26 } }
+// { dg-options "-freflection" }
+
+#include <meta>
+
+struct A { struct { int a; long b; }; };
+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>'}" }

        Jakub

Reply via email to