Re: [PATCH] c++: Fix mangling of empty anon union/struct unnamed NSDMs [PR125541]

Jason Merrill <[email protected]> Wed, 5 Aug 2026 23:58:36 -0400
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
On 6/12/26 4:41 AM, Jakub Jelinek wrote:
> Hi!
> 
> Non-static data members are currently mangled as
> dm <prefix> <unqualified-name>
> This works fine for NSDMs with a name, or even the unnamed anon union/struct
> NSDMs iff they have any named members in them (in that case the
> <unqualified-name> is using anon_aggr_naming_decl).
> As the following testcases show, if the anon union/struct is empty,
> we ICE, because there is nothing we can print as the name.
> The following patch mangles it similarly to unnamed bitfields in that case,
> simply counts the index of such problematic empty unnamed anon union/struct
> within the class and emits _ for the first one, etc.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> 2026-06-12  Jakub Jelinek  <[email protected]>
> 
> 	PR c++/125541
> 	* mangle.cc (write_reflection): Mangle empty anonymous union/struct
> 	data members as "da" rather than "dm" and use index of such a data
> 	member instead of name.
> 
> 	* g++.dg/reflect/mangle1.C: Add further 2 tests.
> 	* g++.dg/reflect/mangle9.C: New test.
> 
> --- gcc/cp/mangle.cc.jj	2026-06-10 17:42:21.452938363 +0200
> +++ gcc/cp/mangle.cc	2026-06-11 17:11:36.438221176 +0200
> @@ -4186,6 +4186,8 @@ write_expression (tree expr)
>   		      [ <alias template-args> ] _ <type> # type alias
>   		  ::= ty <type>				# type
>   		  ::= dm <prefix> <unqualified-name>	# ns data member
> +		  ::= da <prefix> [ <nonnegative number> ] _ # empty anon union
> +							     # data member
>   		  ::= un <prefix> [ <nonnegative number> ] _ # unnamed bitfld
>   		  ::= ct [ <prefix> ] <unqualified-name> # class template
>   		  ::= ft [ <prefix> ] <unqualified-name> # function template
> @@ -4207,6 +4209,11 @@ write_reflection (tree refl)
>   {
>     char prefix[3];
>     tree arg = reflection_mangle_prefix (refl, prefix);
> +  if (strcmp (prefix, "dm") == 0
> +      && DECL_NAME (arg) == NULL_TREE
> +      && ANON_AGGR_TYPE_P (TREE_TYPE (arg))
> +      && anon_aggr_naming_decl (TREE_TYPE (arg)) == NULL_TREE)
> +    strcpy (prefix, "da");
>     write_string (prefix);
>     /* If there is no argument, nothing further needs to be mangled.  */
>     if (arg == NULL_TREE)
> @@ -4257,6 +4264,21 @@ write_reflection (tree refl)
>         write_prefix (ctx);
>         write_unqualified_name (arg);
>       }
> +  else if (strcmp (prefix, "da") == 0)
> +    {
> +      int idx = 0;
> +      tree ctx = decl_mangling_context (arg);
> +      for (tree f = TYPE_FIELDS (ctx); f; f = DECL_CHAIN (f))
> +	if (f == arg)
> +	  break;
> +	else if (TREE_CODE (f) == FIELD_DECL
> +		 && DECL_NAME (f) == NULL_TREE
> +		 && ANON_AGGR_TYPE_P (TREE_TYPE (f))
> +		 && anon_aggr_naming_decl (TREE_TYPE (f)) == NULL_TREE)
> +	  ++idx;
> +      write_prefix (ctx);
> +      write_compact_number (idx);
> +    }
>     else if (strcmp (prefix, "un") == 0)
>       {
>         tree ctx = DECL_CONTEXT (arg);
> --- gcc/testsuite/g++.dg/reflect/mangle1.C.jj	2026-04-15 08:59:38.142570630 +0200
> +++ gcc/testsuite/g++.dg/reflect/mangle1.C	2026-06-11 17:18:09.754062005 +0200
> @@ -15,6 +15,7 @@ struct S : B {
>     int : 0;
>     static int var;
>   };
> +struct W { union {}; union {}; union {}; union {}; };
>   template <auto> struct TCls {};
>   template <auto> void TFn ();
>   template <auto> int TVar;
> @@ -172,6 +173,8 @@ baz (int x)
>   							    std::meta::reflect_constant (43L),
>   							    std::meta::reflect_constant (NS2::AA { 1, 2 }) } })> (); // data member description
>     bar <340, ^^NS2::X::~X> (); // function
> +  bar <350, members_of (^^W, ctx)[1]> (); // empty anon union non-static data member
> +  bar <351, members_of (^^W, ctx)[7]> (); // empty anon union non-static data member
>   }
>   
>   // { dg-final { scan-assembler "_Z3fooILi1EDmEvv" } }
> @@ -258,3 +261,5 @@ baz (int x)
>   // { dg-final { scan-assembler "_Z3barILi334ELDmdsi___0_EEvv" } }
>   // { dg-final { scan-assembler "_Z3barILi335ELDmdsi_1____Li42ELl43EXtlN3NS22AAELi1ELi2EEEEEvv" } }
>   // { dg-final { scan-assembler "_Z3barILi340ELDmfnN3NS21XD4EvEEvv" } }
> +// { dg-final { scan-assembler "_Z3barILi350ELDmda1W_EEvv" } }
> +// { dg-final { scan-assembler "_Z3barILi351ELDmda1W2_EEvv" } }
> --- gcc/testsuite/g++.dg/reflect/mangle9.C.jj	2026-06-11 17:27:55.199383148 +0200
> +++ gcc/testsuite/g++.dg/reflect/mangle9.C	2026-06-11 17:38:10.949291458 +0200
> @@ -0,0 +1,31 @@
> +// { dg-do compile { target c++26 } }
> +// { dg-options "-freflection -O0" }
> +
> +#include <meta>
> +
> +struct W { union { union {}; union {}; union {}; union {}; };
> +	   union { union {}; union {}; union {}; union {}; }; };
> +
> +constexpr auto ctx = std::meta::access_context::current ();
> +
> +template <int N, std::meta::info I>
> +void
> +bar ()
> +{
> +}
> +
> +void
> +baz (int x)
> +{
> +  constexpr auto a = members_of (^^W, ctx)[0];
> +  bar <10, members_of (a, ctx)[1]> (); // empty anon union non-static data member
> +  bar <11, members_of (a, ctx)[7]> (); // empty anon union non-static data member
> +  constexpr auto b = members_of (^^W, ctx)[2];
> +  bar <12, members_of (b, ctx)[1]> (); // empty anon union non-static data member
> +  bar <13, members_of (b, ctx)[7]> (); // empty anon union non-static data member
> +}
> +
> +// { dg-final { scan-assembler "_Z3barILi10ELDmda1WUt__EEvv" } }
> +// { dg-final { scan-assembler "_Z3barILi11ELDmda1WUt_2_EEvv" } }
> +// { dg-final { scan-assembler "_Z3barILi12ELDmda1WUt0__EEvv" } }
> +// { dg-final { scan-assembler "_Z3barILi13ELDmda1WUt0_2_EEvv" } }
> 
> 	Jakub
>