Hi! The first testcase comes directly from https://eel.is/c++draft/meta.reflection#names-1.8.2 and shows that we don't handle for -freflection the function argument name difference handling isn't performed for local externs, which doesn't go the duplicate_decls route but directly remembers the alias. The following patch handles the arguments similarly to duplicate_decls, it errors on [[indeterminate]] arguments which in alias are not marked like that, merges attributes and does the MULTIPLE_NAMES_PARM_P setting when needed, or DECL_NAME updates (in either direction) or "old param name" attribute addition. Unfortunately there are some important differences in the handling from duplicate_decls, where it is sometimes olddecl and sometimes newdecl that wins the duplicate_decls, the new fndecl can be a function definition, duplicate_decls needs to handle !types_match, etc. And decl (newdecl) here never needs e.g. "old param name" attribute, it can always have DECL_NAME changed.
ALso, I found that the addition of a new alias created completely broken DECL_ARGUMENTS (copied at most the first PARM_DECL, never more than that). That is because copy_decl clears DECL_CHAIN, so the loop always stopped after the first iteration. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2026-01-28 Jakub Jelinek <[email protected]> PR c++/123825 * name-lookup.cc: Include target.h. (push_local_extern_decl_alias): Diagnose indetermine argument on local extern decl vs. lack thereof on previous declaration. Merge PARM_DECL attributes. For -freflection handle differences in PARM_DECL names. Don't copy just the first PARM_DECL when creating a new alias FUNCTION_DECL. * g++.dg/reflect/has_identifier3.C: New test. * g++.dg/reflect/identifier_of3.C: New test. * g++.dg/cpp26/attr-indeterminate5.C: New test. --- gcc/cp/name-lookup.cc.jj 2026-01-26 12:37:18.694329580 +0100 +++ gcc/cp/name-lookup.cc 2026-01-27 22:46:38.449818103 +0100 @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. #include "c-family/known-headers.h" #include "c-family/c-spellcheck.h" #include "bitmap.h" +#include "target.h" static cxx_binding *cxx_binding_make (tree value, tree type); static cp_binding_level *innermost_nonclass_level (void); @@ -3736,6 +3737,75 @@ push_local_extern_decl_alias (tree decl) alias = *iter; if (!validate_constexpr_redeclaration (alias, decl)) return; + if (TREE_CODE (decl) != FUNCTION_DECL) + break; + tree oldarg, newarg; + for (oldarg = DECL_ARGUMENTS (alias), + newarg = DECL_ARGUMENTS (decl); + oldarg && newarg; + oldarg = DECL_CHAIN (oldarg), newarg = DECL_CHAIN (newarg)) + { + if (lookup_attribute (NULL, "indeterminate", + DECL_ATTRIBUTES (newarg)) + && !lookup_attribute (NULL, "indeterminate", + DECL_ATTRIBUTES (oldarg))) + { + auto_diagnostic_group d; + error_at (DECL_SOURCE_LOCATION (newarg), + "%<indeterminate%> attribute not specified " + "for parameter %qD on the first declaration " + "of its function", newarg); + inform (DECL_SOURCE_LOCATION (oldarg), + "earlier declaration"); + } + DECL_ATTRIBUTES (oldarg) + = (*targetm.merge_decl_attributes) (oldarg, newarg); + if (flag_reflection + && DECL_NAME (oldarg) != DECL_NAME (newarg)) + { + if (DECL_NAME (oldarg) && DECL_NAME (newarg)) + { + /* Different names. */ + MULTIPLE_NAMES_PARM_P (oldarg) = 1; + MULTIPLE_NAMES_PARM_P (newarg) = 1; + } + else if (DECL_INITIAL (alias) == NULL_TREE) + { + /* If alias is not a definition, propagate + name to both. */ + if (DECL_NAME (oldarg)) + DECL_NAME (newarg) = DECL_NAME (oldarg); + else + DECL_NAME (oldarg) = DECL_NAME (newarg); + } + else if (DECL_NAME (newarg)) + { + tree opn + = lookup_attribute ("old parm name", + DECL_ATTRIBUTES (oldarg)); + if (opn) + { + if (TREE_VALUE (TREE_VALUE (opn)) + != DECL_NAME (newarg)) + { + /* Different names. */ + MULTIPLE_NAMES_PARM_P (oldarg) = 1; + MULTIPLE_NAMES_PARM_P (newarg) = 1; + } + } + else + /* Save name into attribute. */ + DECL_ATTRIBUTES (oldarg) + = tree_cons (get_identifier ("old parm name"), + tree_cons (NULL_TREE, + DECL_NAME (newarg), + NULL_TREE), + DECL_ATTRIBUTES (oldarg)); + } + else + DECL_NAME (newarg) = DECL_NAME (oldarg); + } + } break; } @@ -3749,7 +3819,9 @@ push_local_extern_decl_alias (tree decl) for (tree *chain = &DECL_ARGUMENTS (alias); *chain; chain = &DECL_CHAIN (*chain)) { + tree next = DECL_CHAIN (*chain); *chain = copy_decl (*chain); + DECL_CHAIN (*chain) = next; DECL_CONTEXT (*chain) = alias; } --- gcc/testsuite/g++.dg/reflect/has_identifier3.C.jj 2026-01-27 13:48:10.621810464 +0100 +++ gcc/testsuite/g++.dg/reflect/has_identifier3.C 2026-01-27 13:48:06.461881128 +0100 @@ -0,0 +1,23 @@ +// PR c++/123825 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } +// Test std::meta::has_identifier. + +#include <meta> + +void fun (int); +constexpr std::meta::info r = parameters_of (^^fun)[0]; +static_assert (!has_identifier (r)); + +void fun (int x); +static_assert (has_identifier (r)); + +void fun (int x); +static_assert (has_identifier (r)); + +void +poison () +{ + void fun (int y); +} +static_assert (!has_identifier (r)); --- gcc/testsuite/g++.dg/reflect/identifier_of3.C.jj 2026-01-27 13:48:49.299153475 +0100 +++ gcc/testsuite/g++.dg/reflect/identifier_of3.C 2026-01-27 14:04:22.703249797 +0100 @@ -0,0 +1,73 @@ +// PR c++/123825 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } +// Test std::meta::identifier_of. + +#include <meta> + +void foo (int, int x, int y, int z); + +int +bar (int, int x, int y, int z, int) +{ + return x + y + z; +} + +constexpr auto foo1 = parameters_of (^^foo)[0]; +constexpr auto foo2 = parameters_of (^^foo)[1]; +constexpr auto foo3 = parameters_of (^^foo)[2]; +constexpr auto foo4 = parameters_of (^^foo)[3]; +constexpr auto bar1 = parameters_of (^^bar)[0]; +constexpr auto bar2 = parameters_of (^^bar)[1]; +constexpr auto bar3 = parameters_of (^^bar)[2]; +constexpr auto bar4 = parameters_of (^^bar)[3]; +constexpr auto bar5 = parameters_of (^^bar)[4]; +static_assert (!has_identifier (foo1)); +static_assert (identifier_of (foo2) == std::string_view ("x")); +static_assert (identifier_of (foo3) == std::string_view ("y")); +static_assert (identifier_of (foo4) == std::string_view ("z")); +static_assert (!has_identifier (bar1)); +static_assert (identifier_of (bar2) == std::string_view ("x")); +static_assert (identifier_of (bar3) == std::string_view ("y")); +static_assert (identifier_of (bar4) == std::string_view ("z")); +static_assert (!has_identifier (bar5)); + +void +baz () +{ + void foo (int w, int, int v, int z); + int bar (int, int, int v, int z, int u); + void qux (int, int x, int y, int z); + constexpr auto qux1 = parameters_of (^^qux)[0]; + constexpr auto qux2 = parameters_of (^^qux)[1]; + constexpr auto qux3 = parameters_of (^^qux)[2]; + constexpr auto qux4 = parameters_of (^^qux)[3]; + static_assert (!has_identifier (qux1)); + static_assert (identifier_of (qux2) == std::string_view ("x")); + static_assert (identifier_of (qux3) == std::string_view ("y")); + static_assert (identifier_of (qux4) == std::string_view ("z")); +} + +static_assert (identifier_of (foo1) == std::string_view ("w")); +static_assert (identifier_of (foo2) == std::string_view ("x")); +static_assert (!has_identifier (foo3)); +static_assert (identifier_of (foo4) == std::string_view ("z")); +static_assert (!has_identifier (bar1)); +static_assert (identifier_of (bar2) == std::string_view ("x")); +static_assert (!has_identifier (bar3)); +static_assert (identifier_of (bar4) == std::string_view ("z")); +static_assert (identifier_of (bar5) == std::string_view ("u")); + +void +fred () +{ + void qux (int w, int, int v, int z); + constexpr auto qux1 = parameters_of (^^qux)[0]; + constexpr auto qux2 = parameters_of (^^qux)[1]; + constexpr auto qux3 = parameters_of (^^qux)[2]; + constexpr auto qux4 = parameters_of (^^qux)[3]; + static_assert (identifier_of (qux1) == std::string_view ("w")); + static_assert (identifier_of (qux2) == std::string_view ("x")); + static_assert (!has_identifier (qux3)); + static_assert (identifier_of (qux4) == std::string_view ("z")); +} --- gcc/testsuite/g++.dg/cpp26/attr-indeterminate5.C.jj 2026-01-27 14:15:53.167527297 +0100 +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate5.C 2026-01-27 14:21:57.931339332 +0100 @@ -0,0 +1,23 @@ +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads +// { dg-do compile { target c++11 } } +// { dg-skip-if "" { c++26 } { "-ftrivial-auto-var-init=*" } { "" } } + +struct S { S (); S (const S &); ~S (); int s; }; +void foo (S s); // { dg-message "earlier declaration" } +void bar (S s [[indeterminate]]); +void baz (S s [[indeterminate]]); + +void +fred () +{ + void foo (S t [[indeterminate]]); // { dg-error "'indeterminate' attribute not specified for parameter 't' on the first declaration of its function" } + void bar (S t [[indeterminate]]); + void baz (S t); + void qux (S t); // { dg-message "earlier declaration" } + void corge (S t [[indeterminate]]); + void garply (S t [[indeterminate]]); +} + +void qux (S u [[indeterminate]]); // { dg-error "'indeterminate' attribute not specified for parameter 'u' on the first declaration of its function" } +void corge (S u [[indeterminate]]); +void garply (S u); Jakub
