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)
+       ;
+      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",
+                      type);
+               break;
+             case ACU_ARRAY:
+               error ("trying to create array of anonymous union type %qT",
+                      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)
+    {
+      /* 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));
+             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));
+             inform (DECL_SOURCE_LOCATION (adecl),
+                     "anonymous struct declared here");
+           }
+       }
     }
 
   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