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
