| /* C++ reflection code. |
| Copyright (C) 2025-2026 Free Software Foundation, Inc. |
| Written by Marek Polacek <polacek@redhat.com> and |
| Jakub Jelinek <jakub@redhat.com>. |
| |
| 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 |
| #include "metafns.h" |
| |
| 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; |
| |
| /* Return the appropriate tsubst flags for processing a metafunction. */ |
| |
| static tsubst_flags_t |
| complain_flags (const constexpr_ctx *ctx) |
| { |
| return cxx_constexpr_quiet_p (ctx) ? tf_none : tf_warning_or_error; |
| } |
| |
| /* 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); |
| |
| /* Create the `std::meta' namespace. */ |
| push_namespace (get_identifier ("std")); |
| push_namespace (get_identifier ("meta"), /*inline*/false); |
| std_meta_node = current_namespace; |
| pop_namespace (); |
| pop_namespace (); |
| } |
| |
| /* 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); |
| SET_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 variable declared by an init-capture, |
| R is ill-formed. */ |
| else if (is_capture_proxy (t)) |
| { |
| error_at (loc, "%<^^%> cannot be applied to a capture %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. */ |
| 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 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-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) |
| /* Resolve all TEMPLATE_ID_EXPRs here. */ |
| t = resolve_nondeduced_context_or_error (t, tf_warning_or_error); |
| |
| /* 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; |
| } |
| |
| /* Return true if DECL comes from std::meta. */ |
| |
| static bool |
| decl_in_std_meta_p (tree decl) |
| { |
| return decl_namespace_context (decl) == std_meta_node; |
| } |
| |
| /* 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; |
| |
| if (!decl_in_std_meta_p (fndecl)) |
| 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; |
| } |
| |
| /* 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); |
| 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 NULL_TREE; |
| } |
| 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 NULL_TREE; |
| } |
| 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 " |
| "rather than %<char%>, %<wchar_t%>, %<char8_t%>, " |
| "%<char16_t%> or %<char32_t%>", inst); |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| /* 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))) |
| && 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 which is not a structural type", inst); |
| structural_type_p (valuet, true); |
| } |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| TREE_VEC_ELT (args, 0) |
| = build_stub_type (valuet, |
| cp_type_quals (valuet) | TYPE_QUAL_CONST, |
| false); |
| if (!is_xible (INIT_EXPR, valuet, args)) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "%<reflect_constant_array%> argument with %qT " |
| "which is not copy constructible", inst); |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| TREE_VEC_ELT (args, 0) = TREE_TYPE (type); |
| tree instr |
| = lookup_template_class (get_identifier ("range_reference_t"), |
| args, /*in_decl*/NULL_TREE, |
| /*context*/ranges_ns, |
| tf_warning_or_error); |
| instr = complete_type (instr); |
| if (instr == error_mark_node) |
| { |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| tree referencet = TYPE_MAIN_VARIANT (instr); |
| 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 " |
| "which is not constructible from %qT " |
| "%<std::ranges::range_reference_t%>", |
| inst, referencet); |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| } |
| } |
| auto_vec<tree, 32> retvec; |
| tree p = convert_from_reference (parm); |
| auto obj_call = [=, &map] (tree obj, tsubst_flags_t complain) { |
| 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 NULL_TREE; |
| } |
| 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); |
| /* Here, we're evaluating an AGGR_INIT_EXPR, which is already |
| embedded in a TARGET_EXPR, so we don't want to add another |
| TARGET_EXPR inside it. Note that SIMPLE_TARGET_EXPR_P would |
| always be false because the TARGET_EXPR_INITIAL is an |
| AGGR_INIT_EXPR with void type. */ |
| if (TREE_CODE (r) == 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) |
| { |
| error_at (loc, "%<std::ranges::data%> or %<std::ranges::size%> " |
| "are not customization point objects"); |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| data = obj_call (data, tf_none); |
| if (error_operand_p (data)) |
| goto non_contiguous; |
| if (data == NULL_TREE) |
| return NULL_TREE; |
| size = obj_call (size, tf_none); |
| if (error_operand_p (size)) |
| goto non_contiguous; |
| if (size == NULL_TREE) |
| return NULL_TREE; |
| 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 NULL_TREE; |
| } |
| begin = obj_call (begin, tf_warning_or_error); |
| if (error_operand_p (begin)) |
| { |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| end = obj_call (end, tf_warning_or_error); |
| if (error_operand_p (end)) |
| { |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| 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 NULL_TREE; |
| } |
| 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 NULL_TREE; |
| } |
| 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) |
| return NULL_TREE; |
| end = cxx_eval_constant_expression (ctx, end, vc_glvalue, non_constant_p, |
| overflow_p, jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| 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 NULL_TREE; |
| } |
| // 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 NULL_TREE; |
| } |
| 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) |
| return NULL_TREE; |
| 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) |
| return NULL_TREE; |
| 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) |
| return NULL_TREE; |
| } |
| 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); |
| } |
| |
| /* 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 |
| /* 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_get_first_fn (r); |
| |
| 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_first_fn (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_first_fn (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); |
| 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; |
| } |
| |
| /* For direct base class relationship R return the binfo related |
| to the derived type. */ |
| |
| static tree |
| direct_base_derived_binfo (tree r) |
| { |
| /* Looping needed for multiple virtual inheritance. */ |
| while (BINFO_INHERITANCE_CHAIN (r)) |
| r = BINFO_INHERITANCE_CHAIN (r); |
| return r; |
| } |
| |
| /* For direct base class relationship R return the derived type |
| (i.e. when R is (D, B) it returns D). */ |
| |
| tree |
| direct_base_derived (tree r) |
| { |
| return BINFO_TYPE (direct_base_derived_binfo (r)); |
| } |
| |
| /* 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_first_fn (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 = direct_base_derived_binfo (r); |
| |
| 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_first_fn (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_first_fn (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_first_fn (r); |
| if (TREE_CODE (r) == FUNCTION_DECL |
| && DECL_VIRTUAL_P (r) |
| && !DECL_STATIC_FUNCTION_P (r) |
| && 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_first_fn (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); |
| 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 (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_first_fn (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; |
| /* 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 (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_first_fn (r); |
| if (TREE_CODE (r) == FUNCTION_DECL && DECL_MAYBE_DELETED (r)) |
| { |
| ++function_depth; |
| maybe_synthesize_method (r); |
| --function_depth; |
| } |
| if (TREE_CODE (r) == FUNCTION_DECL && DECL_DELETED_FN (r)) |
| 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_first_fn (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_first_fn (r); |
| if (TREE_CODE (r) == FUNCTION_DECL && user_provided_p (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_first_fn (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_first_fn (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 in get_reflection. */ |
| if (TREE_CODE (r) == CONST_DECL) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Get the linkage name for T, or NULL_TREE for types with no name |
| or for typedefs. */ |
| |
| static tree |
| reflection_type_linkage_name (tree t) |
| { |
| if (OVERLOAD_TYPE_P (t) && !typedef_variant_p (t)) |
| return TYPE_NAME (t); |
| return NULL_TREE; |
| } |
| |
| /* 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_first_fn (r); |
| r = STRIP_TEMPLATE (r); |
| if (TYPE_P (r)) |
| { |
| r = reflection_type_linkage_name (r); |
| if (!r) |
| return boolean_false_node; |
| } |
| 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_first_fn (r); |
| r = STRIP_TEMPLATE (r); |
| if (TYPE_P (r)) |
| { |
| r = reflection_type_linkage_name (r); |
| if (!r) |
| return boolean_false_node; |
| } |
| 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_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_first_fn (r); |
| r = STRIP_TEMPLATE (r); |
| if (TYPE_P (r)) |
| { |
| r = reflection_type_linkage_name (r); |
| if (!r) |
| return boolean_false_node; |
| } |
| 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_first_fn (r); |
| r = STRIP_TEMPLATE (r); |
| if (TYPE_P (r)) |
| { |
| r = reflection_type_linkage_name (r); |
| if (!r) |
| return boolean_false_node; |
| } |
| if (decl_linkage (r) != lk_none && DECL_LANGUAGE (r) == lang_c) |
| 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_first_fn (r); |
| r = STRIP_TEMPLATE (r); |
| if (TYPE_P (r)) |
| { |
| r = reflection_type_linkage_name (r); |
| if (!r) |
| return boolean_false_node; |
| } |
| 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) |
| { |
| 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_first_fn (r); |
| |
| if (TREE_CODE (r) == FUNCTION_DECL |
| && DECL_OVERLOADED_OPERATOR_P (r) |
| && !DECL_CONV_FN_P (r)) |
| return boolean_true_node; |
| else |
| 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_first_fn (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_first_fn (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_first_fn (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_first_fn (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 |
| && eval_is_operator_function_template (r) == boolean_false_node) |
| return throw_exception (loc, ctx, |
| "reflection does not represent an operator " |
| "function or operator function template", |
| fun, non_constant_p, jump_target); |
| r = maybe_get_first_fn (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. Returns the string |
| literal, or NULL_TREE if the conversion failed. */ |
| |
| static tree |
| get_string_literal (const char *name, tree elt_type) |
| { |
| cpp_string istr, ostr; |
| istr.len = strlen (name) + 1; |
| istr.text = (const unsigned char *) name; |
| if (!cpp_translate_string (parse_in, &istr, &ostr, |
| elt_type == char_type_node |
| ? CPP_STRING : CPP_UTF8STRING, false)) |
| return NULL_TREE; |
| name = (const char *) ostr.text; |
| tree ret = build_string_literal (strlen (name) + 1, name, elt_type); |
| free (const_cast <char *> (name)); |
| return ret; |
| } |
| |
| /* Process std::meta::{,u8}symbol_of. |
| Returns: A string_view or u8string_view containing the characters of the |
| operator symbol name corresponding to op, respectively encoded with the |
| ordinary literal encoding or with UTF-8. |
| Throws: meta::exception unless the value of op corresponds to one of the |
| enumerators in operators. */ |
| |
| static tree |
| eval_symbol_of (location_t loc, const constexpr_ctx *ctx, tree expr, |
| bool *non_constant_p, tree *jump_target, tree elt_type, |
| tree ret_type, tree fun) |
| { |
| maybe_init_meta_operators (loc); |
| if (!tree_fits_uhwi_p (expr)) |
| { |
| fail: |
| return throw_exception (loc, ctx, |
| "operators argument is not a valid operator", |
| fun, non_constant_p, jump_target); |
| } |
| unsigned HOST_WIDE_INT val = tree_to_uhwi (expr); |
| 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 && meta_operators[i][j] == val) |
| { |
| const char *name = ovl_op_info[i][j].name; |
| char buf[64]; |
| if (const char *sp = strchr (name, ' ')) |
| { |
| memcpy (buf, name, sp - name); |
| strcpy (buf + (sp - name), sp + 1); |
| name = buf; |
| } |
| tree str = get_string_literal (name, elt_type); |
| /* Basic character set ought to be better convertible |
| into ordinary literal character set and must be always |
| convertible into UTF-8. */ |
| gcc_checking_assert (str); |
| releasing_vec args (make_tree_vector_single (str)); |
| tree r = build_special_member_call (NULL_TREE, |
| complete_ctor_identifier, |
| &args, ret_type, LOOKUP_NORMAL, |
| tf_warning_or_error); |
| return build_cplus_new (ret_type, r, tf_warning_or_error); |
| } |
| goto fail; |
| } |
| |
| /* has-type (exposition only). |
| Returns: true if r represents a value, annotation, object, variable, |
| function whose type does not contain an undeduced placeholder type and |
| that is not a constructor or destructor, enumerator, non-static data |
| member, unnamed bit-field, direct base class relationship, data member |
| description, or function parameter. Otherwise, false. */ |
| |
| static bool |
| has_type (tree r, reflect_kind kind) |
| { |
| r = MAYBE_BASELINK_FUNCTIONS (r); |
| if (TREE_CODE (r) == FUNCTION_DECL) |
| { |
| if (DECL_CONSTRUCTOR_P (r) || DECL_DESTRUCTOR_P (r)) |
| return false; |
| if (undeduced_auto_decl (r)) |
| return false; |
| return true; |
| } |
| if (CONSTANT_CLASS_P (r) |
| || eval_is_variable (r, kind) == boolean_true_node |
| || eval_is_enumerator (r) == boolean_true_node |
| || TREE_CODE (r) == FIELD_DECL |
| || eval_is_annotation (r, kind) == boolean_true_node |
| || eval_is_function_parameter (r, kind) == boolean_true_node |
| || eval_is_object (kind) == boolean_true_node |
| || eval_is_value (kind) == boolean_true_node |
| || kind == REFLECT_BASE |
| || kind == REFLECT_DATA_MEMBER_SPEC) |
| return true; |
| return false; |
| } |
| |
| /* Helper function for eval_type_of. Assuming has_type is true, return |
| the std::meta::type_of type (rather than reflection thereof). */ |
| |
| static tree |
| type_of (tree r, reflect_kind kind) |
| { |
| r = MAYBE_BASELINK_FUNCTIONS (r); |
| if (TREE_CODE (r) == PARM_DECL && kind == REFLECT_PARM) |
| { |
| r = maybe_update_function_parm (r); |
| tree fn = DECL_CONTEXT (r); |
| tree args = FUNCTION_FIRST_USER_PARM (fn); |
| tree type = FUNCTION_FIRST_USER_PARMTYPE (fn); |
| while (r != args) |
| { |
| args = DECL_CHAIN (args); |
| type = TREE_CHAIN (type); |
| } |
| r = TREE_VALUE (type); |
| } |
| else if (kind == REFLECT_BASE) |
| r = BINFO_TYPE (r); |
| else if (kind == REFLECT_DATA_MEMBER_SPEC) |
| r = TREE_VEC_ELT (r, 0); |
| else if (eval_is_annotation (r, kind) == boolean_true_node) |
| { |
| r = TREE_TYPE (TREE_VALUE (TREE_VALUE (r))); |
| if (CLASS_TYPE_P (r)) |
| { |
| int quals = cp_type_quals (r); |
| quals |= TYPE_QUAL_CONST; |
| r = cp_build_qualified_type (r, quals); |
| } |
| } |
| else if (TREE_CODE (r) == FIELD_DECL && DECL_BIT_FIELD_TYPE (r)) |
| r = DECL_BIT_FIELD_TYPE (r); |
| else |
| r = TREE_TYPE (r); |
| return strip_typedefs (r); |
| } |
| |
| /* Process std::meta::type_of. Returns: |
| -- If r represents the ith parameter of a function F, then the ith type |
| in the parameter-type-list of F. |
| -- Otherwise, if r represents a value, object, variable, function, |
| non-static data member, or unnamed bit-field, then the type of what is |
| represented by r. |
| -- Otherwise, if r represents an annotation, then type_of(constant_of(r)). |
| -- Otherwise, if r represents an enumerator N of an enumeration E, then: |
| -- If E is defined by a declaration D that precedes a point P in the |
| evaluation context and P does not occur within an enum-specifier of |
| D, then a reflection of E. |
| -- Otherwise, a reflection of the type of N prior to the closing brace |
| of the enum-specifier as specified in [dcl.enum]. |
| -- Otherwise, if r represents a direct base class relationship (D,B), then |
| a reflection of B. |
| -- Otherwise, for a data member description (T,N,A,W,NUA), a reflection of |
| the type T. */ |
| |
| static tree |
| eval_type_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (!has_type (r, kind)) |
| return throw_exception (loc, ctx, "reflection does not have a type", |
| fun, non_constant_p, jump_target); |
| return get_reflection_raw (loc, type_of (r, kind)); |
| } |
| |
| /* Process std::meta::source_location_of. |
| Returns: If r represents a value, a type other than a class type or an |
| enumeration type, the global namespace, or a data member description, |
| then source_location{}. Otherwise, an implementation-defined |
| source_location value. */ |
| |
| static tree |
| eval_source_location_of (location_t loc, tree r, reflect_kind kind, |
| tree std_source_location) |
| { |
| if (!NON_UNION_CLASS_TYPE_P (std_source_location)) |
| { |
| error_at (loc, "%qT is not a class type", std_source_location); |
| return error_mark_node; |
| } |
| location_t rloc = UNKNOWN_LOCATION; |
| if (kind == REFLECT_BASE) |
| /* We don't track location_t of the base specifiers, so at least |
| for now use location_t of the base parent (i.e. the derived |
| class). */ |
| r = direct_base_derived (r); |
| if (OVERLOAD_TYPE_P (r) || (TYPE_P (r) && typedef_variant_p (r))) |
| rloc = DECL_SOURCE_LOCATION (TYPE_NAME (r)); |
| else if (DECL_P (r) && r != global_namespace) |
| rloc = DECL_SOURCE_LOCATION (r); |
| else if (eval_is_annotation (r, kind) == boolean_true_node) |
| rloc = EXPR_LOCATION (TREE_VALUE (TREE_VALUE (r))); |
| tree decl = NULL_TREE, field = NULL_TREE; |
| if (rloc != UNKNOWN_LOCATION) |
| { |
| /* Make sure __builtin_source_location (which depends on |
| std::source_location::__impl) will work without errors. */ |
| tree name = get_identifier ("__impl"); |
| decl = lookup_qualified_name (std_source_location, name); |
| if (TREE_CODE (decl) != TYPE_DECL) |
| decl = NULL_TREE; |
| else |
| { |
| name = get_identifier ("__builtin_source_location"); |
| decl = lookup_qualified_name (global_namespace, name); |
| if (TREE_CODE (decl) != FUNCTION_DECL |
| || !fndecl_built_in_p (decl, BUILT_IN_FRONTEND) |
| || DECL_FE_FUNCTION_CODE (decl) != CP_BUILT_IN_SOURCE_LOCATION |
| || !require_deduced_type (decl, tf_warning_or_error)) |
| decl = NULL_TREE; |
| } |
| } |
| if (decl) |
| { |
| field = TYPE_FIELDS (std_source_location); |
| field = next_aggregate_field (field); |
| /* Make sure std::source_location has exactly a single non-static |
| data member (_M_impl in libstdc++, __ptr_ in libc++) with pointer |
| type. Return {._M_impl = &*.Lsrc_locN}. */ |
| if (field != NULL_TREE |
| && POINTER_TYPE_P (TREE_TYPE (field)) |
| && !next_aggregate_field (DECL_CHAIN (field))) |
| { |
| tree call = build_call_nary (TREE_TYPE (TREE_TYPE (decl)), decl, 0); |
| SET_EXPR_LOCATION (call, rloc); |
| call = fold_builtin_source_location (call); |
| return build_constructor_single (std_source_location, field, call); |
| } |
| } |
| return build_constructor (std_source_location, nullptr); |
| } |
| |
| /* If R is (const T &) &foo, get foo. */ |
| |
| static tree |
| maybe_get_reference_referent (tree r) |
| { |
| if (TREE_CODE (r) == NOP_EXPR |
| && TYPE_REF_P (TREE_TYPE (r)) |
| && TREE_CODE (TREE_OPERAND (r, 0)) == ADDR_EXPR) |
| { |
| STRIP_NOPS (r); |
| r = TREE_OPERAND (r, 0); |
| } |
| return r; |
| } |
| |
| /* Process std::meta::object_of. |
| Returns: |
| -- If r represents an object, then r. |
| -- Otherwise, if r represents a reference, then a reflection of the object |
| referred to by that reference. |
| -- Otherwise, r represents a variable; a reflection of the object declared |
| by that variable. |
| Throws: meta::exception unless r is a reflection representing either |
| -- an object with static storage duration, or |
| -- a variable that either declares or refers to such an object, and if that |
| variable is a reference R, then either |
| -- R is usable in constant expressions, or |
| -- the lifetime of R began within the core constant expression currently |
| under evaluation. */ |
| |
| static tree |
| eval_object_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, bool *overflow_p, |
| tree *jump_target, tree fun) |
| { |
| tree orig = r; |
| if (TYPE_REF_P (TREE_TYPE (r))) |
| r = cxx_eval_constant_expression (ctx, r, vc_prvalue, non_constant_p, |
| overflow_p, jump_target); |
| r = maybe_get_reference_referent (r); |
| if (eval_has_static_storage_duration (orig, kind) == boolean_false_node |
| && (orig == r |
| || eval_has_static_storage_duration (r, kind) == boolean_false_node)) |
| return throw_exception (loc, ctx, "reflection does not represent an" |
| " object with static storage duration," |
| " or a reference to such an object", |
| fun, non_constant_p, jump_target); |
| return get_reflection_raw (loc, r, REFLECT_OBJECT); |
| } |
| |
| /* Process std::meta::constant_of. |
| Let R be a constant expression of type info such that R == r is true. |
| If r represents an annotation, then let C be its underlying constant. |
| Effects: Equivalent to: |
| if constexpr (is_annotation(R)) { |
| return C; |
| } else if constexpr (is_array_type(type_of(R)) { |
| return reflect_constant_array([: R :]); |
| } else if constexpr (is_function_type(type_of(R)) { |
| return reflect_function([: R :]); |
| } else { |
| return reflect_constant([: R :]); |
| } |
| Throws: meta::exception unless either r represents an annotation or |
| [: R :] is a valid splice-expression. */ |
| |
| static tree |
| eval_constant_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, bool *overflow_p, |
| tree *jump_target, tree fun) |
| { |
| tree type; |
| if (has_type (r, kind)) |
| type = type_of (r, kind); |
| else |
| type = maybe_strip_typedefs (r); |
| |
| /* So that outer_automatic_var_p works below in check_splice_expr. */ |
| temp_override<tree> ovr (current_function_decl); |
| current_function_decl = cxx_constexpr_caller (ctx); |
| |
| if (eval_is_annotation (r, kind) == boolean_true_node) |
| r = tree_strip_any_location_wrapper (TREE_VALUE (TREE_VALUE (r))); |
| else if (eval_is_array_type (loc, type) == boolean_true_node) |
| { |
| const tsubst_flags_t complain = complain_flags (ctx); |
| /* Create a call to reflect_constant_array so that we can simply |
| let eval_reflect_constant_array do its job. */ |
| tree name = get_identifier ("reflect_constant_array"); |
| tree call = lookup_qualified_name (std_meta_node, name); |
| if (error_operand_p (call) || !is_overloaded_fn (call)) |
| { |
| if (complain) |
| error_at (loc, "couldn%'t look up %<%D::%D%>", std_meta_node, name); |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| /* We want the argument to be a CONSTRUCTOR or a STRING_CST. */ |
| r = cxx_eval_constant_expression (ctx, r, vc_prvalue, non_constant_p, |
| overflow_p, jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| releasing_vec args (make_tree_vector_single (r)); |
| call = finish_call_expr (call, &args, /*disallow_virtual=*/true, |
| /*koenig_p=*/false, complain); |
| if (call == error_mark_node) |
| { |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| return eval_reflect_constant_array (loc, ctx, call, non_constant_p, |
| overflow_p, jump_target, fun); |
| } |
| else if (eval_is_function_type (type) == boolean_true_node) |
| return eval_reflect_function (loc, ctx, type, r, non_constant_p, |
| jump_target, fun); |
| else if (!check_splice_expr (loc, UNKNOWN_LOCATION, r, |
| /*address_p=*/false, |
| /*member_access_p=*/false, |
| /*complain_p=*/false) |
| /* One cannot query the value of a function template. |
| ??? But if [:^^X:] where X is a template is OK, should we |
| really throw? We need an LWG issue. */ |
| || eval_is_template (r) == boolean_true_node) |
| return throw_exception (loc, ctx, "reflection does not represent an " |
| "annotation or a valid argument to " |
| "a splice-expression", |
| fun, non_constant_p, jump_target); |
| |
| r = cxx_eval_constant_expression (ctx, r, vc_prvalue, non_constant_p, |
| overflow_p, jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| /* Figure out the type for reflect_constant. */ |
| type = TREE_TYPE (convert_from_reference (r)); |
| type = type_decays_to (type); |
| type = cv_unqualified (type); |
| |
| return eval_reflect_constant (loc, ctx, type, r, non_constant_p, jump_target, |
| fun); |
| } |
| |
| /* Process std::meta::dealias. |
| Returns: If r represents an entity, then a reflection representing the |
| underlying entity of what r represents. Otherwise, r. |
| This implements LWG 4427 so we do not throw. */ |
| |
| static tree |
| eval_dealias (location_t loc, tree r, reflect_kind kind) |
| { |
| r = maybe_strip_typedefs (r); |
| if (TREE_CODE (r) == NAMESPACE_DECL) |
| r = ORIGINAL_NAMESPACE (r); |
| return get_reflection_raw (loc, r, kind); |
| } |
| |
| /* Process std::meta::is_noexcept. |
| Returns: true if r represents a noexcept function type or a function |
| with a non-throwing exception specification ([except.spec]). |
| Otherwise, false. |
| Note: If r represents a function template that is declared noexcept, |
| is_noexcept (r) is still false because in general such queries |
| for templates cannot be answered. */ |
| |
| static tree |
| eval_is_noexcept (tree r) |
| { |
| if (eval_is_function (r) == boolean_true_node) |
| { |
| r = maybe_get_first_fn (r); |
| maybe_instantiate_noexcept (r); |
| if (TYPE_NOTHROW_P (TREE_TYPE (r))) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| if (eval_is_function_type (r) == boolean_true_node |
| && TYPE_NOTHROW_P (r)) |
| return boolean_true_node; |
| |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_const. |
| Let T be type_of(r) if has-type(r) is true. Otherwise, let T be dealias(r). |
| Returns: true if T represents a const type, or a const-qualified function |
| type. Otherwise, false. */ |
| |
| static tree |
| eval_is_const (tree r, reflect_kind kind) |
| { |
| if (has_type (r, kind)) |
| r = type_of (r, kind); |
| else |
| r = maybe_strip_typedefs (r); |
| r = strip_array_types (r); |
| if (TREE_CODE (r) == METHOD_TYPE) |
| { |
| if (type_memfn_quals (r) & TYPE_QUAL_CONST) |
| return boolean_true_node; |
| } |
| else if (TYPE_P (r) && TYPE_READONLY (r)) |
| return boolean_true_node; |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_volatile. |
| Let T be type_of(r) if has-type(r) is true. Otherwise, let T be dealias(r). |
| Returns: true if T represents a volatile type, or a volatile-qualified |
| function type. Otherwise, false. */ |
| |
| static tree |
| eval_is_volatile (tree r, reflect_kind kind) |
| { |
| if (has_type (r, kind)) |
| r = type_of (r, kind); |
| else |
| r = maybe_strip_typedefs (r); |
| r = strip_array_types (r); |
| if (TREE_CODE (r) == METHOD_TYPE) |
| { |
| if (type_memfn_quals (r) & TYPE_QUAL_VOLATILE) |
| return boolean_true_node; |
| } |
| else if (TYPE_P (r) && TYPE_VOLATILE (r)) |
| return boolean_true_node; |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::has_template_arguments. |
| Returns: true if r represents a specialization of a function template, |
| variable template, class template, or an alias template. Otherwise, |
| false. */ |
| |
| static tree |
| eval_has_template_arguments (tree r) |
| { |
| r = MAYBE_BASELINK_FUNCTIONS (r); |
| /* Presumably for |
| typedef cls_tmpl<int> TYPE; |
| 'has_template_arguments (^^TYPE)' should be false? */ |
| if (TYPE_P (r) |
| && typedef_variant_p (r) |
| && !alias_template_specialization_p (r, nt_opaque)) |
| return boolean_false_node; |
| if (primary_template_specialization_p (r) |
| || variable_template_specialization_p (r)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::template_of. |
| Returns: A reflection of the template of the specialization represented |
| by r. |
| Throws: meta::exception unless has_template_arguments(r) is true. */ |
| |
| static tree |
| eval_template_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| if (eval_has_template_arguments (r) != boolean_true_node) |
| return throw_exception_notargs (loc, ctx, fun, non_constant_p, jump_target); |
| |
| r = MAYBE_BASELINK_FUNCTIONS (r); |
| if (TYPE_P (r) && typedef_variant_p (r)) |
| r = TI_TEMPLATE (TYPE_ALIAS_TEMPLATE_INFO (r)); |
| else if (CLASS_TYPE_P (r) && CLASSTYPE_TEMPLATE_INFO (r)) |
| r = CLASSTYPE_TI_TEMPLATE (r); |
| else if (VAR_OR_FUNCTION_DECL_P (r) && DECL_TEMPLATE_INFO (r)) |
| r = DECL_TI_TEMPLATE (r); |
| else |
| gcc_assert (false); |
| |
| gcc_assert (TREE_CODE (r) == TEMPLATE_DECL); |
| return get_reflection_raw (loc, r); |
| } |
| |
| /* Process std::meta::has_parent |
| Returns: |
| -- If r represents the global namespace, then false. |
| -- Otherwise, if r represents an entity that has C language linkage, |
| then false. |
| -- Otherwise, if r represents an entity that has a language linkage |
| other than C++ language linkage, then an implementation-defined value. |
| -- Otherwise, if r represents a type that is neither a class nor enumeration |
| type, then false. |
| -- Otherwise, if r represents an entity or direct base class relationship, |
| then true. |
| -- Otherwise, false. */ |
| |
| static tree |
| eval_has_parent (tree r, reflect_kind kind) |
| { |
| if (kind == REFLECT_OBJECT |
| || CONSTANT_CLASS_P (r) |
| || r == global_namespace |
| || kind == REFLECT_DATA_MEMBER_SPEC) |
| return boolean_false_node; |
| if (TYPE_P (r)) |
| { |
| if (TYPE_NAME (r) |
| && DECL_P (TYPE_NAME (r)) |
| && DECL_LANGUAGE (TYPE_NAME (r)) == lang_c) |
| return boolean_false_node; |
| else if (OVERLOAD_TYPE_P (r) || typedef_variant_p (r)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| r = maybe_get_first_fn (r); |
| if (kind == REFLECT_BASE) |
| return boolean_true_node; |
| if (!DECL_P (r)) |
| return boolean_false_node; |
| if (TREE_CODE (r) != NAMESPACE_DECL && DECL_LANGUAGE (r) == lang_c) |
| return boolean_false_node; |
| return boolean_true_node; |
| } |
| |
| /* Process std::meta::parent_of. |
| Returns: |
| -- If r represents a non-static data member that is a direct member of an |
| anonymous union, or an unnamed bit-field declared within the |
| member-specification of such a union, then a reflection representing the |
| innermost enclosing anonymous union. |
| -- Otherwise, if r represents an enumerator, then a reflection representing |
| the corresponding enumeration type. |
| -- Otherwise, if r represents a direct base class relationship (D,B), then |
| a reflection representing D. |
| -- Otherwise, let E be a class, function, or namespace whose class scope, |
| function parameter scope, or namespace scope, respectively, is the |
| innermost such scope that either is, or encloses, the target scope of a |
| declaration of what is represented by r. |
| -- If E is the function call operator of a closure type for a |
| consteval-block-declaration, then parent_of(parent_of(^^E)). |
| -- Otherwise, ^^E. */ |
| |
| static tree |
| eval_parent_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (eval_has_parent (r, kind) != boolean_true_node) |
| return throw_exception (loc, ctx, "reflection does not represent an " |
| "entity with parent", |
| fun, non_constant_p, jump_target); |
| tree c; |
| r = maybe_get_first_fn (r); |
| if (TYPE_P (r)) |
| { |
| if (TYPE_NAME (r) && DECL_P (TYPE_NAME (r))) |
| c = CP_DECL_CONTEXT (TYPE_NAME (r)); |
| else |
| c = CP_TYPE_CONTEXT (r); |
| } |
| else if (kind == REFLECT_BASE) |
| c = direct_base_derived (r); |
| else |
| c = CP_DECL_CONTEXT (r); |
| tree lam; |
| while (LAMBDA_FUNCTION_P (c) |
| && (lam = CLASSTYPE_LAMBDA_EXPR (CP_DECL_CONTEXT (c))) |
| && LAMBDA_EXPR_CONSTEVAL_BLOCK_P (lam)) |
| c = CP_TYPE_CONTEXT (CP_DECL_CONTEXT (c)); |
| return get_reflection_raw (loc, c); |
| } |
| |
| /* 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 (get_identifier ("vector"), 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; |
| } |
| |
| /* Build std::vector<info>{ ELTS }. */ |
| |
| static tree |
| get_vector_of_info_elts (vec<constructor_elt, va_gc> *elts) |
| { |
| tree ctor = build_constructor (init_list_type_node, elts); |
| CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true; |
| TREE_CONSTANT (ctor) = true; |
| TREE_STATIC (ctor) = true; |
| tree type = get_vector_info (); |
| if (!type) |
| return error_mark_node; |
| tree r = finish_compound_literal (type, ctor, tf_warning_or_error, |
| fcl_functional); |
| /* Here, we're evaluating an AGGR_INIT_EXPR, which is already |
| embedded in a TARGET_EXPR, so we don't want to add another |
| TARGET_EXPR inside it. Note that SIMPLE_TARGET_EXPR_P would |
| always be false because the TARGET_EXPR_INITIAL is an |
| AGGR_INIT_EXPR with void type. */ |
| if (TREE_CODE (r) == TARGET_EXPR) |
| r = TARGET_EXPR_INITIAL (r); |
| return r; |
| } |
| |
| /* Process std::meta::parameters_of. |
| Returns: |
| -- If r represents a function F, then a vector containing reflections of |
| the parameters of F, in the order in which they appear in a declaration |
| of F. |
| -- Otherwise, r represents a function type T; a vector containing |
| reflections of the types in parameter-type-list of T, in the order in |
| which they appear in the parameter-type-list. |
| |
| Throws: meta::exception unless r represents a function or a function |
| type. */ |
| |
| static tree |
| eval_parameters_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| if (eval_is_function (r) != boolean_true_node |
| && eval_is_function_type (r) != boolean_true_node) |
| return throw_exception_nofn (loc, ctx, fun, non_constant_p, jump_target); |
| |
| r = maybe_get_first_fn (r); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| tree args = (TREE_CODE (r) == FUNCTION_DECL |
| ? FUNCTION_FIRST_USER_PARM (r) |
| : TYPE_ARG_TYPES (r)); |
| for (tree arg = args; arg && arg != void_list_node; arg = TREE_CHAIN (arg)) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, arg, REFLECT_PARM)); |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Process std::meta::variable_of. |
| Returns: The reflection of the parameter variable corresponding to r. |
| |
| Throws: meta::exception unless |
| -- r represents a parameter of a function F and |
| -- there is a point P in the evaluation context for which the innermost |
| non-block scope enclosing P is the function parameter scope associated |
| with F. */ |
| |
| static tree |
| eval_variable_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (eval_is_function_parameter (r, kind) == boolean_false_node |
| /* This doesn't consider the points corresponding to injected |
| declarations, but that doesn't seem needed. */ |
| || DECL_CONTEXT (r) != current_function_decl) |
| return throw_exception (loc, ctx, "reflection does not represent " |
| "parameter of current function", |
| fun, non_constant_p, jump_target); |
| r = maybe_update_function_parm (r); |
| return get_reflection_raw (loc, r, REFLECT_UNDEF); |
| } |
| |
| /* Process std::meta::return_type_of. |
| Returns: The reflection of the return type of the function or function type |
| represented by r. |
| |
| Throws: meta::exception unless either r represents a function and |
| has-type(r) is true or r represents a function type. */ |
| |
| static tree |
| eval_return_type_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if ((eval_is_function (r) != boolean_true_node || !has_type (r, kind)) |
| && eval_is_function_type (r) != boolean_true_node) |
| return throw_exception (loc, ctx, "reflection does not represent a " |
| "function or function type with a return type", |
| fun, non_constant_p, jump_target); |
| |
| r = MAYBE_BASELINK_FUNCTIONS (r); |
| if (TREE_CODE (r) == FUNCTION_DECL) |
| r = TREE_TYPE (r); |
| r = TREE_TYPE (r); |
| return get_reflection_raw (loc, r, REFLECT_UNDEF); |
| } |
| |
| /* Process std::meta::offset_of. |
| Let V be the offset in bits from the beginning of a complete object of the |
| type represented by parent_of(r) to the subobject associated with the |
| entity represented by r. |
| Returns: {V / CHAR_BIT, V % CHAR_BIT}. |
| Throws: meta::exception unless r represents a non-static data member, |
| unnamed bit-field, or direct base class relationship (D,B) for which either |
| B is not a virtual base class or D is not an abstract class. */ |
| |
| static tree |
| eval_offset_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, tree member_offset, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| tree byte_off = NULL_TREE, bit_off = NULL_TREE; |
| if (kind == REFLECT_BASE) |
| { |
| tree d = direct_base_derived (r); |
| if (BINFO_VIRTUAL_P (r) && ABSTRACT_CLASS_TYPE_P (d)) |
| return throw_exception (loc, ctx, |
| "reflection of virtual direct base " |
| "relationship with abstract derived " |
| "class", fun, non_constant_p, jump_target); |
| byte_off = BINFO_OFFSET (r); |
| } |
| else if (TREE_CODE (r) != FIELD_DECL) |
| return throw_exception (loc, ctx, "reflection unsuitable for offset_of", |
| fun, non_constant_p, jump_target); |
| else |
| bit_off = bit_position (r); |
| if (TREE_CODE (bit_off ? bit_off : byte_off) != INTEGER_CST) |
| return throw_exception (loc, ctx, "non-constant offset for offset_of", |
| fun, non_constant_p, jump_target); |
| if (TREE_CODE (member_offset) != RECORD_TYPE) |
| { |
| fail: |
| error_at (loc, "unexpected return type of %qs", "std::meta::offset_of"); |
| return build_zero_cst (member_offset); |
| } |
| tree bytes = next_aggregate_field (TYPE_FIELDS (member_offset)); |
| if (!bytes || !INTEGRAL_TYPE_P (TREE_TYPE (bytes))) |
| goto fail; |
| tree bits = next_aggregate_field (DECL_CHAIN (bytes)); |
| if (!bits || !INTEGRAL_TYPE_P (TREE_TYPE (bits))) |
| goto fail; |
| if (next_aggregate_field (DECL_CHAIN (bits))) |
| goto fail; |
| tree bytesv; |
| if (byte_off) |
| bytesv = byte_off; |
| else |
| bytesv = size_binop (TRUNC_DIV_EXPR, bit_off, bitsize_unit_node); |
| bytesv = fold_convert (TREE_TYPE (bytes), bytesv); |
| tree bitsv; |
| if (byte_off) |
| bitsv = build_zero_cst (TREE_TYPE (bits)); |
| else |
| { |
| bitsv = size_binop (TRUNC_MOD_EXPR, bit_off, bitsize_unit_node); |
| bitsv = fold_convert (TREE_TYPE (bits), bitsv); |
| } |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| CONSTRUCTOR_APPEND_ELT (elts, bytes, bytesv); |
| CONSTRUCTOR_APPEND_ELT (elts, bits, bitsv); |
| return build_constructor (member_offset, elts); |
| } |
| |
| /* Process std::meta::size_of. |
| Returns: If r represents |
| -- a non-static data member of type T, |
| -- a data member description (T,N,A,W,NUA), or |
| -- dealias(r) represents a type T, |
| then sizeof(T) if T is not a reference type and size_of(add_pointer(^^T)) |
| otherwise. Otherwise, size_of(type_of(r)). |
| |
| Throws: meta::exception unless all of the following conditions are met: |
| -- dealias(r) is a reflection of a type, object, value, variable of |
| non-reference type, non-static data member that is not a bit-field, |
| direct base class relationship, or data member description |
| (T,N,A,W,NUA) where W is not _|_. |
| -- If dealias(r) represents a type, then is_complete_type(r) is true. */ |
| |
| static tree |
| eval_size_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, tree ret_type, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| if (eval_is_type (r) != boolean_true_node |
| && eval_is_object (kind) != boolean_true_node |
| && eval_is_value (kind) != boolean_true_node |
| && (eval_is_variable (r, kind) != boolean_true_node |
| || TYPE_REF_P (TREE_TYPE (r))) |
| && (TREE_CODE (r) != FIELD_DECL || DECL_C_BIT_FIELD (r)) |
| && kind != REFLECT_BASE |
| && (kind != REFLECT_DATA_MEMBER_SPEC || TREE_VEC_ELT (r, 3))) |
| return throw_exception (loc, ctx, "reflection not suitable for size_of", |
| fun, non_constant_p, jump_target); |
| if (!INTEGRAL_TYPE_P (ret_type)) |
| { |
| error_at (loc, "unexpected return type of %qs", "std::meta::size_of"); |
| return build_zero_cst (ret_type); |
| } |
| tree type; |
| if (TYPE_P (r)) |
| type = r; |
| else |
| type = type_of (r, kind); |
| tree ret; |
| if (!complete_type_or_maybe_complain (type, NULL_TREE, tf_none) |
| /* No special casing of references needed, c_sizeof_or_alignof_type |
| returns the same size for POINTER_TYPE and REFERENCE_TYPE. */ |
| || ((ret = c_sizeof_or_alignof_type (loc, type, true, false, 0)) |
| == error_mark_node)) |
| return throw_exception (loc, ctx, |
| "reflection with incomplete type in size_of", |
| fun, non_constant_p, jump_target); |
| return fold_convert (ret_type, ret); |
| } |
| |
| /* Process std::meta::alignment_of. |
| Returns: |
| -- If dealias(r) represents a type T, then alignment_of(add_pointer(r)) if |
| T is a reference type and the alignment requirement of T otherwise. |
| -- Otherwise, if dealias(r) represents a variable or object, then the |
| alignment requirement of the variable or object. |
| -- Otherwise, if r represents a direct base class relationship, then |
| alignment_of(type_of(r)). |
| -- Otherwise, if r represents a non-static data member M of a class C, |
| then the alignment of the direct member subobject corresponding to M of a |
| complete object of type C. |
| -- Otherwise, r represents a data member description (T,N,A,W,NUA). |
| If A is not _|_, then the value A. Otherwise, alignment_of(^^T). |
| Throws: meta::exception unless all of the following conditions are met: |
| -- dealias(r) is a reflection of a type, object, variable of non-reference |
| type, non-static data member that is not a bit-field, direct base class |
| relationship, or data member description (T,N,A,W,NUA) where W is _|_. |
| -- If dealias(r) represents a type, then is_complete_type(r) is true. */ |
| |
| static tree |
| eval_alignment_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, tree ret_type, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| if (eval_is_type (r) != boolean_true_node |
| && eval_is_object (kind) != boolean_true_node |
| && (eval_is_variable (r, kind) != boolean_true_node |
| || TYPE_REF_P (TREE_TYPE (r))) |
| && (TREE_CODE (r) != FIELD_DECL || DECL_C_BIT_FIELD (r)) |
| && kind != REFLECT_BASE |
| && (kind != REFLECT_DATA_MEMBER_SPEC || TREE_VEC_ELT (r, 3))) |
| return throw_exception (loc, ctx, "reflection not suitable for alignment_of", |
| fun, non_constant_p, jump_target); |
| if (!INTEGRAL_TYPE_P (ret_type)) |
| { |
| error_at (loc, "unexpected return type of %qs", "std::meta::alignment_of"); |
| return build_zero_cst (ret_type); |
| } |
| tree type; |
| if (kind == REFLECT_DATA_MEMBER_SPEC) |
| { |
| if (TREE_VEC_ELT (r, 2)) |
| return fold_convert (ret_type, TREE_VEC_ELT (r, 2)); |
| else |
| type = TREE_VEC_ELT (r, 0); |
| } |
| else if (kind == REFLECT_BASE) |
| type = BINFO_TYPE (r); |
| else if (TREE_CODE (r) == FIELD_DECL |
| || eval_is_variable (r, kind) == boolean_true_node |
| || (eval_is_object (kind) == boolean_true_node |
| && ((DECL_P (r) && TREE_CODE (r) != FUNCTION_DECL) |
| || TREE_CODE (r) == COMPONENT_REF))) |
| { |
| if (TREE_CODE (r) == COMPONENT_REF) |
| r = TREE_OPERAND (r, 1); |
| return build_int_cst (ret_type, MAX (DECL_ALIGN_UNIT (r), 1)); |
| } |
| else if (TYPE_P (r)) |
| type = r; |
| else if (eval_is_object (kind) == boolean_true_node) |
| type = TREE_TYPE (r); |
| else |
| gcc_unreachable (); |
| if (TYPE_REF_P (type)) |
| type = ptr_type_node; |
| if (FUNC_OR_METHOD_TYPE_P (type)) |
| return throw_exception (loc, ctx, "alignment_of on function type", |
| fun, non_constant_p, jump_target); |
| tree ret; |
| if (!complete_type_or_maybe_complain (type, NULL_TREE, tf_none) |
| /* No special casing of references needed, c_sizeof_or_alignof_type |
| returns the same alignment for POINTER_TYPE and REFERENCE_TYPE. */ |
| || ((ret = c_sizeof_or_alignof_type (loc, type, false, true, 0)) |
| == error_mark_node)) |
| return throw_exception (loc, ctx, |
| "reflection with incomplete type in alignment_of", |
| fun, non_constant_p, jump_target); |
| return fold_convert (ret_type, ret); |
| } |
| |
| /* Process std::meta::bit_size_of. |
| Returns: |
| -- If r represents an unnamed bit-field or a non-static data member that |
| is a bit-field with width W, then W. |
| -- Otherwise, if r represents a data member description (T,N,A,W,NUA) |
| and W is not _|_, then W. |
| -- Otherwise, CHAR_BIT * size_of(r). |
| |
| Throws: meta::exception unless all of the following conditions are met: |
| |
| -- dealias(r) is a reflection of a type, object, value, variable of |
| non-reference type, non-static data member, unnamed bit-field, direct |
| base class relationship, or data member description. |
| -- If dealias(r) represents a type T, there is a point within the |
| evaluation context from which T is not incomplete. */ |
| |
| static tree |
| eval_bit_size_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, tree ret_type, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| if (eval_is_type (r) != boolean_true_node |
| && eval_is_object (kind) != boolean_true_node |
| && eval_is_value (kind) != boolean_true_node |
| && (eval_is_variable (r, kind) != boolean_true_node |
| || TYPE_REF_P (TREE_TYPE (r))) |
| && TREE_CODE (r) != FIELD_DECL |
| && kind != REFLECT_BASE |
| && kind != REFLECT_DATA_MEMBER_SPEC) |
| return throw_exception (loc, ctx, |
| "reflection not suitable for bit_size_of", |
| fun, non_constant_p, jump_target); |
| if (!INTEGRAL_TYPE_P (ret_type)) |
| { |
| error_at (loc, "unexpected return type of %qs", |
| "std::meta::bit_size_of"); |
| return build_zero_cst (ret_type); |
| } |
| tree type; |
| if (TREE_CODE (r) == FIELD_DECL && DECL_C_BIT_FIELD (r)) |
| return fold_convert (ret_type, DECL_SIZE (r)); |
| else if (TYPE_P (r)) |
| type = r; |
| else if (kind == REFLECT_DATA_MEMBER_SPEC && TREE_VEC_ELT (r, 3)) |
| return fold_convert (ret_type, TREE_VEC_ELT (r, 3)); |
| else |
| type = type_of (r, kind); |
| tree ret; |
| if (!complete_type_or_maybe_complain (type, NULL_TREE, tf_none) |
| /* No special casing of references needed, c_sizeof_or_alignof_type |
| returns the same size for POINTER_TYPE and REFERENCE_TYPE. */ |
| || ((ret = c_sizeof_or_alignof_type (loc, type, true, false, 0)) |
| == error_mark_node)) |
| return throw_exception (loc, ctx, |
| "reflection with incomplete type in bit_size_of", |
| fun, non_constant_p, jump_target); |
| ret = size_binop (MULT_EXPR, ret, size_int (BITS_PER_UNIT)); |
| return fold_convert (ret_type, ret); |
| } |
| |
| /* Process std::meta::has_identifier. |
| Returns: |
| -- If r represents an entity that has a typedef name for linkage purposes, |
| then true. |
| -- Otherwise, if r represents an unnamed entity, then false. |
| -- Otherwise, if r represents a type alias, then !has_template_arguments(r). |
| -- Otherwise, if r represents a type, then true if |
| -- r represents a cv-unqualified class type and has_template_arguments(r) |
| is false, or |
| -- r represents a cv-unqualified enumeration type. |
| Otherwise, false. |
| -- Otherwise, if r represents a class type, then !has_template_arguments(r). |
| -- Otherwise, if r represents a function, then true if |
| has_template_arguments(r) is false and the function is not a constructor, |
| destructor, operator function, or conversion function. Otherwise, false. |
| -- Otherwise, if r represents a template, then true if r does not represent |
| a constructor template, operator function template, or conversion |
| function template. Otherwise, false. |
| -- Otherwise, if r represents the ith parameter of a function F that is an |
| (implicit or explicit) specialization of a templated function T and the |
| ith parameter of the instantiated declaration of T whose template |
| arguments are those of F would be instantiated from a pack, then false. |
| -- Otherwise, if r represents the parameter P of a function F, then let S |
| be the set of declarations, ignoring any explicit instantiations, that |
| precede some point in the evaluation context and that declare either F |
| or a templated function of which F is a specialization; true if |
| -- there is a declaration D in S that introduces a name N for either P |
| or the parameter corresponding to P in the templated function that |
| D declares and |
| -- no declaration in S does so using any name other than N. |
| Otherwise, false. |
| -- Otherwise, if r represents a variable, then false if the declaration of |
| that variable was instantiated from a function parameter pack. |
| Otherwise, !has_template_arguments(r). |
| -- Otherwise, if r represents a structured binding, then false if the |
| declaration of that structured binding was instantiated from a |
| structured binding pack. Otherwise, true. |
| -- Otherwise, if r represents an enumerator, non-static-data member, |
| namespace, or namespace alias, then true. |
| -- Otherwise, if r represents a direct base class relationship, then |
| has_identifier(type_of(r)). |
| -- Otherwise, r represents a data member description (T,N,A,W,NUA); true if |
| N is not _|_. Otherwise, false. */ |
| |
| static tree |
| eval_has_identifier (tree r, reflect_kind kind) |
| { |
| r = maybe_get_first_fn (r); |
| if (kind == REFLECT_BASE) |
| { |
| r = type_of (r, kind); |
| kind = REFLECT_UNDEF; |
| } |
| if (DECL_P (r) |
| && kind != REFLECT_PARM |
| && (!DECL_NAME (r) || IDENTIFIER_ANON_P (DECL_NAME (r)))) |
| return boolean_false_node; |
| if (TYPE_P (r) && (!TYPE_NAME (r) |
| || (TYPE_ANON_P (r) && !typedef_variant_p (r)) |
| || (DECL_P (TYPE_NAME (r)) |
| && !DECL_NAME (TYPE_NAME (r))))) |
| return boolean_false_node; |
| if (eval_is_type_alias (r) == boolean_true_node |
| || (CLASS_TYPE_P (r) && !cv_qualified_p (r))) |
| { |
| if (eval_has_template_arguments (r) == boolean_true_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| if (TYPE_P (r)) |
| { |
| if (TREE_CODE (r) == ENUMERAL_TYPE && !cv_qualified_p (r)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| if (eval_is_function (r) == boolean_true_node) |
| { |
| if (eval_has_template_arguments (r) == boolean_true_node |
| || eval_is_constructor (r) == boolean_true_node |
| || eval_is_destructor (r) == boolean_true_node |
| || eval_is_operator_function (r) == boolean_true_node |
| || eval_is_conversion_function (r) == boolean_true_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| if (eval_is_template (r) == boolean_true_node) |
| { |
| if (eval_is_constructor_template (r) == boolean_true_node |
| || eval_is_operator_function_template (r) == boolean_true_node |
| || eval_is_conversion_function_template (r) == boolean_true_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| if (eval_is_function_parameter (r, kind) == boolean_true_node) |
| { |
| r = maybe_update_function_parm (r); |
| if (MULTIPLE_NAMES_PARM_P (r)) |
| return boolean_false_node; |
| if (DECL_NAME (r)) |
| { |
| if (strchr (IDENTIFIER_POINTER (DECL_NAME (r)), '#')) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| if (lookup_attribute ("old parm name", DECL_ATTRIBUTES (r))) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| if (eval_is_variable (r, kind) == boolean_true_node) |
| { |
| if (strchr (IDENTIFIER_POINTER (DECL_NAME (r)), '#')) |
| return boolean_false_node; |
| if (eval_has_template_arguments (r) == boolean_true_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| if (eval_is_structured_binding (r, kind) == boolean_true_node) |
| { |
| if (strchr (IDENTIFIER_POINTER (DECL_NAME (r)), '#')) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| if (eval_is_enumerator (r) == boolean_true_node |
| || TREE_CODE (r) == FIELD_DECL |
| || (TREE_CODE (r) == NAMESPACE_DECL && r != global_namespace)) |
| return boolean_true_node; |
| if (kind == REFLECT_DATA_MEMBER_SPEC && TREE_VEC_ELT (r, 1)) |
| return boolean_true_node; |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::{,u8}identifier_of. |
| Let E be UTF-8 for u8identifier_of, and otherwise the ordinary literal |
| encoding. |
| Returns: An NTMBS, encoded with E, determined as follows: |
| -- If r represents an entity with a typedef name for linkage purposes, |
| then that name. |
| -- Otherwise, if r represents a literal operator or literal operator |
| template, then the ud-suffix of the operator or operator template. |
| -- Otherwise, if r represents the parameter P of a function F, then let S |
| be the set of declarations, ignoring any explicit instantiations, that |
| precede some point in the evaluation context and that declare either F |
| or a templated function of which F is a specialization; the name that |
| was introduced by a declaration in S for the parameter corresponding |
| to P. |
| -- Otherwise, if r represents an entity, then the identifier introduced by |
| the declaration of that entity. |
| -- Otherwise, if r represents a direct base class relationship, then |
| identifier_of(type_of(r)) or u8identifier_of(type_of(r)), respectively. |
| -- Otherwise, r represents a data member description (T,N,A,W,NUA); |
| a string_view or u8string_view, respectively, containing the identifier |
| N. |
| Throws: meta::exception unless has_identifier(r) is true and the identifier |
| that would be returned (see above) is representable by E. */ |
| |
| static tree |
| eval_identifier_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, tree *jump_target, |
| tree elt_type, tree ret_type, tree fun) |
| { |
| if (eval_has_identifier (r, kind) == boolean_false_node) |
| return throw_exception (loc, ctx, |
| "reflection with has_identifier false", |
| fun, non_constant_p, jump_target); |
| r = maybe_get_first_fn (r); |
| const char *name = NULL; |
| if (kind == REFLECT_BASE) |
| { |
| r = type_of (r, kind); |
| kind = REFLECT_UNDEF; |
| } |
| if (eval_is_function_parameter (r, kind) == boolean_true_node) |
| { |
| r = maybe_update_function_parm (r); |
| if (DECL_NAME (r)) |
| name = IDENTIFIER_POINTER (DECL_NAME (r)); |
| else |
| { |
| tree opn = lookup_attribute ("old parm name", DECL_ATTRIBUTES (r)); |
| opn = TREE_VALUE (TREE_VALUE (opn)); |
| name = IDENTIFIER_POINTER (opn); |
| } |
| } |
| else if (DECL_P (r) && UDLIT_OPER_P (DECL_NAME (r))) |
| name = UDLIT_OP_SUFFIX (DECL_NAME (r)); |
| else if (DECL_P (r)) |
| name = IDENTIFIER_POINTER (DECL_NAME (r)); |
| else if (TYPE_P (r)) |
| { |
| if (DECL_P (TYPE_NAME (r))) |
| name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (r))); |
| else |
| name = IDENTIFIER_POINTER (TYPE_NAME (r)); |
| } |
| else if (kind == REFLECT_DATA_MEMBER_SPEC) |
| name = IDENTIFIER_POINTER (TREE_VEC_ELT (r, 1)); |
| else |
| gcc_unreachable (); |
| tree str = get_string_literal (name, elt_type); |
| if (str == NULL_TREE) |
| { |
| if (elt_type == char_type_node) |
| return throw_exception (loc, ctx, "identifier_of not representable" |
| " in ordinary literal encoding", |
| fun, non_constant_p, jump_target); |
| else |
| return throw_exception (loc, ctx, "u8identifier_of not representable" |
| " in UTF-8", |
| fun, non_constant_p, jump_target); |
| } |
| releasing_vec args (make_tree_vector_single (str)); |
| tree ret = build_special_member_call (NULL_TREE, complete_ctor_identifier, |
| &args, ret_type, LOOKUP_NORMAL, |
| tf_warning_or_error); |
| return build_cplus_new (ret_type, ret, tf_warning_or_error); |
| } |
| |
| /* Process std::meta::{,u8}display_string_of. |
| Returns: An implementation-defined string_view or u8string_view, |
| respectively. |
| Recommended practice: Where possible, implementations should return a |
| string suitable for identifying the represented construct. */ |
| |
| static tree |
| eval_display_string_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, bool *non_constant_p, |
| tree *jump_target, tree elt_type, tree ret_type, |
| tree fun) |
| { |
| #if __GNUC__ >= 10 |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wformat" |
| #pragma GCC diagnostic ignored "-Wformat-diag" |
| #endif |
| r = maybe_get_first_fn (r); |
| pretty_printer pp, *refpp = global_dc->get_reference_printer (); |
| pp_format_decoder (&pp) = pp_format_decoder (refpp); |
| pp.set_format_postprocessor (pp_format_postprocessor (refpp)->clone ()); |
| if (r == unknown_type_node) |
| pp_printf (&pp, "<null reflection>"); |
| else if (TYPE_P (r)) |
| pp_printf (&pp, "%T", r); |
| else if (kind == REFLECT_PARM) |
| { |
| r = maybe_update_function_parm (r); |
| tree fn = DECL_CONTEXT (r); |
| if (DECL_NAME (r)) |
| pp_printf (&pp, "<parameter %D of %D>", r, fn); |
| else |
| { |
| int idx = 1; |
| for (tree args = FUNCTION_FIRST_USER_PARM (fn); |
| r != args; args = DECL_CHAIN (args)) |
| ++idx; |
| pp_printf (&pp, "<unnamed parameter %d of %D>", idx, fn); |
| } |
| } |
| else if (kind == REFLECT_VALUE || kind == REFLECT_OBJECT) |
| pp_printf (&pp, "%E", r); |
| else if (DECL_P (r) && (DECL_NAME (r) || TREE_CODE (r) == NAMESPACE_DECL)) |
| pp_printf (&pp, "%D", r); |
| else if (TREE_CODE (r) == FIELD_DECL) |
| pp_printf (&pp, "%T::<unnamed bit-field>", DECL_CONTEXT (r)); |
| else if (kind == REFLECT_BASE) |
| { |
| tree d = direct_base_derived (r); |
| pp_printf (&pp, "%T: %T", d, BINFO_TYPE (r)); |
| } |
| else if (kind == REFLECT_DATA_MEMBER_SPEC) |
| pp_printf (&pp, "(%T, %E, %E, %E, %s)", TREE_VEC_ELT (r, 0), |
| TREE_VEC_ELT (r, 1), TREE_VEC_ELT (r, 2), TREE_VEC_ELT (r, 3), |
| TREE_VEC_ELT (r, 4) == boolean_true_node |
| ? "true" : "false"); |
| else if (eval_is_annotation (r, kind) == boolean_true_node) |
| pp_printf (&pp, "[[=%E]]", |
| tree_strip_any_location_wrapper (TREE_VALUE (TREE_VALUE (r)))); |
| else |
| pp_string (&pp, "<unsupported reflection>"); |
| #if __GNUC__ >= 10 |
| #pragma GCC diagnostic pop |
| #endif |
| tree str = get_string_literal (pp_formatted_text (&pp), elt_type); |
| if (str == NULL_TREE) |
| { |
| if (elt_type == char_type_node) |
| return throw_exception (loc, ctx, "identifier_of not representable" |
| " in ordinary literal encoding", |
| fun, non_constant_p, jump_target); |
| else |
| return throw_exception (loc, ctx, "u8identifier_of not representable" |
| " in UTF-8", fun, non_constant_p, |
| jump_target); |
| } |
| releasing_vec args (make_tree_vector_single (str)); |
| tree ret = build_special_member_call (NULL_TREE, complete_ctor_identifier, |
| &args, ret_type, LOOKUP_NORMAL, |
| tf_warning_or_error); |
| return build_cplus_new (ret_type, ret, tf_warning_or_error); |
| } |
| |
| /* Determine the reflection kind for R. */ |
| |
| static reflect_kind |
| get_reflection_kind (tree r) |
| { |
| if (eval_is_type (r) == boolean_true_node |
| || eval_is_template (r) == boolean_true_node |
| || eval_is_function (r) == boolean_true_node) |
| return REFLECT_UNDEF; |
| return obvalue_p (r) ? REFLECT_OBJECT : REFLECT_VALUE; |
| } |
| |
| /* Get the reflection of template argument ARG as per |
| std::meta::template_arguments_of. */ |
| |
| static tree |
| get_reflection_of_targ (tree arg) |
| { |
| const location_t loc = location_of (arg); |
| /* canonicalize_type_argument already strip_typedefs. */ |
| arg = STRIP_REFERENCE_REF (arg); |
| arg = maybe_get_reference_referent (arg); |
| return get_reflection_raw (loc, arg, get_reflection_kind (arg)); |
| } |
| |
| /* Process std::meta::template_arguments_of. |
| Returns: A vector containing reflections of the template arguments of the |
| template specialization represented by r, in the order in which they appear |
| in the corresponding template argument list. |
| For a given template argument A, its corresponding reflection R is |
| determined as follows: |
| |
| -- If A denotes a type or type alias, then R is a reflection representing |
| the underlying entity of A. |
| -- Otherwise, if A denotes a class template, variable template, concept, |
| or alias template, then R is a reflection representing A. |
| -- Otherwise, A is a constant template argument. Let P be the |
| corresponding template parameter. |
| -- If P has reference type, then R is a reflection representing the |
| object or function referred to by A. |
| -- Otherwise, if P has class type, then R represents the corresponding |
| template parameter object. |
| -- Otherwise, R is a reflection representing the value of A. |
| |
| Throws: meta::exception unless has_template_arguments(r) is true. */ |
| |
| static tree |
| eval_template_arguments_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| if (eval_has_template_arguments (r) != boolean_true_node) |
| return throw_exception_notargs (loc, ctx, fun, non_constant_p, jump_target); |
| |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| tree args = NULL_TREE; |
| if (TYPE_P (r) && typedef_variant_p (r)) |
| { |
| if (tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (r)) |
| args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (tinfo)); |
| } |
| else |
| args = get_template_innermost_arguments (r); |
| gcc_assert (args); |
| for (tree arg : tree_vec_range (args)) |
| { |
| if (ARGUMENT_PACK_P (arg)) |
| { |
| tree pargs = ARGUMENT_PACK_ARGS (arg); |
| for (tree a : tree_vec_range (pargs)) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_of_targ (a)); |
| } |
| else |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, get_reflection_of_targ (arg)); |
| } |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Helper for eval_remove_const to build non-const type. */ |
| |
| static tree |
| remove_const (tree type) |
| { |
| return cp_build_qualified_type (type, |
| cp_type_quals (type) & ~TYPE_QUAL_CONST); |
| } |
| |
| /* Process std::meta::annotations_of and annotations_of_with_type. |
| Let E be |
| -- the corresponding base-specifier if item represents a direct base class |
| relationship, |
| -- otherwise, the entity represented by item. |
| Returns: A vector containing all of the reflections R representing each |
| annotation applying to each declaration of E that precedes either some |
| point in the evaluation context or a point immediately following the |
| class-specifier of the outermost class for which such a point is in a |
| complete-class context. |
| For any two reflections R1 and R2 in the returned vector, if the annotation |
| represented by R1 precedes the annotation represented by R2, then R1 |
| appears before R2. |
| If R1 and R2 represent annotations from the same translation unit T, any |
| element in the returned vector between R1 and R2 represents an annotation |
| from T. |
| |
| Throws: meta::exception unless item represents a type, type alias, |
| variable, function, namespace, enumerator, direct base class relationship, |
| or non-static data member. */ |
| |
| static tree |
| eval_annotations_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, tree type, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| if (!(eval_is_type (r) == boolean_true_node |
| || eval_is_type_alias (r) == boolean_true_node |
| || eval_is_variable (r, kind) == boolean_true_node |
| || eval_is_function (r) == boolean_true_node |
| || eval_is_namespace (r) == boolean_true_node |
| || eval_is_enumerator (r) == boolean_true_node |
| || eval_is_base (r, kind) == boolean_true_node |
| || eval_is_nonstatic_data_member (r) == boolean_true_node)) |
| return throw_exception (loc, ctx, |
| "reflection does not represent a type," |
| " type alias, variable, function, namespace," |
| " enumerator, direct base class relationship," |
| " or non-static data member", |
| fun, non_constant_p, jump_target); |
| |
| if (type) |
| { |
| type = maybe_strip_typedefs (type); |
| if (!TYPE_P (type) |
| || !complete_type_or_maybe_complain (type, NULL_TREE, tf_none)) |
| return throw_exception (loc, ctx, |
| "reflection does not represent a complete" |
| " type or type alias", fun, non_constant_p, |
| jump_target); |
| type = remove_const (type); |
| } |
| |
| if (kind == REFLECT_BASE) |
| { |
| gcc_assert (TREE_CODE (r) == TREE_BINFO); |
| tree c = direct_base_derived_binfo (r), binfo = r, base_binfo; |
| |
| r = NULL_TREE; |
| for (unsigned ix = 0; BINFO_BASE_ITERATE (c, ix, base_binfo); ix++) |
| if (base_binfo == binfo) |
| { |
| if (ix + BINFO_BASE_BINFOS (c)->length () |
| < vec_safe_length (BINFO_BASE_ACCESSES (c))) |
| r = BINFO_BASE_ACCESS (c, ix + BINFO_BASE_BINFOS (c)->length ()); |
| break; |
| } |
| } |
| else if (TYPE_P (r)) |
| r = TYPE_ATTRIBUTES (r); |
| else if (DECL_P (r)) |
| r = DECL_ATTRIBUTES (r); |
| else |
| gcc_unreachable (); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| for (tree a = r; (a = lookup_attribute ("internal ", "annotation ", a)); |
| a = TREE_CHAIN (a)) |
| { |
| gcc_checking_assert (TREE_CODE (TREE_VALUE (a)) == TREE_LIST); |
| tree val = TREE_VALUE (TREE_VALUE (a)); |
| if (type) |
| { |
| tree at = TREE_TYPE (val); |
| if (at != type && !same_type_p (remove_const (at), type)) |
| continue; |
| } |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, a, REFLECT_ANNOTATION)); |
| } |
| if (elts) |
| { |
| /* Reverse the order. */ |
| unsigned l = elts->length (); |
| constructor_elt *ptr = elts->address (); |
| |
| for (unsigned i = 0; i < l / 2; i++) |
| std::swap (ptr[i], ptr[l - i - 1]); |
| } |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Process std::meta::reflect_constant. |
| Mandates: is_copy_constructible_v<T> is true and T is a cv-unqualified |
| structural type that is not a reference type. |
| Let V be: |
| -- if T is a class type, then an object that is template-argument-equivalent |
| to the value of expr; |
| -- otherwise, the value of expr. |
| Returns: template_arguments_of(^^TCls<V>)[0], with TCls as defined below. |
| Throws: meta::exception unless the template-id TCls<V> would be valid given |
| the invented template |
| template<T P> struct TCls; */ |
| |
| static tree |
| eval_reflect_constant (location_t loc, const constexpr_ctx *ctx, tree type, |
| tree expr, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (!structural_type_p (type) |
| || CP_TYPE_VOLATILE_P (type) |
| || CP_TYPE_CONST_P (type) |
| || TYPE_REF_P (type)) |
| { |
| error_at (loc, "%qT must be a cv-unqualified structural type that is " |
| "not a reference type", type); |
| return error_mark_node; |
| } |
| expr = convert_reflect_constant_arg (type, convert_from_reference (expr)); |
| if (expr == error_mark_node) |
| return throw_exception (loc, ctx, "reflect_constant failed", fun, |
| non_constant_p, jump_target); |
| return get_reflection_raw (loc, expr, get_reflection_kind (expr)); |
| } |
| |
| /* Process std::meta::reflect_object. |
| Mandates: T is an object type. |
| Returns: A reflection of the object designated by expr. |
| Throws: meta::exception unless expr is suitable for use as a constant |
| template argument for a constant template parameter of type T&. */ |
| |
| static tree |
| eval_reflect_object (location_t loc, const constexpr_ctx *ctx, tree type, |
| tree expr, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (eval_is_object_type (loc, type) != boolean_true_node) |
| { |
| error_at (loc, "%qT must be an object type", TREE_TYPE (type)); |
| return error_mark_node; |
| } |
| type = cp_build_reference_type (type, /*rval=*/false); |
| tree e = convert_reflect_constant_arg (type, convert_from_reference (expr)); |
| if (e == error_mark_node) |
| return throw_exception (loc, ctx, "reflect_object failed", fun, |
| non_constant_p, jump_target); |
| /* We got (const T &) &foo. Get the referent, since we want the object |
| designated by EXPR. */ |
| expr = maybe_get_reference_referent (expr); |
| return get_reflection_raw (loc, expr, REFLECT_OBJECT); |
| } |
| |
| /* Process std::meta::reflect_function. |
| Mandates: T is a function type. |
| Returns: A reflection of the function designated by fn. |
| Throws: meta::exception unless fn is suitable for use as a constant |
| template argument for a constant template parameter of type T&. */ |
| |
| static tree |
| eval_reflect_function (location_t loc, const constexpr_ctx *ctx, tree type, |
| tree expr, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (eval_is_function_type (type) != boolean_true_node) |
| { |
| error_at (loc, "%qT must be a function type", TREE_TYPE (type)); |
| return error_mark_node; |
| } |
| type = cp_build_reference_type (type, /*rval=*/false); |
| tree e = convert_reflect_constant_arg (type, convert_from_reference (expr)); |
| if (e == error_mark_node) |
| return throw_exception (loc, ctx, "reflect_function failed", fun, |
| non_constant_p, jump_target); |
| /* We got (void (&<Ta885>) (void)) fn. Get the function. */ |
| expr = maybe_get_reference_referent (expr); |
| return get_reflection_raw (loc, expr); |
| } |
| |
| /* Reflection type traits [meta.reflection.traits]. |
| |
| Every function and function template declared in this subclause throws |
| an exception of type meta::exception unless the following conditions are |
| met: |
| -- For every parameter p of type info, is_type(p) is true. |
| -- For every parameter r whose type is constrained on reflection_range, |
| ranges::all_of(r, is_type) is true. */ |
| |
| /* Evaluate reflection type traits for which we have corresponding built-in |
| traits. KIND says which trait we are interested in; TYPE1 and TYPE2 are |
| arguments to the trait. */ |
| |
| static tree |
| eval_type_trait (location_t loc, tree type1, tree type2, cp_trait_kind kind) |
| { |
| tree r = finish_trait_expr (loc, kind, type1, type2); |
| STRIP_ANY_LOCATION_WRAPPER (r); |
| return r; |
| } |
| |
| /* Like above, but for type traits that take only one type. */ |
| |
| static tree |
| eval_type_trait (location_t loc, tree type, cp_trait_kind kind) |
| { |
| return eval_type_trait (loc, type, NULL_TREE, kind); |
| } |
| |
| /* Process std::meta::is_function_type. */ |
| |
| static tree |
| eval_is_function_type (tree type) |
| { |
| if (FUNC_OR_METHOD_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_void_type. */ |
| |
| static tree |
| eval_is_void_type (tree type) |
| { |
| if (VOID_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_null_pointer_type. */ |
| |
| static tree |
| eval_is_null_pointer_type (tree type) |
| { |
| if (NULLPTR_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_integral_type. */ |
| |
| static tree |
| eval_is_integral_type (tree type) |
| { |
| if (CP_INTEGRAL_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_floating_point_type. */ |
| |
| static tree |
| eval_is_floating_point_type (tree type) |
| { |
| if (FLOAT_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_array_type. */ |
| |
| static tree |
| eval_is_array_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_ARRAY); |
| } |
| |
| /* Process std::meta::is_pointer_type. */ |
| |
| static tree |
| eval_is_pointer_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_POINTER); |
| } |
| |
| /* Process std::meta::is_lvalue_reference_type. */ |
| |
| static tree |
| eval_is_lvalue_reference_type (tree type) |
| { |
| if (TYPE_REF_P (type) && !TYPE_REF_IS_RVALUE (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_rvalue_reference_type. */ |
| |
| static tree |
| eval_is_rvalue_reference_type (tree type) |
| { |
| if (TYPE_REF_P (type) && TYPE_REF_IS_RVALUE (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_member_object_pointer_type. */ |
| |
| static tree |
| eval_is_member_object_pointer_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_MEMBER_OBJECT_POINTER); |
| } |
| |
| /* Process std::meta::is_member_function_pointer_type. */ |
| |
| static tree |
| eval_is_member_function_pointer_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_MEMBER_FUNCTION_POINTER); |
| } |
| |
| /* Process std::meta::is_enum_type. */ |
| |
| static tree |
| eval_is_enum_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_ENUM); |
| } |
| |
| /* Process std::meta::is_union_type. */ |
| |
| static tree |
| eval_is_union_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_UNION); |
| } |
| |
| /* Process std::meta::is_class_type. */ |
| |
| static tree |
| eval_is_class_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_CLASS); |
| } |
| |
| /* Process std::meta::is_reflection_type. */ |
| |
| static tree |
| eval_is_reflection_type (tree type) |
| { |
| if (REFLECTION_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_reference_type. */ |
| |
| static tree |
| eval_is_reference_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_REFERENCE); |
| } |
| |
| /* Process std::meta::is_arithmetic_type. */ |
| |
| static tree |
| eval_is_arithmetic_type (tree type) |
| { |
| if (ARITHMETIC_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_object_type. */ |
| |
| static tree |
| eval_is_object_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_OBJECT); |
| } |
| |
| /* Process std::meta::is_scalar_type. */ |
| |
| static tree |
| eval_is_scalar_type (tree type) |
| { |
| if (SCALAR_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_fundamental_type. */ |
| |
| static tree |
| eval_is_fundamental_type (tree type) |
| { |
| if (ARITHMETIC_TYPE_P (type) |
| || VOID_TYPE_P (type) |
| || NULLPTR_TYPE_P (type) |
| || REFLECTION_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_compound_type. */ |
| |
| static tree |
| eval_is_compound_type (tree type) |
| { |
| if (eval_is_fundamental_type (type) == boolean_false_node) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_member_pointer_type. */ |
| |
| static tree |
| eval_is_member_pointer_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_MEMBER_POINTER); |
| } |
| |
| /* Process std::meta::is_const_type. */ |
| |
| static tree |
| eval_is_const_type (tree type) |
| { |
| if (CP_TYPE_CONST_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_volatile_type. */ |
| |
| static tree |
| eval_is_volatile_type (tree type) |
| { |
| if (CP_TYPE_VOLATILE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_trivially_copyable_type. */ |
| |
| static tree |
| eval_is_trivially_copyable_type (tree type) |
| { |
| if (trivially_copyable_p (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_standard_layout_type. */ |
| |
| static tree |
| eval_is_standard_layout_type (tree type) |
| { |
| if (std_layout_type_p (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_empty_type. */ |
| |
| static tree |
| eval_is_empty_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_EMPTY); |
| } |
| |
| /* Process std::meta::is_polymorphic_type. */ |
| |
| static tree |
| eval_is_polymorphic_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_POLYMORPHIC); |
| } |
| |
| /* Process std::meta::is_abstract_type. */ |
| |
| static tree |
| eval_is_abstract_type (tree type) |
| { |
| if (ABSTRACT_CLASS_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_final_type. */ |
| |
| static tree |
| eval_is_final_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_FINAL); |
| } |
| |
| /* Process std::meta::is_final. |
| Returns: true if r represents a final class or a final member function. |
| Otherwise, false. */ |
| |
| static tree |
| eval_is_final (tree r) |
| { |
| if (eval_is_function (r) == boolean_true_node) |
| { |
| r = maybe_get_first_fn (r); |
| if (TREE_CODE (r) == FUNCTION_DECL && DECL_FINAL_P (r)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| if (eval_is_type (r) == boolean_true_node |
| && CLASS_TYPE_P (r) |
| && CLASSTYPE_FINAL (r)) |
| return boolean_true_node; |
| |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_aggregate_type. */ |
| |
| static tree |
| eval_is_aggregate_type (tree type) |
| { |
| if (CP_AGGREGATE_TYPE_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_consteval_only_type. */ |
| |
| static tree |
| eval_is_consteval_only_type (tree type) |
| { |
| if (consteval_only_p (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_signed_type. */ |
| |
| static tree |
| eval_is_signed_type (tree type) |
| { |
| if (ARITHMETIC_TYPE_P (type) && !TYPE_UNSIGNED (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_unsigned_type. */ |
| |
| static tree |
| eval_is_unsigned_type (tree type) |
| { |
| if (ARITHMETIC_TYPE_P (type) && TYPE_UNSIGNED (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_bounded_array_type. */ |
| |
| static tree |
| eval_is_bounded_array_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_BOUNDED_ARRAY); |
| } |
| |
| /* Process std::meta::is_unbounded_array_type. */ |
| |
| static tree |
| eval_is_unbounded_array_type (tree type) |
| { |
| if (array_of_unknown_bound_p (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_scoped_enum_type. */ |
| |
| static tree |
| eval_is_scoped_enum_type (tree type) |
| { |
| if (SCOPED_ENUM_P (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_constructible_type. */ |
| |
| static tree |
| eval_is_constructible_type (tree type, tree tvec) |
| { |
| if (is_xible (INIT_EXPR, type, tvec)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_default_constructible_type. */ |
| |
| static tree |
| eval_is_default_constructible_type (tree type) |
| { |
| if (is_xible (INIT_EXPR, type, make_tree_vec (0))) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_copy_constructible_type. */ |
| |
| static tree |
| eval_is_copy_constructible_type (tree type) |
| { |
| tree arg = make_tree_vec (1); |
| TREE_VEC_ELT (arg, 0) |
| = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false); |
| if (is_xible (INIT_EXPR, type, arg)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_move_constructible_type. */ |
| |
| static tree |
| eval_is_move_constructible_type (tree type) |
| { |
| tree arg = make_tree_vec (1); |
| TREE_VEC_ELT (arg, 0) = cp_build_reference_type (type, /*rval=*/true); |
| if (is_xible (INIT_EXPR, type, arg)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_assignable_type. */ |
| |
| static tree |
| eval_is_assignable_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_ASSIGNABLE); |
| } |
| |
| /* Process std::meta::is_copy_assignable_type. */ |
| |
| static tree |
| eval_is_copy_assignable_type (tree type) |
| { |
| tree type1 = cp_build_reference_type (type, /*rval=*/false); |
| tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, |
| false); |
| if (is_xible (MODIFY_EXPR, type1, type2)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_move_assignable_type. */ |
| |
| static tree |
| eval_is_move_assignable_type (tree type) |
| { |
| tree type1 = cp_build_reference_type (type, /*rval=*/false); |
| tree type2 = cp_build_reference_type (type, /*rval=*/true); |
| if (is_xible (MODIFY_EXPR, type1, type2)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_destructible_type. */ |
| |
| static tree |
| eval_is_destructible_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_DESTRUCTIBLE); |
| } |
| |
| /* Process std::meta::is_trivially_constructible_type. */ |
| |
| static tree |
| eval_is_trivially_constructible_type (tree type, tree tvec) |
| { |
| if (is_trivially_xible (INIT_EXPR, type, tvec)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_trivially_default_constructible_type. */ |
| |
| static tree |
| eval_is_trivially_default_constructible_type (tree type) |
| { |
| if (is_trivially_xible (INIT_EXPR, type, make_tree_vec (0))) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_trivially_copy_constructible_type. */ |
| |
| static tree |
| eval_is_trivially_copy_constructible_type (tree type) |
| { |
| tree arg = make_tree_vec (1); |
| TREE_VEC_ELT (arg, 0) |
| = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false); |
| if (is_trivially_xible (INIT_EXPR, type, arg)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_trivially_move_constructible_type. */ |
| |
| static tree |
| eval_is_trivially_move_constructible_type (tree type) |
| { |
| tree arg = make_tree_vec (1); |
| TREE_VEC_ELT (arg, 0) = cp_build_reference_type (type, /*rval=*/true); |
| if (is_trivially_xible (INIT_EXPR, type, arg)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_trivially_assignable_type. */ |
| |
| static tree |
| eval_is_trivially_assignable_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_TRIVIALLY_ASSIGNABLE); |
| } |
| |
| /* Process std::meta::is_trivially_copy_assignable_type. */ |
| |
| static tree |
| eval_is_trivially_copy_assignable_type (tree type) |
| { |
| tree type1 = cp_build_reference_type (type, /*rval=*/false); |
| tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, |
| false); |
| if (is_trivially_xible (MODIFY_EXPR, type1, type2)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_trivially_move_assignable_type. */ |
| |
| static tree |
| eval_is_trivially_move_assignable_type (tree type) |
| { |
| tree type1 = cp_build_reference_type (type, /*rval=*/false); |
| tree type2 = cp_build_reference_type (type, /*rval=*/true); |
| if (is_trivially_xible (MODIFY_EXPR, type1, type2)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_trivially_destructible_type. */ |
| |
| static tree |
| eval_is_trivially_destructible_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_TRIVIALLY_DESTRUCTIBLE); |
| } |
| |
| /* Process std::meta::is_nothrow_constructible_type. */ |
| |
| static tree |
| eval_is_nothrow_constructible_type (tree type, tree tvec) |
| { |
| if (is_nothrow_xible (INIT_EXPR, type, tvec)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_nothrow_default_constructible_type. */ |
| |
| static tree |
| eval_is_nothrow_default_constructible_type (tree type) |
| { |
| if (is_nothrow_xible (INIT_EXPR, type, make_tree_vec (0))) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_nothrow_copy_constructible_type. */ |
| |
| static tree |
| eval_is_nothrow_copy_constructible_type (tree type) |
| { |
| tree arg = make_tree_vec (1); |
| TREE_VEC_ELT (arg, 0) |
| = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false); |
| if (is_nothrow_xible (INIT_EXPR, type, arg)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_nothrow_move_constructible_type. */ |
| |
| static tree |
| eval_is_nothrow_move_constructible_type (tree type) |
| { |
| tree arg = make_tree_vec (1); |
| TREE_VEC_ELT (arg, 0) = cp_build_reference_type (type, /*rval=*/true); |
| if (is_nothrow_xible (INIT_EXPR, type, arg)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_nothrow_assignable_type. */ |
| |
| static tree |
| eval_is_nothrow_assignable_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_NOTHROW_ASSIGNABLE); |
| } |
| |
| /* Process std::meta::is_nothrow_copy_assignable_type. */ |
| |
| static tree |
| eval_is_nothrow_copy_assignable_type (tree type) |
| { |
| tree type1 = cp_build_reference_type (type, /*rval=*/false); |
| tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, |
| false); |
| if (is_nothrow_xible (MODIFY_EXPR, type1, type2)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_nothrow_move_assignable_type. */ |
| |
| static tree |
| eval_is_nothrow_move_assignable_type (tree type) |
| { |
| tree type1 = cp_build_reference_type (type, /*rval=*/false); |
| tree type2 = cp_build_reference_type (type, /*rval=*/true); |
| if (is_nothrow_xible (MODIFY_EXPR, type1, type2)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::is_nothrow_destructible_type. */ |
| |
| static tree |
| eval_is_nothrow_destructible_type (location_t loc, tree type) |
| { |
| return eval_type_trait (loc, type, CPTK_IS_NOTHROW_DESTRUCTIBLE); |
| } |
| |
| /* Process std::meta::is_implicit_lifetime_type. */ |
| |
| static tree |
| eval_is_implicit_lifetime_type (tree type) |
| { |
| if (implicit_lifetime_type_p (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::has_virtual_destructor. */ |
| |
| static tree |
| eval_has_virtual_destructor (tree type) |
| { |
| if (type_has_virtual_destructor (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::has_unique_object_representations. */ |
| |
| static tree |
| eval_has_unique_object_representations (tree type) |
| { |
| if (type_has_unique_obj_representations (type)) |
| return boolean_true_node; |
| else |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::reference_constructs_from_temporary. */ |
| |
| static tree |
| eval_reference_constructs_from_temporary (location_t loc, tree type1, |
| tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, |
| CPTK_REF_CONSTRUCTS_FROM_TEMPORARY); |
| } |
| |
| /* Process std::meta::reference_converts_from_temporary. */ |
| |
| static tree |
| eval_reference_converts_from_temporary (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_REF_CONVERTS_FROM_TEMPORARY); |
| } |
| |
| /* Process std::meta::rank. */ |
| |
| static tree |
| eval_rank (tree type) |
| { |
| size_t rank = 0; |
| for (; TREE_CODE (type) == ARRAY_TYPE; type = TREE_TYPE (type)) |
| ++rank; |
| return build_int_cst (size_type_node, rank); |
| } |
| |
| /* Process std::meta::extent. */ |
| |
| static tree |
| eval_extent (location_t loc, tree type, tree i) |
| { |
| size_t rank = tree_to_uhwi (i); |
| while (rank && TREE_CODE (type) == ARRAY_TYPE) |
| { |
| --rank; |
| type = TREE_TYPE (type); |
| } |
| if (rank |
| || TREE_CODE (type) != ARRAY_TYPE |
| || eval_is_bounded_array_type (loc, type) == boolean_false_node) |
| return size_zero_node; |
| return size_binop (PLUS_EXPR, TYPE_MAX_VALUE (TYPE_DOMAIN (type)), |
| size_one_node); |
| } |
| |
| /* Process std::meta::is_same_type. */ |
| |
| static tree |
| eval_is_same_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_SAME); |
| } |
| |
| /* Process std::meta::is_base_of_type. */ |
| |
| static tree |
| eval_is_base_of_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_BASE_OF); |
| } |
| |
| /* Process std::meta::is_virtual_base_of_type. */ |
| |
| static tree |
| eval_is_virtual_base_of_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_VIRTUAL_BASE_OF); |
| } |
| |
| /* Process std::meta::is_convertible_type. */ |
| |
| static tree |
| eval_is_convertible_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_CONVERTIBLE); |
| } |
| |
| /* Process std::meta::is_nothrow_convertible_type. */ |
| |
| static tree |
| eval_is_nothrow_convertible_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_NOTHROW_CONVERTIBLE); |
| } |
| |
| /* Process std::meta::is_layout_compatible_type. */ |
| |
| static tree |
| eval_is_layout_compatible_type (location_t loc, tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, CPTK_IS_LAYOUT_COMPATIBLE); |
| } |
| |
| /* Process std::meta::is_pointer_interconvertible_base_of_type. */ |
| |
| static tree |
| eval_is_pointer_interconvertible_base_of_type (location_t loc, |
| tree type1, tree type2) |
| { |
| return eval_type_trait (loc, type1, type2, |
| CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF); |
| } |
| |
| /* Process std::meta::is_invocable_type. */ |
| |
| static tree |
| eval_is_invocable_type (location_t loc, tree type, tree tvec) |
| { |
| tree r = finish_trait_expr (loc, CPTK_IS_INVOCABLE, type, tvec); |
| STRIP_ANY_LOCATION_WRAPPER (r); |
| return r; |
| } |
| |
| /* Helper for various eval_* type trait functions which can't use builtin |
| trait and have to instantiate std::NAME<ARGS>::value. */ |
| |
| static tree |
| finish_library_value_trait (location_t loc, const constexpr_ctx *ctx, |
| const char *name, tree args, tree call, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| tree inst = lookup_template_class (get_identifier (name), args, |
| /*in_decl*/NULL_TREE, /*context*/std_node, |
| tf_warning_or_error); |
| inst = complete_type (inst); |
| if (inst == error_mark_node |
| || !COMPLETE_TYPE_P (inst) |
| || !CLASS_TYPE_P (inst)) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "couldn%'t instantiate %<std::%s<%T>%>", |
| name, args); |
| *non_constant_p = true; |
| return call; |
| } |
| tree val = lookup_qualified_name (inst, value_identifier, |
| LOOK_want::NORMAL, /*complain*/false); |
| if (val == error_mark_node) |
| return throw_exception (loc, ctx, "value member missing", |
| fun, non_constant_p, jump_target); |
| if (VAR_P (val) || TREE_CODE (val) == CONST_DECL) |
| val = maybe_constant_value (val, NULL_TREE, mce_true); |
| if (TREE_CODE (TREE_TYPE (call)) == BOOLEAN_TYPE) |
| { |
| if (integer_zerop (val)) |
| return boolean_false_node; |
| else if (integer_nonzerop (val)) |
| return boolean_true_node; |
| else |
| return throw_exception (loc, ctx, "unexpected value of value member", |
| fun, non_constant_p, jump_target); |
| } |
| else if (TREE_CODE (val) == INTEGER_CST) |
| { |
| val = build_converted_constant_expr (TREE_TYPE (call), val, tf_none); |
| if (TREE_CODE (val) == INTEGER_CST) |
| return val; |
| } |
| return throw_exception (loc, ctx, "unexpected value of value member", |
| fun, non_constant_p, jump_target); |
| } |
| |
| /* Process std::meta::is_{,nothrow_}invocable_r_type. */ |
| |
| static tree |
| eval_is_invocable_r_type (location_t loc, const constexpr_ctx *ctx, |
| tree tres, tree type, tree tvec, tree call, |
| bool *non_constant_p, tree *jump_target, tree fun, |
| const char *name) |
| { |
| /* Create std::is_invocable_r<TYPE>::value. */ |
| tree args = make_tree_vec (TREE_VEC_LENGTH (tvec) + 2); |
| TREE_VEC_ELT (args, 0) = tres; |
| TREE_VEC_ELT (args, 1) = type; |
| for (int i = 0; i < TREE_VEC_LENGTH (tvec); ++i) |
| TREE_VEC_ELT (args, i + 2) = TREE_VEC_ELT (tvec, i); |
| return finish_library_value_trait (loc, ctx, name, args, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::is_nothrow_invocable_type. */ |
| |
| static tree |
| eval_is_nothrow_invocable_type (location_t loc, tree type, tree tvec) |
| { |
| tree r = finish_trait_expr (loc, CPTK_IS_NOTHROW_INVOCABLE, type, tvec); |
| STRIP_ANY_LOCATION_WRAPPER (r); |
| return r; |
| } |
| |
| /* Process std::meta::is_{,nothrow_}swappable_with_type. */ |
| |
| static tree |
| eval_is_swappable_with_type (location_t loc, const constexpr_ctx *ctx, |
| tree type1, tree type2, tree call, |
| bool *non_constant_p, tree *jump_target, tree fun, |
| const char *name) |
| { |
| /* Create std::is_swappable_with<TYPE>::value. */ |
| tree args = make_tree_vec (2); |
| TREE_VEC_ELT (args, 0) = type1; |
| TREE_VEC_ELT (args, 1) = type2; |
| return finish_library_value_trait (loc, ctx, name, args, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::is_{,nothrow_}swappable_type. */ |
| |
| static tree |
| eval_is_swappable_type (location_t loc, const constexpr_ctx *ctx, |
| tree type, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun, const char *name) |
| { |
| /* Create std::is_swappable<TYPE>::value. */ |
| tree args = make_tree_vec (1); |
| TREE_VEC_ELT (args, 0) = type; |
| return finish_library_value_trait (loc, ctx, name, args, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::remove_cvref. */ |
| |
| static tree |
| eval_remove_cvref (location_t loc, tree type) |
| { |
| if (TYPE_REF_P (type)) |
| type = TREE_TYPE (type); |
| type = finish_trait_type (CPTK_REMOVE_CV, type, NULL_TREE, tf_none); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::decay. */ |
| |
| static tree |
| eval_decay (location_t loc, tree type) |
| { |
| type = finish_trait_type (CPTK_DECAY, type, NULL_TREE, tf_none); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Helper for various eval_* type trait functions which can't use builtin |
| trait and have to instantiate std::NAME<ARGS>::type. */ |
| |
| static tree |
| finish_library_type_trait (location_t loc, const constexpr_ctx *ctx, |
| const char *name, tree args, tree call, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| tree inst = lookup_template_class (get_identifier (name), args, |
| /*in_decl*/NULL_TREE, |
| /*context*/std_node, |
| tf_warning_or_error); |
| if (inst == error_mark_node) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "couldn%'t instantiate %<std::%s<%T>%>", |
| name, args); |
| *non_constant_p = true; |
| return call; |
| } |
| tree type = make_typename_type (inst, type_identifier, |
| none_type, tf_none); |
| if (type == error_mark_node) |
| return throw_exception (loc, ctx, "type member missing", |
| fun, non_constant_p, jump_target); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::common_{type,reference}. */ |
| |
| static tree |
| eval_common_type (location_t loc, const constexpr_ctx *ctx, tree tvec, |
| tree call, bool *non_constant_p, tree *jump_target, tree fun, |
| const char *name) |
| { |
| return finish_library_type_trait (loc, ctx, name, tvec, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::underlying_type. */ |
| |
| static tree |
| eval_underlying_type (location_t loc, const constexpr_ctx *ctx, tree type, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| if (TREE_CODE (type) != ENUMERAL_TYPE || !COMPLETE_TYPE_P (type)) |
| return throw_exception (loc, ctx, "reflection does not represent " |
| "a complete enumeration type", |
| fun, non_constant_p, jump_target); |
| type = finish_underlying_type (type); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::invoke_result. */ |
| |
| static tree |
| eval_invoke_result (location_t loc, const constexpr_ctx *ctx, tree type, |
| tree tvec, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| tree args = make_tree_vec (TREE_VEC_LENGTH (tvec) + 1); |
| TREE_VEC_ELT (args, 0) = type; |
| for (int i = 0; i < TREE_VEC_LENGTH (tvec); ++i) |
| TREE_VEC_ELT (args, i + 1) = TREE_VEC_ELT (tvec, i); |
| return finish_library_type_trait (loc, ctx, "invoke_result", args, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::unwrap_{reference,ref_decay}. */ |
| |
| static tree |
| eval_unwrap_reference (location_t loc, const constexpr_ctx *ctx, tree type, |
| tree call, bool *non_constant_p, tree *jump_target, |
| tree fun, const char *name) |
| { |
| tree args = make_tree_vec (1); |
| TREE_VEC_ELT (args, 0) = type; |
| return finish_library_type_trait (loc, ctx, name, args, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::type_order. */ |
| |
| static tree |
| eval_type_order (tree type1, tree type2) |
| { |
| return type_order_value (strip_typedefs (type1), strip_typedefs (type2)); |
| } |
| |
| /* Process std::meta::enumerators_of. |
| Returns: A vector containing the reflections of each enumerator of the |
| enumeration represented by dealias(type_enum), in the order in which they |
| are declared. |
| Throws: meta::exception unless dealias(type_enum) represents an enumeration |
| type, and is_enumerable_type(type_enum) is true. */ |
| |
| static tree |
| eval_enumerators_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| if (TREE_CODE (r) != ENUMERAL_TYPE |
| || eval_is_enumerable_type (r) == boolean_false_node) |
| return throw_exception (loc, ctx, "reflection does not represent an " |
| "enumerable enumeration type", fun, |
| non_constant_p, jump_target); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| for (tree t = TYPE_VALUES (r); t; t = TREE_CHAIN (t)) |
| { |
| tree e = TREE_VALUE (t); |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, get_reflection_raw (loc, e)); |
| } |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Process std::meta::remove_const. |
| Returns: a reflection representing the type denoted by |
| std::remove_const_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_remove_const (location_t loc, tree type) |
| { |
| return get_reflection_raw (loc, remove_const (strip_typedefs (type))); |
| } |
| |
| /* Process std::meta::remove_volatile. |
| Returns: a reflection representing the type denoted by |
| std::remove_volatile_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_remove_volatile (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| int quals = cp_type_quals (type); |
| quals &= ~TYPE_QUAL_VOLATILE; |
| type = cp_build_qualified_type (type, quals); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::remove_cv. |
| Returns: a reflection representing the type denoted by |
| std::remove_cv_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_remove_cv (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| type = finish_trait_type (CPTK_REMOVE_CV, type, NULL_TREE, tf_none); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::add_const. |
| Returns: a reflection representing the type denoted by |
| std::add_const_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_add_const (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| if (!TYPE_REF_P (type) && !FUNC_OR_METHOD_TYPE_P (type)) |
| { |
| int quals = cp_type_quals (type); |
| quals |= TYPE_QUAL_CONST; |
| type = cp_build_qualified_type (type, quals); |
| } |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::add_volatile. |
| Returns: a reflection representing the type denoted by |
| std::add_volatile_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_add_volatile (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| if (!TYPE_REF_P (type) && !FUNC_OR_METHOD_TYPE_P (type)) |
| { |
| int quals = cp_type_quals (type); |
| quals |= TYPE_QUAL_VOLATILE; |
| type = cp_build_qualified_type (type, quals); |
| } |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::add_cv. |
| Returns: a reflection representing the type denoted by |
| std::add_cv_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_add_cv (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| if (!TYPE_REF_P (type) && !FUNC_OR_METHOD_TYPE_P (type)) |
| { |
| int quals = cp_type_quals (type); |
| quals |= (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE); |
| type = cp_build_qualified_type (type, quals); |
| } |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::remove_reference. |
| Returns: a reflection representing the type denoted by |
| std::remove_reference_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_remove_reference (location_t loc, tree type) |
| { |
| if (TYPE_REF_P (type)) |
| type = TREE_TYPE (type); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::add_lvalue_reference. |
| Returns: a reflection representing the type denoted by |
| std::add_lvalue_reference_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_add_lvalue_reference (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| type = finish_trait_type (CPTK_ADD_LVALUE_REFERENCE, type, NULL_TREE, tf_none); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::add_rvalue_reference. |
| Returns: a reflection representing the type denoted by |
| std::add_rvalue_reference_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_add_rvalue_reference (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| type = finish_trait_type (CPTK_ADD_RVALUE_REFERENCE, type, NULL_TREE, tf_none); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::make_signed and std::meta::make_unsigned. |
| Returns: a reflection representing the type denoted by |
| std::make_signed_t<T> or std::make_unsigned_t<T>, respectively, where T is |
| the type or type alias represented by type. */ |
| |
| static tree |
| eval_make_signed (location_t loc, const constexpr_ctx *ctx, tree type, |
| bool unsignedp, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (!INTEGRAL_TYPE_P (type) || TREE_CODE (type) == BOOLEAN_TYPE) |
| return throw_exception (loc, ctx, "reflection represents non-integral " |
| "or bool type", fun, non_constant_p, |
| jump_target); |
| tree ret = type; |
| if (TREE_CODE (type) == ENUMERAL_TYPE |
| || TYPE_MAIN_VARIANT (type) == wchar_type_node |
| || TYPE_MAIN_VARIANT (type) == char8_type_node |
| || TYPE_MAIN_VARIANT (type) == char16_type_node |
| || TYPE_MAIN_VARIANT (type) == char32_type_node) |
| { |
| tree unit = TYPE_SIZE_UNIT (type); |
| tree types[] = { |
| signed_char_type_node, |
| short_integer_type_node, |
| integer_type_node, |
| long_integer_type_node, |
| long_long_integer_type_node }; |
| ret = NULL_TREE; |
| for (unsigned i = 0; i < ARRAY_SIZE (types); ++i) |
| if (tree_int_cst_equal (TYPE_SIZE_UNIT (types[i]), unit)) |
| { |
| ret = c_common_signed_or_unsigned_type (unsignedp, types[i]); |
| break; |
| } |
| if (!ret) |
| ret = c_common_type_for_size (TYPE_PRECISION (type), unsignedp); |
| } |
| else if (TYPE_MAIN_VARIANT (type) == char_type_node) |
| ret = unsignedp ? unsigned_char_type_node : signed_char_type_node; |
| else if (unsignedp ^ (!!TYPE_UNSIGNED (type))) |
| ret = c_common_signed_or_unsigned_type (unsignedp, type); |
| if (ret != type) |
| { |
| int quals = cp_type_quals (type); |
| quals &= (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE); |
| ret = cp_build_qualified_type (ret, quals); |
| } |
| else |
| ret = strip_typedefs (type); |
| return get_reflection_raw (loc, ret); |
| } |
| |
| /* Process std::meta::remove_extent. |
| Returns: a reflection representing the type denoted by |
| std::remove_extent_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_remove_extent (location_t loc, tree type) |
| { |
| if (TREE_CODE (type) == ARRAY_TYPE) |
| type = TREE_TYPE (type); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::remove_all_extents. |
| Returns: a reflection representing the type denoted by |
| std::remove_all_extents_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_remove_all_extents (location_t loc, tree type) |
| { |
| type = strip_array_types (type); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::remove_pointer. |
| Returns: a reflection representing the type denoted by |
| std::remove_pointer_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_remove_pointer (location_t loc, tree type) |
| { |
| if (TYPE_PTR_P (type)) |
| type = TREE_TYPE (type); |
| type = strip_typedefs (type); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::add_pointer. |
| Returns: a reflection representing the type denoted by |
| std::add_pointer_t<T>, where T is the type or type alias |
| represented by type. */ |
| |
| static tree |
| eval_add_pointer (location_t loc, tree type) |
| { |
| type = strip_typedefs (type); |
| type = finish_trait_type (CPTK_ADD_POINTER, type, NULL_TREE, tf_none); |
| return get_reflection_raw (loc, type); |
| } |
| |
| /* Process std::meta::is_lvalue_reference_qualified and |
| std::meta::is_rvalue_reference_qualified. |
| Let T be type_of(r) if has-type(r) is true. Otherwise, let T be |
| dealias(r). |
| Returns: true if T represents an lvalue- or rvalue-qualified |
| function type, respectively. Otherwise, false. |
| RVALUE_P is true if we're processing is_rvalue_*, false if we're |
| processing is_lvalue_*. */ |
| |
| static tree |
| eval_is_lrvalue_reference_qualified (tree r, reflect_kind kind, |
| bool rvalue_p) |
| { |
| if (has_type (r, kind)) |
| r = type_of (r, kind); |
| else |
| r = maybe_strip_typedefs (r); |
| if (FUNC_OR_METHOD_TYPE_P (r) |
| && FUNCTION_REF_QUALIFIED (r) |
| && rvalue_p == FUNCTION_RVALUE_QUALIFIED (r)) |
| return boolean_true_node; |
| |
| return boolean_false_node; |
| } |
| |
| /* Process std::meta::can_substitute. |
| Let Z be the template represented by templ and let Args... be a sequence of |
| prvalue constant expressions that compute the reflections held by the |
| elements of arguments, in order. |
| Returns: true if Z<[:Args:]...> is a valid template-id that does not name |
| a function whose type contains an undeduced placeholder type. |
| Otherwise, false. |
| Throws: meta::exception unless templ represents a template, and every |
| reflection in arguments represents a construct usable as a template |
| argument. */ |
| |
| static tree |
| eval_can_substitute (location_t loc, const constexpr_ctx *ctx, |
| tree r, tree rvec, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| if (eval_is_template (r) != boolean_true_node) |
| return throw_exception (loc, ctx, |
| "reflection does not represent a template", |
| fun, non_constant_p, jump_target); |
| for (int i = 0; i < TREE_VEC_LENGTH (rvec); ++i) |
| { |
| tree ra = TREE_VEC_ELT (rvec, i); |
| tree a = REFLECT_EXPR_HANDLE (ra); |
| reflect_kind kind = REFLECT_EXPR_KIND (ra); |
| // TODO: It is unclear on what kinds of reflections we should throw |
| // and what kinds of exceptions should merely result in can_substitute |
| // returning false. |
| if (a == unknown_type_node |
| || kind == REFLECT_PARM |
| || eval_is_namespace (a) == boolean_true_node |
| || eval_is_constructor (a) == boolean_true_node |
| || eval_is_destructor (a) == boolean_true_node |
| || eval_is_annotation (a, kind) == boolean_true_node |
| || (TREE_CODE (a) == FIELD_DECL && !DECL_UNNAMED_BIT_FIELD (a)) |
| || kind == REFLECT_DATA_MEMBER_SPEC |
| || kind == REFLECT_BASE |
| || (!TYPE_P (a) |
| && eval_is_template (a) == boolean_false_node |
| && !has_type (a, kind))) |
| return throw_exception (loc, ctx, |
| "invalid argument to can_substitute", |
| fun, non_constant_p, jump_target); |
| a = resolve_nondeduced_context (a, tf_warning_or_error); |
| TREE_VEC_ELT (rvec, i) = a; |
| } |
| if (DECL_TYPE_TEMPLATE_P (r) || DECL_TEMPLATE_TEMPLATE_PARM_P (r)) |
| { |
| tree type = lookup_template_class (r, rvec, NULL_TREE, NULL_TREE, |
| tf_none); |
| if (type == error_mark_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| else if (concept_definition_p (r)) |
| { |
| tree c = build_concept_check (r, rvec, tf_none); |
| if (c == error_mark_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| else if (variable_template_p (r)) |
| { |
| tree var = lookup_template_variable (r, rvec, tf_none); |
| if (var == error_mark_node) |
| return boolean_false_node; |
| var = finish_template_variable (var, tf_none); |
| if (var == error_mark_node) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| else |
| { |
| tree fn = lookup_template_function (r, rvec); |
| if (fn == error_mark_node) |
| return boolean_false_node; |
| fn = resolve_nondeduced_context_or_error (fn, tf_none); |
| if (fn == error_mark_node) |
| return boolean_false_node; |
| return boolean_true_node; |
| } |
| } |
| |
| /* Process std::meta::substitute. |
| Let Z be the template represented by templ and let Args... be a sequence of |
| prvalue constant expressions that compute the reflections held by the |
| elements of arguments, in order. |
| Returns: ^^Z<[:Args:]...>. |
| Throws: meta::exception unless can_substitute(templ, arguments) is true. */ |
| |
| static tree |
| eval_substitute (location_t loc, const constexpr_ctx *ctx, |
| tree r, tree rvec, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| tree cs = eval_can_substitute (loc, ctx, r, rvec, non_constant_p, jump_target, |
| fun); |
| if (*jump_target) |
| return cs; |
| if (cs == boolean_false_node) |
| return throw_exception (loc, ctx, "can_substitute returned false", |
| fun, non_constant_p, jump_target); |
| tree ret = NULL_TREE; |
| if (DECL_TYPE_TEMPLATE_P (r) || DECL_TEMPLATE_TEMPLATE_PARM_P (r)) |
| ret = lookup_template_class (r, rvec, NULL_TREE, NULL_TREE, tf_none); |
| else if (concept_definition_p (r)) |
| { |
| ret = build_concept_check (r, rvec, tf_none); |
| ret = evaluate_concept_check (ret); |
| return get_reflection_raw (loc, ret, REFLECT_VALUE); |
| } |
| else if (variable_template_p (r)) |
| { |
| ret = lookup_template_variable (r, rvec, tf_none); |
| ret = finish_template_variable (ret, tf_none); |
| } |
| else |
| ret = lookup_template_function (r, rvec); |
| return get_reflection_raw (loc, ret); |
| } |
| |
| /* Process std::meta::tuple_size. |
| Returns: tuple_size_v<T>, where T is the type represented by |
| dealias(type). */ |
| |
| static tree |
| eval_tuple_size (location_t loc, const constexpr_ctx *ctx, tree type, |
| tree call, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| /* Create std::tuple_size<TYPE>::value. */ |
| tree args = make_tree_vec (1); |
| TREE_VEC_ELT (args, 0) = type; |
| return finish_library_value_trait (loc, ctx, "tuple_size", args, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::tuple_element. |
| Returns: A reflection representing the type denoted by |
| tuple_element_t<I, T>, where T is the type represented by dealias(type) |
| and I is a constant equal to index. */ |
| |
| static tree |
| eval_tuple_element (location_t loc, const constexpr_ctx *ctx, tree i, |
| tree type, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| /* Create std::tuple_element<I,TYPE>::type. */ |
| tree args = make_tree_vec (2); |
| TREE_VEC_ELT (args, 0) = i; |
| TREE_VEC_ELT (args, 1) = type; |
| return finish_library_type_trait (loc, ctx, "tuple_element", |
| args, call, non_constant_p, jump_target, |
| fun); |
| } |
| |
| /* Process std::meta::variant_size. |
| Returns: variant_size_v<T>, where T is the type represented by |
| dealias(type). */ |
| |
| static tree |
| eval_variant_size (location_t loc, const constexpr_ctx *ctx, tree type, |
| tree call, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| /* Create std::variant_size<TYPE>::value. */ |
| tree args = make_tree_vec (1); |
| TREE_VEC_ELT (args, 0) = type; |
| return finish_library_value_trait (loc, ctx, "variant_size", args, call, |
| non_constant_p, jump_target, fun); |
| } |
| |
| /* Process std::meta::variant_alternative. |
| Returns: A reflection representing the type denoted by |
| variant_alternative_t<I, T>, where T is the type represented by |
| dealias(type) and I is a constant equal to index. */ |
| |
| static tree |
| eval_variant_alternative (location_t loc, const constexpr_ctx *ctx, tree i, |
| tree type, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| /* Create std::variant_alternative<I,TYPE>::type. */ |
| tree args = make_tree_vec (2); |
| TREE_VEC_ELT (args, 0) = i; |
| TREE_VEC_ELT (args, 1) = type; |
| return finish_library_type_trait (loc, ctx, "variant_alternative", |
| args, call, non_constant_p, jump_target, |
| fun); |
| } |
| |
| /* Process std::meta::data_member_spec. |
| Returns: A reflection of a data member description (T,N,A,W,NUA) where |
| -- T is the type represented by dealias(type), |
| -- N is either the identifier encoded by options.name or _|_ if |
| options.name does not contain a value, |
| -- A is either the alignment value held by options.alignment or _|_ if |
| options.alignment does not contain a value, |
| -- W is either the value held by options.bit_width or _|_ if |
| options.bit_width does not contain a value, and |
| -- NUA is the value held by options.no_unique_address. |
| Throws: meta::exception unless the following conditions are met: |
| -- dealias(type) represents either an object type or a reference type; |
| -- if options.name contains a value, then: |
| -- holds_alternative<u8string>(options.name->contents) is true and |
| get<u8string>(options.name->contents) contains a valid identifier |
| that is not a keyword when interpreted with UTF-8, or |
| -- holds_alternative<string>(options.name->contents) is true and |
| get<string>(options.name->contents) contains a valid identifier |
| that is not a keyword when interpreted with the ordinary literal |
| encoding; |
| -- if options.name does not contain a value, then options.bit_width |
| contains a value; |
| -- if options.bit_width contains a value V, then |
| -- is_integral_type(type) || is_enum_type(type) is true, |
| -- options.alignment does not contain a value, |
| -- options.no_unique_address is false, |
| -- V is not negative, and |
| -- if V equals 0, then options.name does not contain a value; and |
| -- if options.alignment contains a value, it is an alignment value not less |
| than alignment_of(type). */ |
| |
| static tree |
| eval_data_member_spec (location_t loc, const constexpr_ctx *ctx, |
| tree type, tree opts, bool *non_constant_p, |
| bool *overflow_p, tree *jump_target, tree fun) |
| { |
| type = strip_typedefs (type); |
| if (!TYPE_OBJ_P (type) && !TYPE_REF_P (type)) |
| return throw_exception (loc, ctx, "type is not object or reference type", |
| fun, non_constant_p, jump_target); |
| opts = convert_from_reference (opts); |
| if (!CLASS_TYPE_P (TREE_TYPE (opts))) |
| { |
| fail: |
| error_at (loc, "unexpected %<data_member_options%> argument"); |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| tree args[5] = { type, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE }; |
| for (tree field = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (opts))); |
| field; field = next_aggregate_field (DECL_CHAIN (field))) |
| if (tree name = DECL_NAME (field)) |
| { |
| if (id_equal (name, "name")) |
| args[1] = field; |
| else if (id_equal (name, "alignment")) |
| args[2] = field; |
| else if (id_equal (name, "bit_width")) |
| args[3] = field; |
| else if (id_equal (name, "no_unique_address")) |
| args[4] = field; |
| } |
| for (int i = 1; i < 5; ++i) |
| { |
| if (args[i] == NULL_TREE) |
| goto fail; |
| tree opt = build3 (COMPONENT_REF, TREE_TYPE (args[i]), opts, args[i], |
| NULL_TREE); |
| if (i == 4) |
| { |
| /* The no_unique_address handling is simple. */ |
| if (TREE_CODE (TREE_TYPE (opt)) != BOOLEAN_TYPE) |
| goto fail; |
| opt = cxx_eval_constant_expression (ctx, opt, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (TREE_CODE (opt) != INTEGER_CST) |
| goto fail; |
| if (integer_zerop (opt)) |
| args[i] = boolean_false_node; |
| else |
| args[i] = boolean_true_node; |
| continue; |
| } |
| /* Otherwise the member is optional<something>. */ |
| if (!CLASS_TYPE_P (TREE_TYPE (opt))) |
| goto fail; |
| tree has_value = build_static_cast (loc, boolean_type_node, opt, |
| tf_warning_or_error); |
| if (error_operand_p (has_value)) |
| goto fail; |
| has_value = cxx_eval_constant_expression (ctx, has_value, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (TREE_CODE (has_value) != INTEGER_CST) |
| goto fail; |
| if (integer_zerop (has_value)) |
| { |
| /* If it doesn't have value, store NULL_TREE. */ |
| args[i] = NULL_TREE; |
| continue; |
| } |
| tree deref = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, opt, |
| NULL_TREE, tf_warning_or_error); |
| if (error_operand_p (deref)) |
| goto fail; |
| if (i != 1) |
| { |
| /* For alignment and bit_width otherwise it should be int. */ |
| if (TYPE_MAIN_VARIANT (TREE_TYPE (deref)) != integer_type_node) |
| goto fail; |
| deref = cxx_eval_constant_expression (ctx, deref, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (TREE_CODE (deref) != INTEGER_CST) |
| goto fail; |
| args[i] = deref; |
| continue; |
| } |
| /* Otherwise it is a name. */ |
| if (!CLASS_TYPE_P (TREE_TYPE (deref))) |
| goto fail; |
| tree fields[3] = { NULL_TREE, NULL_TREE, NULL_TREE }; |
| for (tree field = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (deref))); |
| field; field = next_aggregate_field (DECL_CHAIN (field))) |
| if (tree name = DECL_NAME (field)) |
| { |
| if (id_equal (name, "_M_is_u8")) |
| fields[0] = field; |
| else if (id_equal (name, "_M_u8s")) |
| fields[1] = field; |
| else if (id_equal (name, "_M_s")) |
| fields[2] = field; |
| } |
| for (int j = 0; j < 3; ++j) |
| { |
| if (fields[j] == NULL_TREE) |
| goto fail; |
| if (j && j == (fields[0] == boolean_true_node ? 2 : 1)) |
| continue; |
| tree f = build3 (COMPONENT_REF, TREE_TYPE (fields[j]), deref, |
| fields[j], NULL_TREE); |
| if (j == 0) |
| { |
| /* The _M_is_u8 handling is simple. */ |
| if (TREE_CODE (TREE_TYPE (f)) != BOOLEAN_TYPE) |
| goto fail; |
| f = cxx_eval_constant_expression (ctx, f, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (TREE_CODE (f) != INTEGER_CST) |
| goto fail; |
| if (integer_zerop (f)) |
| fields[0] = boolean_false_node; |
| else |
| fields[0] = boolean_true_node; |
| continue; |
| } |
| /* _M_u8s/_M_s handling is the same except for encoding. */ |
| if (!CLASS_TYPE_P (TREE_TYPE (f))) |
| goto fail; |
| tree fns = lookup_qualified_name (TREE_TYPE (f), |
| get_identifier ("c_str")); |
| if (error_operand_p (fns)) |
| goto fail; |
| f = build_new_method_call (f, fns, NULL, NULL_TREE, LOOKUP_NORMAL, |
| NULL, tf_warning_or_error); |
| if (error_operand_p (f)) |
| goto fail; |
| f = cxx_eval_constant_expression (ctx, f, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| STRIP_NOPS (f); |
| if (TREE_CODE (f) != ADDR_EXPR) |
| goto fail; |
| f = TREE_OPERAND (f, 0); |
| f = cxx_eval_constant_expression (ctx, f, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (TREE_CODE (f) != CONSTRUCTOR |
| || TREE_CODE (TREE_TYPE (f)) != ARRAY_TYPE) |
| goto fail; |
| tree eltt = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))); |
| if (eltt != (j == 1 ? char8_type_node : char_type_node)) |
| goto fail; |
| tree field, value; |
| unsigned k; |
| unsigned HOST_WIDE_INT l = 0; |
| bool ntmbs = false; |
| FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (f), k, field, value) |
| if (!tree_fits_shwi_p (value)) |
| goto fail; |
| else if (field == NULL_TREE) |
| { |
| if (integer_zerop (value)) |
| { |
| ntmbs = true; |
| break; |
| } |
| ++l; |
| } |
| 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 fail; |
| if (integer_zerop (value)) |
| { |
| l = tree_to_uhwi (lo); |
| ntmbs = true; |
| break; |
| } |
| l = tree_to_uhwi (hi) + 1; |
| } |
| else if (tree_fits_uhwi_p (field)) |
| { |
| l = tree_to_uhwi (field); |
| if (integer_zerop (value)) |
| { |
| ntmbs = true; |
| break; |
| } |
| ++l; |
| } |
| else |
| goto fail; |
| if (!ntmbs || l > INT_MAX - 1) |
| goto fail; |
| char *namep; |
| unsigned len = l; |
| if (l < 64) |
| namep = XALLOCAVEC (char, l + 1); |
| else |
| namep = XNEWVEC (char, l + 1); |
| memset (namep, 0, l + 1); |
| l = 0; |
| FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (f), k, field, value) |
| if (field == NULL_TREE) |
| { |
| if (integer_zerop (value)) |
| break; |
| namep[l] = tree_to_shwi (value); |
| ++l; |
| } |
| else if (TREE_CODE (field) == RANGE_EXPR) |
| { |
| tree lo = TREE_OPERAND (field, 0); |
| tree hi = TREE_OPERAND (field, 1); |
| if (integer_zerop (value)) |
| break; |
| unsigned HOST_WIDE_INT m = tree_to_uhwi (hi); |
| for (l = tree_to_uhwi (lo); l <= m; ++l) |
| namep[l] = tree_to_shwi (value); |
| } |
| else |
| { |
| l = tree_to_uhwi (field); |
| namep[l++] = tree_to_shwi (value); |
| } |
| namep[len] = '\0'; |
| /* Convert namep from execution charset to SOURCE_CHARSET. */ |
| cpp_string istr, ostr; |
| istr.len = strlen (namep) + 1; |
| istr.text = (const unsigned char *) namep; |
| if (!cpp_translate_string (parse_in, &istr, &ostr, |
| j == 2 ? CPP_STRING : CPP_UTF8STRING, |
| true)) |
| { |
| if (len >= 64) |
| XDELETEVEC (namep); |
| if (j == 1) |
| return throw_exception (loc, ctx, |
| "conversion from ordinary literal " |
| "encoding to source charset " |
| "failed", fun, non_constant_p, |
| jump_target); |
| else |
| return throw_exception (loc, ctx, |
| "conversion from UTF-8 encoding to " |
| "source charset failed", |
| fun, non_constant_p, jump_target); |
| } |
| if (len >= 64) |
| XDELETEVEC (namep); |
| if (!cpp_valid_identifier (parse_in, ostr.text)) |
| return throw_exception (loc, ctx, |
| "name is not a valid identifier", |
| fun, non_constant_p, jump_target); |
| args[i] = get_identifier ((const char *) ostr.text); |
| switch (get_identifier_kind (args[i])) |
| { |
| case cik_keyword: |
| return throw_exception (loc, ctx, "name is a keyword", |
| fun, non_constant_p, jump_target); |
| case cik_trait: |
| return throw_exception (loc, ctx, "name is a built-in trait", |
| fun, non_constant_p, jump_target); |
| default: |
| break; |
| } |
| } |
| } |
| if (args[1] == NULL_TREE && args[3] == NULL_TREE) |
| return throw_exception (loc, ctx, |
| "neither name nor bit_width specified", |
| fun, non_constant_p, jump_target); |
| if (args[3]) |
| { |
| if (!CP_INTEGRAL_TYPE_P (type) && TREE_CODE (type) != ENUMERAL_TYPE) |
| return throw_exception (loc, ctx, |
| "bit_width specified with non-integral " |
| "and non-enumeration type", |
| fun, non_constant_p, jump_target); |
| if (args[2]) |
| return throw_exception (loc, ctx, |
| "both alignment and bit_width specified", |
| fun, non_constant_p, jump_target); |
| if (args[4] == boolean_true_node) |
| return throw_exception (loc, ctx, |
| "bit_width specified with " |
| "no_unique_address true", |
| fun, non_constant_p, jump_target); |
| if (integer_zerop (args[3]) && args[1]) |
| return throw_exception (loc, ctx, |
| "bit_width 0 with specified name", |
| fun, non_constant_p, jump_target); |
| if (tree_int_cst_sgn (args[3]) < 0) |
| return throw_exception (loc, ctx, "bit_width is negative", |
| fun, non_constant_p, jump_target); |
| } |
| if (args[2]) |
| { |
| if (!integer_pow2p (args[2])) |
| return throw_exception (loc, ctx, |
| "alignment is not power of two", |
| fun, non_constant_p, jump_target); |
| if (tree_int_cst_sgn (args[2]) < 0) |
| return throw_exception (loc, ctx, "alignment is negative", |
| fun, non_constant_p, jump_target); |
| tree al = cxx_sizeof_or_alignof_type (loc, type, ALIGNOF_EXPR, true, |
| tf_none); |
| if (TREE_CODE (al) == INTEGER_CST |
| && wi::to_widest (al) > wi::to_widest (args[2])) |
| return throw_exception (loc, ctx, |
| "alignment is smaller than alignment_of", |
| fun, non_constant_p, jump_target); |
| } |
| tree ret = make_tree_vec (5); |
| for (int i = 0; i < 5; ++i) |
| TREE_VEC_ELT (ret, i) = args[i]; |
| return get_reflection_raw (loc, ret, REFLECT_DATA_MEMBER_SPEC); |
| } |
| |
| /* Process std::meta::define_aggregate. |
| Let C be the type represented by class_type and r_K be the Kth reflection |
| value in mdescrs. |
| For every r_K in mdescrs, let (T_K,N_K,A_K,W_K,NUA_K) be the corresponding |
| data member description represented by r_K. |
| Constant When: |
| -- class_type represents a cv-unqualified class type; |
| -- C is incomplete from every point in the evaluation context; |
| -- is_data_member_spec(r_K) is true for every r_K; |
| -- is_complete_type(T_K) is true for every r_K; and |
| -- for every pair (r_K,r_L) where K<L, if N_K is not _|_ and N_L is not |
| _|_, then either: |
| -- N_K is not the same identifier as N_L or |
| -- N_K is the identifier _ (U+005F LOW LINE). |
| Effects: Produces an injected declaration D that defines C and has |
| properties as follows: |
| -- The target scope of D is the scope to which C belongs. |
| -- The locus of D follows immediately after the core constant expression |
| currently under evaluation. |
| -- The characteristic sequence of D is the sequence of reflection values |
| r_K. |
| -- If C is a specialization of a templated class T, and C is not a local |
| class, then D is an explicit specialization of T. |
| -- For each r_K, there is a corresponding entity M_K with public access |
| belonging to the class scope of D with the following properties: |
| -- If N_K is _|_, M_K is an unnamed bit-field. |
| Otherwise, M_K is a non-static data member whose name is the |
| identifier N_K. |
| -- The type of M_K is T_K. |
| -- M_K is declared with the attribute [[no_unique_address]] if and only |
| if NUA_K is true. |
| -- If W_K is not _|_, M_K is a bit-field whose width is that value. |
| Otherwise, M_K is not a bit-field. |
| -- If A_K is not _|_, M_K has the alignment-specifier alignas(A_K). |
| Otherwise, M_K has no alignment-specifier. |
| -- For every r_L in mdescrs such that K<L, the declaration corresponding to |
| r_K precedes the declaration corresponding to r_L. |
| Returns: class_type. |
| Remarks: If C is a specialization of a templated class and it has not been |
| instantiated, C is treated as an explicit specialization. */ |
| |
| static tree |
| eval_define_aggregate (location_t loc, const constexpr_ctx *ctx, |
| tree type, tree rvec, tree call, bool *non_constant_p) |
| { |
| tree orig_type = type; |
| if (!CLASS_TYPE_P (type)) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "first %<define_aggregate%> argument is not a class " |
| "type reflection"); |
| *non_constant_p = true; |
| return call; |
| } |
| if (typedef_variant_p (type)) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "first %<define_aggregate%> argument is a reflection " |
| "of a type alias"); |
| *non_constant_p = true; |
| return call; |
| } |
| if (cv_qualified_p (type)) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "first %<define_aggregate%> argument is a " |
| "cv-qualified class type reflection"); |
| *non_constant_p = true; |
| return call; |
| } |
| if (COMPLETE_TYPE_P (type)) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "first %<define_aggregate%> argument is a complete " |
| "class type reflection"); |
| *non_constant_p = true; |
| return call; |
| } |
| hash_set<tree> nameset; |
| for (int i = 0; i < TREE_VEC_LENGTH (rvec); ++i) |
| { |
| tree ra = TREE_VEC_ELT (rvec, i); |
| tree a = REFLECT_EXPR_HANDLE (ra); |
| if (REFLECT_EXPR_KIND (ra) != REFLECT_DATA_MEMBER_SPEC) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "%<define_aggregate%> argument not a data member " |
| "description"); |
| *non_constant_p = true; |
| return call; |
| } |
| if (eval_is_complete_type (TREE_VEC_ELT (a, 0)) != boolean_true_node) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "%<define_aggregate%> argument data member " |
| "description without complete type"); |
| *non_constant_p = true; |
| return call; |
| } |
| if (TREE_VEC_ELT (a, 1) |
| && !id_equal (TREE_VEC_ELT (a, 1), "_") |
| && nameset.add (TREE_VEC_ELT (a, 1))) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "name %qD used in multiple data member " |
| "descriptions", TREE_VEC_ELT (a, 1)); |
| *non_constant_p = true; |
| return call; |
| } |
| if (TYPE_WARN_IF_NOT_ALIGN (type) |
| && TREE_VEC_ELT (a, 3)) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "cannot declare bit-field in " |
| "%<warn_if_not_aligned%> type"); |
| *non_constant_p = true; |
| return call; |
| } |
| } |
| tree consteval_block = cxx_constexpr_consteval_block (ctx); |
| if (consteval_block == NULL_TREE) |
| { |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "%<define_aggregate%> not evaluated from " |
| "%<consteval%> block"); |
| *non_constant_p = true; |
| return call; |
| } |
| iloc_sentinel ils = loc; |
| type = TYPE_MAIN_VARIANT (type); |
| type = strip_typedefs (type); |
| tree cscope = NULL_TREE, tscope = NULL_TREE; |
| for (tree c = TYPE_CONTEXT (CP_DECL_CONTEXT (consteval_block)); c; |
| c = get_containing_scope (c)) |
| { |
| if (c == type) |
| { |
| auto_diagnostic_group d; |
| error_at (loc, "%<define_aggregate%> evaluated from " |
| "%<consteval%> block enclosed by %qT being " |
| "defined", type); |
| inform (DECL_SOURCE_LOCATION (consteval_block), |
| "%<consteval%> block defined here"); |
| return get_reflection_raw (loc, orig_type); |
| } |
| if (cscope == NULL_TREE |
| && (TYPE_P (c) || TREE_CODE (c) == FUNCTION_DECL)) |
| cscope = c; |
| } |
| for (tree c = TYPE_CONTEXT (type); c; c = get_containing_scope (c)) |
| { |
| if (c == consteval_block) |
| { |
| auto_diagnostic_group d; |
| error_at (loc, "%<define_aggregate%> evaluated from " |
| "%<consteval%> block which encloses %qT being " |
| "defined", type); |
| inform (DECL_SOURCE_LOCATION (consteval_block), |
| "%<consteval%> block defined here"); |
| return get_reflection_raw (loc, orig_type); |
| } |
| if (tscope == NULL_TREE |
| && (TYPE_P (c) || TREE_CODE (c) == FUNCTION_DECL)) |
| tscope = c; |
| } |
| if (cscope != tscope) |
| { |
| auto_diagnostic_group d; |
| if (cscope && tscope) |
| { |
| for (tree c = tscope; c; c = get_containing_scope (c)) |
| if (c == cscope) |
| { |
| if (DECL_P (tscope)) |
| error_at (loc, "%qD intervenes between %qT scope and " |
| "%<consteval%> block %<define_aggregate%> " |
| "is evaluated from", tscope, type); |
| else |
| error_at (loc, "%qT intervenes between %qT scope and " |
| "%<consteval%> block %<define_aggregate%> " |
| "is evaluated from", tscope, type); |
| cscope = NULL_TREE; |
| tscope = NULL_TREE; |
| break; |
| } |
| for (tree c = cscope; c; c = get_containing_scope (c)) |
| if (c == tscope) |
| { |
| if (DECL_P (cscope)) |
| error_at (loc, "%qD intervenes between %<consteval%> block " |
| "%<define_aggregate%> is evaluated from and " |
| "%qT scope", cscope, type); |
| else |
| error_at (loc, "%qT intervenes between %<consteval%> block " |
| "%<define_aggregate%> is evaluated from and " |
| "%qT scope", cscope, type); |
| cscope = NULL_TREE; |
| tscope = NULL_TREE; |
| break; |
| } |
| if (cscope && tscope) |
| { |
| if (DECL_P (cscope) && DECL_P (tscope)) |
| error_at (loc, "%<define_aggregate%> evaluated from " |
| "%<consteval%> block enclosed by %qD while " |
| "%qT type being defined is enclosed by %qD", |
| cscope, type, tscope); |
| else if (DECL_P (cscope)) |
| error_at (loc, "%<define_aggregate%> evaluated from " |
| "%<consteval%> block enclosed by %qD while " |
| "%qT type being defined is enclosed by %qT", |
| cscope, type, tscope); |
| else if (DECL_P (tscope)) |
| error_at (loc, "%<define_aggregate%> evaluated from " |
| "%<consteval%> block enclosed by %qT while " |
| "%qT type being defined is enclosed by %qD", |
| cscope, type, tscope); |
| else if (tscope) |
| error_at (loc, "%<define_aggregate%> evaluated from " |
| "%<consteval%> block enclosed by %qT while " |
| "%qT type being defined is enclosed by %qT", |
| cscope, type, tscope); |
| } |
| } |
| else if (cscope && DECL_P (cscope)) |
| error_at (loc, "%qD intervenes between %<consteval%> block " |
| "%<define_aggregate%> is evaluated from and %qT scope", |
| cscope, type); |
| else if (cscope) |
| error_at (loc, "%qT intervenes between %<consteval%> block " |
| "%<define_aggregate%> is evaluated from and %qT scope", |
| cscope, type); |
| else if (tscope && DECL_P (tscope)) |
| error_at (loc, "%qD intervenes between %qT scope and %<consteval%> " |
| "block %<define_aggregate%> is evaluated from", |
| tscope, type); |
| else |
| error_at (loc, "%qT intervenes between %qT scope and %<consteval%> " |
| "block %<define_aggregate%> is evaluated from", |
| tscope, type); |
| inform (DECL_SOURCE_LOCATION (consteval_block), |
| "%<consteval%> block defined here"); |
| return get_reflection_raw (loc, orig_type); |
| } |
| if (primary_template_specialization_p (type)) |
| { |
| type = maybe_process_partial_specialization (type); |
| if (type == error_mark_node) |
| { |
| *non_constant_p = true; |
| return call; |
| } |
| } |
| if (!TYPE_BINFO (type)) |
| xref_basetypes (type, NULL_TREE); |
| pushclass (type); |
| gcc_assert (!TYPE_FIELDS (type)); |
| tree fields = NULL_TREE; |
| for (int i = 0; i < TREE_VEC_LENGTH (rvec); ++i) |
| { |
| tree ra = TREE_VEC_ELT (rvec, i); |
| tree a = REFLECT_EXPR_HANDLE (ra); |
| tree f = build_decl (cp_expr_loc_or_input_loc (ra), FIELD_DECL, |
| TREE_VEC_ELT (a, 1), TREE_VEC_ELT (a, 0)); |
| DECL_CHAIN (f) = fields; |
| DECL_IN_AGGR_P (f) = 1; |
| DECL_CONTEXT (f) = type; |
| TREE_PUBLIC (f) = 1; |
| /* Bit-field. */ |
| if (TREE_VEC_ELT (a, 3)) |
| { |
| /* Temporarily stash the width in DECL_BIT_FIELD_REPRESENTATIVE. |
| check_bitfield_decl picks it from there later and sets DECL_SIZE |
| accordingly. */ |
| DECL_BIT_FIELD_REPRESENTATIVE (f) = TREE_VEC_ELT (a, 3); |
| SET_DECL_C_BIT_FIELD (f); |
| DECL_NONADDRESSABLE_P (f) = 1; |
| /* If this bit-field is unnamed, it's padding. */ |
| if (!TREE_VEC_ELT (a, 1)) |
| DECL_PADDING_P (f) = 1; |
| } |
| else if (TREE_VEC_ELT (a, 2)) |
| { |
| SET_DECL_ALIGN (f, tree_to_uhwi (TREE_VEC_ELT (a, 2)) |
| * BITS_PER_UNIT); |
| DECL_USER_ALIGN (f) = 1; |
| } |
| if (TREE_VEC_ELT (a, 4) == boolean_true_node) |
| { |
| tree attr = build_tree_list (NULL_TREE, |
| get_identifier ("no_unique_address")); |
| attr = build_tree_list (attr, NULL_TREE); |
| cplus_decl_attributes (&f, attr, 0); |
| } |
| fields = f; |
| } |
| TYPE_FIELDS (type) = fields; |
| finish_struct (type, NULL_TREE); |
| return get_reflection_raw (loc, orig_type); |
| } |
| |
| /* Implement std::meta::reflect_constant_string. |
| Let CharT be ranges::range_value_t<R>. |
| Mandates: CharT is one of char, wchar_t, char8_t, char16_t, char32_t. |
| Let V be the pack of values of type CharT whose elements are the |
| corresponding elements of r, except that if r refers to a string literal |
| object, then V does not include the trailing null terminator of r. |
| Let P be the template parameter object of type |
| const CharT[sizeof...(V) + 1] initialized with {V..., CharT()}. |
| Returns: ^^P. */ |
| |
| static tree |
| eval_reflect_constant_string (location_t loc, const constexpr_ctx *ctx, |
| tree call, bool *non_constant_p, |
| bool *overflow_p, tree *jump_target, tree fun) |
| { |
| tree str = get_range_elts (loc, ctx, call, 0, non_constant_p, overflow_p, |
| jump_target, REFLECT_CONSTANT_STRING, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| tree decl = get_template_parm_object (str, |
| mangle_template_parm_object (str)); |
| DECL_MERGEABLE (decl) = 1; |
| return get_reflection_raw (loc, decl); |
| } |
| |
| /* Implement std::meta::reflect_constant_array. |
| Let T be ranges::range_value_t<R>. |
| Mandates: T is a structural type, |
| is_constructible_v<T, ranges::range_reference_t<R>> is true, and |
| is_copy_constructible_v<T> is true. |
| Let V be the pack of values of type info of the same size as r, where the |
| ith element is reflect_constant(e_i), where e_i is the ith element of r. |
| Let P be |
| -- If sizeof...(V) > 0 is true, then the template parameter object of type |
| const T[sizeof...(V)] initialized with {[:V:]...}. |
| -- Otherwise, the template parameter object of type array<T, 0> initialized |
| with {}. |
| Returns: ^^P. */ |
| |
| static tree |
| eval_reflect_constant_array (location_t loc, const constexpr_ctx *ctx, |
| tree call, bool *non_constant_p, |
| bool *overflow_p, tree *jump_target, tree fun) |
| { |
| tree str = get_range_elts (loc, ctx, call, 0, non_constant_p, overflow_p, |
| jump_target, REFLECT_CONSTANT_ARRAY, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| tree decl = get_template_parm_object (str, |
| mangle_template_parm_object (str)); |
| DECL_MERGEABLE (decl) = 1; |
| return get_reflection_raw (loc, decl); |
| } |
| |
| /* Process std::meta::access_context::current. */ |
| |
| static tree |
| eval_access_context_current (location_t loc, const constexpr_ctx *ctx, |
| tree call, bool *non_constant_p) |
| { |
| tree scope = cxx_constexpr_caller (ctx); |
| /* Ignore temporary current_function_decl changes caused by |
| push_access_scope. */ |
| if (scope == NULL_TREE && current_function_decl) |
| scope = current_function_decl_without_access_scope (); |
| /* [meta.reflection.access.context]/(5.1.2): Otherwise, if an initialization |
| by an inherited constructor is using I, a point whose immediate scope is |
| the class scope corresponding to C. */ |
| if (scope && DECL_INHERITED_CTOR (scope)) |
| scope = DECL_CONTEXT (scope); |
| if (scope == NULL_TREE) |
| { |
| if (cxx_constexpr_manifestly_const_eval (ctx) != mce_true) |
| { |
| /* Outside of functions limit this to manifestly constant-evaluation |
| so that we don't fold it prematurely. */ |
| if (!cxx_constexpr_quiet_p (ctx)) |
| error_at (loc, "%<access_context::current%> used outside of " |
| "manifestly constant-evaluation"); |
| *non_constant_p = true; |
| return call; |
| } |
| if (current_class_type) |
| scope = current_class_type; |
| else if (current_namespace) |
| scope = current_namespace; |
| else |
| scope = global_namespace; |
| } |
| tree lam; |
| while (LAMBDA_FUNCTION_P (scope) |
| && (lam = CLASSTYPE_LAMBDA_EXPR (CP_DECL_CONTEXT (scope))) |
| && LAMBDA_EXPR_CONSTEVAL_BLOCK_P (lam)) |
| scope = CP_TYPE_CONTEXT (CP_DECL_CONTEXT (scope)); |
| tree access_context = TREE_TYPE (call); |
| if (TREE_CODE (access_context) != RECORD_TYPE) |
| { |
| fail: |
| error_at (loc, "unexpected return type of %qs", |
| "std::meta::access_context::current"); |
| return build_zero_cst (access_context); |
| } |
| tree scopef = next_aggregate_field (TYPE_FIELDS (access_context)); |
| if (!scopef || !REFLECTION_TYPE_P (TREE_TYPE (scopef))) |
| goto fail; |
| tree classf = next_aggregate_field (DECL_CHAIN (scopef)); |
| if (!classf || !REFLECTION_TYPE_P (TREE_TYPE (classf))) |
| goto fail; |
| if (next_aggregate_field (DECL_CHAIN (classf))) |
| goto fail; |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| CONSTRUCTOR_APPEND_ELT (elts, scopef, get_reflection_raw (loc, scope)); |
| CONSTRUCTOR_APPEND_ELT (elts, classf, get_null_reflection ()); |
| return build_constructor (access_context, elts); |
| } |
| |
| /* Helper function to extract scope and designating class from |
| access_context ACTX. */ |
| |
| static bool |
| extract_access_context (location_t loc, tree actx, tree *scope, |
| tree *designating_class) |
| { |
| if (TREE_CODE (actx) != CONSTRUCTOR |
| || CONSTRUCTOR_NELTS (actx) != 2 |
| || !REFLECT_EXPR_P (CONSTRUCTOR_ELT (actx, 0)->value) |
| || !REFLECT_EXPR_P (CONSTRUCTOR_ELT (actx, 1)->value)) |
| { |
| error_at (loc, "invalid %<access_context%> argument"); |
| return false; |
| } |
| *scope = REFLECT_EXPR_HANDLE (CONSTRUCTOR_ELT (actx, 0)->value); |
| *designating_class = REFLECT_EXPR_HANDLE (CONSTRUCTOR_ELT (actx, 1)->value); |
| if (*scope == unknown_type_node) |
| *scope = NULL_TREE; |
| else if (TREE_CODE (*scope) != FUNCTION_DECL |
| && TREE_CODE (*scope) != NAMESPACE_DECL |
| && !CLASS_TYPE_P (*scope)) |
| { |
| error_at (loc, "unexpected %<access_context::scope()%>"); |
| return false; |
| } |
| else if (CLASS_TYPE_P (*scope)) |
| *scope = TYPE_MAIN_VARIANT (*scope); |
| if (*designating_class == unknown_type_node) |
| *designating_class = NULL_TREE; |
| else if (!CLASS_TYPE_P (*designating_class) |
| || !COMPLETE_TYPE_P (*designating_class)) |
| { |
| error_at (loc, "unexpected %<access_context::designating_class()%>"); |
| return false; |
| } |
| else |
| *designating_class = TYPE_MAIN_VARIANT (*designating_class); |
| return true; |
| } |
| |
| /* Process std::meta::is_accessible. |
| Let PARENT-CLS(r) be: |
| -- If parent_of(r) represents a class C, then C. |
| -- Otherwise, PARENT-CLS(parent_of(r)). |
| Let DESIGNATING-CLS(r, ctx) be: |
| -- If ctx.designating_class() represents a class C, then C. |
| -- Otherwise, PARENT-CLS(r). |
| Returns: |
| -- If r represents an unnamed bit-field F, then is_accessible(r_H, ctx), |
| where r_H represents a hypothetical non-static data member of the class |
| represented by PARENT-CLS(r) with the same access as F. |
| -- Otherwise, if r does not represent a class member or a direct base class |
| relationship, then true. |
| -- Otherwise, if r represents |
| -- a class member that is not a (possibly indirect or variant) member of |
| DESIGNATING-CLS(r, ctx) or |
| -- a direct base class relationship such that parent_of(r) does not |
| represent DESIGNATING-CLS(r, ctx) or a (direct or indirect) base |
| class thereof, |
| then false. |
| -- Otherwise, if ctx.scope() is the null reflection, then true. |
| -- Otherwise, letting P be a program point whose immediate scope is the |
| function parameter scope, class scope, or namespace scope corresponding |
| to the function, class, or namespace represented by ctx.scope(): |
| -- If r represents a direct base class relationship (D,B), then true if |
| base class B of DESIGNATING-CLS(r, ctx) is accessible at P; |
| otherwise false. |
| -- Otherwise, r represents a class member M; true if M would be |
| accessible at P with the designating class (as DESIGNATING-CLS(r, ctx) |
| if the effect of any using-declarations were ignored. Otherwise, |
| false. |
| Throws: meta::exception if r represents a class member for which |
| PARENT-CLS(r) is an incomplete class. */ |
| |
| static tree |
| eval_is_accessible (location_t loc, const constexpr_ctx *ctx, tree r, |
| reflect_kind kind, tree actx, tree call, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| tree scope = NULL_TREE, designating_class = NULL_TREE, c; |
| if (!extract_access_context (loc, actx, &scope, &designating_class)) |
| { |
| *non_constant_p = true; |
| return call; |
| } |
| |
| if (eval_is_class_member (r) == boolean_true_node) |
| { |
| r = maybe_get_first_fn (r); |
| c = r; |
| if (TREE_CODE (r) == CONST_DECL && UNSCOPED_ENUM_P (DECL_CONTEXT (r))) |
| c = DECL_CONTEXT (r); |
| if (TYPE_P (c)) |
| { |
| if (TYPE_NAME (c) && DECL_P (TYPE_NAME (c))) |
| c = CP_DECL_CONTEXT (TYPE_NAME (c)); |
| else |
| c = CP_TYPE_CONTEXT (c); |
| } |
| else |
| c = CP_DECL_CONTEXT (r); |
| } |
| else if (kind == REFLECT_BASE) |
| { |
| c = direct_base_derived (r); |
| r = BINFO_TYPE (r); |
| } |
| else |
| return boolean_true_node; |
| if (!CLASS_TYPE_P (c) || !COMPLETE_TYPE_P (c)) |
| return throw_exception (loc, ctx, |
| "incomplete parent class", |
| fun, non_constant_p, jump_target); |
| if (designating_class) |
| { |
| tree p = c; |
| while (ANON_AGGR_TYPE_P (p) && p != designating_class) |
| p = CP_TYPE_CONTEXT (p); |
| if (p != designating_class |
| && (!CLASS_TYPE_P (p) |
| || !DERIVED_FROM_P (p, designating_class))) |
| return boolean_false_node; |
| } |
| if (scope == NULL_TREE) |
| return boolean_true_node; |
| if (designating_class == NULL_TREE) |
| designating_class = c; |
| if (TREE_CODE (scope) == NAMESPACE_DECL) |
| push_to_top_level (); |
| else if (TYPE_P (scope)) |
| push_access_scope (TYPE_NAME (scope)); |
| else |
| push_access_scope (scope); |
| tree ret = boolean_false_node; |
| if (kind == REFLECT_BASE) |
| { |
| if (accessible_base_p (designating_class, r, /*consider_local_p=*/true)) |
| ret = boolean_true_node; |
| } |
| else |
| { |
| tree o = TYPE_P (r) ? TYPE_NAME (r) : r; |
| if (accessible_p (TYPE_BINFO (designating_class), o, |
| /*consider_local_p=*/true)) |
| ret = boolean_true_node; |
| } |
| if (TREE_CODE (scope) == NAMESPACE_DECL) |
| pop_from_top_level (); |
| else if (TYPE_P (scope)) |
| pop_access_scope (TYPE_NAME (scope)); |
| else |
| pop_access_scope (scope); |
| return ret; |
| } |
| |
| /* Returns true if R is C-members-of-representable from |
| current point P. */ |
| |
| static bool |
| members_of_representable_p (tree c, tree r) |
| { |
| if (TREE_CODE (r) == CONST_DECL) |
| return false; |
| if (LAMBDA_TYPE_P (c) && !LAMBDA_FUNCTION_P (r)) |
| return false; |
| if (TYPE_P (r)) |
| { |
| if (CP_DECL_CONTEXT (TYPE_NAME (r)) != c) |
| return false; |
| if (LAMBDA_TYPE_P (r)) |
| return false; |
| if (OVERLOAD_TYPE_P (r)) |
| return true; |
| if (typedef_variant_p (r)) |
| return true; |
| } |
| else if (DECL_P (r)) |
| { |
| if (CP_DECL_CONTEXT (r) != c) |
| return false; |
| if (DECL_CLASS_TEMPLATE_P (r) |
| || DECL_FUNCTION_TEMPLATE_P (r) |
| || variable_template_p (r) |
| || DECL_ALIAS_TEMPLATE_P (r) |
| || concept_definition_p (r) |
| || TREE_CODE (r) == FIELD_DECL |
| || TREE_CODE (r) == NAMESPACE_DECL) |
| return true; |
| if (VAR_OR_FUNCTION_DECL_P (r) && !undeduced_auto_decl (r)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Callback for vector qsort to compare members by ascending DECL_UID. */ |
| |
| static int |
| members_cmp (const void *a, const void *b) |
| { |
| const constructor_elt *ea = (const constructor_elt *) a; |
| const constructor_elt *eb = (const constructor_elt *) b; |
| tree vala = REFLECT_EXPR_HANDLE (ea->value); |
| tree valb = REFLECT_EXPR_HANDLE (eb->value); |
| if (TYPE_P (vala)) |
| vala = TYPE_NAME (vala); |
| if (TYPE_P (valb)) |
| valb = TYPE_NAME (valb); |
| if (DECL_UID (vala) < DECL_UID (valb)) |
| return -1; |
| if (DECL_UID (vala) > DECL_UID (valb)) |
| return 1; |
| gcc_assert (ea == eb); |
| return 0; |
| } |
| |
| /* Enumerate members of namespace NS for eval_members_of. */ |
| |
| static vec<constructor_elt, va_gc> * |
| namespace_members_of (location_t loc, tree ns) |
| { |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| hash_set<tree> *seen = nullptr; |
| for (tree o : *DECL_NAMESPACE_BINDINGS (ns)) |
| { |
| if (TREE_CODE (o) == OVERLOAD && OVL_LOOKUP_P (o)) |
| { |
| if (TREE_TYPE (o)) |
| { |
| tree m = TREE_TYPE (TREE_TYPE (o)); |
| if (members_of_representable_p (ns, m)) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| } |
| if (OVL_DEDUP_P (o) || !OVL_FUNCTION (o)) |
| continue; |
| o = OVL_FUNCTION (o); |
| } |
| for (ovl_iterator iter (o); iter; ++iter) |
| { |
| if (iter.hidden_p ()) |
| continue; |
| tree b = *iter; |
| tree m = b; |
| |
| if (VAR_P (b) && DECL_ANON_UNION_VAR_P (b)) |
| { |
| /* TODO: This doesn't handle namespace N { static union {}; } |
| but we pedwarn on that, so perhaps it doesn't need to be |
| handled. */ |
| tree v = DECL_VALUE_EXPR (b); |
| gcc_assert (v && TREE_CODE (v) == COMPONENT_REF); |
| tree var = TREE_OPERAND (v, 0); |
| tree type = TREE_TYPE (var); |
| if (!seen) |
| seen = new hash_set<tree>; |
| if (members_of_representable_p (ns, type) && !seen->add (type)) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, type)); |
| if (members_of_representable_p (ns, var) && !seen->add (var)) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, var)); |
| continue; |
| } |
| if (TREE_CODE (b) == TYPE_DECL) |
| m = TREE_TYPE (b); |
| if (!members_of_representable_p (ns, m)) |
| continue; |
| if (DECL_DECOMPOSITION_P (m) && !DECL_DECOMP_IS_BASE (m)) |
| { |
| tree base = DECL_DECOMP_BASE (m); |
| if (!seen) |
| seen = new hash_set<tree>; |
| if (members_of_representable_p (ns, base) && !seen->add (base)) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, base)); |
| if (!DECL_HAS_VALUE_EXPR_P (m)) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m, |
| REFLECT_VAR)); |
| continue; |
| } |
| /* eval_is_accessible should be always true for namespace members, |
| so don't bother calling it here. */ |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| } |
| } |
| delete seen; |
| if (elts) |
| elts->qsort (members_cmp); |
| return elts; |
| } |
| |
| /* Enumerate members of class R for eval_*members_of. KIND is |
| one of METAFN_{,{,NON}STATIC_DATA_}MEMBERS_OF or |
| METAFN_HAS_INACCESSIBLE_NONSTATIC_DATA_MEMBERS. |
| For the last kind don't append any elts except for the first one for |
| which is_accessible returned false. */ |
| |
| static vec<constructor_elt, va_gc> * |
| class_members_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| tree actx, tree call, bool *non_constant_p, |
| tree *jump_target, enum metafn_code kind, tree fun) |
| { |
| if (kind == METAFN_MEMBERS_OF) |
| { |
| if (modules_p ()) |
| lazy_load_pendings (TYPE_NAME (r)); |
| if (CLASSTYPE_LAZY_DEFAULT_CTOR (r)) |
| lazily_declare_fn (sfk_constructor, r); |
| if (CLASSTYPE_LAZY_COPY_CTOR (r)) |
| lazily_declare_fn (sfk_copy_constructor, r); |
| if (CLASSTYPE_LAZY_MOVE_CTOR (r)) |
| lazily_declare_fn (sfk_move_constructor, r); |
| if (CLASSTYPE_LAZY_DESTRUCTOR (r)) |
| lazily_declare_fn (sfk_destructor, r); |
| if (CLASSTYPE_LAZY_COPY_ASSIGN (r)) |
| lazily_declare_fn (sfk_copy_assignment, r); |
| if (CLASSTYPE_LAZY_MOVE_ASSIGN (r)) |
| lazily_declare_fn (sfk_move_assignment, r); |
| } |
| auto_vec <tree, 8> implicitly_declared; |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| for (tree field = TYPE_FIELDS (r); field; field = DECL_CHAIN (field)) |
| { |
| tree m = field; |
| if (TREE_CODE (field) == FIELD_DECL && DECL_ARTIFICIAL (field)) |
| continue; /* Ignore bases. */ |
| else if (DECL_SELF_REFERENCE_P (field)) |
| continue; |
| else if (TREE_CODE (field) == TYPE_DECL) |
| m = TREE_TYPE (field); |
| else if (TREE_CODE (field) == FUNCTION_DECL) |
| { |
| /* Ignore cloned cdtors. */ |
| if (DECL_CLONED_FUNCTION_P (field)) |
| continue; |
| /* Ignore functions with unsatisfied constraints. */ |
| if (!constraints_satisfied_p (field)) |
| continue; |
| if (DECL_MAYBE_DELETED (field)) |
| { |
| ++function_depth; |
| maybe_synthesize_method (field); |
| --function_depth; |
| } |
| } |
| if (members_of_representable_p (r, m)) |
| { |
| if (kind == METAFN_STATIC_DATA_MEMBERS_OF |
| && eval_is_variable (m, REFLECT_UNDEF) != boolean_true_node) |
| continue; /* For static_data_members_of only include |
| is_variable. */ |
| else if ((kind == METAFN_NONSTATIC_DATA_MEMBERS_OF |
| || kind == METAFN_HAS_INACCESSIBLE_NONSTATIC_DATA_MEMBERS) |
| && eval_is_nonstatic_data_member (m) != boolean_true_node) |
| continue; /* For nonstatic_data_members_of only include |
| is_nonstatic_data_member. */ |
| tree a = eval_is_accessible (loc, ctx, m, REFLECT_UNDEF, actx, call, |
| non_constant_p, jump_target, fun); |
| if (*jump_target || *non_constant_p) |
| return nullptr; |
| if (a == boolean_false_node) |
| { |
| if (kind == METAFN_HAS_INACCESSIBLE_NONSTATIC_DATA_MEMBERS |
| && elts == nullptr) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, boolean_true_node); |
| continue; |
| } |
| gcc_assert (a == boolean_true_node); |
| if (kind == METAFN_MEMBERS_OF |
| && TREE_CODE (m) == FUNCTION_DECL |
| && DECL_ARTIFICIAL (m)) |
| { |
| /* Implicitly-declared special members or operator== members |
| appear after any user declared members. */ |
| implicitly_declared.safe_push (m); |
| continue; |
| } |
| else if (kind == METAFN_HAS_INACCESSIBLE_NONSTATIC_DATA_MEMBERS) |
| continue; |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| } |
| } |
| /* TYPE_DECLs in TYPE_FIELDS come after other decls due to the "struct |
| stat hack" (see finish_member_declaration), so for members_of the |
| declaration order is not preserved. */ |
| if (kind == METAFN_MEMBERS_OF && elts) |
| elts->qsort (members_cmp); |
| if (kind == METAFN_MEMBERS_OF && !implicitly_declared.is_empty ()) |
| { |
| gcc_assert (implicitly_declared.length () <= 8); |
| for (tree m : implicitly_declared) |
| if (default_ctor_p (m)) |
| { |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| break; |
| } |
| for (tree m : implicitly_declared) |
| if (DECL_COPY_CONSTRUCTOR_P (m)) |
| { |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| break; |
| } |
| for (tree m : implicitly_declared) |
| if (special_function_p (m) == sfk_copy_assignment) |
| { |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| break; |
| } |
| for (tree m : implicitly_declared) |
| if (DECL_MOVE_CONSTRUCTOR_P (m)) |
| { |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| break; |
| } |
| for (tree m : implicitly_declared) |
| if (special_function_p (m) == sfk_move_assignment) |
| { |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| break; |
| } |
| for (tree m : implicitly_declared) |
| if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (m)) |
| { |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| break; |
| } |
| for (tree m : implicitly_declared) |
| if (DECL_OVERLOADED_OPERATOR_IS (m, EQ_EXPR)) |
| { |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, m)); |
| break; |
| } |
| } |
| return elts; |
| } |
| |
| /* Enumerate bases of class R for eval_*of. KIND is METAFN_BASES_OF |
| or METAFN_HAS_INACCESSIBLE_BASES. */ |
| |
| static vec<constructor_elt, va_gc> * |
| class_bases_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| tree actx, tree call, bool *non_constant_p, |
| tree *jump_target, enum metafn_code kind, tree fun) |
| { |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| tree binfo = TYPE_BINFO (r), base_binfo; |
| for (unsigned i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) |
| { |
| tree a = eval_is_accessible (loc, ctx, base_binfo, REFLECT_BASE, actx, |
| call, non_constant_p, jump_target, fun); |
| if (*jump_target || *non_constant_p) |
| return nullptr; |
| if (a == boolean_false_node) |
| { |
| if (kind == METAFN_HAS_INACCESSIBLE_BASES && elts == nullptr) |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, boolean_true_node); |
| continue; |
| } |
| gcc_assert (a == boolean_true_node); |
| if (kind == METAFN_HAS_INACCESSIBLE_BASES) |
| continue; |
| CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, |
| get_reflection_raw (loc, base_binfo, |
| REFLECT_BASE)); |
| } |
| return elts; |
| } |
| |
| /* Implement std::meta::members_of. |
| A declaration D members-of-precedes a point P if D precedes either P or the |
| point immediately following the class-specifier of the outermost class for |
| which P is in a complete-class context. |
| A declaration D of a member M of a class or namespace Q is |
| Q-members-of-eligible if |
| -- the host scope of D is the class scope or namespace scope associated |
| with Q, |
| -- D is not a friend declaration, |
| -- M is not a closure type, |
| -- M is not a specialization of a template, |
| -- if Q is a class that is not a closure type, then M is a direct member of |
| Q that is not a variant member of a nested anonymous union of Q, and |
| -- if Q is a closure type, then M is a function call operator or function |
| call operator template. |
| It is implementation-defined whether declarations of other members of a |
| closure type Q are Q-members-of-eligible. |
| A member M of a class or namespace Q is Q-members-of-representable from a |
| point P if a Q-members-of-eligible declaration of M members-of-precedes P, |
| and M is |
| -- a class or enumeration type |
| -- a type alias |
| -- a class template, function template, variable template, alias template, |
| or concept, |
| -- a variable or reference V for which the type of V does not contain an |
| undeduced placeholder type, |
| -- a function F for which |
| -- the type of F does not contain an undeduced placeholder type, |
| -- the constraints (if any) of F are satisfied, and |
| -- if F is a prospective destructor, F is the selected destructor, |
| -- a non-static data member, |
| -- a namespace, or |
| -- a namespace alias. |
| Returns: A vector containing reflections of all members M of the entity Q |
| represented by dealias(r) for which |
| -- M is Q-members-of-representable from some point in the evaluation |
| context and |
| -- is_accessible(^^M, ctx) is true. |
| If dealias(r) represents a class C, then the vector also contains |
| reflections representing all unnamed bit-fields B whose declarations |
| inhabit the class scope corresponding to C for which |
| is_accessible(^^B, ctx) is true. |
| Reflections of class members and unnamed bit-fields that are declared |
| appear in the order in which they are declared. |
| Throws: meta::exception unless dealias(r) is a reflection representing |
| either a class type that is complete from some point in the evaluation |
| context or a namespace. */ |
| |
| static tree |
| eval_members_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| tree actx, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| r = maybe_strip_typedefs (r); |
| if (TREE_CODE (r) == NAMESPACE_DECL) |
| r = ORIGINAL_NAMESPACE (r); |
| vec<constructor_elt, va_gc> *elts; |
| if (TREE_CODE (r) == NAMESPACE_DECL) |
| elts = namespace_members_of (loc, r); |
| else if (CLASS_TYPE_P (r) |
| && complete_type_or_maybe_complain (r, NULL_TREE, tf_none)) |
| { |
| elts = class_members_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, METAFN_MEMBERS_OF, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| } |
| else |
| return throw_exception (loc, ctx, |
| "neither complete class type nor namespace", |
| fun, non_constant_p, jump_target); |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Implement std::meta::bases_of. |
| Returns: Let C be the class represented by dealias(type). |
| A vector containing the reflections of all the direct base class |
| relationships B, if any, of C such that is_accessible(^^B, ctx) is true. |
| The direct base class relationships appear in the order in which the |
| corresponding base classes appear in the base-specifier-list of C. |
| Throws: meta::exception unless dealias(type) represents a class type that |
| is complete from some point in the evaluation context. */ |
| |
| static tree |
| eval_bases_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| tree actx, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| r = maybe_strip_typedefs (r); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| if (CLASS_TYPE_P (r) |
| && complete_type_or_maybe_complain (r, NULL_TREE, tf_none)) |
| { |
| elts = class_bases_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, METAFN_BASES_OF, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| } |
| else |
| return throw_exception (loc, ctx, "not a complete class type", |
| fun, non_constant_p, jump_target); |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Implement std::meta::static_data_members_of. |
| Returns: A vector containing each element e of members_of(type, ctx) such |
| that is_variable(e) is true, preserving their order. |
| Throws: meta::exception unless dealias(type) represents a class type that |
| is complete from some point in the evaluation context. */ |
| |
| static tree |
| eval_static_data_members_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| tree actx, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| r = maybe_strip_typedefs (r); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| if (CLASS_TYPE_P (r) |
| && complete_type_or_maybe_complain (r, NULL_TREE, tf_none)) |
| { |
| elts = class_members_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, METAFN_STATIC_DATA_MEMBERS_OF, |
| fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| } |
| else |
| return throw_exception (loc, ctx, "not a complete class type", |
| fun, non_constant_p, jump_target); |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Implement std::meta::nonstatic_data_members_of. |
| Returns: A vector containing each element e of members_of(type, ctx) such |
| that is_nonstatic_data_member(e) is true, preserving their order. |
| Throws: meta::exception unless dealias(type) represents a class type that |
| is complete from some point in the evaluation context. */ |
| |
| static tree |
| eval_nonstatic_data_members_of (location_t loc, const constexpr_ctx *ctx, |
| tree r, tree actx, tree call, |
| bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| r = maybe_strip_typedefs (r); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| if (CLASS_TYPE_P (r) |
| && complete_type_or_maybe_complain (r, NULL_TREE, tf_none)) |
| { |
| elts = class_members_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, METAFN_NONSTATIC_DATA_MEMBERS_OF, |
| fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| } |
| else |
| return throw_exception (loc, ctx, "not a complete class type", |
| fun, non_constant_p, jump_target); |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Implement std::meta::subobjects_of. |
| Returns: A vector containing each element of bases_of(type, ctx) followed |
| by each element of nonstatic_data_members_of(type, ctx), preserving their |
| order. |
| Throws: meta::exception unless dealias(type) represents a class type that |
| is complete from some point in the evaluation context. */ |
| |
| static tree |
| eval_subobjects_of (location_t loc, const constexpr_ctx *ctx, tree r, |
| tree actx, tree call, bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| r = maybe_strip_typedefs (r); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| if (CLASS_TYPE_P (r) |
| && complete_type_or_maybe_complain (r, NULL_TREE, tf_none)) |
| { |
| elts = class_bases_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, METAFN_BASES_OF, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| vec<constructor_elt, va_gc> *elts2 |
| = class_members_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, METAFN_NONSTATIC_DATA_MEMBERS_OF, |
| fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (elts == nullptr) |
| elts = elts2; |
| else if (elts2) |
| vec_safe_splice (elts, elts2); |
| } |
| else |
| return throw_exception (loc, ctx, "not a complete class type", |
| fun, non_constant_p, jump_target); |
| return get_vector_of_info_elts (elts); |
| } |
| |
| /* Implement std::meta::has_inaccessible_nonstatic_data_members. |
| Returns: true if is_accessible(R, ctx) is false for any R in |
| nonstatic_data_members_of(r, access_context::unchecked()). |
| Otherwise, false. |
| Throws: meta::exception if |
| -- the evaluation of |
| nonstatic_data_members_of(r, access_context::unchecked()) would exit via |
| an exception and or |
| -- r represents a closure type. */ |
| |
| static tree |
| eval_has_inaccessible_nonstatic_data_members (location_t loc, |
| const constexpr_ctx *ctx, |
| tree r, tree actx, tree call, |
| bool *non_constant_p, |
| tree *jump_target, tree fun) |
| { |
| r = maybe_strip_typedefs (r); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| if (CLASS_TYPE_P (r) |
| && complete_type_or_maybe_complain (r, NULL_TREE, tf_none)) |
| { |
| if (LAMBDA_TYPE_P (r)) |
| return throw_exception (loc, ctx, "closure type", fun, |
| non_constant_p, jump_target); |
| elts = class_members_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, |
| METAFN_HAS_INACCESSIBLE_NONSTATIC_DATA_MEMBERS, |
| fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| } |
| else |
| return throw_exception (loc, ctx, "not a complete class type", |
| fun, non_constant_p, jump_target); |
| if (elts == nullptr) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| |
| /* Implement std::meta::has_inaccessible_bases. |
| Returns: true if is_accessible(R, ctx) is false for any R in |
| bases_of(r, access_context::unchecked()). Otherwise, false. |
| Throws: meta::exception if the evaluation of |
| bases_of(r, access_context::unchecked()) would exit via an exception. */ |
| |
| static tree |
| eval_has_inaccessible_bases (location_t loc, const constexpr_ctx *ctx, |
| tree r, tree actx, tree call, |
| bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| r = maybe_strip_typedefs (r); |
| vec<constructor_elt, va_gc> *elts = nullptr; |
| if (CLASS_TYPE_P (r) |
| && complete_type_or_maybe_complain (r, NULL_TREE, tf_none)) |
| { |
| elts = class_bases_of (loc, ctx, r, actx, call, non_constant_p, |
| jump_target, METAFN_HAS_INACCESSIBLE_BASES, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| } |
| else |
| return throw_exception (loc, ctx, "not a complete class type", |
| fun, non_constant_p, jump_target); |
| if (elts == nullptr) |
| return boolean_false_node; |
| else |
| return boolean_true_node; |
| } |
| |
| /* Implement std::meta::has_inaccessible_subobjects. |
| Effects: Equivalent to: |
| return has_inaccessible_bases(r, ctx) |
| || has_inaccessible_nonstatic_data_members(r, ctx); */ |
| |
| static tree |
| eval_has_inaccessible_subobjects (location_t loc, const constexpr_ctx *ctx, |
| tree r, tree actx, tree call, |
| bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| tree b = eval_has_inaccessible_bases (loc, ctx, r, actx, call, |
| non_constant_p, jump_target, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (b == boolean_true_node) |
| return b; |
| return eval_has_inaccessible_nonstatic_data_members (loc, ctx, r, actx, |
| call, non_constant_p, |
| jump_target, fun); |
| } |
| |
| /* Implement std::meta::exception::_S_exception_cvt_to_utf8 and |
| std::meta::exception::_S_exception_cvt_from_utf8. This is |
| an implementation specific metafunction which translates string_view |
| into u8string_view resp. u8string_view into string_view for use in |
| std::meta::exception constructors. On translation failure returns an empty |
| {u8,}string_view. TO_UTF8 is true for _S_exception_cvt_to_utf8 and false |
| for _S_exception_cvt_from_utf8. */ |
| |
| static tree |
| eval_exception__S_exception_cvt_tofrom_utf8 (location_t loc, |
| const constexpr_ctx *ctx, |
| tree call, bool *non_constant_p, |
| bool *overflow_p, |
| tree *jump_target, tree fun, |
| bool to_utf8) |
| { |
| tree str = get_range_elts (loc, ctx, call, 0, non_constant_p, overflow_p, |
| jump_target, REFLECT_CONSTANT_STRING, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| if (TREE_CODE (str) != STRING_CST |
| || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (str))) |
| != (to_utf8 ? char_type_node : char8_type_node))) |
| { |
| error_at (loc, "unexpected argument to %qs", |
| to_utf8 ? "_S_exception_cvt_to_utf8" |
| : "_S_exception_cvt_from_utf8"); |
| *non_constant_p = true; |
| return call; |
| } |
| /* We need to translate the string twice for the theoretical case |
| of non-UTF8 SOURCE_CHARSET. First translate from {exec charset,UTF-8} to |
| SOURCE_CHARSET... */ |
| cpp_string istr, ostr; |
| istr.len = TREE_STRING_LENGTH (str) + 1; |
| istr.text = (const unsigned char *) TREE_STRING_POINTER (str); |
| const char *name; |
| if (!cpp_translate_string (parse_in, &istr, &ostr, |
| to_utf8 ? CPP_STRING : CPP_UTF8STRING, true)) |
| { |
| ostr.text = NULL; |
| name = ""; |
| } |
| else |
| name = (const char *) ostr.text; |
| /* And then let get_string_literal translate from SOURCE_CHARSET to |
| {UTF-8,exec charset}. */ |
| tree dchar_type = to_utf8 ? char8_type_node : char_type_node; |
| str = get_string_literal (name, dchar_type); |
| free (const_cast <unsigned char *> (ostr.text)); |
| if (str == NULL_TREE) |
| { |
| str = get_string_literal ("", dchar_type); |
| gcc_assert (str); |
| } |
| releasing_vec args (make_tree_vector_single (str)); |
| tree ret = build_special_member_call (NULL_TREE, complete_ctor_identifier, |
| &args, TREE_TYPE (call), LOOKUP_NORMAL, |
| tf_warning_or_error); |
| return build_cplus_new (TREE_TYPE (call), ret, tf_warning_or_error); |
| } |
| |
| /* Helper for eval_extract, extracting a reference type T. |
| Returns: If r represents an object O, then a reference to O. |
| Otherwise, a reference to the object declared, or referred to, by the |
| variable represented by r. |
| Throws: meta::exception unless |
| -- r represents a variable or object of type U, |
| -- is_convertible_v<remove_reference_t<U>(*)[], |
| remove_reference_t<T>(*)[]> is true, and |
| -- If r represents a variable, then either that variable is usable in |
| constant expressions or its lifetime began within the core constant |
| expression currently under evaluation. */ |
| |
| static tree |
| extract_ref (location_t loc, const constexpr_ctx *ctx, tree T, tree r, |
| reflect_kind kind, bool *non_constant_p, tree *jump_target, |
| tree fun) |
| { |
| auto adjust_type = [](tree type) -> tree |
| { |
| if (TYPE_REF_P (type)) |
| type = TREE_TYPE (type); |
| type = build_cplus_array_type (type, NULL_TREE); |
| return build_pointer_type (type); |
| }; |
| |
| const bool var_p = eval_is_variable (r, kind) == boolean_true_node; |
| if (var_p || eval_is_object (kind) == boolean_true_node) |
| { |
| /* The wording is saying that U is the type of r. */ |
| tree U = TREE_TYPE (r); |
| if (is_convertible (adjust_type (U), adjust_type (T)) |
| && (!var_p || is_constant_expression (r))) |
| { |
| if (TYPE_REF_P (TREE_TYPE (r))) |
| { |
| r = DECL_INITIAL (r); |
| r = maybe_get_reference_referent (r); |
| gcc_checking_assert (!TYPE_REF_P (TREE_TYPE (r))); |
| } |
| return build_address (r); |
| } |
| } |
| |
| return throw_exception (loc, ctx, "value cannot be extracted", fun, |
| non_constant_p, jump_target); |
| } |
| |
| /* Helper for extract_value. Return true iff we can extract value of |
| type U using type T. */ |
| |
| static bool |
| can_extract_value_p (tree T, tree U) |
| { |
| if (POINTER_TYPE_P (U) |
| && (similar_type_p (T, U) |
| || (FUNCTION_POINTER_TYPE_P (T) && FUNCTION_POINTER_TYPE_P (U))) |
| && is_convertible (U, T)) |
| return true; |
| else if (same_type_ignoring_top_level_qualifiers_p (T, U)) |
| return true; |
| else if (TREE_CODE (U) == ARRAY_TYPE |
| && POINTER_TYPE_P (T) |
| && is_convertible (U, T)) |
| { |
| /* remove_extent_t<U> */ |
| U = TREE_TYPE (U); |
| U = strip_typedefs (U); |
| /* remove_extent_t<U>* */ |
| U = build_pointer_type (U); |
| return similar_type_p (T, U); |
| } |
| else if (LAMBDA_TYPE_P (U) |
| && FUNCTION_POINTER_TYPE_P (T) |
| && is_convertible (U, T)) |
| return true; |
| return false; |
| } |
| |
| /* Helper for eval_extract, extracting a value. |
| Let U be the type of the value or object that r represents. |
| Returns: static_cast<T>([:R:]), where R is a constant expression of |
| type info such that R == r is true. |
| Throws: meta::exception unless |
| -- U is a pointer type, T and U are either similar or both function pointer |
| types, and is_convertible_v<U, T> is true, |
| -- U is not a pointer type and the cv-unqualified types of T and U are the |
| same, |
| -- U is an array type, T is a pointer type, remove_extent_t<U>* and T are |
| similar types, and the value r represents is convertible to T, or |
| -- U is a closure type, T is a function pointer type, and the value that r |
| represents is convertible to T. */ |
| |
| static tree |
| extract_value (location_t loc, const constexpr_ctx *ctx, tree T, tree r, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| if (REFLECT_EXPR_P (r)) |
| { |
| r = REFLECT_EXPR_HANDLE (r); |
| if (can_extract_value_p (T, TREE_TYPE (r))) |
| return build_static_cast (loc, T, r, tf_none); |
| } |
| return throw_exception (loc, ctx, "value cannot be extracted", fun, |
| non_constant_p, jump_target); |
| } |
| |
| /* Helper for extract_member_or_function. Return true iff we can |
| extract an NSDM or function R of kind KIND using type T. */ |
| |
| static bool |
| can_extract_member_or_function_p (tree T, tree r, reflect_kind kind) |
| { |
| if (eval_is_nonstatic_data_member (r) == boolean_true_node) |
| { |
| if (eval_is_bit_field (r, kind) == boolean_true_node) |
| return false; |
| /* static union { int m; }; extract<int>(^^m); is invalid. */ |
| if (TREE_CODE (r) == FIELD_DECL |
| && ANON_UNION_TYPE_P (DECL_CONTEXT (r))) |
| { |
| tree c = CP_TYPE_CONTEXT (DECL_CONTEXT (r)); |
| while (ANON_UNION_TYPE_P (c)) |
| c = CP_TYPE_CONTEXT (c); |
| if (!TYPE_P (c)) |
| return false; |
| } |
| /* Create the X C::* type. */ |
| tree type = build_offset_type (CP_DECL_CONTEXT (r), TREE_TYPE (r)); |
| if (similar_type_p (type, T) && is_convertible (type, T)) |
| return true; |
| return false; |
| } |
| else if (DECL_IOBJ_MEMBER_FUNCTION_P (r)) |
| { |
| tree F = TREE_TYPE (r); |
| F = build_pointer_type (F); |
| F = build_ptrmemfunc_type (F); |
| if (same_type_p (T, F)) |
| return true; |
| return false; |
| } |
| else if (TREE_CODE (r) == FUNCTION_DECL) |
| { |
| tree F = TREE_TYPE (r); |
| F = build_pointer_type (F); |
| if (same_type_p (T, F)) |
| return true; |
| return false; |
| } |
| |
| return false; |
| } |
| |
| /* Helper for eval_extract, extracting a NSDM or function. |
| Returns: |
| -- If T is a pointer type, then a pointer value pointing to the function |
| represented by r. |
| -- Otherwise, a pointer-to-member value designating the non-static data |
| member or function represented by r. |
| Throws: meta::exception unless |
| -- r represents a non-static data member with type X, that is not |
| a bit-field, that is a direct member of class C, T and X C::* |
| are similar types, and is_convertible_v<X C::*, T> is true; |
| -- r represents an implicit object member function with type F or |
| F noexcept that is a direct member of a class C, and T is F C::*; or |
| -- r represents a non-member function, static member function, or |
| explicit object member function of function type F or F noexcept, and |
| T is F*. */ |
| |
| static tree |
| extract_member_or_function (location_t loc, const constexpr_ctx *ctx, |
| tree T, tree r, reflect_kind kind, |
| bool *non_constant_p, tree *jump_target, tree fun) |
| { |
| r = MAYBE_BASELINK_FUNCTIONS (r); |
| if (!can_extract_member_or_function_p (T, r, kind)) |
| return throw_exception (loc, ctx, "value cannot be extracted", fun, |
| non_constant_p, jump_target); |
| |
| const tsubst_flags_t complain = complain_flags (ctx); |
| if (POINTER_TYPE_P (T)) |
| return cp_build_addr_expr (r, complain); |
| else |
| { |
| if (!mark_used (r, complain)) |
| { |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| r = build_offset_ref (DECL_CONTEXT (r), r, /*address_p=*/true, complain); |
| r = cp_build_addr_expr (r, complain); |
| r = cp_convert (T, r, complain); |
| return r; |
| } |
| } |
| |
| /* Process std::meta::extract. |
| Let U be remove_cv_t<T>. |
| Effects: Equivalent to: |
| if constexpr (is_reference_type(^^T)) { |
| return extract-ref<T>(r); |
| } else if constexpr (is_nonstatic_data_member(r) || is_function(r)) { |
| return extract-member-or-function<U>(r); |
| } else { |
| return extract-value<U>(constant_of(r)); |
| } |
| */ |
| |
| static tree |
| eval_extract (location_t loc, const constexpr_ctx *ctx, tree type, tree r, |
| reflect_kind kind, bool *non_constant_p, bool *overflow_p, |
| tree *jump_target, tree fun) |
| { |
| if (eval_is_reference_type (loc, type) == boolean_true_node) |
| return extract_ref (loc, ctx, type, r, kind, non_constant_p, jump_target, |
| fun); |
| type = cv_unqualified (type); |
| if (eval_is_nonstatic_data_member (r) == boolean_true_node |
| || eval_is_function (r) == boolean_true_node) |
| return extract_member_or_function (loc, ctx, type, r, kind, non_constant_p, |
| jump_target, fun); |
| else |
| { |
| r = eval_constant_of (loc, ctx, r, kind, non_constant_p, overflow_p, |
| jump_target, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| return extract_value (loc, ctx, type, r, non_constant_p, jump_target, |
| fun); |
| } |
| } |
| |
| /* Expand a call to a metafunction FUN. CALL is the CALL_EXPR. |
| JUMP_TARGET is set if we are throwing std::meta::exception. */ |
| |
| tree |
| process_metafunction (const constexpr_ctx *ctx, tree fun, tree call, |
| bool *non_constant_p, bool *overflow_p, |
| tree *jump_target) |
| { |
| tree name = DECL_NAME (fun); |
| const char *ident = IDENTIFIER_POINTER (name); |
| const location_t loc = cp_expr_loc_or_input_loc (call); |
| const metafn_info *minfo |
| = metafn_lookup::find (ident, IDENTIFIER_LENGTH (name)); |
| if (minfo == NULL) |
| { |
| not_found: |
| error_at (loc, "unknown metafunction %qD", fun); |
| *non_constant_p = true; |
| return NULL_TREE; |
| } |
| tree h = NULL_TREE, h1 = NULL_TREE, hvec = NULL_TREE, expr = NULL_TREE; |
| tree type = NULL_TREE, ht, info; |
| reflect_kind kind = REFLECT_UNDEF; |
| for (int argno = 0; argno < 3; ++argno) |
| switch ((minfo->kind >> ((argno + 1) * METAFN_KIND_SHIFT)) |
| & METAFN_KIND_MASK) |
| { |
| case METAFN_KIND_ARG_VOID: |
| break; |
| case METAFN_KIND_ARG_INFO: |
| case METAFN_KIND_ARG_TINFO: |
| gcc_assert (argno < 2); |
| info = get_info (ctx, call, argno, non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| ht = REFLECT_EXPR_HANDLE (info); |
| if (((minfo->kind >> ((argno + 1) * METAFN_KIND_SHIFT)) |
| & METAFN_KIND_MASK) == METAFN_KIND_ARG_TINFO) |
| { |
| if (eval_is_type (ht) != boolean_true_node) |
| return throw_exception_nontype (loc, ctx, fun, non_constant_p, |
| jump_target); |
| } |
| if (argno == 0) |
| { |
| kind = REFLECT_EXPR_KIND (info); |
| h = ht; |
| } |
| else |
| h1 = ht; |
| break; |
| case METAFN_KIND_ARG_REFLECTION_RANGE: |
| gcc_assert (argno == 1); |
| hvec = get_info_vec (loc, ctx, call, argno, non_constant_p, |
| overflow_p, jump_target, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| break; |
| case METAFN_KIND_ARG_REFLECTION_RANGET: |
| hvec = get_type_info_vec (loc, ctx, call, argno, non_constant_p, |
| overflow_p, jump_target, fun); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| break; |
| case METAFN_KIND_ARG_INPUT_RANGE: |
| /* Handled in eval_reflect_constant_*. */ |
| gcc_assert (argno == 0); |
| break; |
| case METAFN_KIND_ARG_TEMPLATE_PARM: |
| case METAFN_KIND_ARG_TEMPLATE_PARM_REF: |
| type = TREE_VEC_ELT (get_template_innermost_arguments (fun), 0); |
| /* FALLTHRU */ |
| case METAFN_KIND_ARG_SIZE_T: |
| case METAFN_KIND_ARG_OPERATORS: |
| gcc_assert (argno == 0); |
| expr = get_nth_callarg (call, 0); |
| expr = cxx_eval_constant_expression (ctx, expr, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| break; |
| case METAFN_KIND_ARG_UNSIGNED: |
| case METAFN_KIND_ARG_ACCESS_CONTEXT: |
| case METAFN_KIND_ARG_DATA_MEMBER_OPTIONS: |
| gcc_assert (argno == 1); |
| expr = get_nth_callarg (call, argno); |
| expr = cxx_eval_constant_expression (ctx, expr, vc_prvalue, |
| non_constant_p, overflow_p, |
| jump_target); |
| if (*jump_target || *non_constant_p) |
| return NULL_TREE; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| switch (minfo->code) |
| { |
| case METAFN_OPERATOR_OF: |
| return eval_operator_of (loc, ctx, h, non_constant_p, jump_target, |
| TREE_TYPE (call), fun); |
| case METAFN_SYMBOL_OF: |
| return eval_symbol_of (loc, ctx, expr, non_constant_p, jump_target, |
| char_type_node, TREE_TYPE (call), fun); |
| case METAFN_U8SYMBOL_OF: |
| return eval_symbol_of (loc, ctx, expr, non_constant_p, jump_target, |
| char8_type_node, TREE_TYPE (call), fun); |
| case METAFN_HAS_IDENTIFIER: |
| return eval_has_identifier (h, kind); |
| case METAFN_IDENTIFIER_OF: |
| return eval_identifier_of (loc, ctx, h, kind, non_constant_p, jump_target, |
| char_type_node, TREE_TYPE (call), fun); |
| case METAFN_U8IDENTIFIER_OF: |
| return eval_identifier_of (loc, ctx, h, kind, non_constant_p, jump_target, |
| char8_type_node, TREE_TYPE (call), fun); |
| case METAFN_DISPLAY_STRING_OF: |
| return eval_display_string_of (loc, ctx, h, kind, non_constant_p, |
| jump_target, char_type_node, |
| TREE_TYPE (call), fun); |
| case METAFN_U8DISPLAY_STRING_OF: |
| return eval_display_string_of (loc, ctx, h, kind, non_constant_p, |
| jump_target, char8_type_node, |
| TREE_TYPE (call), fun); |
| case METAFN_SOURCE_LOCATION_OF: |
| return eval_source_location_of (loc, h, kind, TREE_TYPE (call)); |
| case METAFN_TYPE_OF: |
| return eval_type_of (loc, ctx, h, kind, non_constant_p, jump_target, fun); |
| case METAFN_OBJECT_OF: |
| return eval_object_of (loc, ctx, h, kind, non_constant_p, overflow_p, |
| jump_target, fun); |
| case METAFN_CONSTANT_OF: |
| return eval_constant_of (loc, ctx, h, kind, non_constant_p, overflow_p, |
| jump_target, fun); |
| case METAFN_IS_PUBLIC: |
| return eval_is_public (h, kind); |
| case METAFN_IS_PROTECTED: |
| return eval_is_protected (h, kind); |
| case METAFN_IS_PRIVATE: |
| return eval_is_private (h, kind); |
| case METAFN_IS_VIRTUAL: |
| return eval_is_virtual (h, kind); |
| case METAFN_IS_PURE_VIRTUAL: |
| return eval_is_pure_virtual (h); |
| case METAFN_IS_OVERRIDE: |
| return eval_is_override (h); |
| case METAFN_IS_FINAL: |
| return eval_is_final (h); |
| case METAFN_IS_DELETED: |
| return eval_is_deleted (h); |
| case METAFN_IS_DEFAULTED: |
| return eval_is_defaulted (h); |
| case METAFN_IS_USER_PROVIDED: |
| return eval_is_user_provided (h); |
| case METAFN_IS_USER_DECLARED: |
| return eval_is_user_declared (h); |
| case METAFN_IS_EXPLICIT: |
| return eval_is_explicit (h); |
| case METAFN_IS_NOEXCEPT: |
| return eval_is_noexcept (h); |
| case METAFN_IS_BIT_FIELD: |
| return eval_is_bit_field (h, kind); |
| case METAFN_IS_ENUMERATOR: |
| return eval_is_enumerator (h); |
| case METAFN_IS_ANNOTATION: |
| return eval_is_annotation (h, kind); |
| case METAFN_IS_CONST: |
| return eval_is_const (h, kind); |
| case METAFN_IS_VOLATILE: |
| return eval_is_volatile (h, kind); |
| case METAFN_IS_MUTABLE_MEMBER: |
| return eval_is_mutable_member (h); |
| case METAFN_IS_LVALUE_REFERENCE_QUALIFIED: |
| return eval_is_lrvalue_reference_qualified (h, kind, /*rvalue_p=*/false); |
| case METAFN_IS_RVALUE_REFERENCE_QUALIFIED: |
| return eval_is_lrvalue_reference_qualified (h, kind, /*rvalue_p=*/true); |
| case METAFN_HAS_STATIC_STORAGE_DURATION: |
| return eval_has_static_storage_duration (h, kind); |
| case METAFN_HAS_THREAD_STORAGE_DURATION: |
| return eval_has_thread_storage_duration (h, kind); |
| case METAFN_HAS_AUTOMATIC_STORAGE_DURATION: |
| return eval_has_automatic_storage_duration (h, kind); |
| case METAFN_HAS_INTERNAL_LINKAGE: |
| return eval_has_internal_linkage (h, kind); |
| case METAFN_HAS_MODULE_LINKAGE: |
| return eval_has_module_linkage (h, kind); |
| case METAFN_HAS_EXTERNAL_LINKAGE: |
| return eval_has_external_linkage (h, kind); |
| case METAFN_HAS_C_LANGUAGE_LINKAGE: |
| return eval_has_c_language_linkage (h, kind); |
| case METAFN_HAS_LINKAGE: |
| return eval_has_linkage (h, kind); |
| case METAFN_IS_COMPLETE_TYPE: |
| return eval_is_complete_type (h); |
| case METAFN_IS_ENUMERABLE_TYPE: |
| return eval_is_enumerable_type (h); |
| case METAFN_IS_VARIABLE: |
| return eval_is_variable (h, kind); |
| case METAFN_IS_TYPE: |
| return eval_is_type (h); |
| case METAFN_IS_NAMESPACE: |
| return eval_is_namespace (h); |
| case METAFN_IS_TYPE_ALIAS: |
| return eval_is_type_alias (h); |
| case METAFN_IS_NAMESPACE_ALIAS: |
| return eval_is_namespace_alias (h); |
| case METAFN_IS_FUNCTION: |
| return eval_is_function (h); |
| case METAFN_IS_CONVERSION_FUNCTION: |
| return eval_is_conversion_function (h); |
| case METAFN_IS_OPERATOR_FUNCTION: |
| return eval_is_operator_function (h); |
| case METAFN_IS_LITERAL_OPERATOR: |
| return eval_is_literal_operator (h); |
| case METAFN_IS_SPECIAL_MEMBER_FUNCTION: |
| return eval_is_special_member_function (h); |
| case METAFN_IS_CONSTRUCTOR: |
| return eval_is_constructor (h); |
| case METAFN_IS_DEFAULT_CONSTRUCTOR: |
| return eval_is_default_constructor (h); |
| case METAFN_IS_COPY_CONSTRUCTOR: |
| return eval_is_copy_constructor (h); |
| case METAFN_IS_MOVE_CONSTRUCTOR: |
| return eval_is_move_constructor (h); |
| case METAFN_IS_ASSIGNMENT: |
| return eval_is_assignment (h); |
| case METAFN_IS_COPY_ASSIGNMENT: |
| return eval_is_copy_assignment (h); |
| case METAFN_IS_MOVE_ASSIGNMENT: |
| return eval_is_move_assignment (h); |
| case METAFN_IS_DESTRUCTOR: |
| return eval_is_destructor (h); |
| case METAFN_IS_FUNCTION_PARAMETER: |
| return eval_is_function_parameter (h, kind); |
| case METAFN_IS_EXPLICIT_OBJECT_PARAMETER: |
| return eval_is_explicit_object_parameter (h, kind); |
| case METAFN_HAS_DEFAULT_ARGUMENT: |
| return eval_has_default_argument (h, kind); |
| case METAFN_HAS_ELLIPSIS_PARAMETER: |
| return eval_has_ellipsis_parameter (h); |
| case METAFN_IS_TEMPLATE: |
| return eval_is_template (h); |
| case METAFN_IS_FUNCTION_TEMPLATE: |
| return eval_is_function_template (h); |
| case METAFN_IS_VARIABLE_TEMPLATE: |
| return eval_is_variable_template (h); |
| case METAFN_IS_CLASS_TEMPLATE: |
| return eval_is_class_template (h); |
| case METAFN_IS_ALIAS_TEMPLATE: |
| return eval_is_alias_template (h); |
| case METAFN_IS_CONVERSION_FUNCTION_TEMPLATE: |
| return eval_is_conversion_function_template (h); |
| case METAFN_IS_OPERATOR_FUNCTION_TEMPLATE: |
| return eval_is_operator_function_template (h); |
| case METAFN_IS_LITERAL_OPERATOR_TEMPLATE: |
| return eval_is_literal_operator_template (h); |
| case METAFN_IS_CONSTRUCTOR_TEMPLATE: |
| return eval_is_constructor_template (h); |
| case METAFN_IS_CONCEPT: |
| return eval_is_concept (h); |
| case METAFN_IS_VALUE: |
| return eval_is_value (kind); |
| case METAFN_IS_OBJECT: |
| return eval_is_object (kind); |
| case METAFN_IS_STRUCTURED_BINDING: |
| return eval_is_structured_binding (h, kind); |
| case METAFN_IS_CLASS_MEMBER: |
| return eval_is_class_member (h); |
| case METAFN_IS_NAMESPACE_MEMBER: |
| return eval_is_namespace_member (h); |
| case METAFN_IS_NONSTATIC_DATA_MEMBER: |
| return eval_is_nonstatic_data_member (h); |
| case METAFN_IS_STATIC_MEMBER: |
| return eval_is_static_member (h); |
| case METAFN_IS_BASE: |
| return eval_is_base (h, kind); |
| case METAFN_HAS_DEFAULT_MEMBER_INITIALIZER: |
| return eval_has_default_member_initializer (h); |
| case METAFN_HAS_PARENT: |
| return eval_has_parent (h, kind); |
| case METAFN_PARENT_OF: |
| return eval_parent_of (loc, ctx, h, kind, non_constant_p, jump_target, |
| fun); |
| case METAFN_DEALIAS: |
| return eval_dealias (loc, h, kind); |
| case METAFN_HAS_TEMPLATE_ARGUMENTS: |
| return eval_has_template_arguments (h); |
| case METAFN_TEMPLATE_OF: |
| return eval_template_of (loc, ctx, h, non_constant_p, jump_target, fun); |
| case METAFN_TEMPLATE_ARGUMENTS_OF: |
| return eval_template_arguments_of (loc, ctx, h, non_constant_p, |
| jump_target, fun); |
| case METAFN_PARAMETERS_OF: |
| return eval_parameters_of (loc, ctx, h, non_constant_p, jump_target, |
| fun); |
| case METAFN_VARIABLE_OF: |
| return eval_variable_of (loc, ctx, h, kind, non_constant_p, jump_target, |
| fun); |
| case METAFN_RETURN_TYPE_OF: |
| return eval_return_type_of (loc, ctx, h, kind, non_constant_p, |
| jump_target, fun); |
| case METAFN_IS_ACCESSIBLE: |
| return eval_is_accessible (loc, ctx, h, kind, expr, call, |
| non_constant_p, jump_target, fun); |
| case METAFN_HAS_INACCESSIBLE_NONSTATIC_DATA_MEMBERS: |
| return eval_has_inaccessible_nonstatic_data_members (loc, ctx, h, expr, |
| call, |
| non_constant_p, |
| jump_target, fun); |
| case METAFN_HAS_INACCESSIBLE_BASES: |
| return eval_has_inaccessible_bases (loc, ctx, h, expr, call, |
| non_constant_p, jump_target, fun); |
| case METAFN_HAS_INACCESSIBLE_SUBOBJECTS: |
| return eval_has_inaccessible_subobjects (loc, ctx, h, expr, call, |
| non_constant_p, jump_target, |
| fun); |
| case METAFN_MEMBERS_OF: |
| return eval_members_of (loc, ctx, h, expr, call, non_constant_p, |
| jump_target, fun); |
| case METAFN_BASES_OF: |
| return eval_bases_of (loc, ctx, h, expr, call, non_constant_p, |
| jump_target, fun); |
| case METAFN_STATIC_DATA_MEMBERS_OF: |
| return eval_static_data_members_of (loc, ctx, h, expr, call, |
| non_constant_p, jump_target, |
| fun); |
| case METAFN_NONSTATIC_DATA_MEMBERS_OF: |
| return eval_nonstatic_data_members_of (loc, ctx, h, expr, call, |
| non_constant_p, jump_target, |
| fun); |
| case METAFN_SUBOBJECTS_OF: |
| return eval_subobjects_of (loc, ctx, h, expr, call, non_constant_p, |
| jump_target, fun); |
| case METAFN_ENUMERATORS_OF: |
| return eval_enumerators_of (loc, ctx, h, non_constant_p, jump_target, |
| fun); |
| case METAFN_OFFSET_OF: |
| return eval_offset_of (loc, ctx, h, kind, TREE_TYPE (call), |
| non_constant_p, jump_target, fun); |
| case METAFN_SIZE_OF: |
| return eval_size_of (loc, ctx, h, kind, TREE_TYPE (call), non_constant_p, |
| jump_target, fun); |
| case METAFN_ALIGNMENT_OF: |
| return eval_alignment_of (loc, ctx, h, kind, TREE_TYPE (call), |
| non_constant_p, jump_target, fun); |
| case METAFN_BIT_SIZE_OF: |
| return eval_bit_size_of (loc, ctx, h, kind, TREE_TYPE (call), |
| non_constant_p, jump_target, fun); |
| case METAFN_EXTRACT: |
| { |
| type = TREE_VEC_ELT (get_template_innermost_arguments (fun), 0); |
| return eval_extract (loc, ctx, type, h, kind, non_constant_p, |
| overflow_p, jump_target, fun); |
| } |
| case METAFN_CAN_SUBSTITUTE: |
| return eval_can_substitute (loc, ctx, h, hvec, non_constant_p, |
| jump_target, fun); |
| case METAFN_SUBSTITUTE: |
| return eval_substitute (loc, ctx, h, hvec, non_constant_p, jump_target, |
| fun); |
| case METAFN_REFLECT_CONSTANT: |
| return eval_reflect_constant (loc, ctx, type, expr, non_constant_p, |
| jump_target, fun); |
| case METAFN_REFLECT_OBJECT: |
| return eval_reflect_object (loc, ctx, type, expr, non_constant_p, |
| jump_target, fun); |
| case METAFN_REFLECT_FUNCTION: |
| return eval_reflect_function (loc, ctx, type, expr, non_constant_p, |
| jump_target, fun); |
| case METAFN_REFLECT_CONSTANT_STRING: |
| return eval_reflect_constant_string (loc, ctx, call, non_constant_p, |
| overflow_p, jump_target, fun); |
| case METAFN_REFLECT_CONSTANT_ARRAY: |
| return eval_reflect_constant_array (loc, ctx, call, non_constant_p, |
| overflow_p, jump_target, fun); |
| case METAFN_DATA_MEMBER_SPEC: |
| return eval_data_member_spec (loc, ctx, h, expr, non_constant_p, |
| overflow_p, jump_target, fun); |
| case METAFN_IS_DATA_MEMBER_SPEC: |
| return eval_is_data_member_spec (h, kind); |
| case METAFN_DEFINE_AGGREGATE: |
| return eval_define_aggregate (loc, ctx, h, hvec, call, non_constant_p); |
| case METAFN_IS_VOID_TYPE: |
| return eval_is_void_type (h); |
| case METAFN_IS_NULL_POINTER_TYPE: |
| return eval_is_null_pointer_type (h); |
| case METAFN_IS_INTEGRAL_TYPE: |
| return eval_is_integral_type (h); |
| case METAFN_IS_FLOATING_POINT_TYPE: |
| return eval_is_floating_point_type (h); |
| case METAFN_IS_ARRAY_TYPE: |
| return eval_is_array_type (loc, h); |
| case METAFN_IS_POINTER_TYPE: |
| return eval_is_pointer_type (loc, h); |
| case METAFN_IS_LVALUE_REFERENCE_TYPE: |
| return eval_is_lvalue_reference_type (h); |
| case METAFN_IS_RVALUE_REFERENCE_TYPE: |
| return eval_is_rvalue_reference_type (h); |
| case METAFN_IS_MEMBER_OBJECT_POINTER_TYPE: |
| return eval_is_member_object_pointer_type (loc, h); |
| case METAFN_IS_MEMBER_FUNCTION_POINTER_TYPE: |
| return eval_is_member_function_pointer_type (loc, h); |
| case METAFN_IS_ENUM_TYPE: |
| return eval_is_enum_type (loc, h); |
| case METAFN_IS_UNION_TYPE: |
| return eval_is_union_type (loc, h); |
| case METAFN_IS_CLASS_TYPE: |
| return eval_is_class_type (loc, h); |
| case METAFN_IS_FUNCTION_TYPE: |
| return eval_is_function_type (h); |
| case METAFN_IS_REFLECTION_TYPE: |
| return eval_is_reflection_type (h); |
| case METAFN_IS_REFERENCE_TYPE: |
| return eval_is_reference_type (loc, h); |
| case METAFN_IS_ARITHMETIC_TYPE: |
| return eval_is_arithmetic_type (h); |
| case METAFN_IS_FUNDAMENTAL_TYPE: |
| return eval_is_fundamental_type (h); |
| case METAFN_IS_OBJECT_TYPE: |
| return eval_is_object_type (loc, h); |
| case METAFN_IS_SCALAR_TYPE: |
| return eval_is_scalar_type (h); |
| case METAFN_IS_COMPOUND_TYPE: |
| return eval_is_compound_type (h); |
| case METAFN_IS_MEMBER_POINTER_TYPE: |
| return eval_is_member_pointer_type (loc, h); |
| case METAFN_IS_CONST_TYPE: |
| return eval_is_const_type (h); |
| case METAFN_IS_VOLATILE_TYPE: |
| return eval_is_volatile_type (h); |
| case METAFN_IS_TRIVIALLY_COPYABLE_TYPE: |
| return eval_is_trivially_copyable_type (h); |
| case METAFN_IS_STANDARD_LAYOUT_TYPE: |
| return eval_is_standard_layout_type (h); |
| case METAFN_IS_EMPTY_TYPE: |
| return eval_is_empty_type (loc, h); |
| case METAFN_IS_POLYMORPHIC_TYPE: |
| return eval_is_polymorphic_type (loc, h); |
| case METAFN_IS_ABSTRACT_TYPE: |
| return eval_is_abstract_type (h); |
| case METAFN_IS_FINAL_TYPE: |
| return eval_is_final_type (loc, h); |
| case METAFN_IS_AGGREGATE_TYPE: |
| return eval_is_aggregate_type (h); |
| case METAFN_IS_CONSTEVAL_ONLY_TYPE: |
| return eval_is_consteval_only_type (h); |
| case METAFN_IS_SIGNED_TYPE: |
| return eval_is_signed_type (h); |
| case METAFN_IS_UNSIGNED_TYPE: |
| return eval_is_unsigned_type (h); |
| case METAFN_IS_BOUNDED_ARRAY_TYPE: |
| return eval_is_bounded_array_type (loc, h); |
| case METAFN_IS_UNBOUNDED_ARRAY_TYPE: |
| return eval_is_unbounded_array_type (h); |
| case METAFN_IS_SCOPED_ENUM_TYPE: |
| return eval_is_scoped_enum_type (h); |
| case METAFN_IS_CONSTRUCTIBLE_TYPE: |
| return eval_is_constructible_type (h, hvec); |
| case METAFN_IS_DEFAULT_CONSTRUCTIBLE_TYPE: |
| return eval_is_default_constructible_type (h); |
| case METAFN_IS_COPY_CONSTRUCTIBLE_TYPE: |
| return eval_is_copy_constructible_type (h); |
| case METAFN_IS_MOVE_CONSTRUCTIBLE_TYPE: |
| return eval_is_move_constructible_type (h); |
| case METAFN_IS_ASSIGNABLE_TYPE: |
| return eval_is_assignable_type (loc, h, h1); |
| case METAFN_IS_COPY_ASSIGNABLE_TYPE: |
| return eval_is_copy_assignable_type (h); |
| case METAFN_IS_MOVE_ASSIGNABLE_TYPE: |
| return eval_is_move_assignable_type (h); |
| case METAFN_IS_SWAPPABLE_WITH_TYPE: |
| return eval_is_swappable_with_type (loc, ctx, h, h1, call, |
| non_constant_p, jump_target, fun, |
| "is_swappable_with"); |
| case METAFN_IS_SWAPPABLE_TYPE: |
| return eval_is_swappable_type (loc, ctx, h, call, non_constant_p, |
| jump_target, fun, "is_swappable"); |
| case METAFN_IS_DESTRUCTIBLE_TYPE: |
| return eval_is_destructible_type (loc, h); |
| case METAFN_IS_TRIVIALLY_CONSTRUCTIBLE_TYPE: |
| return eval_is_trivially_constructible_type (h, hvec); |
| case METAFN_IS_TRIVIALLY_DEFAULT_CONSTRUCTIBLE_TYPE: |
| return eval_is_trivially_default_constructible_type (h); |
| case METAFN_IS_TRIVIALLY_COPY_CONSTRUCTIBLE_TYPE: |
| return eval_is_trivially_copy_constructible_type (h); |
| case METAFN_IS_TRIVIALLY_MOVE_CONSTRUCTIBLE_TYPE: |
| return eval_is_trivially_move_constructible_type (h); |
| case METAFN_IS_TRIVIALLY_ASSIGNABLE_TYPE: |
| return eval_is_trivially_assignable_type (loc, h, h1); |
| case METAFN_IS_TRIVIALLY_COPY_ASSIGNABLE_TYPE: |
| return eval_is_trivially_copy_assignable_type (h); |
| case METAFN_IS_TRIVIALLY_MOVE_ASSIGNABLE_TYPE: |
| return eval_is_trivially_move_assignable_type (h); |
| case METAFN_IS_TRIVIALLY_DESTRUCTIBLE_TYPE: |
| return eval_is_trivially_destructible_type (loc, h); |
| case METAFN_IS_NOTHROW_CONSTRUCTIBLE_TYPE: |
| return eval_is_nothrow_constructible_type (h, hvec); |
| case METAFN_IS_NOTHROW_DEFAULT_CONSTRUCTIBLE_TYPE: |
| return eval_is_nothrow_default_constructible_type (h); |
| case METAFN_IS_NOTHROW_COPY_CONSTRUCTIBLE_TYPE: |
| return eval_is_nothrow_copy_constructible_type (h); |
| case METAFN_IS_NOTHROW_MOVE_CONSTRUCTIBLE_TYPE: |
| return eval_is_nothrow_move_constructible_type (h); |
| case METAFN_IS_NOTHROW_ASSIGNABLE_TYPE: |
| return eval_is_nothrow_assignable_type (loc, h, h1); |
| case METAFN_IS_NOTHROW_COPY_ASSIGNABLE_TYPE: |
| return eval_is_nothrow_copy_assignable_type (h); |
| case METAFN_IS_NOTHROW_MOVE_ASSIGNABLE_TYPE: |
| return eval_is_nothrow_move_assignable_type (h); |
| case METAFN_IS_NOTHROW_SWAPPABLE_WITH_TYPE: |
| return eval_is_swappable_with_type (loc, ctx, h, h1, call, |
| non_constant_p, jump_target, fun, |
| "is_nothrow_swappable_with"); |
| case METAFN_IS_NOTHROW_SWAPPABLE_TYPE: |
| return eval_is_swappable_type (loc, ctx, h, call, non_constant_p, |
| jump_target, fun, "is_nothrow_swappable"); |
| case METAFN_IS_NOTHROW_DESTRUCTIBLE_TYPE: |
| return eval_is_nothrow_destructible_type (loc, h); |
| case METAFN_IS_IMPLICIT_LIFETIME_TYPE: |
| return eval_is_implicit_lifetime_type (h); |
| case METAFN_HAS_VIRTUAL_DESTRUCTOR: |
| return eval_has_virtual_destructor (h); |
| case METAFN_HAS_UNIQUE_OBJECT_REPRESENTATIONS: |
| return eval_has_unique_object_representations (h); |
| case METAFN_REFERENCE_CONSTRUCTS_FROM_TEMPORARY: |
| return eval_reference_constructs_from_temporary (loc, h, h1); |
| case METAFN_REFERENCE_CONVERTS_FROM_TEMPORARY: |
| return eval_reference_converts_from_temporary (loc, h, h1); |
| case METAFN_RANK: |
| return eval_rank (h); |
| case METAFN_EXTENT: |
| return eval_extent (loc, h, expr); |
| case METAFN_IS_SAME_TYPE: |
| return eval_is_same_type (loc, h, h1); |
| case METAFN_IS_BASE_OF_TYPE: |
| return eval_is_base_of_type (loc, h, h1); |
| case METAFN_IS_VIRTUAL_BASE_OF_TYPE: |
| return eval_is_virtual_base_of_type (loc, h, h1); |
| case METAFN_IS_CONVERTIBLE_TYPE: |
| return eval_is_convertible_type (loc, h, h1); |
| case METAFN_IS_NOTHROW_CONVERTIBLE_TYPE: |
| return eval_is_nothrow_convertible_type (loc, h, h1); |
| case METAFN_IS_LAYOUT_COMPATIBLE_TYPE: |
| return eval_is_layout_compatible_type (loc, h, h1); |
| case METAFN_IS_POINTER_INTERCONVERTIBLE_BASE_OF_TYPE: |
| return eval_is_pointer_interconvertible_base_of_type (loc, h, h1); |
| case METAFN_IS_INVOCABLE_TYPE: |
| return eval_is_invocable_type (loc, h, hvec); |
| case METAFN_IS_INVOCABLE_R_TYPE: |
| return eval_is_invocable_r_type (loc, ctx, h, h1, hvec, call, |
| non_constant_p, jump_target, fun, |
| "is_invocable_r"); |
| case METAFN_IS_NOTHROW_INVOCABLE_TYPE: |
| return eval_is_nothrow_invocable_type (loc, h, hvec); |
| case METAFN_IS_NOTHROW_INVOCABLE_R_TYPE: |
| return eval_is_invocable_r_type (loc, ctx, h, h1, hvec, call, |
| non_constant_p, jump_target, fun, |
| "is_nothrow_invocable_r"); |
| case METAFN_REMOVE_CONST: |
| return eval_remove_const (loc, h); |
| case METAFN_REMOVE_VOLATILE: |
| return eval_remove_volatile (loc, h); |
| case METAFN_REMOVE_CV: |
| return eval_remove_cv (loc, h); |
| case METAFN_ADD_CONST: |
| return eval_add_const (loc, h); |
| case METAFN_ADD_VOLATILE: |
| return eval_add_volatile (loc, h); |
| case METAFN_ADD_CV: |
| return eval_add_cv (loc, h); |
| case METAFN_REMOVE_REFERENCE: |
| return eval_remove_reference (loc, h); |
| case METAFN_ADD_LVALUE_REFERENCE: |
| return eval_add_lvalue_reference (loc, h); |
| case METAFN_ADD_RVALUE_REFERENCE: |
| return eval_add_rvalue_reference (loc, h); |
| case METAFN_MAKE_SIGNED: |
| return eval_make_signed (loc, ctx, h, false, non_constant_p, jump_target, |
| fun); |
| case METAFN_MAKE_UNSIGNED: |
| return eval_make_signed (loc, ctx, h, true, non_constant_p, jump_target, |
| fun); |
| case METAFN_REMOVE_EXTENT: |
| return eval_remove_extent (loc, h); |
| case METAFN_REMOVE_ALL_EXTENTS: |
| return eval_remove_all_extents (loc, h); |
| case METAFN_REMOVE_POINTER: |
| return eval_remove_pointer (loc, h); |
| case METAFN_ADD_POINTER: |
| return eval_add_pointer (loc, h); |
| case METAFN_REMOVE_CVREF: |
| return eval_remove_cvref (loc, h); |
| case METAFN_DECAY: |
| return eval_decay (loc, h); |
| case METAFN_COMMON_TYPE: |
| return eval_common_type (loc, ctx, hvec, call, non_constant_p, |
| jump_target, fun, ident); |
| case METAFN_COMMON_REFERENCE: |
| return eval_common_type (loc, ctx, hvec, call, non_constant_p, |
| jump_target, fun, ident); |
| case METAFN_UNDERLYING_TYPE: |
| return eval_underlying_type (loc, ctx, h, non_constant_p, jump_target, |
| fun); |
| case METAFN_INVOKE_RESULT: |
| return eval_invoke_result (loc, ctx, h, hvec, call, non_constant_p, |
| jump_target, fun); |
| case METAFN_UNWRAP_REFERENCE: |
| return eval_unwrap_reference (loc, ctx, h, call, non_constant_p, |
| jump_target, fun, ident); |
| case METAFN_UNWRAP_REF_DECAY: |
| return eval_unwrap_reference (loc, ctx, h, call, non_constant_p, |
| jump_target, fun, ident); |
| case METAFN_TUPLE_SIZE: |
| return eval_tuple_size (loc, ctx, h, call, non_constant_p, jump_target, |
| fun); |
| case METAFN_TUPLE_ELEMENT: |
| return eval_tuple_element (loc, ctx, expr, h1, call, |
| non_constant_p, jump_target, fun); |
| case METAFN_VARIANT_SIZE: |
| return eval_variant_size (loc, ctx, h, call, non_constant_p, |
| jump_target, fun); |
| case METAFN_VARIANT_ALTERNATIVE: |
| return eval_variant_alternative (loc, ctx, expr, h1, call, |
| non_constant_p, jump_target, fun); |
| case METAFN_TYPE_ORDER: |
| return eval_type_order (h, h1); |
| case METAFN_ANNOTATIONS_OF: |
| return eval_annotations_of (loc, ctx, h, kind, NULL_TREE, non_constant_p, |
| jump_target, fun); |
| case METAFN_ANNOTATIONS_OF_WITH_TYPE: |
| return eval_annotations_of (loc, ctx, h, kind, h1, non_constant_p, |
| jump_target, fun); |
| /* Special metafunctions. */ |
| case METAFN_ACCESS_CONTEXT_CURRENT: |
| if (DECL_CLASS_SCOPE_P (fun) |
| && TYPE_NAME (DECL_CONTEXT (fun)) |
| && TREE_CODE (TYPE_NAME (DECL_CONTEXT (fun))) == TYPE_DECL |
| && DECL_NAME (TYPE_NAME (DECL_CONTEXT (fun))) |
| && id_equal (DECL_NAME (TYPE_NAME (DECL_CONTEXT (fun))), |
| "access_context")) |
| return eval_access_context_current (loc, ctx, call, non_constant_p); |
| goto not_found; |
| case METAFN_EXCEPTION__S_EXCEPTION_CVT_TO_UTF8: |
| case METAFN_EXCEPTION__S_EXCEPTION_CVT_FROM_UTF8: |
| if (DECL_CLASS_SCOPE_P (fun) |
| && TYPE_NAME (DECL_CONTEXT (fun)) |
| && TREE_CODE (TYPE_NAME (DECL_CONTEXT (fun))) == TYPE_DECL |
| && DECL_NAME (TYPE_NAME (DECL_CONTEXT (fun))) |
| && id_equal (DECL_NAME (TYPE_NAME (DECL_CONTEXT (fun))), |
| "exception")) |
| { |
| bool to_utf8 |
| = minfo->code == METAFN_EXCEPTION__S_EXCEPTION_CVT_TO_UTF8; |
| return eval_exception__S_exception_cvt_tofrom_utf8 (loc, ctx, call, |
| non_constant_p, |
| overflow_p, |
| jump_target, |
| fun, to_utf8); |
| } |
| goto not_found; |
| } |
| goto not_found; |
| } |
| |
| /* Splice reflection REFL; i.e., return its entity. */ |
| |
| tree |
| splice (tree refl) |
| { |
| if (refl == error_mark_node) |
| return error_mark_node; |
| |
| /* Who in the world am I? That's the great puzzle and we have to wait |
| until instantiation to find out. */ |
| if (instantiation_dependent_expression_p (refl)) |
| return build_nt (SPLICE_EXPR, refl); |
| |
| /* [basic.splice] "The constant-expression of a splice-specifier shall |
| be a converted constant expression of type std::meta::info." */ |
| refl = build_converted_constant_expr (meta_info_type_node, refl, |
| tf_warning_or_error); |
| |
| if (processing_template_decl) |
| refl = fold_non_dependent_expr (refl, tf_warning_or_error, true); |
| else |
| refl = cxx_constant_value (refl); |
| if (!REFLECT_EXPR_P (refl)) |
| /* I don't wanna do your dirty work no more. */ |
| return error_mark_node; |
| |
| /* We are bringing some entity from the unevaluated expressions world |
| to possibly outside of that, mark it used. */ |
| if (!mark_used (REFLECT_EXPR_HANDLE (refl))) |
| return error_mark_node; |
| |
| refl = REFLECT_EXPR_HANDLE (refl); |
| /* Function templates are wrapped in OVERLOAD from name lookup |
| and a lot of places assume that. Furthermore, if reflection comes |
| from ^^fntmpl, it is wrapped with OVERLOAD already, only when |
| it comes from e.g. members_of it is not. */ |
| if (DECL_FUNCTION_TEMPLATE_P (refl)) |
| refl = ovl_make (refl, NULL_TREE); |
| |
| return refl; |
| } |
| |
| /* A walker for consteval_only_p. It cannot be a lambda, because we |
| have to call this recursively, sigh. */ |
| |
| static tree |
| consteval_only_type_r (tree *tp, int *walk_subtrees, void *data) |
| { |
| tree t = *tp; |
| /* Types can contain themselves recursively, hence this. */ |
| auto visited = static_cast<hash_set<tree> *>(data); |
| |
| if (!TYPE_P (t)) |
| return NULL_TREE; |
| |
| if (REFLECTION_TYPE_P (t)) |
| return t; |
| |
| if (typedef_variant_p (t)) |
| /* Tell cp_walk_subtrees to look through typedefs. */ |
| *walk_subtrees = 2; |
| |
| if (RECORD_OR_UNION_TYPE_P (t)) |
| { |
| /* Don't walk template arguments; A<info>::type isn't a consteval-only |
| type. */ |
| *walk_subtrees = 0; |
| /* So we have to walk the fields manually. */ |
| for (tree member = TYPE_FIELDS (t); |
| member; member = DECL_CHAIN (member)) |
| if (TREE_CODE (member) == FIELD_DECL) |
| if (tree r = cp_walk_tree (&TREE_TYPE (member), |
| consteval_only_type_r, visited, visited)) |
| return r; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* True if T is a consteval-only type as per [basic.types.general]: |
| "A type is consteval-only if it is either std::meta::info or a type |
| compounded from a consteval-only type", or something that has |
| a consteval-only type. */ |
| |
| bool |
| consteval_only_p (tree t) |
| { |
| if (!flag_reflection) |
| return false; |
| |
| if (!TYPE_P (t)) |
| t = TREE_TYPE (t); |
| |
| if (!t) |
| return false; |
| |
| /* We need the complete type otherwise we'd have no fields for class |
| templates and thus come up with zilch for things like |
| template<typename T> |
| struct X : T { }; |
| which could be consteval-only, depending on T. */ |
| t = complete_type (t); |
| |
| /* Classes with std::meta::info members are also consteval-only. */ |
| hash_set<tree> visited; |
| return !!cp_walk_tree (&t, consteval_only_type_r, &visited, &visited); |
| } |
| |
| /* Detect if a consteval-only expression EXPR or a consteval-only |
| variable EXPR not declared constexpr/constinit is used outside |
| a manifestly constant-evaluated context. E.g.: |
| |
| void f() { |
| constexpr auto r = ^^int; // OK |
| [: r :] i = 42; // still OK |
| auto z = r; // bad |
| } |
| |
| But |
| |
| consteval void g() { |
| constexpr auto r = ^^int; |
| auto z = r; |
| } |
| |
| is OK. If COMPLAIN, emit an error; otherwise we're in the search-only |
| mode. Return true if we found a problematic expression. */ |
| |
| bool |
| check_out_of_consteval_use (tree expr, bool complain/*=true*/) |
| { |
| if (!flag_reflection || in_immediate_context ()) |
| return false; |
| |
| auto walker = [](tree *tp, int *walk_subtrees, void *) -> tree |
| { |
| tree t = *tp; |
| |
| /* No need to look into types or unevaluated operands. */ |
| if (TYPE_P (t) |
| || unevaluated_p (TREE_CODE (t)) |
| /* Don't walk INIT_EXPRs, because we'd emit bogus errors about |
| member initializers. */ |
| || TREE_CODE (t) == INIT_EXPR |
| /* Don't walk BIND_EXPR_VARS. */ |
| || TREE_CODE (t) == BIND_EXPR |
| /* And don't recurse on DECL_EXPRs. */ |
| || TREE_CODE (t) == DECL_EXPR) |
| { |
| *walk_subtrees = false; |
| return NULL_TREE; |
| } |
| |
| /* A subexpression of a manifestly constant-evaluated expression is |
| an immediate function context. For example, |
| |
| consteval void foo (std::meta::info) { } |
| void g() { foo (^^void); } |
| |
| is all good. */ |
| if (tree decl = cp_get_callee_fndecl_nofold (t)) |
| if (immediate_invocation_p (decl)) |
| { |
| *walk_subtrees = false; |
| return NULL_TREE; |
| } |
| |
| if (VAR_P (t) |
| && (DECL_DECLARED_CONSTEXPR_P (t) || DECL_DECLARED_CONSTINIT_P (t))) |
| /* This is fine, don't bother checking the type. */ |
| return NULL_TREE; |
| |
| /* Now check the type to see if we are dealing with a consteval-only |
| expression. */ |
| if (!consteval_only_p (t)) |
| return NULL_TREE; |
| |
| if (current_function_decl |
| /* Already escalated. */ |
| && (DECL_IMMEDIATE_FUNCTION_P (current_function_decl) |
| /* These functions are magic. */ |
| || is_std_allocator_allocate (current_function_decl))) |
| { |
| *walk_subtrees = false; |
| return NULL_TREE; |
| } |
| |
| /* We might have to escalate if we are in an immediate-escalating |
| function. */ |
| if (immediate_escalating_function_p (current_function_decl)) |
| { |
| promote_function_to_consteval (current_function_decl); |
| *walk_subtrees = false; |
| return NULL_TREE; |
| } |
| |
| *walk_subtrees = false; |
| return t; |
| }; |
| |
| if (tree t = cp_walk_tree_without_duplicates (&expr, walker, nullptr)) |
| { |
| if (complain) |
| { |
| if (VAR_P (t)) |
| { |
| auto_diagnostic_group d; |
| error_at (cp_expr_loc_or_input_loc (t), |
| "consteval-only variable %qD not declared %<constexpr%> " |
| "used outside a constant-evaluated context", t); |
| if (TREE_STATIC (t) || CP_DECL_THREAD_LOCAL_P (t)) |
| inform (DECL_SOURCE_LOCATION (t), "add %<constexpr%> or " |
| "%<constinit%>"); |
| else |
| inform (DECL_SOURCE_LOCATION (t), "add %<constexpr%>"); |
| } |
| else |
| error_at (cp_expr_loc_or_input_loc (t), |
| "consteval-only expressions are only allowed in " |
| "a constant-evaluated context"); |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Return true if the reflections LHS and RHS are equal. */ |
| |
| bool |
| compare_reflections (tree lhs, tree rhs) |
| { |
| reflect_kind lkind; |
| do |
| { |
| lkind = REFLECT_EXPR_KIND (lhs); |
| if (lkind != REFLECT_EXPR_KIND (rhs)) |
| return false; |
| lhs = REFLECT_EXPR_HANDLE (lhs); |
| rhs = REFLECT_EXPR_HANDLE (rhs); |
| } |
| while (REFLECT_EXPR_P (lhs) && REFLECT_EXPR_P (rhs)); |
| |
| lhs = resolve_nondeduced_context (lhs, tf_warning_or_error); |
| rhs = resolve_nondeduced_context (rhs, tf_warning_or_error); |
| |
| /* TEMPLATE_DECLs are wrapped in an OVERLOAD. When we have |
| |
| template_of (^^fun_tmpl<int>) == ^^fun_tmpl |
| |
| the RHS will be OVERLOAD<TEMPLATE_DECL> but the LHS will |
| only be TEMPLATE_DECL. They should compare equal, though. */ |
| // ??? Can we do something better? |
| lhs = maybe_get_first_fn (lhs); |
| rhs = maybe_get_first_fn (rhs); |
| if (lkind == REFLECT_PARM) |
| { |
| lhs = maybe_update_function_parm (lhs); |
| rhs = maybe_update_function_parm (rhs); |
| } |
| else if (lkind == REFLECT_DATA_MEMBER_SPEC) |
| return (TREE_VEC_ELT (lhs, 0) == TREE_VEC_ELT (rhs, 0) |
| && TREE_VEC_ELT (lhs, 1) == TREE_VEC_ELT (rhs, 1) |
| && tree_int_cst_equal (TREE_VEC_ELT (lhs, 2), |
| TREE_VEC_ELT (rhs, 2)) |
| && tree_int_cst_equal (TREE_VEC_ELT (lhs, 3), |
| TREE_VEC_ELT (rhs, 3)) |
| && TREE_VEC_ELT (lhs, 4) == TREE_VEC_ELT (rhs, 4)); |
| |
| if (lhs == rhs) |
| return true; |
| |
| /* Some trees are not shared. */ |
| if (TREE_CODE (lhs) == TREE_CODE (rhs)) |
| switch (TREE_CODE (lhs)) |
| { |
| case ARRAY_REF: |
| case COMPONENT_REF: |
| case REAL_CST: |
| return cp_tree_equal (lhs, rhs); |
| default: |
| break; |
| } |
| |
| if (TYPE_P (lhs) && TYPE_P (rhs)) |
| if (!typedef_variant_p (lhs) && !typedef_variant_p (rhs)) |
| return same_type_p (lhs, rhs); |
| |
| return false; |
| } |
| |
| /* Return true if T is a valid splice-type-specifier. |
| [dcl.type.splice]: For a splice-type-specifier of the form |
| "typename[opt] splice-specifier", the splice-specifier shall designate |
| a type, a class template, or an alias template. |
| For a splice-type-specifier of the form |
| "typename[opt] splice-specialization-specifier", the splice-specifier |
| of the splice-specialization-specifier shall designate a template T |
| that is either a class template or an alias template. */ |
| |
| bool |
| valid_splice_type_p (const_tree t) |
| { |
| return TYPE_P (t); |
| } |
| |
| /* Return true if T is a valid splice-scope-specifier. |
| [basic.lookup.qual.general]: If a splice-scope-specifier is followed |
| by a ::, it shall either be a dependent splice-scope-specifier or it |
| shall designate a namespace, class, enumeration, or dependent type. */ |
| |
| bool |
| valid_splice_scope_p (const_tree t) |
| { |
| return (CLASS_TYPE_P (t) |
| || TREE_CODE (t) == ENUMERAL_TYPE |
| || TREE_CODE (t) == NAMESPACE_DECL); |
| } |
| |
| /* Check a function DECL for CWG 3115: Every function of consteval-only |
| type shall be an immediate function. */ |
| |
| void |
| check_consteval_only_fn (tree decl) |
| { |
| if (!DECL_IMMEDIATE_FUNCTION_P (decl) |
| && consteval_only_p (decl) |
| /* But if the function can be escalated, merrily we roll along. */ |
| && !immediate_escalating_function_p (decl) |
| && !is_std_allocator_allocate (decl)) |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "function of consteval-only type must be declared %qs", |
| "consteval"); |
| } |
| |
| /* Check if T is a valid result of splice-expression. ADDRESS_P is true if |
| we are taking the address of the splice. MEMBER_ACCESS_P is true if this |
| splice is used in foo.[: bar :] or foo->[: bar :] context. COMPLAIN_P is |
| true if any errors should be emitted. Returns true is no problems are |
| found, false otherwise. */ |
| |
| bool |
| check_splice_expr (location_t loc, location_t start_loc, tree t, |
| bool address_p, bool member_access_p, bool complain_p) |
| { |
| /* We may not have gotten an expression. */ |
| if (TREE_CODE (t) == TYPE_DECL |
| || TREE_CODE (t) == NAMESPACE_DECL |
| || TYPE_P (t)) |
| { |
| if (complain_p) |
| { |
| if (TYPE_P (t)) |
| { |
| auto_diagnostic_group d; |
| error_at (loc, "expected a reflection of an expression instead " |
| "of type %qT", t); |
| if (start_loc != UNKNOWN_LOCATION) |
| { |
| rich_location richloc (line_table, start_loc); |
| richloc.add_fixit_insert_before (start_loc, "typename"); |
| inform (&richloc, "add %<typename%> to denote a type " |
| "outside a type-only context"); |
| } |
| else |
| inform (loc, "add %<typename%> to denote a type outside " |
| "a type-only context"); |
| } |
| else |
| error_at (loc, "expected a reflection of an expression instead " |
| "of %qD", t); |
| } |
| return false; |
| } |
| /* [expr.prim.splice]/2 For a splice-expression of the form |
| splice-specifier, the expression is ill-formed if it is: */ |
| /* -- a constructor or a destructor */ |
| if (TREE_CODE (t) == FUNCTION_DECL |
| && (DECL_CONSTRUCTOR_P (t) || DECL_DESTRUCTOR_P (t))) |
| { |
| if (complain_p) |
| error_at (loc, "cannot use constructor or destructor %qD in a splice " |
| "expression", t); |
| return false; |
| } |
| /* -- an unnamed bit-field */ |
| if (TREE_CODE (t) == FIELD_DECL && DECL_UNNAMED_BIT_FIELD (t)) |
| { |
| if (complain_p) |
| error_at (loc, "cannot use an unnamed bit-field %qD in a splice " |
| "expression", t); |
| return false; |
| } |
| /* Class members may not be implicitly referenced through a splice. |
| But taking the address is fine, and so is class member access a la |
| foo.[: ^^S::bar :]. */ |
| if (!address_p |
| && !member_access_p |
| && DECL_P (t) |
| && DECL_NONSTATIC_MEMBER_P (t)) |
| { |
| if (complain_p) |
| error_at (loc, "cannot implicitly reference a class member %qD " |
| "through a splice", t); |
| return false; |
| } |
| /* [expr.unary.op]/3.1 "If the operand [of unary &] is a qualified-id or |
| splice-expression designating a non-static member m, other than an |
| explicit object member function, m shall be a direct member of some |
| class C that is not an anonymous union." */ |
| if (address_p |
| && TREE_CODE (t) == FIELD_DECL |
| && ANON_UNION_TYPE_P (DECL_CONTEXT (t)) |
| && !TYPE_P (context_for_name_lookup (t))) |
| { |
| if (complain_p) |
| error_at (loc, "unary %<&%> applied to an anonymous union member " |
| "%qD that is not a direct member of a named class", t); |
| return false; |
| } |
| |
| /* [expr.prim.splice]/2: "The expression is ill-formed if S [the construct |
| designated by splice-specifier] is |
| -- a local entity such that there is a lambda scope that intervenes |
| between the expression and the point at which S was introduced" |
| This also checks ODR violations (reflect/odr1.C). */ |
| if (outer_automatic_var_p (t) |
| && process_outer_var_ref (t, tf_none) == error_mark_node) |
| { |
| /* Not letting process_outer_var_ref emit the error so that we can |
| say "in a splice expression". */ |
| if (complain_p) |
| { |
| auto_diagnostic_group d; |
| error_at (loc, "use of local variable with automatic storage from " |
| "containing function in a splice expression"); |
| inform (DECL_SOURCE_LOCATION (t), "%q#D declared here", t); |
| } |
| return false; |
| } |
| |
| /* If we had a reflect_kind here, we could just check for |
| REFLECT_ANNOTATION and be done with it. But we don't have it yet (TODO), |
| so do it the suboptimal way. */ |
| if (TREE_CODE (t) == TREE_LIST && annotation_p (t)) |
| { |
| if (complain_p) |
| error_at (loc, "cannot use an annotation %qE in a splice expression", |
| t); |
| return false; |
| } |
| |
| /* Same, but with REFLECT_DATA_MEMBER_SPEC. */ |
| if (TREE_CODE (t) == TREE_VEC) |
| { |
| if (complain_p) |
| error_at (loc, "cannot use a data member specification in a " |
| "splice expression"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Create a new SPLICE_SCOPE tree. EXPR is its SPLICE_SCOPE_EXPR, and |
| TYPE_P says if it should have SPLICE_SCOPE_TYPE_P set. */ |
| |
| tree |
| make_splice_scope (tree expr, bool type_p) |
| { |
| tree t = cxx_make_type (SPLICE_SCOPE); |
| SPLICE_SCOPE_EXPR (t) = expr; |
| SPLICE_SCOPE_TYPE_P (t) = type_p; |
| return t; |
| } |
| |
| /* Return true if T is a splice expression; that is, it is either [:T:] or |
| [:T:]<arg>. */ |
| |
| bool |
| dependent_splice_p (const_tree t) |
| { |
| return (TREE_CODE (t) == SPLICE_EXPR |
| || (TREE_CODE (t) == TEMPLATE_ID_EXPR |
| && TREE_CODE (TREE_OPERAND (t, 0)) == SPLICE_EXPR)); |
| } |
| |
| /* Annotation index for mangling. */ |
| |
| static GTY(()) int annotation_idx; |
| |
| /* Helper function for mangle.cc (write_reflection). |
| Determine 2 letter mangling prefix and store it into prefix. |
| Additionally return the reflection handle possibly adjusted so that |
| write_reflection can mangle the operands of it if any are needed. */ |
| |
| tree |
| reflection_mangle_prefix (tree refl, char prefix[3]) |
| { |
| tree h = REFLECT_EXPR_HANDLE (refl); |
| reflect_kind kind = REFLECT_EXPR_KIND (refl); |
| if (h == unknown_type_node) |
| { |
| strcpy (prefix, "nu"); |
| return NULL_TREE; |
| } |
| if (eval_is_value (kind) == boolean_true_node) |
| { |
| strcpy (prefix, "vl"); |
| if (VAR_P (h) && DECL_NTTP_OBJECT_P (h)) |
| h = tparm_object_argument (h); |
| return h; |
| } |
| if (eval_is_object (kind) == boolean_true_node) |
| { |
| strcpy (prefix, "ob"); |
| return h; |
| } |
| if (eval_is_variable (h, kind) == boolean_true_node) |
| { |
| strcpy (prefix, "vr"); |
| return h; |
| } |
| if (eval_is_structured_binding (h, kind) == boolean_true_node) |
| { |
| strcpy (prefix, "sb"); |
| return h; |
| } |
| if (eval_is_function (h) == boolean_true_node) |
| { |
| strcpy (prefix, "fn"); |
| return maybe_get_first_fn (h); |
| } |
| if (eval_is_function_parameter (h, kind) == boolean_true_node) |
| { |
| strcpy (prefix, "pa"); |
| return maybe_update_function_parm (h); |
| } |
| if (eval_is_enumerator (h) == boolean_true_node) |
| { |
| strcpy (prefix, "en"); |
| return h; |
| } |
| if (eval_is_annotation (h, kind) == boolean_true_node) |
| { |
| strcpy (prefix, "an"); |
| if (TREE_PURPOSE (TREE_VALUE (h)) == NULL_TREE) |
| TREE_PURPOSE (TREE_VALUE (h)) |
| = build_int_cst (integer_type_node, annotation_idx++); |
| return TREE_PURPOSE (TREE_VALUE (h)); |
| } |
| if (eval_is_type_alias (h) == boolean_true_node) |
| { |
| strcpy (prefix, "ta"); |
| return h; |
| } |
| if (eval_is_type (h) == boolean_true_node) |
| { |
| strcpy (prefix, "ty"); |
| return h; |
| } |
| if (eval_is_nonstatic_data_member (h) == boolean_true_node) |
| { |
| strcpy (prefix, "dm"); |
| return h; |
| } |
| if (TREE_CODE (h) == FIELD_DECL && DECL_UNNAMED_BIT_FIELD (h)) |
| { |
| strcpy (prefix, "un"); |
| return h; |
| } |
| if (eval_is_class_template (h) == boolean_true_node) |
| { |
| strcpy (prefix, "ct"); |
| return h; |
| } |
| if (eval_is_function_template (h) == boolean_true_node) |
| { |
| strcpy (prefix, "ft"); |
| h = maybe_get_first_fn (h); |
| return h; |
| } |
| if (eval_is_variable_template (h) == boolean_true_node) |
| { |
| strcpy (prefix, "vt"); |
| return h; |
| } |
| if (eval_is_alias_template (h) == boolean_true_node) |
| { |
| strcpy (prefix, "at"); |
| return h; |
| } |
| if (eval_is_concept (h) == boolean_true_node) |
| { |
| strcpy (prefix, "co"); |
| return h; |
| } |
| if (eval_is_namespace_alias (h) == boolean_true_node) |
| { |
| strcpy (prefix, "na"); |
| return h; |
| } |
| if (eval_is_namespace (h) == boolean_true_node) |
| { |
| if (h == global_namespace) |
| { |
| strcpy (prefix, "ng"); |
| return NULL_TREE; |
| } |
| strcpy (prefix, "ns"); |
| return h; |
| } |
| if (eval_is_base (h, kind) == boolean_true_node) |
| { |
| strcpy (prefix, "ba"); |
| return h; |
| } |
| if (eval_is_data_member_spec (h, kind) == boolean_true_node) |
| { |
| strcpy (prefix, "ds"); |
| return h; |
| } |
| gcc_unreachable (); |
| } |
| |
| #include "gt-cp-reflect.h" |