On Thu, May 21, 2026 at 10:28:47AM -0400, Jason Merrill wrote: > We might want an inform to indicate the definition of the anon aggr? > > OK either way.
Makes sense, here is what I'll be testing tonight. 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-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>'}" } Jakub
