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

Reply via email to