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