Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2196233/?format=api
{ "id": 2196233, "url": "http://patchwork.ozlabs.org/api/patches/2196233/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/aY7TMuKCv1ATinXe@tucnak/", "project": { "id": 17, "url": "http://patchwork.ozlabs.org/api/projects/17/?format=api", "name": "GNU Compiler Collection", "link_name": "gcc", "list_id": "gcc-patches.gcc.gnu.org", "list_email": "gcc-patches@gcc.gnu.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<aY7TMuKCv1ATinXe@tucnak>", "list_archive_url": null, "date": "2026-02-13T07:30:58", "name": "c++: Fix up handling of unnamed types named by typedef for linkage purposes for -freflection [PR123810]", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "e7f1738d70712068a5f3a324631429cc1c0f8ca2", "submitter": { "id": 671, "url": "http://patchwork.ozlabs.org/api/people/671/?format=api", "name": "Jakub Jelinek", "email": "jakub@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/aY7TMuKCv1ATinXe@tucnak/mbox/", "series": [ { "id": 492064, "url": "http://patchwork.ozlabs.org/api/series/492064/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=492064", "date": "2026-02-13T07:30:58", "name": "c++: Fix up handling of unnamed types named by typedef for linkage purposes for -freflection [PR123810]", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/492064/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2196233/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2196233/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=C+d/BeJX;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=C+d/BeJX", "sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com", "sourceware.org; spf=pass smtp.mailfrom=redhat.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fC3l22VQWz1xpY\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 13 Feb 2026 18:31:41 +1100 (AEDT)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id BBAA54BA23FE\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 13 Feb 2026 07:31:39 +0000 (GMT)", "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id 31A924BA23FE\n for <gcc-patches@gcc.gnu.org>; Fri, 13 Feb 2026 07:31:05 +0000 (GMT)", "from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-125-YLWrUeo1NqWbORGnSbe-8Q-1; Fri,\n 13 Feb 2026 02:31:02 -0500", "from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 141E41956054\n for <gcc-patches@gcc.gnu.org>; Fri, 13 Feb 2026 07:31:02 +0000 (UTC)", "from tucnak.zalov.cz (unknown [10.44.32.49])\n by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with\n ESMTPS\n id 6F5E41800465; Fri, 13 Feb 2026 07:31:01 +0000 (UTC)", "from tucnak.zalov.cz (localhost [127.0.0.1])\n by tucnak.zalov.cz (8.18.1/8.18.1) with ESMTPS id 61D7UwoQ2322826\n (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT);\n Fri, 13 Feb 2026 08:30:58 +0100", "(from jakub@localhost)\n by tucnak.zalov.cz (8.18.1/8.18.1/Submit) id 61D7UwJE2322825;\n Fri, 13 Feb 2026 08:30:58 +0100" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org BBAA54BA23FE", "OpenDKIM Filter v2.11.0 sourceware.org 31A924BA23FE" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 31A924BA23FE", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 31A924BA23FE", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770967865; cv=none;\n b=GKvG5SHT9GpMxvqIrRtL5hiK1WvC0llO8FrRwRXHFMyeeuMWCb/zJuPZzQSdlDumMTXlamw5UuasUN6oWVpc3Cz3wIQmpaEnR5fT2bEdCKsa9JkPfEwyQHm1ntPjfmQbpV6pBQbZxp9ii2BK6QGWXdWaqsg//h1i6gZv5899shs=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1770967865; c=relaxed/simple;\n bh=+RflCwcrznYDLOraQl6O06AKlXIDZzc7DhOoAK9oe3c=;\n h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version;\n b=LHClZUgIY7mdDrZ+UoTMhI2qdobEtTXds5w9Dejl4maQZYuRCAfY7yI5K+BoPwDDU9HW+8IRe2yNJIrK4KMK7sispcYBuT+p1ohqFtIht/3L+HgKXBfOAxhYfUiwlNoZ5bhMBiC2D/pFgdx3Mb3RSEBRevz+J5MHbA7r+bBg00w=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1770967864;\n h=from:from:reply-to:reply-to:subject:subject:date:date:\n message-id:message-id:to:to:cc:cc:mime-version:mime-version:\n content-type:content-type; bh=ojJsL2vgD0ohiimtIpMPw+jCiR8EgDpO7uZAM3RsQC0=;\n b=C+d/BeJXNgH7NA4nDLTWx6JoKtAbxiAvYmWVcpQPpxPCsmSViXTxOk0qQtkF92sGewlRFZ\n DwvoLuMFkmqa61OyZgGuqfAAlKxi61xi9BwZtAapCZaMQ35tfeS/fwXaagjlk4H+a++c2F\n 8bLQnXArEkf7UO9+pCynFac420UVma4=", "X-MC-Unique": "YLWrUeo1NqWbORGnSbe-8Q-1", "X-Mimecast-MFC-AGG-ID": "YLWrUeo1NqWbORGnSbe-8Q_1770967862", "Date": "Fri, 13 Feb 2026 08:30:58 +0100", "From": "Jakub Jelinek <jakub@redhat.com>", "To": "Jason Merrill <jason@redhat.com>", "Cc": "gcc-patches@gcc.gnu.org", "Subject": "[PATCH] c++: Fix up handling of unnamed types named by typedef for\n linkage purposes for -freflection [PR123810]", "Message-ID": "<aY7TMuKCv1ATinXe@tucnak>", "MIME-Version": "1.0", "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.111", "X-Mimecast-Spam-Score": "0", "X-Mimecast-MFC-PROC-ID": "qgzYPIDwncj1OuvLUL34dcoqOFVKcAWRJkTiD8AJ9fk_1770967862", "X-Mimecast-Originator": "redhat.com", "Content-Type": "text/plain; charset=us-ascii", "Content-Disposition": "inline", "X-BeenThere": "gcc-patches@gcc.gnu.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>", "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>", "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>", "List-Post": "<mailto:gcc-patches@gcc.gnu.org>", "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>", "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>", "Reply-To": "Jakub Jelinek <jakub@redhat.com>", "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org" }, "content": "Hi!\n\nAs mentioned on the PR, we ICE on the following testcase and if members_of\nisn't called on a class with e.g. typedef struct { int d; } D;, we don't\nhandle it correctly, e.g. we say ^^C::D is not an type alias or for\nmembers_of in a namespace that there aren't two entities, the struct itself\nand the type alias for it.\nThis is because name_unnamed_type handles the naming of an unnamed type\nthrough typedef for linkage purposes (where we originally have\na TYPE_DECL with IDENTIFIER_ANON_P DECL_NAME for the type) by replacing\nall occurrences of TYPE_NAME on the type from the old TYPE_DECL to the new\nTYPE_DECL with the user provided name.\nThe ICE for members_of (^^C, uctx) is then because we see two TYPE_DECLs\n(one with IDENTIFIER_ANON_P, one with user name) with the same TREE_TYPE\nand enter the same thing twice into what we want to return and ICE in the\ncomparison routine. Anyway, for is_type_alias purposes, there is no\nis_typedef_decl and can't be because the same TYPE_DECL is used as TYPE_NAME\nof both the type proper and its alias. Without reflection we didn't care\nabout the difference.\n\nSo, the following patch changes name_unnamed_type to do things differently,\nbut only for -freflection, because 1) I don't want to break stuff late in\nstage4 2) without reflection we don't really need it and don't need to\npay the extra memory cost by having another type which is the type alias.\nThe change is that instead of\nTYPE_DECL .anon_NN\n | TREE_TYPE\n v\ntype <----------+\n | TYPE_NAME |\n v |\nTYPE_DECL D |\n | TREE_TYPE |\n +-------------+\nwhere for class context both TYPE_DECLs are in TYPE_FIELDS and for\nnamespace context only the latter one is (as pushdecl ignores the\nIDENTIFIER_ANON_P one) we have\nTYPE_DECL D TYPE_DECL D --- DECL_ORIGINAL_TYPE\n | TREE_TYPE | TREE_TYPE |\n v v |\ntype variant_type |\n ^-------------------------------+\nwhich is except for the same DECL_NAME on both TYPE_DECLs exactly what\nis used for typedef struct D_ { int d; } D;\nVarious spots have been testing for the typedef name for linkage purposes\ncases and were using tests like:\nOVERLOAD_TYPE_P (TREE_TYPE (value))\n&& value == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (value)))\nSo that this can be tested, this patch introduces a new decl_flag on\nthe TYPE_DECLs and marks for -freflection both of these TYPE_DECLs\n(and for -fno-reflection the one without IDENTIFIER_ANON_P name).\nIt is easy to differentiate between the two, the first one is also\nDECL_IMPLICIT_TYPEDEF_P, the latter is not (and on the other side\nhas DECL_ORIGINAL_TYPE non-NULL).\nFor name lookup in namespaces, nothing special needs to be done,\nbecause the originally IDENTIFIER_ANON_P TYPE_DECL wasn't added\nto the bindings, at block scope I had to deal with it in pop_local_binding\nbecause it was unhappy that it got renamed. And finally for class\nscopes, we need to arrange for the latter TYPE_DECL to be found, but\ncurrently it is the second one. The patch currently skips the first one for\nname lookup in fields_linear_search and arranges for count_class_fields\nand member_vec_append_class_fields to also ignore the first one. Wonder if\nthe latter two shouldn't also ignore any other IDENTIFIER_ANON_P TYPE_FIELDS\nchain decls, or do we ever perform name lookup for the anon identifiers?\nAnother option for fields_linear_search would be try to swap the order of\nthe two TYPE_DECLs in TYPE_FIELDS chain somewhere in grokfield.\n\nAnyway, the changes result in minor emitted DWARF changes, say for\ng++.dg/debug/dwarf2/typedef1.C without -freflection there is\n .uleb128 0x4 # (DIE (0x46) DW_TAG_enumeration_type)\n .long .LASF6 # DW_AT_name: \"typedef foo<1>::type type\"\n .byte 0x7 # DW_AT_encoding\n .byte 0x4 # DW_AT_byte_size\n .long 0x70 # DW_AT_type\n .byte 0x1 # DW_AT_decl_file (typedef1.C)\n .byte 0x18 # DW_AT_decl_line\n .byte 0x12 # DW_AT_decl_column\n .long .LASF7 # DW_AT_MIPS_linkage_name: \"N3fooILj1EE4typeE\"\n...\nand no typedef, while with -freflection there is\n .uleb128 0x3 # (DIE (0x3a) DW_TAG_enumeration_type)\n .long .LASF5 # DW_AT_name: \"type\"\n .byte 0x7 # DW_AT_encoding\n .byte 0x4 # DW_AT_byte_size\n .long 0x6c # DW_AT_type\n .byte 0x1 # DW_AT_decl_file (typedef1.C)\n .byte 0x18 # DW_AT_decl_line\n .byte 0x12 # DW_AT_decl_column\n...\n .uleb128 0x5 # (DIE (0x57) DW_TAG_typedef)\n .long .LASF5 # DW_AT_name: \"type\"\n .byte 0x1 # DW_AT_decl_file (typedef1.C)\n .byte 0x18 # DW_AT_decl_line\n .byte 0x1d # DW_AT_decl_column\n .long 0x3a # DW_AT_type\nso, different DW_AT_name on the DW_TAG_enumeration_type, missing\nDW_AT_MIPS_linkage_name and an extra DW_TAG_typedef. While in theory\nI could work harder to hide that detail, I actually think it is a good\nthing to have it the latter way because it represents more exactly\nwhat is going on.\nAnother slight change is different locations in some diagnostics\non g++.dg/lto/odr-3 test (location of the unnamed struct vs. locations\nof the typedef name given to it without -freflection), and a module\nissue which Nathan has some WIP patch for in\nhttps://gcc.gnu.org/bugzilla/show_bug.cgi?id=123810#c11\n\nIn any case, none of those differences show up in normal testsuite runs\ncurrently (as those tests aren't compiled with -freflection), if/when\n-freflection becomes the default for -std=c++26 we can deal with the\nDWARF one as well as different locations in odr-3 and for modules I was\nhoping it could be handled incrementally. I'm not even sure what should\nhappen if one TU has struct D { int d; }; and another one has\ntypedef struct { int d; } D;, shall that be some kind of error? Though\nright now typedef struct { int d; } D; in both results in an error too\nand that definitely needs to be handled.\n\nBootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?\n\nBTW, also wonder about attributes/annotations on the typedef, say for\ntypedef struct { int d; } D [[deprecated]];\nor\ntypedef struct [[=3, =4]] { int d; } D [[=1, =2]];\nWe were running cplus_decl_attributes on the D TYPE_DECL (which was\nand without -freflection still is the TYPE_NAME of the type) with\nATTR_FLAG_TYPE_IN_PLACE flags (not sure what exactly it does when we\nactually don't call it on the type but the TYPE_DECL), now with -freflection\nit is called on the second D TYPE_DECL (i.e. the one later on for the\nset_underlying_type but set_underlying_type is called only later on).\neval_annotations_of right now does maybe_strip_typedefs and for types\nuses TYPE_ATTRIBUTES, so it will never return annotations on type aliases,\nguess we'll need to figure out what exactly it should do for cases like\nthat.\n\n2026-02-13 Jakub Jelinek <jakub@redhat.com>\n\n\tPR c++/123810\n\t* cp-tree.h (TYPE_DECL_FOR_LINKAGE_PURPOSES_P): Define.\n\t(TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P): Likewise.\n\t(TYPE_WAS_UNNAMED): Also check TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P.\n\t* decl.cc (start_decl): Use TYPE_DECL_FOR_LINKAGE_PURPOSES_P.\n\t(maybe_diagnose_non_c_class_typedef_for_l): If t == type, use\n\tDECL_SOURCE_LOCATION (orig) instead of\n\tDECL_SOURCE_LOCATION (TYPE_NAME (t)).\n\t(name_unnamed_type): Set TYPE_DECL_FOR_LINKAGE_PURPOSES_P\n\ton decl. For -freflection don't change TYPE_NAME from\n\torig to decl, but instead change DECL_NAME (orig) to\n\tDECL_NAME (decl) and set TYPE_DECL_FOR_LINKAGE_PURPOSES_P on\n\torig too.\n\t* decl2.cc (grokfield): Use TYPE_DECL_FOR_LINKAGE_PURPOSES_P.\n\t* name-lookup.cc (fields_linear_search): Ignore\n\tTYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P decls.\n\t(count_class_fields): Likewise.\n\t(member_vec_append_class_fields): Likewise.\n\t(pop_local_binding): Likewise.\n\t* reflect.cc (namespace_members_of): For TYPE_DECL with\n\tTYPE_DECL_FOR_LINKAGE_PURPOSES_P set also append\n\treflection of strip_typedefs (m).\n\t* class.cc (find_flexarrays): Handle TYPE_DECLs with\n\tTYPE_DECL_FOR_LINKAGE_PURPOSES_P like the ones with IDENTIFIER_ANON_P\n\tname.\n\n\t* g++.dg/reflect/members_of8.C: New test.\n\t* g++.dg/cpp2a/typedef1.C: Expect one message on a different line.\n\n\n\tJakub", "diff": "--- gcc/cp/cp-tree.h.jj\t2026-02-12 12:33:55.510613450 +0100\n+++ gcc/cp/cp-tree.h\t2026-02-12 14:40:12.649547905 +0100\n@@ -590,6 +590,7 @@ extern GTY(()) tree cp_global_trees[CPTI\n 7: DECL_THUNK_P (in a member FUNCTION_DECL)\n DECL_NORMAL_CAPTURE_P (in FIELD_DECL)\n DECL_DECLARED_CONSTINIT_P (in VAR_DECL)\n+ TYPE_DECL_FOR_LINKAGE_PURPOSES_P (in TYPE_DECL)\n 8: DECL_DECLARED_CONSTEXPR_P (in VAR_DECL, FUNCTION_DECL)\n \n Usage of language-independent fields in a language-dependent manner:\n@@ -3999,6 +4000,20 @@ struct GTY(()) lang_decl {\n && TREE_CODE (TYPE_NAME (NODE)) == TYPE_DECL\t\\\n && TYPE_DECL_ALIAS_P (TYPE_NAME (NODE)))\n \n+/* Nonzero for typedef name for linkage purposes. For -freflection\n+ set also on the originally unnamed TYPE_DECL. */\n+#define TYPE_DECL_FOR_LINKAGE_PURPOSES_P(NODE) \\\n+ DECL_LANG_FLAG_7 (TYPE_DECL_CHECK (NODE))\n+\n+/* Nonzero for TYPE_DECL originally with IDENTIFIER_ANON_P DECL_NAME\n+ later on named by a typedef name for linkage purposes in the\n+ -freflection case (otherwise the TYPE_DECL keeps IDENTIFIER_ANON_P\n+ DECL_NAME). */\n+#define TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P(NODE) \\\n+ (TREE_CODE (NODE) == TYPE_DECL \\\n+ && TYPE_DECL_FOR_LINKAGE_PURPOSES_P (NODE) \\\n+ && DECL_IMPLICIT_TYPEDEF_P (NODE))\n+\n /* If non-NULL for a VAR_DECL, FUNCTION_DECL, TYPE_DECL, TEMPLATE_DECL,\n or CONCEPT_DECL, the entity is either a template specialization (if\n DECL_USE_TEMPLATE is nonzero) or the abstract instance of the\n@@ -5365,10 +5380,13 @@ get_vec_init_expr (tree t)\n \n /* True if TYPE is an unnamed structured type with a typedef for\n linkage purposes. In that case TYPE_NAME and TYPE_STUB_DECL of the\n- MAIN-VARIANT are different. */\n+ MAIN-VARIANT are different or TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P\n+ is true for the TYPE_NAME. */\n #define TYPE_WAS_UNNAMED(NODE)\t\t\t\t\\\n (TYPE_NAME (TYPE_MAIN_VARIANT (NODE))\t\t\t\\\n- != TYPE_STUB_DECL (TYPE_MAIN_VARIANT (NODE)))\n+ != TYPE_STUB_DECL (TYPE_MAIN_VARIANT (NODE))\t\t\\\n+ || TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P\t\t\\\n+\t(TYPE_NAME (TYPE_MAIN_VARIANT (NODE))))\n \n /* C++: all of these are overloaded! These apply only to TYPE_DECLs. */\n \n--- gcc/cp/decl.cc.jj\t2026-02-12 10:32:24.647331121 +0100\n+++ gcc/cp/decl.cc\t2026-02-12 14:21:23.854541467 +0100\n@@ -6585,8 +6585,7 @@ start_decl (const cp_declarator *declara\n /* If this is a typedef that names the class for linkage purposes\n (7.1.3p8), apply any attributes directly to the type. */\n if (TREE_CODE (decl) == TYPE_DECL\n- && OVERLOAD_TYPE_P (TREE_TYPE (decl))\n- && decl == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (decl))))\n+ && TYPE_DECL_FOR_LINKAGE_PURPOSES_P (decl))\n flags = ATTR_FLAG_TYPE_IN_PLACE;\n else\n flags = 0;\n@@ -13698,7 +13697,8 @@ maybe_diagnose_non_c_class_typedef_for_l\n {\n auto_diagnostic_group d;\n if (diagnose_non_c_class_typedef_for_linkage (type, orig))\n-\tinform (DECL_SOURCE_LOCATION (TYPE_NAME (t)),\n+\tinform (type == t ? DECL_SOURCE_LOCATION (orig)\n+\t\t: DECL_SOURCE_LOCATION (TYPE_NAME (t)),\n \t\t\"type is not C-compatible because it has a base class\");\n return true;\n }\n@@ -13764,12 +13764,22 @@ name_unnamed_type (tree type, tree decl)\n gcc_assert (TYPE_UNNAMED_P (type)\n \t || enum_with_enumerator_for_linkage_p (type));\n \n- /* Replace the anonymous decl with the real decl. Be careful not to\n- rename other typedefs (such as the self-reference) of type. */\n tree orig = TYPE_NAME (type);\n- for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))\n- if (TYPE_NAME (t) == orig)\n- TYPE_NAME (t) = decl;\n+ if (flag_reflection)\n+ {\n+ /* For -freflection for typedef struct { ... } S; ^^S needs to be\n+\t a reflection of a type alias. So, TREE_TYPE (DECL) can't be\n+\t TYPE. Instead of what we do below, override DECL_NAME (orig). */\n+ DECL_NAME (orig) = DECL_NAME (decl);\n+ TYPE_DECL_FOR_LINKAGE_PURPOSES_P (orig) = 1;\n+ }\n+ else\n+ /* Replace the anonymous decl with the real decl. Be careful not to\n+ rename other typedefs (such as the self-reference) of type. */\n+ for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))\n+ if (TYPE_NAME (t) == orig)\n+\tTYPE_NAME (t) = decl;\n+ TYPE_DECL_FOR_LINKAGE_PURPOSES_P (decl) = 1;\n \n /* If this is a typedef within a template class, the nested\n type is a (non-primary) template. The name for the\n--- gcc/cp/decl2.cc.jj\t2026-02-12 10:32:24.656330969 +0100\n+++ gcc/cp/decl2.cc\t2026-02-12 12:39:29.271487328 +0100\n@@ -1303,8 +1303,7 @@ grokfield (const cp_declarator *declarat\n \n \t /* If this is a typedef that names the class for linkage purposes\n \t (7.1.3p8), apply any attributes directly to the type. */\n-\t if (OVERLOAD_TYPE_P (TREE_TYPE (value))\n-\t && value == TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (value))))\n+\t if (TYPE_DECL_FOR_LINKAGE_PURPOSES_P (value))\n \t attrflags = ATTR_FLAG_TYPE_IN_PLACE;\n \n \t cplus_decl_attributes (&value, attrlist, attrflags);\n--- gcc/cp/name-lookup.cc.jj\t2026-02-12 10:32:24.663330851 +0100\n+++ gcc/cp/name-lookup.cc\t2026-02-12 13:38:47.498316544 +0100\n@@ -1893,6 +1893,11 @@ fields_linear_search (tree klass, tree n\n \t continue;\n \t}\n \n+ if (TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P (decl))\n+\t/* Ignore TYPE_DECLs formerly unnamed, there is a TYPE_DECL\n+\t for linkage purposes later on in the chain. */\n+\tcontinue;\n+\n if (DECL_DECLARES_FUNCTION_P (decl))\n \t/* Functions are found separately. */\n \tcontinue;\n@@ -2369,7 +2374,13 @@ count_class_fields (tree klass)\n \t && ANON_AGGR_TYPE_P (TREE_TYPE (fields)))\n n_fields += count_class_fields (TREE_TYPE (fields));\n else if (DECL_NAME (fields))\n- n_fields += 1;\n+ {\n+\tif (TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P (fields))\n+\t /* Ignore TYPE_DECLs formerly unnamed, there is a TYPE_DECL\n+\t for linkage purposes later on in the chain. */\n+\t continue;\n+\tn_fields += 1;\n+ }\n \n return n_fields;\n }\n@@ -2393,6 +2404,10 @@ member_vec_append_class_fields (vec<tree\n \tif (TREE_CODE (field) == USING_DECL\n \t && IDENTIFIER_CONV_OP_P (DECL_NAME (field)))\n \t field = ovl_make (conv_op_marker, field);\n+\telse if (TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P (field))\n+\t /* Ignore TYPE_DECLs formerly unnamed, there is a TYPE_DECL\n+\t for linkage purposes later on in the chain. */\n+\t continue;\n \tmember_vec->quick_push (field);\n }\n }\n@@ -2724,7 +2739,10 @@ pop_local_binding (tree id, tree decl)\n binding->value = NULL_TREE;\n else if (binding->type == decl)\n binding->type = NULL_TREE;\n- else\n+ /* Ignore TYPE_DECL renamed in name_unnamed_type for -freflection,\n+ as there will be another TYPE_DECL with the same DECL_NAME for\n+ the typedef. */\n+ else if (!TYPE_DECL_NAMED_FOR_LINKAGE_PURPOSES_P (decl))\n {\n /* Name-independent variable was found after at least one declaration\n \t with the same name. */\n--- gcc/cp/reflect.cc.jj\t2026-02-12 10:32:24.668330767 +0100\n+++ gcc/cp/reflect.cc\t2026-02-12 12:39:29.272806505 +0100\n@@ -6528,6 +6528,11 @@ namespace_members_of (location_t loc, tr\n \t so don't bother calling it here. */\n \t CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE,\n \t\t\t\t get_reflection_raw (loc, m));\n+\t if (TREE_CODE (b) == TYPE_DECL\n+\t && TYPE_DECL_FOR_LINKAGE_PURPOSES_P (b))\n+\t CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE,\n+\t\t\t\t get_reflection_raw (loc,\n+\t\t\t\t\t\t\tstrip_typedefs (m)));\n \t}\n }\n delete seen;\n--- gcc/cp/class.cc.jj\t2026-02-12 10:32:29.075256573 +0100\n+++ gcc/cp/class.cc\t2026-02-12 14:11:58.067039044 +0100\n@@ -7608,7 +7608,8 @@ find_flexarrays (tree t, flexmems_t *fme\n if (TREE_CODE (fld) == TYPE_DECL\n \t && DECL_IMPLICIT_TYPEDEF_P (fld)\n \t && CLASS_TYPE_P (TREE_TYPE (fld))\n-\t && IDENTIFIER_ANON_P (DECL_NAME (fld)))\n+\t && (IDENTIFIER_ANON_P (DECL_NAME (fld))\n+\t || TYPE_DECL_FOR_LINKAGE_PURPOSES_P (fld)))\n \t{\n \t /* Check the nested unnamed type referenced via a typedef\n \t independently of FMEM (since it's not a data member of\n--- gcc/testsuite/g++.dg/reflect/members_of8.C.jj\t2026-02-12 12:39:29.273275302 +0100\n+++ gcc/testsuite/g++.dg/reflect/members_of8.C\t2026-02-12 12:39:29.273275302 +0100\n@@ -0,0 +1,56 @@\n+// PR c++/123810\n+// { dg-do compile { target c++26 } }\n+// { dg-additional-options \"-freflection\" }\n+\n+#include <meta>\n+\n+namespace A {\n+ typedef struct { int b; } B;\n+}\n+struct C {\n+ typedef struct { int d; } D;\n+};\n+struct F {\n+ typedef struct { int g; } G;\n+ typedef struct { int h; } H;\n+ typedef struct { int i; } I;\n+ typedef struct { int j; } J;\n+ typedef struct { int k; } K;\n+ typedef struct { int l; } L;\n+ typedef struct { int m; } M;\n+ typedef struct { int n; } N;\n+};\n+void\n+foo ()\n+{\n+ typedef struct { int e; } E;\n+ static_assert (is_type_alias (^^E));\n+}\n+\n+static_assert (is_type_alias (^^A::B));\n+static_assert (is_type_alias (^^C::D));\n+static_assert (is_type_alias (^^F::G));\n+static_assert (is_type_alias (^^F::N));\n+constexpr auto uctx = std::meta::access_context::unchecked ();\n+static_assert (members_of (^^C, uctx)[0] == dealias (^^C::D));\n+static_assert (members_of (^^C, uctx)[1] == ^^C::D);\n+static_assert (members_of (^^F, uctx)[0] == dealias (^^F::G));\n+static_assert (members_of (^^F, uctx)[1] == ^^F::G);\n+static_assert (members_of (^^F, uctx)[2] == dealias (^^F::H));\n+static_assert (members_of (^^F, uctx)[3] == ^^F::H);\n+static_assert (members_of (^^F, uctx)[4] == dealias (^^F::I));\n+static_assert (members_of (^^F, uctx)[5] == ^^F::I);\n+static_assert (members_of (^^F, uctx)[6] == dealias (^^F::J));\n+static_assert (members_of (^^F, uctx)[7] == ^^F::J);\n+static_assert (members_of (^^F, uctx)[8] == dealias (^^F::K));\n+static_assert (members_of (^^F, uctx)[9] == ^^F::K);\n+static_assert (members_of (^^F, uctx)[10] == dealias (^^F::L));\n+static_assert (members_of (^^F, uctx)[11] == ^^F::L);\n+static_assert (members_of (^^F, uctx)[12] == dealias (^^F::M));\n+static_assert (members_of (^^F, uctx)[13] == ^^F::M);\n+static_assert (members_of (^^F, uctx)[14] == dealias (^^F::N));\n+static_assert (members_of (^^F, uctx)[15] == ^^F::N);\n+static_assert ((members_of (^^A, uctx)[0] == dealias (^^A::B)\n+\t\t&& members_of (^^A, uctx)[1] == ^^A::B)\n+\t || ((members_of (^^A, uctx)[0] == ^^A::B)\n+\t\t && members_of (^^A, uctx)[1] == dealias (^^A::B)));\n--- gcc/testsuite/g++.dg/cpp2a/typedef1.C.jj\t2025-08-15 22:36:04.669612779 +0200\n+++ gcc/testsuite/g++.dg/cpp2a/typedef1.C\t2026-02-12 14:44:27.499255018 +0100\n@@ -33,8 +33,8 @@ typedef struct {\t\t\t\t\t// { dg-message \"un\n static int a;\t\t\t\t\t\t// { dg-error \"static data member '<unnamed struct>::a' in unnamed class\" }\n } B;\n typedef struct : public A {\t\t\t\t// { dg-error \"anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration\" }\n- int a;\n-} C;\t\t\t\t\t\t\t// { dg-message \"type is not C-compatible because it has a base class\" }\n+ int a;\t\t\t\t\t\t// { dg-message \"type is not C-compatible because it has a base class\" \"\" { target *-*-* } .-1 }\n+} C;\n #if __cplusplus >= 201103L\n typedef struct {\t\t\t\t\t// { dg-error \"anonymous non-C-compatible type given name for linkage purposes by 'typedef' declaration\" \"\" { target c++11 } }\n int b = 42;\t\t\t\t\t\t// { dg-message \"type is not C-compatible because 'D::b' has default member initializer\" \"\" { target c++11 } }\n", "prefixes": [] }