On 2/14/26 7:28 PM, Jakub Jelinek wrote:
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
"at least one block extern"
OK with that tweak.
+ 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