Hi!

Based on discussions in the PR that C++ 26 reflection is
entity based rather than declaration based, the following patch
ensures we reflect DECL_LOCAL_DECL_ALIAS of DECL_LOCAL_DECL_P vars
or function decls.
Additionally, while default arguments are intentionally not merged
from the block scope externs to their corresponding namespace scope
function decls, for std::meta::has_default_argument the wording
requires to return true even if there is at least one block scope
extern with default argument for the particular parameter.
So, the patch also records in a new flag whether a default argument
has been present in a block scope extern and propagates it through
further duplicate_decls.  eval_has_default_arguments then uses
both this new flag (for the block scope externs) and the preexisting
search for default arguments in the corresponding type.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-02-14  Jakub Jelinek  <[email protected]>

        PR c++/123612
        * cp-tree.h (DECL_HAS_DEFAULT_ARGUMENT_P): Define.
        * decl.cc (merge_decl_arguments): For -freflection and
        extern_alias set DECL_HAS_DEFAULT_ARGUMENT_P (oldarg)
        if newdecl has a default argument for that parameter,
        otherwise propagate the flag.
        * reflect.cc (get_reflection): For DECL_LOCAL_DECL_P
        use its DECL_LOCAL_DECL_ALIAS.
        (eval_has_default_argument): Return boolean_true_node
        also if DECL_HAS_DEFAULT_ARGUMENT_P flag is set.

        * g++.dg/reflect/pr123612.C: New test.

--- gcc/cp/cp-tree.h.jj 2026-02-13 10:32:21.490992631 +0100
+++ gcc/cp/cp-tree.h    2026-02-13 17:42:08.816452620 +0100
@@ -532,6 +532,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL)
       TARGET_EXPR_INTERNAL_P (in TARGET_EXPR)
       CONTRACT_CONST (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
+      DECL_HAS_DEFAULT_ARGUMENT_P (in PARM_DECL)
    5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
@@ -5329,6 +5330,12 @@ get_vec_init_expr (tree t)
 #define MULTIPLE_NAMES_PARM_P(NODE) \
   TREE_LANG_FLAG_2 (PARM_DECL_CHECK (NODE))
 
+/* Nonzero for PARM_DECL node means that at least one of block scope externs
+   for the corresponding FUNCTION_DECL provided default argument for this
+   parameter.  */
+#define DECL_HAS_DEFAULT_ARGUMENT_P(NODE) \
+  TREE_LANG_FLAG_4 (PARM_DECL_CHECK (NODE))
+
 /* Nonzero for a FIELD_DECL who's NSMDI is currently being
    instantiated.  */
 #define DECL_INSTANTIATING_NSDMI_P(NODE) \
--- gcc/cp/decl.cc.jj   2026-02-12 18:49:18.464869512 +0100
+++ gcc/cp/decl.cc      2026-02-13 18:01:34.745792786 +0100
@@ -1792,7 +1792,10 @@ void
 merge_decl_arguments (tree newdecl, tree olddecl, bool new_defines_function,
                      bool types_match, bool extern_alias)
 {
-  tree oldarg, newarg;
+  tree oldarg, newarg, type = NULL_TREE;
+  tree first_user_parm = NULL_TREE;
+  if (extern_alias)
+    first_user_parm = FUNCTION_FIRST_USER_PARM (newdecl);
   for (oldarg = DECL_ARGUMENTS (olddecl), newarg = DECL_ARGUMENTS (newdecl);
        oldarg && newarg;
        oldarg = DECL_CHAIN (oldarg), newarg = DECL_CHAIN (newarg))
@@ -1813,6 +1816,27 @@ merge_decl_arguments (tree newdecl, tree
         we should do that for the function itself, not just parameters.  */
       if (!extern_alias || flag_reflection)
        DECL_ATTRIBUTES (oldarg) = DECL_ATTRIBUTES (newarg);
+      if (!flag_reflection)
+       continue;
+      /* For extern_alias set DECL_HAS_DEFAULT_ARGUMENT_P on oldarg
+        if the local extern has a default argument for that parameter.  */
+      if (extern_alias)
+       {
+         if (newarg == first_user_parm)
+           type = FUNCTION_FIRST_USER_PARMTYPE (newdecl);
+         else if (type)
+           type = TREE_CHAIN (type);
+         if (type && TREE_PURPOSE (type))
+           DECL_HAS_DEFAULT_ARGUMENT_P (oldarg) = 1;
+       }
+      else
+       {
+         /* Otherwise propagate the flag.  */
+         if (DECL_HAS_DEFAULT_ARGUMENT_P (oldarg))
+           DECL_HAS_DEFAULT_ARGUMENT_P (newarg) = 1;
+         if (DECL_HAS_DEFAULT_ARGUMENT_P (newarg))
+           DECL_HAS_DEFAULT_ARGUMENT_P (oldarg) = 1;
+       }
       /* Merge names for std::meta::has_identifier and
         std::meta::{,u8}identifier_of purposes.  If they are different and
         both oldarg and newarg are named, add flag to force that
@@ -1820,7 +1844,7 @@ merge_decl_arguments (tree newdecl, tree
         unnamed, if neither is a olddecl nor newdecl is definition, propagate
         DECL_NAME to both.  Otherwise stash the old name into "old parm name"
         artificial attribute.  */
-      if (flag_reflection && DECL_NAME (oldarg) != DECL_NAME (newarg))
+      if (DECL_NAME (oldarg) != DECL_NAME (newarg))
        {
          if (DECL_NAME (oldarg) && DECL_NAME (newarg))
            {
--- gcc/cp/reflect.cc.jj        2026-02-13 16:10:49.053338387 +0100
+++ gcc/cp/reflect.cc   2026-02-13 17:50:54.317592740 +0100
@@ -235,6 +235,10 @@ get_reflection (location_t loc, tree t,
        t = dtor;
     }
 
+  /* Look through block scope externs.  */
+  if (VAR_OR_FUNCTION_DECL_P (t) && DECL_LOCAL_DECL_P (t))
+    t = DECL_LOCAL_DECL_ALIAS (t);
+
   if (t == error_mark_node)
     return error_mark_node;
 
@@ -1708,6 +1712,8 @@ eval_has_default_argument (tree r, refle
   if (eval_is_function_parameter (r, kind) == boolean_false_node)
     return boolean_false_node;
   r = maybe_update_function_parm (r);
+  if (DECL_HAS_DEFAULT_ARGUMENT_P (r))
+    return boolean_true_node;
   tree fn = DECL_CONTEXT (r);
   tree args = FUNCTION_FIRST_USER_PARM (fn);
   tree types = FUNCTION_FIRST_USER_PARMTYPE (fn);
--- gcc/testsuite/g++.dg/reflect/pr123612.C.jj  2026-02-13 18:31:24.571629423 
+0100
+++ gcc/testsuite/g++.dg/reflect/pr123612.C     2026-02-13 18:31:11.731845659 
+0100
@@ -0,0 +1,45 @@
+// PR c++/123612
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+int a;
+extern int b;
+int foo (int x, int y, int z = 42);
+
+consteval auto
+bar ()
+{
+  extern int a;
+  return ^^a;
+}
+
+consteval auto
+baz ()
+{
+  extern int b;
+  return ^^b;
+}
+
+consteval auto
+qux ()
+{
+  static_assert (!has_default_argument (parameters_of (^^foo)[0]));
+  static_assert (!has_default_argument (parameters_of (^^foo)[1]));
+  static_assert (has_default_argument (parameters_of (^^foo)[2]));
+  {
+    extern int foo (int x, int y = 5, int z = 16);
+    static_assert (!has_default_argument (parameters_of (^^foo)[0]));
+    static_assert (has_default_argument (parameters_of (^^foo)[1]));
+    static_assert (has_default_argument (parameters_of (^^foo)[2]));
+    return ^^foo;
+  }
+}
+
+static_assert (^^a == bar ());
+static_assert (^^b == baz ());
+static_assert (^^foo == qux ());
+static_assert (!has_default_argument (parameters_of (^^foo)[0]));
+static_assert (has_default_argument (parameters_of (^^foo)[1]));
+static_assert (has_default_argument (parameters_of (^^foo)[2]));

        Jakub

Reply via email to