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