]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: For reflection look through DECL_LOCAL_DECL_P decls and fix up has_default_argum...
authorJakub Jelinek <jakub@redhat.com>
Tue, 17 Feb 2026 07:37:34 +0000 (08:37 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 17 Feb 2026 07:37:34 +0000 (08:37 +0100)
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  <jakub@redhat.com>

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
gcc/cp/decl.cc
gcc/cp/reflect.cc
gcc/testsuite/g++.dg/reflect/pr123612.C [new file with mode: 0644]

index a0683110eb3d098538581065d748d741741e866b..f205777e1afdd51acfc621b2cc41890c8668d3ed 100644 (file)
@@ -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) \
index fb0bc3cccddfdeaaf552f85ec36ace03eb7b053c..97460e448aeb62cd0bb220190a642294cf0cc31e 100644 (file)
@@ -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))
            {
index 367905f5c0bbeb099883629552f7120708e6a9b1..7c9e1815317c20f054984d7bccd30948a2a121b3 100644 (file)
@@ -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 (file)
index 0000000..97da7bb
--- /dev/null
@@ -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]));