https://gcc.gnu.org/g:e60f6111f63e8164158cd5ed2957e77532a63441

commit r16-7537-ge60f6111f63e8164158cd5ed2957e77532a63441
Author: Jakub Jelinek <[email protected]>
Date:   Tue Feb 17 08:37:34 2026 +0100

    c++: For reflection look through DECL_LOCAL_DECL_P decls and fix up 
has_default_argument [PR123612]
    
    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.
    
    2026-02-17  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.

Diff:
---
 gcc/cp/cp-tree.h                        |  7 +++++
 gcc/cp/decl.cc                          | 28 ++++++++++++++++++--
 gcc/cp/reflect.cc                       |  6 +++++
 gcc/testsuite/g++.dg/reflect/pr123612.C | 45 +++++++++++++++++++++++++++++++++
 4 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a0683110eb3d..f205777e1afd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -532,6 +532,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       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 block scope extern
+   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) \
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index fb0bc3cccddf..97460e448aeb 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -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 olddecl, bool 
new_defines_function,
         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 olddecl, bool 
new_defines_function,
         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))
            {
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 367905f5c0bb..7c9e1815317c 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -235,6 +235,10 @@ get_reflection (location_t loc, tree t, reflect_kind 
kind/*=REFLECT_UNDEF*/)
        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, reflect_kind kind)
   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);
diff --git a/gcc/testsuite/g++.dg/reflect/pr123612.C 
b/gcc/testsuite/g++.dg/reflect/pr123612.C
new file mode 100644
index 000000000000..97da7bbdd0e3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/pr123612.C
@@ -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]));

Reply via email to