From: Jakub Jelinek Date: Tue, 17 Feb 2026 07:37:34 +0000 (+0100) Subject: c++: For reflection look through DECL_LOCAL_DECL_P decls and fix up has_default_argum... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e60f6111f63e8164158cd5ed2957e77532a63441;p=thirdparty%2Fgcc.git 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 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 --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a0683110eb3..f205777e1af 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 fb0bc3cccdd..97460e448ae 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 367905f5c0b..7c9e1815317 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 00000000000..97da7bbdd0e --- /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 + +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]));