mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
Implement std::meta::type_of (and has-type exposition only predicate).
Some cases aren't done yet because they depend on the implementation of other metafunctions and decision how those reflections will be represented. I had to change process_metafunction/get_info, because some tests in the new testcase were failing otherwise. cxx_constant_value is a wrong way to evaluate arguments of metafunction, they need to be evaluated in the same constant expression context as the rest, they can also throw, break, continue, return (the latter three only when using GNU statement expressions), and e.g. could perform heap allocation if something during the same evaluation later on deletes those etc. In particular, e.g. type_of (reflect_constant (42)) didn't work, because it attempted to constant evaluate reflect_constant separately.
This commit is contained in:
committed by
Marek Polacek
parent
13a53fb942
commit
0510df7079
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
151
gcc/testsuite/g++.dg/reflect/type_of1.C
Normal file
151
gcc/testsuite/g++.dg/reflect/type_of1.C
Normal file
@@ -0,0 +1,151 @@
|
||||
// { dg-do compile { target c++26 } }
|
||||
// { dg-additional-options "-freflection" }
|
||||
// Test std::meta::type_of.
|
||||
|
||||
#include <meta>
|
||||
|
||||
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<auto> struct TCls {};
|
||||
template<auto> void TFn();
|
||||
template<auto> int TVar;
|
||||
template<auto> 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);
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user