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

Reply via email to