diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index bb02bed415b..a58f940e01f 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1589,17 +1589,6 @@ save_fundef_copy (tree fun, tree copy) *slot = copy; } -/* Whether our evaluation wants a prvalue (e.g. CONSTRUCTOR or _CST), - a glvalue (e.g. VAR_DECL or _REF), or nothing. */ - -enum value_cat { - vc_prvalue = 0, - vc_glvalue = 1, - vc_discard = 2 -}; - -static tree cxx_eval_constant_expression (const constexpr_ctx *, tree, - value_cat, bool *, bool *, tree *); static tree cxx_eval_bare_aggregate (const constexpr_ctx *, tree, value_cat, bool *, bool *, tree *); static tree cxx_fold_indirect_ref (const constexpr_ctx *, location_t, tree, tree, @@ -3805,8 +3794,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; return t; } - tree e = process_metafunction (ctx, t, jump_target); - if (throws (jump_target)) + tree e = process_metafunction (ctx, t, non_constant_p, overflow_p, + jump_target); + if (*jump_target) return NULL_TREE; e = cxx_eval_constant_expression (ctx, e, vc_prvalue, non_constant_p, overflow_p, @@ -8886,7 +8876,7 @@ merge_jump_target (location_t loc, const constexpr_ctx *ctx, tree r, /* FIXME unify with c_fully_fold */ /* FIXME overflow_p is too global */ -static tree +tree cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, value_cat lval, bool *non_constant_p, bool *overflow_p, diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a89bc9cacc8..0d5a2997ff1 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9163,6 +9163,18 @@ extern bool replace_decl (tree *, tree, tree); extern tree cxa_allocate_and_throw_exception (location_t, const constexpr_ctx *, tree); +/* Whether our evaluation wants a prvalue (e.g. CONSTRUCTOR or _CST), + a glvalue (e.g. VAR_DECL or _REF), or nothing. */ +enum value_cat { + vc_prvalue = 0, + vc_glvalue = 1, + vc_discard = 2 +}; + +extern tree cxx_eval_constant_expression (const constexpr_ctx *, tree, + value_cat, bool *, bool *, + tree *); + /* An RAII sentinel used to restrict constexpr evaluation so that it doesn't do anything that causes extra DECL_UID generation. */ @@ -9212,7 +9224,8 @@ extern void coro_set_ramp_function (tree, tree); /* In reflect.cc */ extern void init_reflection (); extern bool metafunction_p (tree) ATTRIBUTE_PURE; -extern tree process_metafunction (const constexpr_ctx *, tree, tree *); +extern tree process_metafunction (const constexpr_ctx *, tree, + bool *, bool *, tree *); extern tree get_reflection (location_t, tree) ATTRIBUTE_PURE; extern tree get_null_reflection () ATTRIBUTE_PURE; extern tree splice (tree); diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index d07944851d5..c1f7e665421 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -237,12 +237,17 @@ metafunction_p (tree fndecl) /* Extract the N-th reflection argument from a metafunction call CALL. */ static tree -get_info (tree call, int n) +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_constant_value (info); + info = cxx_eval_constant_expression (ctx, info, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; return info; } @@ -732,6 +737,83 @@ eval_is_conversion_function_template (const_tree) gcc_assert (!"TODO"); } +/* 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) +{ + 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) == boolean_true_node + || eval_is_enumerator (r) == boolean_true_node + || TREE_CODE (r) == FIELD_DECL + || eval_is_annotation (r) == boolean_true_node) + return true; + // TODO: object, direct base class relationship, data member description. + return false; +} + +/* 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, + tree *jump_target) +{ + if (!has_type (r)) + return throw_exception (loc, ctx, N_("reflection does not have a type"), + r, jump_target); + r = MAYBE_BASELINK_FUNCTIONS (r); + if (TREE_CODE (r) == PARM_DECL) + { + 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 (TREE_CODE (r) == FUNCTION_DECL) + r = TREE_TYPE (TREE_TYPE (r)); + else if (eval_is_annotation (r) == boolean_true_node) + // TODO: or do we need to reflect_constant and get type of that? + r = TREE_TYPE (TREE_VALUE (TREE_VALUE (r))); + else + r = TREE_TYPE (r); + return get_reflection_raw (loc, r); +} + /* Process std::meta::dealias. Returns: A reflection representing the underlying entity of what r represents. @@ -1437,7 +1519,9 @@ eval_add_cv (location_t loc, const constexpr_ctx *ctx, tree type, // TODO Use gperf? tree -process_metafunction (const constexpr_ctx *ctx, tree call, tree *jump_target) +process_metafunction (const constexpr_ctx *ctx, tree call, + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree name = DECL_NAME (cp_get_callee_fndecl_nofold (call)); const char *ident = IDENTIFIER_POINTER (name); @@ -1446,10 +1530,17 @@ process_metafunction (const constexpr_ctx *ctx, tree call, tree *jump_target) { tree expr = get_nth_callarg (call, 0); location_t loc = cp_expr_loc_or_input_loc (expr); + expr = cxx_eval_constant_expression (ctx, expr, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; return eval_reflect_constant (loc, ctx, expr, jump_target); } - tree info = get_info (call, 0); + tree info = get_info (ctx, call, 0, non_constant_p, overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; tree h = REFLECT_EXPR_HANDLE (info); const location_t loc = cp_expr_loc_or_input_loc (info); @@ -1542,38 +1633,66 @@ process_metafunction (const constexpr_ctx *ctx, tree call, tree *jump_target) return eval_is_arithmetic_type (loc, ctx, h, jump_target); if (!strcmp (ident, "same_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_is_same_type (loc, ctx, h, h1, jump_target); } if (!strcmp (ident, "base_of_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_is_base_of_type (loc, ctx, h, h1, jump_target); } if (!strcmp (ident, "virtual_base_of_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_is_virtual_base_of_type (loc, ctx, h, h1, jump_target); } if (!strcmp (ident, "convertible_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_is_convertible_type (loc, ctx, h, h1, jump_target); } if (!strcmp (ident, "nothrow_convertible_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_is_nothrow_convertible_type (loc, ctx, h, h1, jump_target); } if (!strcmp (ident, "layout_compatible_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_is_layout_compatible_type (loc, ctx, h, h1, jump_target); } if (!strcmp (ident, "pointer_interconvertible_base_of_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_is_pointer_interconvertible_base_of_type (loc, ctx, h, h1, jump_target); } @@ -1615,9 +1734,15 @@ process_metafunction (const constexpr_ctx *ctx, tree call, tree *jump_target) return eval_annotations_of (loc, ctx, h, NULL_TREE, jump_target); if (id_equal (name, "annotations_of_with_type")) { - tree h1 = REFLECT_EXPR_HANDLE (get_info (call, 1)); + tree i1 = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + tree h1 = REFLECT_EXPR_HANDLE (i1); return eval_annotations_of (loc, ctx, h, h1, jump_target); } + if (id_equal (name, "type_of")) + return eval_type_of (loc, ctx, h, jump_target); not_found: sorry ("%qE", name); diff --git a/gcc/testsuite/g++.dg/reflect/type_of1.C b/gcc/testsuite/g++.dg/reflect/type_of1.C new file mode 100644 index 00000000000..d350c3d12ab --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/type_of1.C @@ -0,0 +1,151 @@ +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } +// Test std::meta::type_of. + +#include + +using namespace std::meta; + +int arr[] = {1, 2, 3}; +auto [a1, a2, a3] = arr; +void fn(); +auto &fn2(); +enum Enum { A }; +using Alias = int; +struct B {}; +struct S : B { + int mem; + int : 0; +}; +struct T { + T (); + T (const T &); + ~T (); +}; +struct U { + int u; +}; +template struct TCls {}; +template void TFn(); +template int TVar; +template concept Concept = requires { true; }; +namespace NS {}; +namespace NSAlias = NS; + +// constexpr auto ctx = std::meta::access_context::current (); + +consteval bool +has_type (info r) +{ + try { type_of (r); } + catch (std::meta::exception &) { return false; } + return true; +} + +static_assert (has_type (std::meta::reflect_constant (42))); +//static_assert (has_type (std::meta::reflect_object (arr[1]))); +static_assert (has_type (^^arr)); +static_assert (!has_type (^^a3)); +static_assert (has_type (^^fn)); +static_assert (!has_type (^^fn2)); +static_assert (has_type (^^Enum::A)); +static_assert (!has_type (^^Alias)); +static_assert (!has_type (^^S)); +static_assert (has_type (^^S::mem)); +//static_assert (has_type (std::meta::members_of (^^S, ctx)[1])); +static_assert (!has_type (^^TCls)); +static_assert (!has_type (^^TFn)); +static_assert (!has_type (^^TVar)); +static_assert (!has_type (^^Concept)); +static_assert (!has_type (^^NSAlias)); +static_assert (!has_type (^^NS)); +//static_assert (has_type (std::meta::bases_of (^^S, ctx)[0])); +//static_assert (has_type (std::meta::data_member_spec (^^int, {.name="member"}))); + +int +foo (int a, const long b, T c, int d[4], T &e) +{ + static_assert (has_type (^^a)); + static_assert (has_type (^^b)); + static_assert (has_type (^^c)); + static_assert (has_type (^^d)); + static_assert (has_type (^^e)); + static_assert (type_of (^^a) == ^^int); + static_assert (type_of (^^b) == ^^long); + static_assert (type_of (^^c) == ^^T); + using ptr = int *; + static_assert (type_of (^^d) == dealias (^^ptr)); + using ref = T &; + static_assert (type_of (^^e) == dealias (^^ref)); + return 0; +} + +static_assert (type_of (std::meta::reflect_constant (42)) == ^^int); +static_assert (type_of (std::meta::reflect_constant (42.0)) == ^^double); +//static_assert (type_of (std::meta::reflect_constant (U { 42 })) == ^^U); +//static_assert (type_of (std::meta::reflect_object (arr[1])) == ^int); +using int3 = int[3]; +static_assert (type_of (^^arr) == dealias (^^int3)); +static_assert (type_of (^^fn) == ^^void); +static_assert (type_of (^^Enum::A) == ^^Enum); +static_assert (type_of (^^A) == ^^Enum); +static_assert (type_of (^^S::mem) == ^^int); +//static_assert (type_of (std::meta::members_of (^^S, ctx)[1]) == ??); +//static_assert (type_of (std::meta::bases_of (^^S, ctx)[0]) == ??); +//static_assert (type_of (std::meta::data_member_spec (^^int, {.name="member"})) == ??); + +consteval int +test (info x, info y) +{ + if (x == y) + return 0; + throw 1; +} + +using ull = unsigned long long; + +enum Enum2 { + E21, + E22, + E23 = 3L, + E24, + E25 = 5ULL, + E26, + E27 = 7 + test (type_of (^^E21), ^^int) + + test (type_of (^^E22), ^^int) + + test (type_of (^^E23), ^^long) + + test (type_of (^^E24), ^^long) + + test (type_of (^^E25), dealias (^^ull)) + + test (type_of (^^E26), dealias (^^ull)) +}; + +static_assert (type_of (^^E21) == ^^Enum2); +static_assert (type_of (^^E22) == ^^Enum2); +static_assert (type_of (^^E23) == ^^Enum2); +static_assert (type_of (^^E24) == ^^Enum2); +static_assert (type_of (^^E25) == ^^Enum2); +static_assert (type_of (^^E26) == ^^Enum2); +static_assert (type_of (^^E27) == ^^Enum2); + +enum Enum3 : long { + E31, + E32, + E33 = 3L, + E34, + E35 = 5ULL, + E36, + E37 = 7 + test (type_of (^^E31), ^^long) + + test (type_of (^^E32), ^^long) + + test (type_of (^^E33), ^^long) + + test (type_of (^^E34), ^^long) + + test (type_of (^^E35), ^^long) + + test (type_of (^^E36), ^^long) +}; + +static_assert (type_of (^^E31) == ^^Enum3); +static_assert (type_of (^^E32) == ^^Enum3); +static_assert (type_of (^^E33) == ^^Enum3); +static_assert (type_of (^^E34) == ^^Enum3); +static_assert (type_of (^^E35) == ^^Enum3); +static_assert (type_of (^^E36) == ^^Enum3); +static_assert (type_of (^^E37) == ^^Enum3); diff --git a/libstdc++-v3/include/std/meta b/libstdc++-v3/include/std/meta index 67b756ddd61..b13f5a6b438 100644 --- a/libstdc++-v3/include/std/meta +++ b/libstdc++-v3/include/std/meta @@ -89,6 +89,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION consteval bool has_identifier(info); // [meta.reflection.queries], reflection queries + consteval info type_of(info); consteval bool is_enumerator(info); consteval bool is_annotation(info);