[Bug c++/123810] internal compiler error: in members_cmp, at cp/reflect.cc:6450
cvs-commit at gcc dot gnu.org
gcc-bugzilla@gcc.gnu.org
Thu Mar 5 08:23:01 GMT 2026
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123810
--- Comment #13 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:
https://gcc.gnu.org/g:f8152db38660061623150b346d84765676e92844
commit r16-7903-gf8152db38660061623150b346d84765676e92844
Author: Jakub Jelinek <jakub@redhat.com>
Date: Thu Mar 5 09:19:59 2026 +0100
c++: Fix up handling of unnamed types named by typedef for linkage purposes
for -freflection [PR123810]
As mentioned on the PR, we ICE on the following testcase and if members_of
isn't called on a class with e.g. typedef struct { int d; } D;, we don't
handle it correctly, e.g. we say ^^C::D is not an type alias or for
members_of in a namespace that there aren't two entities, the struct itself
and the type alias for it.
This is because name_unnamed_type handles the naming of an unnamed type
through typedef for linkage purposes (where we originally have
a TYPE_DECL with IDENTIFIER_ANON_P DECL_NAME for the type) by replacing
all occurrences of TYPE_NAME on the type from the old TYPE_DECL to the new
TYPE_DECL with the user provided name.
The ICE for members_of (^^C, uctx) is then because we see two TYPE_DECLs
(one with IDENTIFIER_ANON_P, one with user name) with the same TREE_TYPE
and enter the same thing twice into what we want to return and ICE in the
comparison routine. Anyway, for is_type_alias purposes, there is no
is_typedef_decl and can't be because the same TYPE_DECL is used as
TYPE_NAME
of both the type proper and its alias. Without reflection we didn't care
about the difference.
So, the following patch changes name_unnamed_type to do things differently,
but only for -freflection, because 1) I don't want to break stuff late in
stage4 2) without reflection we don't really need it and don't need to
pay the extra memory cost by having another type which is the type alias.
The change is that instead of
TYPE_DECL .anon_NN
| TREE_TYPE
v
type <----------+
| TYPE_NAME |
v |
TYPE_DECL D |
| TREE_TYPE |
+-------------+
where for class context both TYPE_DECLs are in TYPE_FIELDS and for
namespace context only the latter one is (as pushdecl ignores the
IDENTIFIER_ANON_P one) we have
TYPE_DECL D TYPE_DECL D --- DECL_ORIGINAL_TYPE
| TREE_TYPE | TREE_TYPE |
v v |
type variant_type |
^-------------------------------+
which is except for the same DECL_NAME on both TYPE_DECLs exactly what
is used for typedef struct D_ { int d; } D;
Various spots have been testing for the typedef name for linkage purposes
cases and were using tests like:
OVERLOAD_TYPE_P (TREE_TYPE (value))
&& value == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (value)))
So that this can be tested, this patch introduces a new decl_flag on
the TYPE_DECLs and marks for -freflection both of these TYPE_DECLs
(and for -fno-reflection the one without IDENTIFIER_ANON_P name).
It is easy to differentiate between the two, the first one is also
DECL_IMPLICIT_TYPEDEF_P, the latter is not (and on the other side
has DECL_ORIGINAL_TYPE non-NULL).
For name lookup in namespaces, nothing special needs to be done,
because the originally IDENTIFIER_ANON_P TYPE_DECL wasn't added
to the bindings, at block scope I had to deal with it in pop_local_binding
because it was unhappy that it got renamed. And finally for class
scopes, we need to arrange for the latter TYPE_DECL to be found, but
currently it is the second one. The patch currently skips the first one
for
name lookup in fields_linear_search and arranges for count_class_fields
and member_vec_append_class_fields to also ignore the first one. Wonder if
the latter two shouldn't also ignore any other IDENTIFIER_ANON_P
TYPE_FIELDS
chain decls, or do we ever perform name lookup for the anon identifiers?
Another option for fields_linear_search would be try to swap the order of
the two TYPE_DECLs in TYPE_FIELDS chain somewhere in grokfield.
Anyway, the changes result in minor emitted DWARF changes, say for
g++.dg/debug/dwarf2/typedef1.C without -freflection there is
.uleb128 0x4 # (DIE (0x46) DW_TAG_enumeration_type)
.long .LASF6 # DW_AT_name: "typedef foo<1>::type type"
.byte 0x7 # DW_AT_encoding
.byte 0x4 # DW_AT_byte_size
.long 0x70 # DW_AT_type
.byte 0x1 # DW_AT_decl_file (typedef1.C)
.byte 0x18 # DW_AT_decl_line
.byte 0x12 # DW_AT_decl_column
.long .LASF7 # DW_AT_MIPS_linkage_name: "N3fooILj1EE4typeE"
...
and no typedef, while with -freflection there is
.uleb128 0x3 # (DIE (0x3a) DW_TAG_enumeration_type)
.long .LASF5 # DW_AT_name: "type"
.byte 0x7 # DW_AT_encoding
.byte 0x4 # DW_AT_byte_size
.long 0x6c # DW_AT_type
.byte 0x1 # DW_AT_decl_file (typedef1.C)
.byte 0x18 # DW_AT_decl_line
.byte 0x12 # DW_AT_decl_column
...
.uleb128 0x5 # (DIE (0x57) DW_TAG_typedef)
.long .LASF5 # DW_AT_name: "type"
.byte 0x1 # DW_AT_decl_file (typedef1.C)
.byte 0x18 # DW_AT_decl_line
.byte 0x1d # DW_AT_decl_column
.long 0x3a # DW_AT_type
so, different DW_AT_name on the DW_TAG_enumeration_type, missing
DW_AT_MIPS_linkage_name and an extra DW_TAG_typedef. While in theory
I could work harder to hide that detail, I actually think it is a good
thing to have it the latter way because it represents more exactly
what is going on.
Another slight change is different locations in some diagnostics
on g++.dg/lto/odr-3 test (location of the unnamed struct vs. locations
of the typedef name given to it without -freflection), and a module
issue which Nathan has some WIP patch for in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123810#c11
In any case, none of those differences show up in normal testsuite runs
currently (as those tests aren't compiled with -freflection), if/when
-freflection becomes the default for -std=c++26 we can deal with the
DWARF one as well as different locations in odr-3 and for modules I was
hoping it could be handled incrementally. I'm not even sure what should
happen if one TU has struct D { int d; }; and another one has
typedef struct { int d; } D;, shall that be some kind of error? Though
right now typedef struct { int d; } D; in both results in an error too
and that definitely needs to be handled.
2026-03-05 Jakub Jelinek <jakub@redhat.com>
PR c++/123810
* cp-tree.h (TYPE_DECL_FOR_LINKAGE_PURPOSES_P): Define.
(TYPE_DECL_WAS_UNNAMED): Likewise.
(TYPE_WAS_UNNAMED): Also check TYPE_DECL_WAS_UNNAMED.
* decl.cc (start_decl): Use TYPE_DECL_FOR_LINKAGE_PURPOSES_P.
(maybe_diagnose_non_c_class_typedef_for_l): If t == type, use
DECL_SOURCE_LOCATION (orig) instead of
DECL_SOURCE_LOCATION (TYPE_NAME (t)).
(name_unnamed_type): Set TYPE_DECL_FOR_LINKAGE_PURPOSES_P
on decl. For -freflection don't change TYPE_NAME from
orig to decl, but instead change DECL_NAME (orig) to
DECL_NAME (decl) and set TYPE_DECL_FOR_LINKAGE_PURPOSES_P on
orig too.
* decl2.cc (grokfield): Use TYPE_DECL_FOR_LINKAGE_PURPOSES_P.
* name-lookup.cc (fields_linear_search): Ignore
TYPE_DECL_WAS_UNNAMED decls.
(count_class_fields): Likewise.
(member_vec_append_class_fields): Likewise.
(pop_local_binding): Likewise.
* reflect.cc (namespace_members_of): For TYPE_DECL with
TYPE_DECL_FOR_LINKAGE_PURPOSES_P set also append
reflection of strip_typedefs (m).
* class.cc (find_flexarrays): Handle TYPE_DECLs with
TYPE_DECL_WAS_UNNAMED like the ones with IDENTIFIER_ANON_P
name.
* g++.dg/reflect/members_of10.C: New test.
* g++.dg/cpp2a/typedef1.C: Expect one message on a different line.
More information about the Gcc-bugs
mailing list