On Tue, Dec 16, 2025 at 10:48:40PM +0700, Jason Merrill wrote:
> On 11/15/25 8:36 AM, Marek Polacek wrote:
> > This contains reflect.cc.
> >
> > -- >8 --
> > diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> > new file mode 100644
> > index 00000000000..38188920cea
> > --- /dev/null
> > +++ b/gcc/cp/reflect.cc
> > @@ -0,0 +1,8685 @@
> > +/* C++ reflection code.
> > + Copyright (C) 2025 Free Software Foundation, Inc.
> > + Written by Marek Polacek <[email protected]> and
> > + Jakub Jelinek <[email protected]>.
> > +
> > +This file is part of GCC.
> > +
> > +GCC is free software; you can redistribute it and/or modify
> > +it under the terms of the GNU General Public License as published by
> > +the Free Software Foundation; either version 3, or (at your option)
> > +any later version.
> > +
> > +GCC is distributed in the hope that it will be useful,
> > +but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > +GNU General Public License for more details.
> > +
> > +You should have received a copy of the GNU General Public License
> > +along with GCC; see the file COPYING3. If not see
> > +<http://www.gnu.org/licenses/>. */
> > +
> > +#include "config.h"
> > +#include "system.h"
> > +#include "coretypes.h"
> > +#include "target.h"
> > +#include "tm.h"
> > +#include "cp-tree.h"
> > +#include "stringpool.h" // for get_identifier
> > +#include "intl.h"
> > +#include "attribs.h"
> > +#include "c-family/c-pragma.h" // for parse_in
> > +#include "gimplify.h" // for unshare_expr
> > +
> > +static tree eval_is_function_type (tree);
> > +static tree eval_is_object_type (location_t, tree);
> > +static tree eval_reflect_constant (location_t, const constexpr_ctx *, tree,
> > + tree, bool *, tree *, tree);
> > +static tree eval_is_array_type (location_t, tree);
> > +static tree eval_reflect_constant_array (location_t, const constexpr_ctx *,
> > + tree, bool *, bool *, tree *, tree);
> > +static tree eval_reflect_function (location_t, const constexpr_ctx *, tree,
> > + tree, bool *, tree *, tree);
> > +struct constexpr_ctx;
> > +
> > +static GTY(()) tree vector_identifier;
> > +
> > +/* Initialize state for reflection; e.g., initialize meta_info_type_node.
> > */
> > +
> > +void
> > +init_reflection ()
> > +{
> > + /* The type std::meta::info is a scalar type for which equality and
> > + inequality are meaningful, but for which no ordering relation is
> > + defined. */
> > + meta_info_type_node = make_node (META_TYPE);
> > + /* Make it a complete type. */
> > + TYPE_SIZE (meta_info_type_node) = bitsize_int (GET_MODE_BITSIZE
> > (ptr_mode));
> > + TYPE_SIZE_UNIT (meta_info_type_node) = size_int (GET_MODE_SIZE
> > (ptr_mode));
> > + /* Name it. */
> > + record_builtin_type (RID_MAX, "decltype(^^int)", meta_info_type_node);
>
> As I mentioned on the other patch, I'm not sure following the nullptr_t
> pattern is right for std::meta::info, which is more tied to its namespace
> (for ADL and such). But I don't feel strongly about it, no change needed.
No change here.
> > + vector_identifier = get_identifier ("vector");
>
> This is only used in one place, and I'd be surprised if it made a measurable
> difference; let's drop it (or prove me wrong).
Removed in
<https://forge.sourceware.org/marek/gcc/commit/d700317e8f413e9a7584582e413029dd26968ac5>
> > + TREE_TYPE (std_meta_node) = void_type_node;
> > +}
> > +
> > +/* Create a REFLECT_EXPR expression of kind KIND around T. */
> > +
> > +static tree
> > +get_reflection_raw (location_t loc, tree t, reflect_kind kind =
> > REFLECT_UNDEF)
> > +{
> > + t = build1_loc (loc, REFLECT_EXPR, meta_info_type_node, t);
> > + REFLECT_EXPR_KIND (t) = kind;
> > + TREE_CONSTANT (t) = true;
> > + TREE_READONLY (t) = true;
> > + TREE_SIDE_EFFECTS (t) = false;
> > + return t;
> > +}
> > +
> > +/* Return the reflection for T.
> > +
> > + [basic.fundamental]: A value of type std::meta::info is called a
> > reflection.
> > + There exists a unique null reflection; every other reflection is
> > + a representation of
> > +
> > + -- a value of scalar type,
> > + -- an object with static storage duration,
> > + -- a variable,
> > + -- a structured binding,
> > + -- a function,
> > + -- a function parameter,
> > + -- an enumerator,
> > + -- an annotation,
> > + -- a type alias,
> > + -- a type,
> > + -- a class member,
> > + -- an unnamed bit-field,
> > + -- a class template,
> > + -- a function template,
> > + -- a variable template,
> > + -- an alias template,
> > + -- a concept,
> > + -- a namespace alias,
> > + -- a namespace,
> > + -- a direct base class relationship, or
> > + -- a data member description.
> > +
> > + KIND is used to distinguish between categories that are represented
> > + by the same handle. */
> > +
> > +tree
> > +get_reflection (location_t loc, tree t, reflect_kind
> > kind/*=REFLECT_UNDEF*/)
> > +{
> > + STRIP_ANY_LOCATION_WRAPPER (t);
> > +
> > + /* [expr.reflect] If the type-id designates a placeholder type, R is
> > + ill-formed. */
> > + if (is_auto (t))
> > + {
> > + error_at (loc, "%<^^%> cannot be applied to a placeholder type");
> > + return error_mark_node;
> > + }
> > + /* Constant template parameters and pack-index-expressions cannot
> > + appear as operands of the reflection operator. */
> > + else if (PACK_INDEX_P (t))
> > + {
> > + error_at (loc, "%<^^%> cannot be applied to a pack index");
> > + return error_mark_node;
> > + }
> > + else if (TREE_CODE (t) == CONST_DECL && DECL_TEMPLATE_PARM_P (t))
> > + {
> > + error_at (loc, "%<^^%> cannot be applied to a non-type template "
> > + "parameter %qD", t);
> > + return error_mark_node;
> > + }
> > + /* If the id-expression denotes a local parameter introduced by
> > + a requires-expression, R is ill-formed. */
> > + else if (TREE_CODE (t) == PARM_DECL && CONSTRAINT_VAR_P (t))
> > + {
> > + error_at (loc, "%<^^%> cannot be applied to a local parameter of "
> > + "a requires-expression %qD", t);
> > + return error_mark_node;
> > + }
>
> > /* If the id-expression denotes a local entity E for which there is
> > a lambda scope that intervenes between R and the point at which E
> > was introduced, R is ill-formed. */
>
> Looking at this now, the wording seems unusefully imprecise; I'd think what
> we want to say is if an odr-use of E would refer to a lambda capture, it's
> ill-formed. But that's not something to address now.
>
> Practically, I'd move the is_capture_proxy case before this one and drop the
> !normal check; reflecting on any capture proxy is ill-formed. But we still
> need this case for outer vars that aren't captured yet.
Done in
<https://forge.sourceware.org/marek/gcc/commit/360dfc1d52f0427768c28aa9a7d3aa8758e0fb75>
> > else if (outer_automatic_var_p (t)
> > /* Since outer_automatic_var_p is also true when we are in
> > a local class member function, additionally check that
> > we are in a lambda. */
> > && ((current_function_decl
> > && LAMBDA_FUNCTION_P (current_function_decl))
> > || parsing_lambda_declarator ()))
> > {
> > auto_diagnostic_group d;
> > error_at (loc, "%<^^%> cannot be applied a local entity for which "
> > "there is an intervening lambda expression");
> > inform (DECL_SOURCE_LOCATION (t), "%qD declared here", t);
> > return error_mark_node;
> > }
> > /* If the id-expression denotes a variable declared by an
> > init-capture, R is ill-formed. */
> > else if (is_capture_proxy (t) && !is_normal_capture_proxy (t))
> > {
> > error_at (loc, "%<^^%> cannot be applied to a local entity declared "
> > "by init-capture");
> > return error_mark_node;
> > }
> > + /* If lookup finds a declaration that replaced a using-declarator during
> > + a single search, R is ill-formed. */
> > + else if (TREE_CODE (t) == USING_DECL
> > + || (TREE_CODE (t) == OVERLOAD && OVL_USING_P (t)))
> > + {
> > + error_at (loc, "%<^^%> cannot be applied to a using-declarator");
>
> Let's say using-declaration.
Done in
<https://forge.sourceware.org/marek/gcc/commit/e3a23369045543b599144e2961deaa1a11b5bcfa>
> > + return error_mark_node;
> > + }
> > + /* A concept is fine, but not Concept<arg>. */
> > + else if (concept_check_p (t))
> > + {
> > + error_at (loc, "%<^^%> cannot be applied to a concept check");
> > + return error_mark_node;
> > + }
> > +
> > + /* Otherwise, if the template-name names a function template F,
> > + then the template-name interpreted as an id-expression shall
> > + denote an overload set containing only F. R represents F.
> > +
> > + When we have:
> > + template<typename T>
> > + void foo (T) {}
> > + constexpr auto a = ^^foo;
> > + we will get an OVERLOAD containing only one function. */
> > + tree r = MAYBE_BASELINK_FUNCTIONS (t);
> > + if (OVL_P (r))
> > + {
> > + if (!OVL_SINGLE_P (r))
> > + {
> > + error_at (loc, "cannot take the reflection of an overload set");
> > + return error_mark_node;
> > + }
> > + }
> > + /* [expr.reflect] If the id-expression denotes an overload set S,
> > + overload resolution for the expression &S with no target shall
> > + select a unique function; R represents that function. */
> > + else if (!processing_template_decl && t != unknown_type_node)
> > + {
> > + /* We can't resolve all TEMPLATE_ID_EXPRs here (due to
> > + _postfix_dot_deref_expression) but we can weed out the bad ones. */
>
> Is this still true after the lookup adjustments in the other patch?
It's not! So we can resolve all TEMPLATE_ID_EXPRs here and remove
some code in cp_fold_immediate_r and process_metafunction. Done in
<https://forge.sourceware.org/marek/gcc/commit/ba3df95321c47e10d4b2eadf7ec9b2cfe55b3269>
> > + r = resolve_nondeduced_context_or_error (t, tf_warning_or_error);
> > + if (r == error_mark_node)
> > + t = r;
> > + }
> > +
> > + /* For injected-class-name, use the main variant so that comparing
> > + reflections works (cf. compare3.C). */
> > + if (RECORD_OR_UNION_TYPE_P (t)
> > + && TYPE_NAME (t)
> > + && DECL_SELF_REFERENCE_P (TYPE_NAME (t)))
> > + t = TYPE_MAIN_VARIANT (t);
> > +
> > + /* It's annoying to deal with BIT_NOT_EXPR in a reflection later, so
> > + look up the FUNCTION_DECL here. */
> > + if (TREE_CODE (t) == BIT_NOT_EXPR
> > + && CLASS_TYPE_P (TREE_OPERAND (t, 0))
> > + && COMPLETE_TYPE_P (TREE_OPERAND (t, 0)))
> > + {
> > + r = TREE_OPERAND (t, 0);
> > + if (CLASSTYPE_LAZY_DESTRUCTOR (r))
> > + lazily_declare_fn (sfk_destructor, r);
> > + if (tree dtor = CLASSTYPE_DESTRUCTOR (r))
> > + t = dtor;
> > + }
> > +
> > + if (t == error_mark_node)
> > + return error_mark_node;
> > +
> > + return get_reflection_raw (loc, t, kind);
> > +}
> > +
> > +/* Null reflection shared tree. */
> > +
> > +static GTY(()) tree null_reflection;
> > +
> > +/* Return a null reflection value. */
> > +
> > +tree
> > +get_null_reflection ()
> > +{
> > + if (!null_reflection)
> > + null_reflection = get_reflection_raw (UNKNOWN_LOCATION,
> > unknown_type_node);
> > + return null_reflection;
> > +}
> > +
> > +/* Do strip_typedefs on T, but only for types. */
> > +
> > +static tree
> > +maybe_strip_typedefs (tree t)
> > +{
> > + if (TYPE_P (t))
> > + return strip_typedefs (t);
> > + return t;
> > +}
> > +
> > +/* If PARM_DECL comes from an earlier reflection of a function parameter
> > + and function definition is seen after that, DECL_ARGUMENTS is
> > + overwritten and so the old PARM_DECL is no longer present in the
> > + DECL_ARGUMENTS (DECL_CONTEXT (parm)) chain. Return corresponding
> > + PARM_DECL which is in the chain. */
> > +
> > +static tree
> > +maybe_update_function_parm (tree parm)
> > +{
> > + if (!OLD_PARM_DECL_P (parm))
> > + return parm;
> > + tree fn = DECL_CONTEXT (parm);
> > + int oldlen = list_length (parm);
> > + int newlen = list_length (DECL_ARGUMENTS (fn));
> > + gcc_assert (newlen >= oldlen);
> > + tree ret = DECL_ARGUMENTS (fn);
> > + int n = newlen - oldlen;
> > + while (n)
> > + {
> > + ret = DECL_CHAIN (ret);
> > + --n;
> > + }
> > + return ret;
> > +}
> > +
> > +/* Returns true if FNDECL, a FUNCTION_DECL, is a call to a metafunction
> > + declared in namespace std::meta. */
> > +
> > +bool
> > +metafunction_p (tree fndecl)
> > +{
> > + if (!flag_reflection)
> > + return false;
> > +
> > + /* Metafunctions are expected to be marked consteval. */
> > + if (!DECL_IMMEDIATE_FUNCTION_P (fndecl))
> > + return false;
> > +
> > + if (special_function_p (fndecl))
> > + return false;
> > +
> > + /* Is the call from std::meta? */
> > + tree ctx = decl_namespace_context (fndecl);
> > + if (!DECL_NAMESPACE_STD_META_P (ctx))
> > + return false;
> > +
> > + /* They should be user provided and not defined. */
> > + if (!user_provided_p (fndecl)
> > + || (DECL_NAMESPACE_SCOPE_P (fndecl) && DECL_DELETED_FN (fndecl)))
> > + return false;
> > + if (DECL_INITIAL (fndecl))
> > + return false;
> > +
> > + return true;
> > +}
> > +
> > +/* Extract the N-th reflection argument from a metafunction call CALL. */
> > +
> > +static tree
> > +get_info (const constexpr_ctx *ctx, tree call, int n, bool *non_constant_p,
> > + bool *overflow_p, tree *jump_target)
> > +{
> > + gcc_checking_assert (call_expr_nargs (call) > n);
> > + tree info = get_nth_callarg (call, n);
> > + gcc_checking_assert (REFLECTION_TYPE_P (TREE_TYPE (info)));
> > + info = cxx_eval_constant_expression (ctx, info, vc_prvalue,
> > + non_constant_p, overflow_p,
> > + jump_target);
> > + if (*jump_target)
> > + return NULL_TREE;
> > + if (!REFLECT_EXPR_P (info))
> > + {
> > + *non_constant_p = true;
> > + return NULL_TREE;
> > + }
> > + return info;
> > +}
> > +
> > +/* Try to get the underlying FUNCTION_DECL from reflection if any,
> > + otherwise return R. */
>
> Please say more about why get_first_fn isn't suitable (if it isn't).
The problem is that get_fns asserts that there was an overload set,
but often there won't be. What I could do is to introduce
maybe_get_first_fn that uses maybe_get_fns.
> > +static tree
> > +maybe_get_reflection_fndecl (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + r = OVL_FIRST (r);
> > + return r;
> > +}
> > +
> > +/* Helper function for get_range_elts, called through cp_walk_tree. */
> > +
> > +static tree
> > +replace_parm_r (tree *tp, int *walk_subtrees, void *data)
> > +{
> > + tree *p = (tree *) data;
> > + if (*tp == p[0])
> > + *tp = p[1];
> > + else if (TYPE_P (*tp))
> > + *walk_subtrees = 0;
> > + return NULL_TREE;
> > +}
> > +
> > +static tree throw_exception (location_t, const constexpr_ctx *, const char
> > *,
> > + tree, bool *, tree *);
> > +
> > +/* Kinds for get_range_elts. */
> > +
> > +enum get_range_elts_kind {
> > + GET_INFO_VEC,
> > + REFLECT_CONSTANT_STRING,
> > + REFLECT_CONSTANT_ARRAY
> > +};
> > +
> > +/* Extract the N-th input_range argument from a metafunction call CALL
> > + and return it as TREE_VEC or STRING_CST or CONSTRUCTOR. Helper function
> > + for get_info_vec, eval_reflect_constant_string and
> > + eval_reflect_constant_array. For GET_INFO_VEC kind, <meta> ensures
> > + the argument is reference to reflection_range concept and so both
> > + range_value_t is info and range_refernce_t is cv info or cv info & or
> > + cv info &&. */
> > +
> > +static tree
> > +get_range_elts (location_t loc, const constexpr_ctx *ctx, tree call, int n,
> > + bool *non_constant_p, bool *overflow_p, tree *jump_target,
> > + get_range_elts_kind kind, tree fun)
> > +{
> > + gcc_checking_assert (call_expr_nargs (call) > n);
> > + tree arg = get_nth_callarg (call, n);
> > + tree parm = DECL_ARGUMENTS (cp_get_callee_fndecl_nofold (call));
> > + for (int i = 0; i < n; ++i)
> > + parm = DECL_CHAIN (parm);
> > + tree type = TREE_TYPE (arg);
> > + gcc_checking_assert (TYPE_REF_P (type));
> > + arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue, non_constant_p,
> > + overflow_p, jump_target);
> > +fail_ret:
>
> Why is this label here? The use below when looking up "data" fails would
> seem to fall through again, as *non_constant_p hasn't been set, and other
> uses check the same condition before the goto. Why don't the current gotos
> just return NULL_TREE?
Removed in
<https://forge.sourceware.org/marek/gcc/commit/c66a990531b524446a966e480777edee372a48e2>
> > + if (*jump_target || *non_constant_p)
> > + return NULL_TREE;
> > + tree map[2] = { parm, arg };
> > + /* To speed things up, check
> > + if constexpr (std::ranges::contiguous_range <_R>). */
> > + tree ranges_ns = lookup_qualified_name (std_node, "ranges");
> > + if (TREE_CODE (ranges_ns) != NAMESPACE_DECL)
> > + {
> > + error_at (loc, "%<std::ranges%> is not a namespace");
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + tree contiguous_range
> > + = lookup_qualified_name (ranges_ns, "contiguous_range");
> > + if (TREE_CODE (contiguous_range) != TEMPLATE_DECL
> > + || !concept_definition_p (contiguous_range))
> > + contiguous_range = NULL_TREE;
> > + else
> > + {
> > + tree args = make_tree_vec (1);
> > + TREE_VEC_ELT (args, 0) = TREE_TYPE (type);
> > + contiguous_range = build2_loc (loc, TEMPLATE_ID_EXPR,
> > boolean_type_node,
> > + contiguous_range, args);
> > + if (!integer_nonzerop (maybe_constant_value (contiguous_range)))
> > + contiguous_range = NULL_TREE;
> > + }
> > + tree valuet = meta_info_type_node;
> > + tree ret = NULL_TREE;
> > + if (kind != GET_INFO_VEC)
> > + {
> > + tree args = make_tree_vec (1);
> > + TREE_VEC_ELT (args, 0) = TREE_TYPE (type);
> > + tree inst = lookup_template_class (get_identifier ("range_value_t"),
> > + args, /*in_decl*/NULL_TREE,
> > + /*context*/ranges_ns,
> > + tf_warning_or_error);
> > + inst = complete_type (inst);
> > + if (inst == error_mark_node)
> > + {
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + valuet = TYPE_MAIN_VARIANT (inst);
> > + if (kind == REFLECT_CONSTANT_STRING
> > + && valuet != char_type_node
> > + && valuet != wchar_type_node
> > + && valuet != char8_type_node
> > + && valuet != char16_type_node
> > + && valuet != char32_type_node)
> > + {
> > + if (!cxx_constexpr_quiet_p (ctx))
> > + error_at (loc, "%<reflect_constant_string%> called with %qT "
> > + "%<std::ranges::range_value_t%> rather than "
>
> If we were to pass inst rather than valuet to %qT, would that include
> "range_value_t" without us needing to mention it in the format string?
> Likewise for several diagnostics below.
Done in
<https://forge.sourceware.org/marek/gcc/commit/83dc4be2e31442d10c6bb5a5529ff0fbefa16f07>
> > + "%<char%>, %<wchar_t%>, %<char8_t%>, %<char16_t%> "
> > + "or %<char32_t%>", valuet);
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + /* Check for the reflect_object_string special-case, where r
> > + refers to a string literal. In that case CharT() should not
> > + be appended. */
> > + if (kind == REFLECT_CONSTANT_STRING
> > + && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE
> > + && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type))) == valuet
> > + && TYPE_DOMAIN (TREE_TYPE (type)))
> > + {
> > + tree a = arg;
> > + tree maxv = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (type)));
> > + STRIP_NOPS (a);
> > + tree at;
> > + if (TREE_CODE (a) == ADDR_EXPR
> > + && TREE_CODE (TREE_OPERAND (a, 0)) == STRING_CST
> > + && tree_fits_uhwi_p (maxv)
> > + && ((unsigned) TREE_STRING_LENGTH (TREE_OPERAND (a, 0))
> > + == ((tree_to_uhwi (maxv) + 1)
> > + * tree_to_uhwi (TYPE_SIZE_UNIT (valuet))))
> > + && (at = TREE_TYPE (TREE_OPERAND (a, 0)))
> > + && TREE_CODE (at) == ARRAY_TYPE
> > + && TYPE_MAIN_VARIANT (TREE_TYPE (at)) == valuet
> > + && TYPE_DOMAIN (at)
> > + && tree_int_cst_equal (maxv, TYPE_MAX_VALUE (TYPE_DOMAIN (at))))
>
> Most of this seems like same_type_ignoring_top_level_qualifiers_p (TREE_TYPE
> (type), at)?
Done in
<https://forge.sourceware.org/marek/gcc/commit/1a9ddf95ac28aa6a5fddfb93290fa3fabaa08403>
> > + return TREE_OPERAND (a, 0);
> > + }
> > + if (kind == REFLECT_CONSTANT_ARRAY)
> > + {
> > + if (!structural_type_p (valuet))
> > + {
> > + if (!cxx_constexpr_quiet_p (ctx))
> > + {
> > + auto_diagnostic_group d;
> > + error_at (loc, "%<reflect_constant_array%> argument with "
> > + "%qT %<std::ranges::range_value_t%> which "
> > + "is not a structural type", valuet);
> > + structural_type_p (valuet, true);
> > + }
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + tree cvaluet
> > + = cp_build_qualified_type (valuet, cp_type_quals (valuet)
> > + | TYPE_QUAL_CONST);
> > + TREE_VEC_ELT (args, 0)
> > + = cp_build_reference_type (cvaluet, /*rval=*/false);
> > + if (!is_xible (INIT_EXPR, valuet, args))
> > + {
> > + if (!cxx_constexpr_quiet_p (ctx))
> > + error_at (loc, "%<reflect_constant_array%> argument with %qT "
> > + "%<std::ranges::range_value_t%> which is not "
> > + "copy constructible", valuet);
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + TREE_VEC_ELT (args, 0) = TREE_TYPE (type);
> > + inst = lookup_template_class (get_identifier ("range_reference_t"),
> > + args, /*in_decl*/NULL_TREE,
> > + /*context*/ranges_ns,
> > + tf_warning_or_error);
> > + inst = complete_type (inst);
> > + if (inst == error_mark_node)
> > + {
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + tree referencet = TYPE_MAIN_VARIANT (inst);
> > + TREE_VEC_ELT (args, 0) = referencet;
> > + if (!is_xible (INIT_EXPR, valuet, args))
> > + {
> > + if (!cxx_constexpr_quiet_p (ctx))
> > + error_at (loc, "%<reflect_constant_array%> argument with %qT "
> > + "%<std::ranges::range_value_t%> which is not "
> > + "constructible from %qT "
> > + "%<std::ranges::range_reference_t%>",
> > + valuet, referencet);
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + }
> > + }
> > + auto_vec<tree, 32> retvec;
> > + tree p = convert_from_reference (parm);
> > + auto obj_call = [=, &map] (tree obj, tsubst_flags_t complain) {
>
> FWIW in general I encourage just using [&] for lambdas rather than trying to
> micro-optimize value vs reference. No change needed.
OK, kept as-is.
> > + releasing_vec args;
> > + vec_safe_push (args, p);
> > + tree call = finish_call_expr (obj, &args, true, false, complain);
> > + if (call == error_mark_node)
> > + return call;
> > + cp_walk_tree (&call, replace_parm_r, map, NULL);
> > + if (complain != tf_none)
> > + return call;
> > + call = cxx_eval_constant_expression (ctx, call, vc_prvalue,
> > non_constant_p,
> > + overflow_p, jump_target);
> > + if (*jump_target || *non_constant_p)
> > + return NULL_TREE;
> > + return call;
> > + };
> > + auto ret_retvec = [=, &retvec] () {
> > + unsigned HOST_WIDE_INT sz = retvec.length ();
> > + for (size_t i = 0; i < sz; ++i)
> > + {
> > + if (INTEGRAL_TYPE_P (valuet))
> > + {
> > + if (TREE_CODE (retvec[i]) != INTEGER_CST)
> > + return throw_exception (loc, ctx,
> > + "array element not a constant integer",
> > + fun, non_constant_p, jump_target);
> > + }
> > + else
> > + {
> > + gcc_assert (kind == REFLECT_CONSTANT_ARRAY);
> > + tree expr = convert_reflect_constant_arg (valuet, retvec[i]);
> > + if (expr == error_mark_node)
> > + return throw_exception (loc, ctx, "reflect_constant failed",
> > + fun, non_constant_p, jump_target);
> > + if (VAR_P (expr))
> > + expr = unshare_expr (DECL_INITIAL (expr));
> > + retvec[i] = expr;
> > + }
> > + }
> > + if (kind == REFLECT_CONSTANT_ARRAY && sz == 0)
> > + {
> > + /* Return std::array <valuet, 0> {}. */
> > + tree args = make_tree_vec (2);
> > + TREE_VEC_ELT (args, 0) = valuet;
> > + TREE_VEC_ELT (args, 1) = size_zero_node;
> > + tree inst = lookup_template_class (get_identifier ("array"), args,
> > + /*in_decl*/NULL_TREE,
> > + /*context*/std_node,
> > + tf_warning_or_error);
> > + tree type = complete_type (inst);
> > + if (type == error_mark_node)
> > + {
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + tree ctor = build_constructor (init_list_type_node, nullptr);
> > + CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
> > + TREE_CONSTANT (ctor) = true;
> > + TREE_STATIC (ctor) = true;
> > + tree r = finish_compound_literal (type, ctor, tf_warning_or_error,
> > + fcl_functional);
> > + if (TREE_CODE (r) == TARGET_EXPR)
>
> Let's check SIMPLE_TARGET_EXPR_P in places we're going to strip the
> TARGET_EXPR.
Answered in
<https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703972.html>
> > + r = TARGET_EXPR_INITIAL (r);
> > + return r;
> > + }
> > + unsigned esz = tree_to_uhwi (TYPE_SIZE_UNIT (valuet));
> > + unsigned last = kind == REFLECT_CONSTANT_STRING ? esz : 0;
> > + tree index = build_index_type (size_int (last ? sz : sz - 1));
> > + tree at = build_array_type (valuet, index);
> > + at = cp_build_qualified_type (at, TYPE_QUAL_CONST);
> > + if (kind == REFLECT_CONSTANT_STRING
> > + || ((valuet == char_type_node
> > + || valuet == wchar_type_node
> > + || valuet == char8_type_node
> > + || valuet == char16_type_node
> > + || valuet == char32_type_node)
> > + && integer_zerop (retvec.last ())))
> > + {
> > + unsigned HOST_WIDE_INT szt = sz * esz;
> > + char *p;
> > + if (szt < 4096)
> > + p = XALLOCAVEC (char, szt + last);
> > + else
> > + p = XNEWVEC (char, szt + last);
> > + for (size_t i = 0; i < sz; ++i)
> > + native_encode_expr (retvec[i], (unsigned char *) p + i * esz,
> > + esz, 0);
> > + if (last)
> > + memset (p + szt, '\0', last);
> > + tree ret = build_string (szt + last, p);
> > + TREE_TYPE (ret) = at;
> > + TREE_CONSTANT (ret) = 1;
> > + TREE_READONLY (ret) = 1;
> > + TREE_STATIC (ret) = 1;
> > + if (szt >= 4096)
> > + XDELETEVEC (p);
> > + return ret;
> > + }
> > + vec<constructor_elt, va_gc> *elts = nullptr;
> > + for (unsigned i = 0; i < sz; ++i)
> > + CONSTRUCTOR_APPEND_ELT (elts, bitsize_int (i), retvec[i]);
> > + return build_constructor (at, elts);
> > + };
> > + /* If true, call std::ranges::data (p) and std::ranges::size (p)
> > + and if that works out and what the former returns can be handled,
> > + grab the elements from the initializer of the decl pointed by the
> > + first expression. p has to be convert_from_reference (PARM_DECL)
> > + rather than its value, otherwise it is not considered lvalue. */
> > + if (contiguous_range)
> > + {
> > + tree data = lookup_qualified_name (ranges_ns, "data");
> > + tree size = lookup_qualified_name (ranges_ns, "size");
> > + if (TREE_CODE (data) != VAR_DECL || TREE_CODE (size) != VAR_DECL)
> > + goto non_contiguous;
>
> Should this be an error rather than fallback to non-contiguous?
Done in
<https://forge.sourceware.org/marek/gcc/commit/04e75261c715ef6e414ea2e1b6462abc87e37a86>
> > + data = obj_call (data, tf_none);
> > + if (error_operand_p (data))
> > + goto non_contiguous;
> > + if (data == NULL_TREE)
> > + goto fail_ret;
> > + size = obj_call (size, tf_none);
> > + if (error_operand_p (size))
> > + goto non_contiguous;
> > + if (size == NULL_TREE)
> > + goto fail_ret;
> > + if (!tree_fits_uhwi_p (size) || tree_to_uhwi (size) > INT_MAX)
> > + goto non_contiguous;
> > + if (integer_zerop (size))
> > + {
> > + if (kind == GET_INFO_VEC)
> > + return make_tree_vec (0);
> > + return ret_retvec ();
> > + }
> > + STRIP_NOPS (data);
> > + unsigned HOST_WIDE_INT minidx = 0, pplus = 0;
> > + if (TREE_CODE (data) == POINTER_PLUS_EXPR
> > + && tree_fits_uhwi_p (TREE_OPERAND (data, 1))
> > + && !wi::neg_p (wi::to_wide (TREE_OPERAND (data, 1))))
> > + {
> > + pplus = tree_to_uhwi (TREE_OPERAND (data, 1));
> > + data = TREE_OPERAND (data, 0);
> > + STRIP_NOPS (data);
> > + }
> > + if (TREE_CODE (data) != ADDR_EXPR)
> > + goto non_contiguous;
> > + data = TREE_OPERAND (data, 0);
> > + if (TREE_CODE (data) == ARRAY_REF
> > + && tree_fits_uhwi_p (TREE_OPERAND (data, 1)))
> > + {
> > + minidx = tree_to_uhwi (TREE_OPERAND (data, 1));
> > + data = TREE_OPERAND (data, 0);
> > + }
> > + data = cxx_eval_constant_expression (ctx, data, vc_prvalue,
> > + non_constant_p, overflow_p,
> > + jump_target);
> > + if (*jump_target || *non_constant_p)
> > + return NULL_TREE;
> > + if (TREE_CODE (TREE_TYPE (data)) != ARRAY_TYPE
> > + || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (data))) != valuet)
> > + goto non_contiguous;
> > + if (pplus
> > + && (pplus % tree_to_uhwi (TYPE_SIZE_UNIT (valuet))) != 0)
> > + goto non_contiguous;
> > + minidx += pplus / tree_to_uhwi (TYPE_SIZE_UNIT (valuet));
> > + if (kind != GET_INFO_VEC && TREE_CODE (data) == STRING_CST)
> > + {
> > + unsigned esz = tree_to_uhwi (TYPE_SIZE_UNIT (valuet));
> > + unsigned HOST_WIDE_INT sz = tree_to_uhwi (size) * esz;
> > + if (minidx > INT_MAX
> > + || (unsigned) TREE_STRING_LENGTH (data) < sz + minidx * esz)
> > + goto non_contiguous;
> > + if (kind == REFLECT_CONSTANT_ARRAY && sz == 0)
> > + return ret_retvec ();
> > + tree index
> > + = build_index_type (size_int ((kind == REFLECT_CONSTANT_ARRAY
> > + ? -1 : 0) + tree_to_uhwi (size)));
> > + tree at = build_array_type (valuet, index);
> > + at = cp_build_qualified_type (at, TYPE_QUAL_CONST);
> > + const unsigned char *q
> > + = (const unsigned char *) TREE_STRING_POINTER (data);
> > + q += minidx * esz;
> > + if (kind == REFLECT_CONSTANT_ARRAY)
> > + {
> > + unsigned HOST_WIDE_INT i;
> > + for (i = 0; i < esz; ++i)
> > + if (q[sz - esz + i])
> > + break;
> > + if (i != esz)
> > + {
> > + /* Not a NUL terminated string. Build a CONSTRUCTOR
> > + instead. */
> > + for (i = 0; i < sz; i += esz)
> > + {
> > + tree t = native_interpret_expr (valuet, q + i, sz);
> > + retvec.safe_push (t);
> > + }
> > + return ret_retvec ();
> > + }
> > + }
> > + char *p;
> > + if (sz < 4096)
> > + p = XALLOCAVEC (char, sz + esz);
> > + else
> > + p = XNEWVEC (char, sz + esz);
> > + memcpy (p, q, sz);
> > + memset (p + sz, '\0', esz);
> > + ret = build_string (sz + (kind == REFLECT_CONSTANT_ARRAY
> > + ? 0 : esz), p);
> > + TREE_TYPE (ret) = at;
> > + TREE_CONSTANT (ret) = 1;
> > + TREE_READONLY (ret) = 1;
> > + TREE_STATIC (ret) = 1;
> > + if (sz >= 4096)
> > + XDELETEVEC (p);
> > + return ret;
> > + }
> > + if (TREE_CODE (data) != CONSTRUCTOR)
> > + goto non_contiguous;
> > + unsigned sz = tree_to_uhwi (size), i;
> > + unsigned HOST_WIDE_INT j = 0;
> > + tree *r, null = NULL_TREE;
> > + if (kind == GET_INFO_VEC)
> > + {
> > + ret = make_tree_vec (sz);
> > + r = TREE_VEC_BEGIN (ret);
> > + null = get_null_reflection ();
> > + }
> > + else
> > + {
> > + retvec.safe_grow (sz, true);
> > + r = retvec.address ();
> > + }
> > + for (i = 0; i < sz; ++i)
> > + r[i] = null;
> > + tree field, value;
> > + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (data), i, field, value)
> > + if (field == NULL_TREE)
> > + {
> > + if (j >= minidx && j - minidx < sz)
> > + r[j - minidx] = value;
> > + ++j;
> > + }
> > + else if (TREE_CODE (field) == RANGE_EXPR)
> > + {
> > + tree lo = TREE_OPERAND (field, 0);
> > + tree hi = TREE_OPERAND (field, 1);
> > + if (!tree_fits_uhwi_p (lo) || !tree_fits_uhwi_p (hi))
> > + goto non_contiguous;
> > + unsigned HOST_WIDE_INT m = tree_to_uhwi (hi);
> > + for (j = tree_to_uhwi (lo); j <= m; ++j)
> > + if (j >= minidx && j - minidx < sz)
> > + r[j - minidx] = value;
> > + }
> > + else if (tree_fits_uhwi_p (field))
> > + {
> > + j = tree_to_uhwi (field);
> > + if (j >= minidx && j - minidx < sz)
> > + r[j - minidx] = value;
> > + ++j;
> > + }
> > + else
> > + goto non_contiguous;
> > + if (kind == GET_INFO_VEC)
> > + return ret;
> > + for (i = 0; i < sz; ++i)
> > + if (r[i] == NULL_TREE || !tree_fits_shwi_p (r[i]))
> > + goto non_contiguous;
> > + return ret_retvec ();
> > + }
> > + non_contiguous:
> > + /* Otherwise, do it the slower way. Initialize two temporaries,
> > + one to std::ranges::base (p) and another to std::ranges::end (p)
> > + and use a loop. */
> > + tree begin = lookup_qualified_name (ranges_ns, "begin");
> > + tree end = lookup_qualified_name (ranges_ns, "end");
> > + if (TREE_CODE (begin) != VAR_DECL || TREE_CODE (end) != VAR_DECL)
> > + {
> > + error_at (loc, "missing %<std::ranges::begin%> or
> > %<std::ranges::end%>");
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + begin = obj_call (begin, tf_warning_or_error);
> > + if (error_operand_p (begin))
> > + {
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + end = obj_call (end, tf_warning_or_error);
> > + if (error_operand_p (end))
> > + {
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + if (!CLASS_TYPE_P (TREE_TYPE (begin)) && !POINTER_TYPE_P (TREE_TYPE
> > (begin)))
> > + {
> > + error_at (loc, "incorrect type %qT of %<std::ranges::begin(arg)%>",
> > + TREE_TYPE (begin));
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + if (VOID_TYPE_P (TREE_TYPE (end)))
> > + {
> > + error_at (loc, "incorrect type %qT of %<std::ranges::end(arg)%>",
> > + TREE_TYPE (end));
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + begin = get_target_expr (begin);
> > + end = get_target_expr (end);
> > + begin = cxx_eval_constant_expression (ctx, begin, vc_glvalue,
> > non_constant_p,
> > + overflow_p, jump_target);
> > + if (*jump_target || *non_constant_p)
> > + goto fail_ret;
> > + end = cxx_eval_constant_expression (ctx, end, vc_glvalue, non_constant_p,
> > + overflow_p, jump_target);
> > + if (*jump_target || *non_constant_p)
> > + goto fail_ret;
> > + tree cmp = build_new_op (loc, NE_EXPR, LOOKUP_NORMAL, begin, end,
> > + tf_warning_or_error);
> > + tree deref = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, begin,
> > + NULL_TREE, tf_warning_or_error);
> > + tree inc = build_new_op (loc, PREINCREMENT_EXPR, LOOKUP_NORMAL, begin,
> > + NULL_TREE, tf_warning_or_error);
> > + cmp = condition_conversion (cmp);
> > + if (error_operand_p (cmp)
> > + || error_operand_p (deref)
> > + || error_operand_p (inc))
> > + {
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + // TODO: For REFLECT_CONSTANT_* handle proxy iterators.
> > + if (TYPE_MAIN_VARIANT (TREE_TYPE (deref)) != valuet)
> > + {
> > + if (!cxx_constexpr_quiet_p (ctx))
> > + error_at (loc, "unexpected type %qT of iterator dereference",
> > + TREE_TYPE (deref));
> > + *non_constant_p = true;
> > + return call;
> > + }
> > + retvec.truncate (0);
> > + /* while (begin != end) { push (*begin); ++begin; } */
> > + do
> > + {
> > + tree t = cxx_eval_constant_expression (ctx, cmp, vc_prvalue,
> > + non_constant_p, overflow_p,
> > + jump_target);
> > + if (*jump_target || *non_constant_p)
> > + goto fail_ret;
> > + if (integer_zerop (t))
> > + break;
> > + t = cxx_eval_constant_expression (ctx, deref, vc_prvalue,
> > non_constant_p,
> > + overflow_p, jump_target);
> > + if (*jump_target || *non_constant_p)
> > + goto fail_ret;
> > + retvec.safe_push (t);
> > + cxx_eval_constant_expression (ctx, inc, vc_discard, non_constant_p,
> > + overflow_p, jump_target);
> > + if (*jump_target || *non_constant_p)
> > + goto fail_ret;
> > + }
> > + while (true);
> > + if (kind != GET_INFO_VEC)
> > + return ret_retvec ();
> > + ret = make_tree_vec (retvec.length ());
> > + tree v;
> > + unsigned int i;
> > + FOR_EACH_VEC_ELT (retvec, i, v)
> > + TREE_VEC_ELT (ret, i) = v;
> > + return ret;
> > +}
> > +
> > +/* Extract the N-th reflection_range argument from a metafunction call CALL
> > + and return it as TREE_VEC. */
> > +
> > +static tree
> > +get_info_vec (location_t loc, const constexpr_ctx *ctx, tree call, int n,
> > + bool *non_constant_p, bool *overflow_p, tree *jump_target,
> > + tree fun)
> > +{
> > + return get_range_elts (loc, ctx, call, n, non_constant_p, overflow_p,
> > + jump_target, GET_INFO_VEC, fun);
> > +}
> > +
> > +/* Return std::vector<info>. */
> > +
> > +static tree
> > +get_vector_info ()
> > +{
> > + tree args = make_tree_vec (1);
> > + TREE_VEC_ELT (args, 0) = meta_info_type_node;
> > + tree inst = lookup_template_class (vector_identifier, args,
> > + /*in_decl*/NULL_TREE,
> > + /*context*/std_node, tf_none);
> > + inst = complete_type (inst);
> > + if (inst == error_mark_node || !COMPLETE_TYPE_P (inst))
> > + {
> > + error ("couldn%'t look up %qs", "std::vector");
> > + return NULL_TREE;
> > + }
> > +
> > + return inst;
> > +}
> > +
> > +/* Create std::meta::exception{ what, from }. WHAT is the string for
> > what(),
> > + and FROM is the info for from(). */
> > +
> > +static tree
> > +get_meta_exception_object (location_t loc, const char *what, tree from,
> > + bool *non_constant_p)
> > +{
> > + /* Don't throw in a template. */
> > + // TODO For -fno-exceptions, report an error.
> > + if (processing_template_decl)
> > + {
> > + *non_constant_p = true;
> > + return NULL_TREE;
> > + }
> > +
> > + tree type = lookup_qualified_name (std_meta_node, "exception",
> > + LOOK_want::TYPE, /*complain*/true);
> > + if (TREE_CODE (type) != TYPE_DECL || !CLASS_TYPE_P (TREE_TYPE (type)))
> > + {
> > + error_at (loc, "couldn%'t throw %qs", "std::meta::exception");
> > + return NULL_TREE;
> > + }
> > + type = TREE_TYPE (type);
> > + vec<constructor_elt, va_gc> *elts = nullptr;
> > + what = _(what);
> > + /* Translate what from SOURCE_CHARSET to exec charset. */
> > + cpp_string istr, ostr;
> > + istr.len = strlen (what) + 1;
> > + istr.text = (const unsigned char *) what;
> > + if (!cpp_translate_string (parse_in, &istr, &ostr, CPP_STRING, false))
> > + {
> > + what = "";
> > + ostr.text = NULL;
> > + }
> > + else
> > + what = (const char *) ostr.text;
> > + if (TREE_CODE (from) == FUNCTION_DECL && DECL_TEMPLATE_INFO (from))
> > + from = DECL_TI_TEMPLATE (from);
> > + tree string_lit = build_string (strlen (what) + 1, what);
> > + free (const_cast <unsigned char *> (ostr.text));
> > + TREE_TYPE (string_lit) = char_array_type_node;
> > + string_lit = fix_string_type (string_lit);
> > + CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, string_lit);
> > + CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, get_reflection_raw (loc, from));
> > + tree ctor = build_constructor (init_list_type_node, elts);
> > + CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
> > + TREE_CONSTANT (ctor) = true;
> > + TREE_STATIC (ctor) = true;
> > + return finish_compound_literal (type, ctor, tf_warning_or_error,
> > + fcl_functional);
> > +}
> > +
> > +/* Perform 'throw std::meta::exception{...}'. MSGID is the string for
> > what(),
> > + FROM is the reflection for from(). */
> > +
> > +static tree
> > +throw_exception (location_t loc, const constexpr_ctx *ctx, const char
> > *msgid,
> > + tree from, bool *non_constant_p, tree *jump_target)
> > +{
> > + if (tree obj = get_meta_exception_object (loc, msgid, from,
> > non_constant_p))
> > + *jump_target = cxa_allocate_and_throw_exception (loc, ctx, obj);
> > + return NULL_TREE;
> > +}
> > +
> > +/* Wrapper around throw_exception to complain that the reflection does not
> > + represent a type. */
> > +
> > +static tree
> > +throw_exception_nontype (location_t loc, const constexpr_ctx *ctx,
> > + tree from, bool *non_constant_p, tree *jump_target)
> > +{
> > + return throw_exception (loc, ctx,
> > + "reflection does not represent a type",
> > + from, non_constant_p, jump_target);
> > +}
> > +
> > +/* Wrapper around throw_exception to complain that the reflection does not
> > + represent something that satisfies has_template_arguments. */
> > +
> > +static tree
> > +throw_exception_notargs (location_t loc, const constexpr_ctx *ctx,
> > + tree from, bool *non_constant_p, tree *jump_target)
> > +{
> > + return throw_exception (loc, ctx,
> > + "reflection does not have template arguments",
> > + from, non_constant_p, jump_target);
> > +}
> > +
> > +/* Wrapper around throw_exception to complain that the reflection does not
> > + represent a function or a function type. */
> > +
> > +static tree
> > +throw_exception_nofn (location_t loc, const constexpr_ctx *ctx,
> > + tree from, bool *non_constant_p, tree *jump_target)
> > +{
> > + return throw_exception (loc, ctx, "reflection does not represent a "
> > + "function or function type",
> > + from, non_constant_p, jump_target);
> > +}
> > +
> > +/* The values of std::meta::operators enumerators corresponding to
> > + the ovl_op_code and IDENTIFIER_ASSIGN_OP_P pair. */
> > +
> > +static unsigned char meta_operators[2][OVL_OP_MAX];
> > +
> > +/* Init the meta_operators table if not yet initialized. */
> > +
> > +static void
> > +maybe_init_meta_operators (location_t loc)
> > +{
> > + if (meta_operators[0][OVL_OP_ERROR_MARK])
> > + return;
> > + meta_operators[0][OVL_OP_ERROR_MARK] = 1;
> > + tree operators = lookup_qualified_name (std_meta_node, "operators");
> > + if (TREE_CODE (operators) != TYPE_DECL
> > + || TREE_CODE (TREE_TYPE (operators)) != ENUMERAL_TYPE)
> > + {
> > + fail:
> > + error_at (loc, "unexpected %<std::meta::operators%>");
> > + return;
> > + }
> > + char buf[sizeof "op_greater_greater_equals"];
> > + memcpy (buf, "op_", 3);
> > + for (int i = 0; i < 2; ++i)
> > + for (int j = OVL_OP_ERROR_MARK + 1; j < OVL_OP_MAX; ++j)
> > + if (ovl_op_info[i][j].meta_name)
> > + {
> > + strcpy (buf + 3, ovl_op_info[i][j].meta_name);
> > + tree id = get_identifier (buf);
> > + tree t = lookup_enumerator (TREE_TYPE (operators), id);
> > + if (t == NULL_TREE || TREE_CODE (t) != CONST_DECL)
> > + goto fail;
> > + tree v = DECL_INITIAL (t);
> > + if (!tree_fits_uhwi_p (v) || tree_to_uhwi (v) > UCHAR_MAX)
> > + goto fail;
> > + meta_operators[i][j] = tree_to_uhwi (v);
> > + }
> > +}
> > +
> > +/* Process std::meta::is_variable.
> > + Returns: true if r represents a variable. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_variable (const_tree r, reflect_kind kind)
> > +{
> > + /* ^^param is a variable but parameters_of(parent_of(^^param))[0] is
> > not. */
> > + if ((TREE_CODE (r) == PARM_DECL && kind != REFLECT_PARM)
> > + || (VAR_P (r)
> > + && kind == REFLECT_UNDEF
> > + /* The definition of a variable excludes non-static data members. */
> > + && !DECL_ANON_UNION_VAR_P (r)
> > + /* A structured binding is not a variable. */
> > + && !(DECL_DECOMPOSITION_P (r) && !DECL_DECOMP_IS_BASE (r)))
> > + || (VAR_P (r)
> > + /* Underlying variable of tuple using structured binding is a
> > + variable. */
> > + && kind == REFLECT_VAR
> > + && DECL_DECOMPOSITION_P (r)
> > + && !DECL_DECOMP_IS_BASE (r)))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_type.
> > + Returns: true if r represents an entity whose underlying entity is
> > + a type. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_type (const_tree r)
> > +{
> > + /* Null reflection isn't a type. */
> > + if (TYPE_P (r) && r != unknown_type_node)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_type_alias.
> > + Returns: true if r represents a type alias. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_type_alias (const_tree r)
> > +{
> > + if (TYPE_P (r) && typedef_variant_p (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_namespace.
> > + Returns: true if r represents an entity whose underlying entity is
> > + a namespace. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_namespace (const_tree r)
> > +{
> > + if (TREE_CODE (r) == NAMESPACE_DECL)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_namespace_alias.
> > + Returns: true if r represents a namespace alias. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_namespace_alias (const_tree r)
> > +{
> > + if (TREE_CODE (r) == NAMESPACE_DECL && DECL_NAMESPACE_ALIAS (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_function.
> > + Returns: true if r represents a function. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_function (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
>
> How about maybe_get_reflection_fndecl to be consistent with the next
> function?
Done in
<https://forge.sourceware.org/marek/gcc/commit/c32e660552e838d5df8ad74c4a150645793c459d>
> > +
> > + if (TREE_CODE (r) == FUNCTION_DECL)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_function_template.
> > + Returns: true if r represents a function template. Otherwise, false.
> > */
> > +
> > +static tree
> > +eval_is_function_template (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > +
> > + if (DECL_FUNCTION_TEMPLATE_P (r))
> > + return boolean_true_node;
> > +
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_variable_template.
> > + Returns: true if r represents a variable template. Otherwise, false.
> > */
> > +
> > +static tree
> > +eval_is_variable_template (tree r)
> > +{
> > + if (variable_template_p (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_class_template.
> > + Returns: true if r represents a class template. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_class_template (const_tree r)
> > +{
> > + if (DECL_CLASS_TEMPLATE_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_alias_template.
> > + Returns: true if r represents an alias template. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_alias_template (const_tree r)
> > +{
> > + if (DECL_ALIAS_TEMPLATE_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_concept.
> > + Returns: true if r represents a concept. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_concept (const_tree r)
> > +{
> > + if (concept_definition_p (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_object.
> > + Returns: true if r represents an object. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_object (reflect_kind kind)
> > +{
> > + if (kind == REFLECT_OBJECT)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_value.
> > + Returns: true if r represents a value. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_value (reflect_kind kind)
> > +{
> > + if (kind == REFLECT_VALUE)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Like get_info_vec, but throw exception if any of the elements aren't
> > + eval_is_type reflections and change their content to the corresponding
> > + REFLECT_EXPR_HANDLE. */
> > +
> > +static tree
> > +get_type_info_vec (location_t loc, const constexpr_ctx *ctx, tree call,
> > int n,
> > + bool *non_constant_p, bool *overflow_p, tree *jump_target,
> > + tree fun)
> > +{
> > + tree vec = get_info_vec (loc, ctx, call, n, non_constant_p, overflow_p,
> > + jump_target, fun);
> > + if (*jump_target || *non_constant_p)
> > + return NULL_TREE;
> > + for (int i = 0; i < TREE_VEC_LENGTH (vec); i++)
> > + {
> > + tree type = REFLECT_EXPR_HANDLE (TREE_VEC_ELT (vec, i));
> > + if (eval_is_type (type) != boolean_true_node)
> > + return throw_exception_nontype (loc, ctx, fun, non_constant_p,
> > + jump_target);
> > + TREE_VEC_ELT (vec, i) = type;
> > + }
> > + return vec;
> > +}
> > +
> > +/* Process std::meta::is_structured_binding.
> > + Returns: true if r represents a structured binding. Otherwise, false.
> > */
> > +
> > +static tree
> > +eval_is_structured_binding (const_tree r, reflect_kind kind)
> > +{
> > + if (DECL_DECOMPOSITION_P (r)
> > + && !DECL_DECOMP_IS_BASE (r)
> > + && kind != REFLECT_VAR)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_class_member.
> > + Returns: true if r represents a class member. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_class_member (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == CONST_DECL)
> > + {
> > + /* [class.mem.general]/5 - The enumerators of an unscoped enumeration
> > + defined in the class are members of the class. */
> > + if (UNSCOPED_ENUM_P (DECL_CONTEXT (r)))
> > + r = DECL_CONTEXT (r);
> > + else
> > + return boolean_false_node;
> > + }
> > + else if (TYPE_P (r) && typedef_variant_p (r))
> > + r = TYPE_NAME (r);
> > + else if (VAR_P (r) && DECL_ANON_UNION_VAR_P (r))
> > + return boolean_true_node;
>
> This surprises me, I'd think that an anon-union variable is equivalent to a
> member-access expression, but is not a class member itself, but perhaps it
> makes sense for reflection to blur that distinction. It would be nice if
> the standard were clear either way.
Answered in
<https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703973.html>
> > + if (DECL_P (r) && DECL_CLASS_SCOPE_P (r))
> > + return boolean_true_node;
> > + else if (TYPE_P (r) && TYPE_CLASS_SCOPE_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Helper function for eval_is_{public, protected, private}. */
> > +
> > +static tree
> > +eval_is_expected_access (tree r, reflect_kind kind, tree expected_access)
> > +{
> > + if (eval_is_class_member (r) == boolean_true_node)
> > + {
> > + r = maybe_get_reflection_fndecl (r);
> > +
> > + if (TYPE_P (r))
> > + {
> > + if (TYPE_NAME (r) == NULL_TREE || !DECL_P (TYPE_NAME (r)))
> > + return boolean_false_node;
> > + r = TYPE_NAME (r);
> > + }
> > +
> > + bool matches = false;
> > + if (expected_access == access_private_node)
> > + matches = TREE_PRIVATE (r);
> > + else if (expected_access == access_protected_node)
> > + matches = TREE_PROTECTED (r);
> > + else if (expected_access == access_public_node)
> > + matches = !(TREE_PRIVATE (r) || TREE_PROTECTED (r));
> > + else
> > + gcc_unreachable ();
> > +
> > + if (matches)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > + }
> > +
> > + if (kind == REFLECT_BASE)
> > + {
> > + gcc_assert (TREE_CODE (r) == TREE_BINFO);
> > + tree c = r;
> > + while (BINFO_INHERITANCE_CHAIN (c))
> > + c = BINFO_INHERITANCE_CHAIN (c);
> > +
> > + tree base_binfo;
> > + for (unsigned ix = 0; BINFO_BASE_ITERATE (c, ix, base_binfo); ix++)
> > + if (base_binfo == r)
> > + {
> > + tree access = BINFO_BASE_ACCESS (c, ix);
> > + if (access == expected_access)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > + }
> > + gcc_unreachable ();
> > + }
> > +
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_public.
> > + Returns: true if r represents either:
> > + - a class member or unnamed bit-field that is public or
> > + - a direct base class relationship (D, B) for which
> > + B is a public base class of D.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_public (tree r, reflect_kind kind)
> > +{
> > + return eval_is_expected_access (r, kind, access_public_node);
> > +}
> > +
> > +/* Process std::meta::is_protected.
> > + Returns: true if r represents either:
> > + - a class member or unnamed bit-field that is protected, or
> > + - a direct base class relationship (D, B) for which
> > + B is a protected base class of D.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_protected (tree r, reflect_kind kind)
> > +{
> > + return eval_is_expected_access (r, kind, access_protected_node);
> > +}
> > +
> > +/* Process std::meta::is_private
> > + Returns: true if r represents either:
> > + - a class member or unnamed bit-field that is private, or
> > + - a direct base class relationship (D, B) for which
> > + B is a private base class of D.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_private (tree r, reflect_kind kind)
> > +{
> > + return eval_is_expected_access (r, kind, access_private_node);
> > +}
> > +
> > +/* Process std::meta::is_virtual.
> > + Returns: true if r represents either a virtual member function or a
> > direct
> > + base class relationship (D,B) for which B is a virtual base class of D.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_virtual (tree r, reflect_kind kind)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_VIRTUAL_P (r))
> > + return boolean_true_node;
> > +
> > + if (kind == REFLECT_BASE && BINFO_VIRTUAL_P (r))
> > + return boolean_true_node;
> > +
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_pure_virtual.
> > + Returns: true if r represents a member function that is pure virtual.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_pure_virtual (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_PURE_VIRTUAL_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Helper function for eval_is_override, return true if FNDECL in TYPE
> > + overrides another function. */
> > +
> > +static bool
> > +is_override (tree type, tree fndecl)
> > +{
> > + tree binfo = TYPE_BINFO (type), base_binfo;
> > +
> > + for (unsigned ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++)
> > + {
> > + tree basetype = BINFO_TYPE (base_binfo);
> > + if (TYPE_POLYMORPHIC_P (basetype))
> > + {
> > + if (look_for_overrides_here (basetype, fndecl))
> > + return true;
> > + if (is_override (basetype, fndecl))
> > + return true;
> > + }
> > + }
> > + return false;
> > +}
> > +
> > +/* Process std::meta::is_override.
> > + Returns: true if r represents a member function that overrides another
> > + member function. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_override (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL
> > + && DECL_CLASS_SCOPE_P (r)
> > + && !DECL_CONSTRUCTOR_P (r)
> > + && (IDENTIFIER_VIRTUAL_P (DECL_NAME (r))
> > + || DECL_CONV_FN_P (r))
> > + && !DECL_STATIC_FUNCTION_P (r)
>
> I'd think checking DECL_VIRTUAL_P could replace most of the above
> conditions.
Changed to use DECL_VIRTUAL_P in
<https://forge.sourceware.org/marek/gcc/commit/9a040fb9c52c963ac5cf5ecffa4f4a0769446b24>
> > + && is_override (DECL_CONTEXT (r), r))
> > + return boolean_true_node;
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_namespace_member.
> > + Returns: true if r represents a namespace member. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_namespace_member (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == CONST_DECL)
> > + {
> > + if (UNSCOPED_ENUM_P (DECL_CONTEXT (r)))
> > + r = DECL_CONTEXT (r);
> > + else
> > + return boolean_false_node;
> > + }
> > + else if (TYPE_P (r) && typedef_variant_p (r))
> > + r = TYPE_NAME (r);
> > + else if (VAR_P (r) && DECL_ANON_UNION_VAR_P (r))
> > + return boolean_false_node;
> > + if (r == global_namespace || r == unknown_type_node)
> > + return boolean_false_node;
> > + if (DECL_P (r) && DECL_NAMESPACE_SCOPE_P (r))
> > + return boolean_true_node;
> > + else if (TYPE_P (r) && TYPE_NAMESPACE_SCOPE_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_nonstatic_data_member.
> > + Returns: true if r represents a non-static data member.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_nonstatic_data_member (const_tree r)
> > +{
> > + if (VAR_P (r) && DECL_ANON_UNION_VAR_P (r))
> > + return boolean_true_node;
>
> As above.
Answered in
<https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703973.html>
> > + if (TREE_CODE (r) == FIELD_DECL && !DECL_UNNAMED_BIT_FIELD (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_static_member.
> > + Returns: true if r represents a static member.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_static_member (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + r = STRIP_TEMPLATE (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_STATIC_FUNCTION_P (r))
> > + return boolean_true_node;
> > + else if (VAR_P (r) && DECL_CLASS_SCOPE_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_base.
> > + Returns: true if r represents a direct base class relationship.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_base (tree r, reflect_kind kind)
> > +{
> > + if (kind == REFLECT_BASE)
> > + {
> > + gcc_assert (TREE_CODE (r) == TREE_BINFO);
> > + return boolean_true_node;
> > + }
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_default_member_initializer.
> > + Returns: true if r represents a non-static data member that has a
> > default
> > + member initializer. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_default_member_initializer (const_tree r)
> > +{
> > + if (TREE_CODE (r) == FIELD_DECL
> > + && !DECL_UNNAMED_BIT_FIELD (r)
> > + && DECL_INITIAL (r) != NULL_TREE)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_static_storage_duration.
> > + Returns: true if r represents an object or variable that has static
> > + storage duration. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_static_storage_duration (const_tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_true_node
> > + && decl_storage_duration (CONST_CAST_TREE (r)) == dk_static)
> > + return boolean_true_node;
>
> How about a subobject of a variable with x storage duration?
struct S { int m; } s;
static_assert (has_static_storage_duration (reflect_object (s.m)));
works due to...
> > + /* This includes DECL_NTTP_OBJECT_P objects. */
> > + else if (eval_is_object (kind) == boolean_true_node)
...this check. Tested in storage_duration1.C with std::pair.
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_thread_storage_duration.
> > + Returns: true if r represents an object or variable that has thread
> > + storage duration. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_thread_storage_duration (const_tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_true_node
> > + && decl_storage_duration (CONST_CAST_TREE (r)) == dk_thread)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_automatic_storage_duration.
> > + Returns: true if r represents an object or variable that has automatic
> > + storage duration. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_automatic_storage_duration (const_tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_true_node
> > + && decl_storage_duration (CONST_CAST_TREE (r)) == dk_auto)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_mutable_member.
> > + Returns: true if r represents a mutable non-static data member.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_mutable_member (tree r)
> > +{
> > + if (VAR_P (r) && DECL_ANON_UNION_VAR_P (r))
> > + {
> > + tree v = DECL_VALUE_EXPR (r);
> > + if (v != error_mark_node && TREE_CODE (v) == COMPONENT_REF)
> > + r = TREE_OPERAND (v, 1);
> > + }
> > + if (TREE_CODE (r) == FIELD_DECL
> > + && !DECL_UNNAMED_BIT_FIELD (r)
> > + && DECL_MUTABLE_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_template.
> > + Returns: true if r represents a function template, class template,
> > variable
> > + template, alias template, or concept. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_template (tree r)
> > +{
> > + if (eval_is_function_template (r) == boolean_true_node
> > + || eval_is_class_template (r) == boolean_true_node
> > + || eval_is_variable_template (r) == boolean_true_node
> > + || eval_is_alias_template (r) == boolean_true_node
> > + || eval_is_concept (r) == boolean_true_node)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_function_parameter.
> > + Returns: true if r represents a function parameter. Otherwise, false.
> > */
> > +
> > +static tree
> > +eval_is_function_parameter (const_tree r, reflect_kind kind)
> > +{
> > + if (kind == REFLECT_PARM)
> > + {
> > + gcc_checking_assert (TREE_CODE (r) == PARM_DECL);
> > + return boolean_true_node;
> > + }
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_data_member_spec.
> > + Returns: true if r represents a data member description.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_data_member_spec (const_tree r, reflect_kind kind)
> > +{
> > + if (kind == REFLECT_DATA_MEMBER_SPEC)
> > + {
> > + gcc_checking_assert (TREE_CODE (r) == TREE_VEC);
> > + return boolean_true_node;
> > + }
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_explicit_object_parameter.
> > + Returns: true if r represents a function parameter that is an explicit
> > + object parameter. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_explicit_object_parameter (const_tree r, reflect_kind kind)
> > +{
> > + if (eval_is_function_parameter (r, kind) == boolean_true_node
> > + && r == DECL_ARGUMENTS (DECL_CONTEXT (r))
> > + && DECL_XOBJ_MEMBER_FUNCTION_P (DECL_CONTEXT (r)))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_default_argument.
> > + Returns: If r represents a parameter P of a function F, then:
> > + -- If F is a specialization of a templated function T, then true if
> > there
> > + exists a declaration D of T that precedes some point in the
> > evaluation
> > + context and D specifies a default argument for the parameter of T
> > + corresponding to P. Otherwise, false.
> > + -- Otherwise, if there exists a declaration D of F that precedes some
> > + point in the evaluation context and D specifies a default argument
> > + for P, then true.
> > + Otherwise, false. */
> > +
> > +static tree
> > +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);
> > + tree fn = DECL_CONTEXT (r);
> > + tree args = FUNCTION_FIRST_USER_PARM (fn);
> > + tree types = FUNCTION_FIRST_USER_PARMTYPE (fn);
> > + while (r != args)
> > + {
> > + args = DECL_CHAIN (args);
> > + types = TREE_CHAIN (types);
> > + }
> > + if (TREE_PURPOSE (types))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_ellipsis_parameter.
> > + Returns: true if r represents a function or function type that has an
> > + ellipsis in its parameter-type-list. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_ellipsis_parameter (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL)
> > + r = TREE_TYPE (r);
> > + if (FUNC_OR_METHOD_TYPE_P (r) && stdarg_p (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_deleted.
> > + Returns: true if r represents a function that is deleted.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_deleted (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_DELETED_FN (r))
>
> Is there no way to ask this about function templates?
Answered in
<https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703978.html>
but it looks like it still needs the LWG change.
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_defaulted.
> > + Returns: true if r represents a function that is defaulted.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_defaulted (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_DEFAULTED_FN (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_user_provided.
> > + Returns: true if r represents a function that is user-provided.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_user_provided (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL
> > + && user_provided_p (r)
> > + // TODO: user_provided_p is false for non-members defaulted on
> > + // first declaration.
>
> Maybe we could set DECL_INITIALIZED_IN_CLASS_P for non-member functions, too
> (and probably rename it).
I suppose, but I'd rather not do it as part of the Reflection patch.
> > + && (!DECL_NAMESPACE_SCOPE_P (r) || !DECL_DELETED_FN (r)))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_user_declared.
> > + Returns: true if r represents a function that is user-declared.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_user_declared (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && !DECL_ARTIFICIAL (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_explicit.
> > + Returns: true if r represents
> > + a member function that is declared explicit.
> > + Otherwise, false.
> > + If r represents a member function template
> > + that is declared explicit, is_explicit(r)
> > + is still false because in general such queries
> > + for templates cannot be answered. */
> > +
> > +static tree
> > +eval_is_explicit (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > +
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_NONCONVERTING_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_bit_field.
> > + Returns: true if r represents a bit-field, or if r represents a data
> > member
> > + description (T,N,A,W,NUA) for which W is not _|_. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_bit_field (const_tree r, reflect_kind kind)
> > +{
> > + if (TREE_CODE (r) == FIELD_DECL && DECL_C_BIT_FIELD (r))
> > + return boolean_true_node;
> > + else if (kind == REFLECT_DATA_MEMBER_SPEC && TREE_VEC_ELT (r, 3))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_enumerator.
> > + Returns: true if r represents an enumerator. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_enumerator (const_tree r)
> > +{
> > + /* This doesn't check !DECL_TEMPLATE_PARM_P because such CONST_DECLs
> > + would already have been rejected. */
>
> Please mention where they would have been rejected.
Done in
<https://forge.sourceware.org/marek/gcc/commit/6334459c417554fc35977c639b219799b3354b03>
> > + if (TREE_CODE (r) == CONST_DECL)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_internal_linkage.
> > + Returns: true if r represents a variable, function, type, template, or
> > + namespace whose name has internal linkage. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_internal_linkage (tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_false_node
> > + && eval_is_function (r) == boolean_false_node
> > + && eval_is_type (r) == boolean_false_node
> > + && eval_is_template (r) == boolean_false_node
> > + && eval_is_namespace (r) == boolean_false_node)
> > + return boolean_false_node;
> > + r = maybe_get_reflection_fndecl (r);
> > + r = STRIP_TEMPLATE (r);
> > + if (TYPE_P (r))
> > + {
> > + if (TYPE_NAME (r) == NULL_TREE
> > + || !DECL_P (TYPE_NAME (r))
> > + || (!DECL_IMPLICIT_TYPEDEF_P (TYPE_NAME (r))
> > + && TYPE_NAME (r) == TYPE_NAME (TYPE_MAIN_VARIANT (r))
> > + && !TYPE_MAIN_DECL (r)))
>
> It seems simpler to go directly to TYPE_NAME (TYPE_MAIN_VARIANT (r)) to find
> the name with linkage, and probably also check OVERLOAD_TYPE_P.
I didn't understand this.
> And this test gets repeated several times below, so let's add a
> type_linkage_name function.
But I've at least introduced this fn:
<https://forge.sourceware.org/marek/gcc/commit/e1186439cac9caab995a5a7073a769e85c21d476>
> > + return boolean_false_node;
> > + r = TYPE_NAME (r);
> > + }
> > + if (decl_linkage (r) == lk_internal)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_module_linkage.
> > + Returns: true if r represents a variable, function, type, template, or
> > + namespace whose name has module linkage. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_module_linkage (tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_false_node
> > + && eval_is_function (r) == boolean_false_node
> > + && eval_is_type (r) == boolean_false_node
> > + && eval_is_template (r) == boolean_false_node
> > + && eval_is_namespace (r) == boolean_false_node)
> > + return boolean_false_node;
> > + r = maybe_get_reflection_fndecl (r);
> > + r = STRIP_TEMPLATE (r);
> > + if (TYPE_P (r))
> > + {
> > + if (TYPE_NAME (r) == NULL_TREE
> > + || !DECL_P (TYPE_NAME (r))
> > + || (!DECL_IMPLICIT_TYPEDEF_P (TYPE_NAME (r))
> > + && TYPE_NAME (r) == TYPE_NAME (TYPE_MAIN_VARIANT (r))
> > + && !TYPE_MAIN_DECL (r)))
> > + return boolean_false_node;
> > + r = TYPE_NAME (r);
> > + }
> > + if (decl_linkage (r) == lk_external
>
> Maybe we should (separately) add lk_module to decl_linkage, or at least a
> decl_module_linkage_p predicate.
Sounds nice. Not done yet.
> > + && DECL_LANG_SPECIFIC (r)
> > + && DECL_MODULE_ATTACH_P (r)
> > + && !DECL_MODULE_EXPORT_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_external_linkage.
> > + Returns: true if r represents a variable, function, type, template, or
> > + namespace whose name has external linkage. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_external_linkage (tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_false_node
> > + && eval_is_function (r) == boolean_false_node
> > + && eval_is_type (r) == boolean_false_node
> > + && eval_is_template (r) == boolean_false_node
> > + && eval_is_namespace (r) == boolean_false_node)
> > + return boolean_false_node;
> > + r = maybe_get_reflection_fndecl (r);
> > + r = STRIP_TEMPLATE (r);
> > + if (TYPE_P (r))
> > + {
> > + if (TYPE_NAME (r) == NULL_TREE
> > + || !DECL_P (TYPE_NAME (r))
> > + || (!DECL_IMPLICIT_TYPEDEF_P (TYPE_NAME (r))
> > + && TYPE_NAME (r) == TYPE_NAME (TYPE_MAIN_VARIANT (r))
> > + && !TYPE_MAIN_DECL (r)))
> > + return boolean_false_node;
> > + r = TYPE_NAME (r);
> > + }
> > + if (decl_linkage (r) == lk_external
> > + && !(DECL_LANG_SPECIFIC (r)
> > + && DECL_MODULE_ATTACH_P (r)
> > + && !DECL_MODULE_EXPORT_P (r)))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_c_language_linkage
> > + Returns: true if r represents a variable, function, or function type
> > with
> > + C language linkage. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_c_language_linkage (tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_false_node
> > + && eval_is_function (r) == boolean_false_node
> > + && eval_is_function_type (r) == boolean_false_node)
> > + return boolean_false_node;
> > + r = maybe_get_reflection_fndecl (r);
> > + r = STRIP_TEMPLATE (r);
> > + if (TYPE_P (r))
> > + {
> > + if (TYPE_NAME (r) == NULL_TREE
> > + || !DECL_P (TYPE_NAME (r))
> > + || (!DECL_IMPLICIT_TYPEDEF_P (TYPE_NAME (r))
> > + && TYPE_NAME (r) == TYPE_NAME (TYPE_MAIN_VARIANT (r))
> > + && !TYPE_MAIN_DECL (r)))
> > + return boolean_false_node;
> > + r = TYPE_NAME (r);
> > + }
> > + if (decl_linkage (r) == lk_external && DECL_LANGUAGE (r) == lang_c)
>
> A decl doesn't need to have external linkage to have C language linkage:
>
> extern "C" {
> static void f() { }
> }
Fixed in
<https://forge.sourceware.org/marek/gcc/commit/8822a3c9aee1f41311a6261434b253dfdcdb8a12>
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::has_linkage.
> > + Returns: true if r represents a variable, function, type, template, or
> > + namespace whose name has any linkage. Otherwise, false. */
> > +
> > +static tree
> > +eval_has_linkage (tree r, reflect_kind kind)
> > +{
> > + if (eval_is_variable (r, kind) == boolean_false_node
> > + && eval_is_function (r) == boolean_false_node
> > + && eval_is_type (r) == boolean_false_node
> > + && eval_is_template (r) == boolean_false_node
> > + && eval_is_namespace (r) == boolean_false_node)
> > + return boolean_false_node;
> > + r = maybe_get_reflection_fndecl (r);
> > + r = STRIP_TEMPLATE (r);
> > + if (TYPE_P (r))
> > + {
> > + if (TYPE_NAME (r) == NULL_TREE
> > + || !DECL_P (TYPE_NAME (r))
> > + || (!DECL_IMPLICIT_TYPEDEF_P (TYPE_NAME (r))
> > + && TYPE_NAME (r) == TYPE_NAME (TYPE_MAIN_VARIANT (r))
> > + && !TYPE_MAIN_DECL (r)))
> > + return boolean_false_node;
> > + r = TYPE_NAME (r);
> > + }
> > + if (decl_linkage (r) != lk_none)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_complete_type.
> > + Returns: true if is_type(r) is true and there is some point in the
> > + evaluation context from which the type represented by dealias(r) is
> > + not an incomplete type. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_complete_type (const_tree r)
> > +{
> > + if (eval_is_type (r) == boolean_true_node)
> > + {
> > + r = strip_typedefs (const_cast<tree> (r));
>
> Why strip_typedefs?
Removed in
<https://forge.sourceware.org/marek/gcc/commit/24d0b111d88b9543899c7e5f609f72d94d11ec4c>
> > + complete_type (const_cast<tree> (r));
> > + if (COMPLETE_TYPE_P (r))
> > + return boolean_true_node;
> > + }
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_enumerable_type.
> > + A type T is enumerable from a point P if either
> > + -- T is a class type complete at point P or
> > + -- T is an enumeration type defined by a declaration D such that D is
> > + reachable from P but P does not occur within an enum-specifier of D.
> > + Returns: true if dealias(r) represents a type that is enumerable from
> > some
> > + point in the evaluation context. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_enumerable_type (const_tree r)
> > +{
> > + if (CLASS_TYPE_P (r))
> > + {
> > + complete_type (const_cast<tree> (r));
> > + if (COMPLETE_TYPE_P (r))
> > + return boolean_true_node;
> > + }
> > + else if (TREE_CODE (r) == ENUMERAL_TYPE)
> > + {
> > + r = TYPE_MAIN_VARIANT (r);
> > + if (!ENUM_IS_OPAQUE (r) && !ENUM_BEING_DEFINED_P (r))
> > + return boolean_true_node;
> > + }
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_annotation.
> > + Returns: true if r represents an annotation. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_annotation (const_tree r, reflect_kind kind)
> > +{
> > + if (kind == REFLECT_ANNOTATION)
> > + {
> > + gcc_assert (TREE_CODE (r) == TREE_LIST);
> > + return boolean_true_node;
> > + }
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_conversion_function.
> > + Returns: true if r represents a function that is a conversion function.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_conversion_function (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_CONV_FN_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_operator_function.
> > + Returns: true if r represents a function that is an operator function.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_operator_function (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > +
> > + if (TREE_CODE (r) == FUNCTION_DECL)
> > + {
> > + r = STRIP_TEMPLATE (r);
>
> This does nothing since we just checked r is FUNCTION_DECL.
Removed in
<https://forge.sourceware.org/marek/gcc/commit/458f9197a560a5b2e77ad528aa272422338551e7>
> > + if (DECL_OVERLOADED_OPERATOR_P (r) && !DECL_CONV_FN_P (r))
> > + return boolean_true_node;
> > + }
> > +
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_literal_operator.
> > + Returns: true if r represents a function that is a literal operator.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_literal_operator (const_tree r)
> > +{
> > + /* No MAYBE_BASELINK_FUNCTIONS here because a literal operator
> > + must be a non-member function. */
> > + if (TREE_CODE (r) == FUNCTION_DECL && UDLIT_OPER_P (DECL_NAME (r)))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_special_member_function.
> > + Returns: true if r represents a function that is a special member
> > function.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_special_member_function (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && special_memfn_p (r) != sfk_none)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_constructor.
> > + Returns: true if r represents a function that is a constructor.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_constructor (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_CONSTRUCTOR_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_default_constructor.
> > + Returns: true if r represents a function that is a default constructor.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_default_constructor (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && default_ctor_p (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_copy_constructor.
> > + Returns: true if r represents a function that is a copy constructor.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_copy_constructor (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_COPY_CONSTRUCTOR_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_move_constructor.
> > + Returns: true if r represents a function that is a move constructor.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_move_constructor (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL && DECL_MOVE_CONSTRUCTOR_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_assignment.
> > + Returns: true if r represents a function that is an assignment operator.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_assignment (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL
> > + && DECL_ASSIGNMENT_OPERATOR_P (r)
> > + && DECL_OVERLOADED_OPERATOR_IS (r, NOP_EXPR))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_copy_assignment.
> > + Returns: true if r represents a function that is a copy assignment
> > + operator. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_copy_assignment (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL
> > + && special_function_p (r) == sfk_copy_assignment)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_move_assignment.
> > + Returns: true if r represents a function that is a move assignment
> > + operator. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_move_assignment (tree r)
> > +{
> > + r = MAYBE_BASELINK_FUNCTIONS (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL
> > + && special_function_p (r) == sfk_move_assignment)
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_destructor.
> > + Returns: true if r represents a function that is a destructor.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_destructor (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > + if (TREE_CODE (r) == FUNCTION_DECL
> > + && DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_conversion_function_template.
> > + Returns: true if r represents a conversion function template.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_conversion_function_template (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > +
> > + if (DECL_FUNCTION_TEMPLATE_P (r) && DECL_CONV_FN_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_operator_function_template.
> > + Returns: true if r represents an operator function template.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_operator_function_template (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > +
> > + if (DECL_FUNCTION_TEMPLATE_P (r))
> > + {
> > + r = STRIP_TEMPLATE (r);
> > + if (DECL_OVERLOADED_OPERATOR_P (r) && !DECL_CONV_FN_P (r))
> > + return boolean_true_node;
> > + }
> > +
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_literal_operator_template.
> > + Returns: true if r represents a literal operator template.
> > + Otherwise, false. */
> > +
> > +static tree
> > +eval_is_literal_operator_template (tree r)
> > +{
> > + /* No MAYBE_BASELINK_FUNCTIONS here because a literal operator
> > + template must be a non-member function template. */
> > + r = OVL_FIRST (r);
> > +
> > + if (DECL_FUNCTION_TEMPLATE_P (r) && UDLIT_OPER_P (DECL_NAME (r)))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::is_constructor_template.
> > + Returns: true if r represents a function that is an operator function
> > + template. Otherwise, false. */
> > +
> > +static tree
> > +eval_is_constructor_template (tree r)
> > +{
> > + r = maybe_get_reflection_fndecl (r);
> > +
> > + if (DECL_FUNCTION_TEMPLATE_P (r) && DECL_CONSTRUCTOR_P (r))
> > + return boolean_true_node;
> > + else
> > + return boolean_false_node;
> > +}
> > +
> > +/* Process std::meta::operator_of.
> > + Returns: The value of the enumerator from the operators whose
> > corresponding
> > + operator-function-id is the unqualified name of the entity represented
> > by
> > + r.
> > + Throws: meta::exception unless r represents an operator function or
> > + operator function template. */
> > +
> > +static tree
> > +eval_operator_of (location_t loc, const constexpr_ctx *ctx, tree r,
> > + bool *non_constant_p, tree *jump_target, tree ret_type,
> > + tree fun)
> > +{
> > + if (eval_is_operator_function (r) == boolean_false_node)
>
> Doesn't this need to handle _is_operator_function_template (r) too?
Yes, done in
<https://forge.sourceware.org/marek/gcc/commit/cf9b878240679ea9f3b787ecb982a5d2cbab008d>
> > + return throw_exception (loc, ctx,
> > + "reflection does not represent an operator "
> > + "function", fun, non_constant_p, jump_target);
> > + r = maybe_get_reflection_fndecl (r);
> > + r = STRIP_TEMPLATE (r);
> > + maybe_init_meta_operators (loc);
> > + int i = IDENTIFIER_ASSIGN_OP_P (DECL_NAME (r)) ? 1 : 0;
> > + int j = IDENTIFIER_CP_INDEX (DECL_NAME (r));
> > + return build_int_cst (ret_type, meta_operators[i][j]);
> > +}
> > +
> > +/* Helper to build a string literal containing '\0' terminated NAME.
> > + ELT_TYPE must be either char_type_node or char8_type_node, and the
> > + function takes care of converting the name from SOURCE_CHARSET
> > + to ordinary literal charset resp. UTF-8 and returning the string
> > + literal. Returns NULL_TREE if the conversion failed. */
>
> "resp. UTF-8. Returns the string literal, or NULL_TREE if the conversion
> failed."
Tweaked in
<https://forge.sourceware.org/marek/gcc/commit/86c14a38543b3aaa52aef0a574de5eab0f44cba8>
> > +static tree
> > +temp_string_literal (const char *name, tree elt_type)
>
> Why "temp"? A string literal isn't a temporary.
Renamed in
<https://forge.sourceware.org/marek/gcc/commit/86c14a38543b3aaa52aef0a574de5eab0f44cba8>
Thanks,
Marek