On 7/20/26 9:19 AM, Jakub Jelinek wrote:
Hi!

Sorry for dropping the ball on this for almost 2 months.
Here is an updated patch for both
https://gcc.gnu.org/pipermail/gcc-patches/2026-May/717586.html
and
https://gcc.gnu.org/pipermail/gcc-patches/2026-May/717931.html
combined.

As requested in
https://gcc.gnu.org/pipermail/gcc-patches/2026-May/718558.html
this attempts to diagnose as much as possible from
abstract_virtuals_error and compared to the older patches also
diagnoses when one attempts to create objects (variables, temporary objects,
etc.) with type array of anonymous unions or anonymous structs.

There is one case where I have to add a call to abstract_virtuals_error,
for pure virtual classes finish_compound_literal works fine, because
they are always TYPE_NON_AGGREGATE_CLASS and so call build_functional_cast
which through build_functional_cast_1 calls abstract_virtuals_error.
But, anonymous unions or anonymous structs can be aggregate classes, so
if we need to create a compound literal of such types, nothing would
diagnose that.

So far tested with
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,29 make check-g++
and make check-target-libstdc++-v3, ok for trunk if it passes full
bootstrap/regtest?

2026-07-20  Jakub Jelinek  <[email protected]>

gcc/cp/
        * typeck2.cc: Implement part of CWG3130 - Naming function members of
        anonymous unions.
        (abstract_virtuals_error): Diagnose trying to create
        an object with anonymous union or struct type.
        * semantics.cc (finish_compound_literal): Call abstract_virtuals_error.
        (finish_member_declaration): Diagnose named members with anonymous
        union or struct type.
        * decl.cc (xref_basetypes): Diagnose anonymous structs as bases.
gcc/testsuite/
        * g++.dg/cpp0x/anon-union4.C: New test.
        * g++.dg/reflect/anon6.C: New test.
        * g++.dg/reflect/anon7.C: New test.
        * g++.dg/reflect/anon8.C: New test.
        * g++.dg/reflect/anon9.C: New test.
        * g++.dg/template/anonunion3.C: Expect an error.

--- gcc/cp/typeck2.cc.jj        2026-06-08 21:38:14.802519917 +0200
+++ gcc/cp/typeck2.cc   2026-07-20 12:19:32.422057672 +0200
@@ -151,6 +151,97 @@ abstract_virtuals_error (tree decl, tree
       be abstract.  */
    if (!CLASS_TYPE_P (type))
      return 0;
+
+  if (ANON_AGGR_TYPE_P (type))
+    {
+      /* [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 (decl && VAR_P (decl) && DECL_NAME (decl) == NULL_TREE)
+       ;

This could use a comment about why this doesn't allow temporaries.

+      else if (!(complain & tf_error))
+       return 1;
+      else if (ANON_UNION_TYPE_P (type))
+       {
+         if (!decl)
+           switch (use)
+             {
+             default:
+               error ("temporary object with anonymous union type %qT", type);
+               break;
+             case ACU_CATCH:
+               error ("%<catch%> parameter with anonymous union type %qT",
+                      type);
+               break;
+             case ACU_THROW:
+               error ("trying to throw anonymous union type %qT object",

Let's avoid "trying".  Perhaps "%<throw%> operand has anonymous union type"?

Also, this doesn't seem to appear in the testcase?

Incidentally, I also don't see a functional cast like U() in the testcase.

+                      type);
+               break;
+             case ACU_ARRAY:
+               error ("trying to create array of anonymous union type %qT",

Similarly, just "array of anonymous union type".

+                      type);
+               break;
+             }
+         else if (VAR_P (decl))
+           error_at (location_of (decl),
+                     "declaration of variable %qD with anonymous union type "
+                     "%qT", decl, type);
+         else if (TREE_CODE (decl) == PARM_DECL && DECL_NAME (decl))
+           error_at (location_of (decl),
+                     "declaration of parameter %qD with anonymous union type "
+                     "%qT", decl, type);
+         else if (TREE_CODE (decl) == PARM_DECL)
+           error_at (location_of (decl),
+                     "declaration of a parameter with anonymous union type "
+                     "%qT", type);
+         inform (aloc, "anonymous union declared here");
+         if (decl)
+           TREE_TYPE (decl) = error_mark_node;
+         return 1;
+       }
+      else
+       {
+         if (!decl)
+           switch (use)
+             {
+             default:
+               error ("temporary object with anonymous struct type %qT",
+                      type);
+               break;
+             case ACU_CATCH:
+               error ("%<catch%> parameter with anonymous struct type %qT",
+                      type);
+               break;
+             case ACU_THROW:
+               error ("trying to throw anonymous struct type %qT object",
+                      type);
+               break;
+             case ACU_ARRAY:
+               error ("trying to create array of anonymous struct type %qT",
+                      type);
+               break;
+             }
+         else if (VAR_P (decl))
+           error_at (location_of (decl),
+                     "declaration of variable %qD with anonymous struct type "
+                     "%qT", decl, type);
+         else if (TREE_CODE (decl) == PARM_DECL && DECL_NAME (decl))
+           error_at (location_of (decl),
+                     "declaration of parameter %qD with anonymous struct type "
+                     "%qT", decl, type);
+         else if (TREE_CODE (decl) == PARM_DECL)
+           error_at (location_of (decl),
+                     "declaration of a parameter with anonymous struct type "
+                     "%qT", type);
+         inform (aloc, "anonymous struct declared here");
+         if (decl)
+           TREE_TYPE (decl) = error_mark_node;
+         return 1;
+       }
+    }
+
    type = TYPE_MAIN_VARIANT (type);
#if 0
--- gcc/cp/semantics.cc.jj      2026-07-08 11:10:07.727602357 +0200
+++ gcc/cp/semantics.cc 2026-07-20 14:15:46.858954421 +0200
@@ -3946,6 +3946,8 @@ finish_compound_literal (tree type, tree
        if (type == error_mark_node)
        return error_mark_node;
      }
+  if (abstract_virtuals_error (ACU_UNKNOWN, type, complain))
+    return error_mark_node;
    compound_literal = digest_init_flags (type, compound_literal,
                                        LOOKUP_NORMAL | LOOKUP_NO_NARROWING,
                                        complain);
@@ -4279,12 +4281,69 @@ 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)

The change from == FIELD_DECL to != TYPE_DECL looks to be for diagnosing static data members of anon aggr type, but I don't see any such in the testcase.

+    {
+      /* 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 struct declared here");
+       }
+    }
+  else if (TREE_TYPE (decl)
+          && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+          && 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);
+      tree type = strip_array_types (TREE_TYPE (decl));
+      if (ANON_AGGR_TYPE_P (type))
+       {
+         /* [class.union.anon]/1: Each object of such an unnamed type shall
+            be such an unnamed object.  */
+         tree adecl = TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (type));
+         auto_diagnostic_group d;
+         if (ANON_UNION_TYPE_P (TREE_TYPE (decl)))
+           {
+             error_at (location_of (decl),
+                       "declaration of member %qD with array of anonymous "
+                       "unions type %qT", decl, TREE_TYPE (decl));

Singular "union"

+             inform (DECL_SOURCE_LOCATION (adecl),
+                     "anonymous union declared here");
+           }
+         else
+           {
+             error_at (location_of (decl),
+                       "declaration of member %qD with array of anonymous "
+                       "structs type %qT", decl, TREE_TYPE (decl));

and "struct"

+             inform (DECL_SOURCE_LOCATION (adecl),
+                     "anonymous struct declared here");
+           }
+       }

The 2x2 code duplication makes me sad, but I guess it makes sense.

      }
if (TREE_CODE (decl) == USING_DECL)
--- gcc/cp/decl.cc.jj   2026-07-20 09:57:44.554017764 +0200
+++ gcc/cp/decl.cc      2026-07-20 12:19:32.458057189 +0200
@@ -18758,6 +18758,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/testsuite/g++.dg/cpp0x/anon-union4.C.jj 2026-07-20 12:19:32.459486417 
+0200
+++ gcc/testsuite/g++.dg/cpp0x/anon-union4.C    2026-07-20 12:19:32.459486417 
+0200
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++11 } }
+
+struct A { A () = default; A (int); int a; };
+struct B { union { int a; A b; }; };
+
+B *
+foo ()
+{
+  return new B ();
+}
--- gcc/testsuite/g++.dg/reflect/anon6.C.jj     2026-07-20 12:19:32.460222215 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon6.C        2026-07-20 13:18:26.433742635 
+0200
@@ -0,0 +1,86 @@
+// 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>'}" }
+U &plugh ();
+auto c = plugh ();                     // { dg-error "declaration of variable 'c' with 
anonymous union type 'A::<unnamed union>'" }
+                                       // { dg-error "temporary object with anonymous union type 
'A::<unnamed union>'" "" { target *-*-* } .-1 }
+static union { int d; long e; };       // { dg-message "anonymous union declared 
here" }
+using V = typename [: parent_of (^^e) :];
+V &thud () { throw 1; }
+auto f = thud ();                      // { dg-error "declaration of variable 'f' with 
anonymous union type '<unnamed union>'" }
+                                       // { dg-error "temporary object with anonymous union type 
'<unnamed union>'" "" { target *-*-* } .-1 }
+V g;                                   // { dg-error "declaration of variable 'g' with 
anonymous union type 'V' {aka '<unnamed union>'}" }
+struct D { V d; };                     // { dg-error "declaration of member 'D::d' with 
anonymous union type 'V' {aka '<unnamed union>'}" }
+struct E { U e[2]; };                  // { dg-error "declaration of member 'E::e' with 
array of anonymous structs type 'U \\\[2\\\]' {aka 'A::<unnamed union> \\\[2\\\]'}" }
+struct F { V f[2]; };                  // { dg-error "declaration of member 'F::f' with 
array of anonymous structs type 'V \\\[2\\\]' {aka '<unnamed union> \\\[2\\\]'}" }
+using UA = U[2];
+using VA = V[3];
+
+void
+foo (U x)                              // { dg-error "declaration of parameter 'x' 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>'}" }
+  U r[4];                              // { dg-error "trying to create array of anonymous 
union type 'U' {aka 'A::<unnamed union>'}" }
+  V s[2];                              // { dg-error "trying to create array of 
anonymous union type 'V'" }
+  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 "'catch' parameter with anonymous 
union type 'U' {aka 'A::<unnamed union>'}" }
+    {
+    }
+  try
+    {
+    }
+  catch (U)                            // { dg-error "'catch' parameter 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>'}" }
+{
+}
+
+long
+garply (const U &x)
+{
+  return x.b;
+}
+
+void
+corge ()
+{
+  garply (U { .b = 42 });              // { dg-error "temporary object with anonymous 
union type 'U' {aka 'A::<unnamed union>'}" }
+}
+
+V *
+xyzzy ()
+{
+  return new V { .e = 42 };            // { dg-error "temporary object with anonymous 
union type 'V' {aka '<unnamed union>'}" }
+}
+
+void qux (U);
+void fred (V x);
+void waldo (int, ...);
+
+void
+boo (U &x, V &y)
+{
+  qux (x);                             // { dg-error "temporary object with anonymous 
union type 'U' {aka 'A::<unnamed union>'}" }
+  fred (y);                            // { dg-error "temporary object with anonymous 
union type 'V' {aka '<unnamed union>'}" }
+  waldo (1, x);                                // { dg-error "temporary object with 
anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  waldo (2, y);                                // { dg-error "temporary object with 
anonymous union type 'V' {aka '<unnamed union>'}" }
+}
--- gcc/testsuite/g++.dg/reflect/anon7.C.jj     2026-07-20 12:19:32.459923680 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon7.C        2026-07-20 13:18:39.706565469 
+0200
@@ -0,0 +1,77 @@
+// { 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>'}" }
+U &plugh ();
+auto c = plugh ();                     // { dg-error "declaration of variable 'c' with 
anonymous struct type 'A::<unnamed struct>'" }
+                                       // { dg-error "temporary object with anonymous struct type 
'A::<unnamed struct>'" "" { target *-*-* } .-1 }
+U g;                                   // { dg-error "declaration of variable 'g' with 
anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+struct E { U e[2]; };                  // { dg-error "declaration of member 'E::e' with 
array of anonymous structs type 'U \\\[2\\\]' {aka 'A::<unnamed struct> \\\[2\\\]'}" 
}
+using UA = U[2];
+
+void
+foo (U x)                              // { dg-error "declaration of parameter 'x' 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>'}" }
+  U r[4];                              // { dg-error "trying to create array of 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 "'catch' parameter with anonymous 
struct type 'U' {aka 'A::<unnamed struct>'}" }
+    {
+    }
+  try
+    {
+    }
+  catch (U)                            // { dg-error "'catch' parameter 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" }
+
+long
+garply (const U &x)
+{
+  return x.b;
+}
+
+void
+corge ()
+{
+  garply (U { .b = 42 });              // { dg-error "temporary object with anonymous 
struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
+
+U *
+xyzzy ()
+{
+  return new U { .b = 42 };            // { dg-error "temporary object with anonymous 
struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
+
+void qux (U);
+void fred (U x);
+void waldo (int, ...);
+
+void
+boo (U &x)
+{
+  qux (x);                             // { dg-error "temporary object with anonymous 
struct type 'U' {aka 'A::<unnamed struct>'}" }
+  fred (x);                            // { dg-error "temporary object with anonymous 
struct type 'U' {aka 'A::<unnamed struct>'}" }
+  waldo (1, x);                                // { dg-error "temporary object with 
anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
--- gcc/testsuite/g++.dg/reflect/anon8.C.jj     2026-07-20 12:19:32.460133697 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon8.C        2026-07-20 12:19:32.460133697 
+0200
@@ -0,0 +1,24 @@
+// CWG3130 - Naming function members of anonymous unions
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct A { union { int a; long b; }; };
+using U = typename [: parent_of (^^A::a) :];
+
+long
+foo (U &x)
+{
+  return x.b;
+}
+
+int
+main ()
+{
+  A a;
+  a.b = 42;
+  constexpr auto ctx = std::meta::access_context::unchecked ();
+  if (foo (a.[: members_of (^^A, ctx)[1] :]) != 42)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/reflect/anon9.C.jj     2026-07-20 12:19:32.460038282 
+0200
+++ gcc/testsuite/g++.dg/reflect/anon9.C        2026-07-20 12:19:32.460038282 
+0200
@@ -0,0 +1,24 @@
+// { dg-do run { target c++26 } }
+// { dg-options "-freflection" }
+
+#include <meta>
+
+struct A { struct { int a; long b; }; };
+using U = typename [: parent_of (^^A::a) :];
+
+long
+foo (U &x)
+{
+  return x.a + x.b;
+}
+
+int
+main ()
+{
+  A a;
+  a.a = 2;
+  a.b = 42;
+  constexpr auto ctx = std::meta::access_context::unchecked ();
+  if (foo (a.[: members_of (^^A, ctx)[1] :]) != 44)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/template/anonunion3.C.jj       2026-05-30 
09:43:35.123290512 +0200
+++ gcc/testsuite/g++.dg/template/anonunion3.C  2026-07-20 12:19:32.460908786 
+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