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.

+  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).

+  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.

  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.

+      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?

+      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).

+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?

+  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.

+                          "%<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)?

+           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.

+    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.

+         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?

+      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?

+
+  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.

+  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.

+      && 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.

+  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?

+  /* This includes DECL_NTTP_OBJECT_P objects.  */
+  else if (eval_is_object (kind) == boolean_true_node)
+    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?

+    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).

+      && (!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.

+  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.

And this test gets repeated several times below, so let's add a type_linkage_name function.

+       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.

+      && 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() { }
}

+    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?

+      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.

+      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?

+    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."

+static tree
+temp_string_literal (const char *name, tree elt_type)

Why "temp"?  A string literal isn't a temporary.

More soon.

Jason


Reply via email to