mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
Implement std::meta::access_context.
This commit is contained in:
committed by
Marek Polacek
parent
3abdff74a0
commit
09f2c4896f
@@ -1348,6 +1348,18 @@ cxx_constexpr_manifestly_const_eval (const constexpr_ctx *ctx)
|
||||
return ctx->manifestly_const_eval;
|
||||
}
|
||||
|
||||
/* Return ctx->call->fundef->decl or NULL_TREE. For use in
|
||||
reflect.cc. */
|
||||
|
||||
tree
|
||||
cxx_constexpr_caller (const constexpr_ctx *ctx)
|
||||
{
|
||||
if (ctx->call)
|
||||
return ctx->call->fundef->decl;
|
||||
else
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Predicates for the meaning of *jump_target. */
|
||||
|
||||
static bool
|
||||
@@ -10510,7 +10522,7 @@ find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/)
|
||||
/* Find immediate function decls in *TP if any. */
|
||||
|
||||
static tree
|
||||
find_immediate_fndecl (tree *tp, int */*walk_subtrees*/, void */*data*/)
|
||||
find_immediate_fndecl (tree *tp, int *walk_subtrees, void */*data*/)
|
||||
{
|
||||
if (TREE_CODE (*tp) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (*tp))
|
||||
return *tp;
|
||||
@@ -10518,6 +10530,8 @@ find_immediate_fndecl (tree *tp, int */*walk_subtrees*/, void */*data*/)
|
||||
&& TREE_CODE (PTRMEM_CST_MEMBER (*tp)) == FUNCTION_DECL
|
||||
&& DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (*tp)))
|
||||
return PTRMEM_CST_MEMBER (*tp);
|
||||
if (TREE_CODE (*tp) == REFLECT_EXPR)
|
||||
*walk_subtrees = 0;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
|
||||
@@ -7950,6 +7950,7 @@ extern void remove_dummy_lambda_op (tree, tree);
|
||||
extern tree canonical_type_parameter (tree);
|
||||
extern void push_access_scope (tree);
|
||||
extern void pop_access_scope (tree);
|
||||
extern tree current_function_decl_without_access_scope ();
|
||||
extern bool check_template_shadow (tree);
|
||||
extern tree get_innermost_template_args (tree, int);
|
||||
extern void maybe_begin_member_template_processing (tree);
|
||||
@@ -9229,6 +9230,7 @@ extern tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
|
||||
tree *);
|
||||
extern bool cxx_constexpr_quiet_p (const constexpr_ctx *);
|
||||
extern mce_value cxx_constexpr_manifestly_const_eval (const constexpr_ctx *);
|
||||
extern tree cxx_constexpr_caller (const constexpr_ctx *);
|
||||
|
||||
/* An RAII sentinel used to restrict constexpr evaluation so that it
|
||||
doesn't do anything that causes extra DECL_UID generation. */
|
||||
|
||||
12
gcc/cp/pt.cc
12
gcc/cp/pt.cc
@@ -277,6 +277,18 @@ pop_access_scope (tree t)
|
||||
pop_from_top_level ();
|
||||
}
|
||||
|
||||
/* Return current function, ignoring temporary overrides
|
||||
of current_function_decl by push_access_scope. */
|
||||
|
||||
tree
|
||||
current_function_decl_without_access_scope ()
|
||||
{
|
||||
if (vec_safe_length (saved_access_scope))
|
||||
return (*saved_access_scope)[0];
|
||||
else
|
||||
return current_function_decl;
|
||||
}
|
||||
|
||||
/* Do any processing required when DECL (a member template
|
||||
declaration) is finished. Returns the TEMPLATE_DECL corresponding
|
||||
to DECL, unless it is a specialization, in which case the DECL
|
||||
|
||||
@@ -263,8 +263,18 @@ metafunction_p (tree fndecl)
|
||||
return false;
|
||||
|
||||
/* Is the call from std::meta? */
|
||||
fndecl = decl_namespace_context (fndecl);
|
||||
return DECL_NAMESPACE_STD_META_P (fndecl);
|
||||
tree ctx = decl_namespace_context (fndecl);
|
||||
if (!DECL_NAMESPACE_STD_META_P (ctx))
|
||||
return false;
|
||||
|
||||
/* They should be user provided and not defined. */
|
||||
if (!user_provided_p (fndecl)
|
||||
|| (DECL_NAMESPACE_SCOPE_P (fndecl) && DECL_DELETED_FN (fndecl)))
|
||||
return false;
|
||||
if (DECL_INITIAL (fndecl))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Extract the N-th reflection argument from a metafunction call CALL. */
|
||||
@@ -5733,6 +5743,65 @@ eval_is_implicit_lifetime_type (location_t loc, const constexpr_ctx *ctx,
|
||||
return boolean_false_node;
|
||||
}
|
||||
|
||||
/* 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 ();
|
||||
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);
|
||||
}
|
||||
|
||||
/* Expand a call to a metafunction FUN. CALL is the CALL_EXPR.
|
||||
JUMP_TARGET is set if we are throwing std::meta::exception. */
|
||||
|
||||
@@ -5823,6 +5892,14 @@ process_metafunction (const constexpr_ctx *ctx, tree fun, tree call,
|
||||
if (id_equal (name, "reflect_constant_array"))
|
||||
return eval_reflect_constant_array (loc, ctx, call, non_constant_p,
|
||||
overflow_p, jump_target);
|
||||
if (id_equal (name, "current")
|
||||
&& 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);
|
||||
|
||||
tree info = get_info (ctx, call, 0, non_constant_p, overflow_p, jump_target);
|
||||
if (*jump_target)
|
||||
|
||||
175
gcc/testsuite/g++.dg/reflect/access_context1.C
Normal file
175
gcc/testsuite/g++.dg/reflect/access_context1.C
Normal file
@@ -0,0 +1,175 @@
|
||||
// { dg-do compile { target c++26 } }
|
||||
// { dg-additional-options "-freflection" }
|
||||
// Test std::meta::access_context.
|
||||
|
||||
#include <meta>
|
||||
|
||||
using namespace std::meta;
|
||||
|
||||
constexpr info null_reflection;
|
||||
struct S {};
|
||||
|
||||
constexpr access_context a = access_context::unprivileged ();
|
||||
static_assert (a.scope () == ^^:: && a.designating_class () == null_reflection);
|
||||
constexpr access_context b = access_context::unchecked ();
|
||||
static_assert (b.scope () == null_reflection && b.designating_class () == null_reflection);
|
||||
constexpr access_context c = access_context::current ();
|
||||
static_assert (c.scope () == ^^:: && c.designating_class () == null_reflection);
|
||||
constexpr access_context d = access_context::unprivileged ().via (^^S);
|
||||
static_assert (d.scope () == ^^:: && d.designating_class () == ^^S);
|
||||
constexpr access_context e = access_context::unchecked ().via (^^const S);
|
||||
static_assert (e.scope () == null_reflection && e.designating_class () == ^^const S);
|
||||
constexpr access_context f = access_context::current ().via (^^volatile S);
|
||||
static_assert (f.scope () == ^^:: && f.designating_class () == ^^volatile S);
|
||||
|
||||
consteval info
|
||||
foo (info x = access_context::current ().scope ())
|
||||
{
|
||||
static_assert (access_context::current ().scope () == ^^foo);
|
||||
return x;
|
||||
}
|
||||
|
||||
static_assert (foo (^^std) == ^^std);
|
||||
static_assert (foo () == ^^::);
|
||||
|
||||
namespace N
|
||||
{
|
||||
struct S {};
|
||||
constexpr access_context a = access_context::current ().via (^^::S).via (^^S);
|
||||
static_assert (a.scope () == ^^N && a.designating_class () == ^^S);
|
||||
static_assert (access_context::unprivileged ().scope () == ^^::);
|
||||
static_assert (access_context::unchecked ().scope () == null_reflection);
|
||||
namespace M
|
||||
{
|
||||
constexpr access_context a = access_context::current ().via (^^S).via (^^::S);
|
||||
static_assert (a.scope () == ^^M && a.designating_class () == ^^::S);
|
||||
static_assert (foo () == ^^M);
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^M);
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^M);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct T
|
||||
{
|
||||
static_assert (access_context::unprivileged ().scope () == ^^::);
|
||||
static_assert (access_context::unchecked ().scope () == null_reflection);
|
||||
static_assert (access_context::current ().scope () == ^^T);
|
||||
static_assert (access_context::unprivileged ().designating_class () == null_reflection);
|
||||
static_assert (access_context::unchecked ().designating_class () == null_reflection);
|
||||
static_assert (access_context::current ().designating_class () == null_reflection);
|
||||
static_assert (foo () == ^^T);
|
||||
info t = access_context::current ().scope ();
|
||||
};
|
||||
|
||||
struct U
|
||||
{
|
||||
consteval U () {}
|
||||
info u = access_context::current ().scope ();
|
||||
};
|
||||
|
||||
struct V : U
|
||||
{
|
||||
using U::U;
|
||||
info v = access_context::current ().scope ();
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^V);
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^V);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
bar ()
|
||||
{
|
||||
static constexpr access_context a = access_context::current ();
|
||||
static_assert (a.scope () == ^^bar && a.designating_class () == null_reflection);
|
||||
static_assert (foo (^^foo) == ^^foo);
|
||||
static_assert (foo () == ^^bar);
|
||||
static_assert (T {}.t == ^^bar);
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^bar);
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^bar);
|
||||
}
|
||||
}
|
||||
auto l = [] () {
|
||||
int v = 0;
|
||||
static_assert (access_context::current ().scope () == parent_of (^^v));
|
||||
static_assert (parent_of (parent_of (access_context::current ().scope ())) == ^^bar);
|
||||
};
|
||||
auto l2 = [] () -> int (&) [access_context::current ().scope () == ^^bar ? 2 : 3] {
|
||||
static int a[2];
|
||||
return a;
|
||||
};
|
||||
}
|
||||
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^::);
|
||||
consteval {
|
||||
static_assert (access_context::current ().scope () == ^^::);
|
||||
}
|
||||
static_assert (access_context::current ().via (^^S).via (null_reflection).designating_class () == null_reflection);
|
||||
}
|
||||
|
||||
consteval bool
|
||||
baz ()
|
||||
{
|
||||
constexpr U u;
|
||||
static_assert (is_constructor (u.u) && parent_of (u.u) == ^^U);
|
||||
constexpr V v;
|
||||
static_assert (is_constructor (v.u) && parent_of (v.u) == ^^U);
|
||||
static_assert (is_constructor (v.v) && parent_of (v.v) == ^^V);
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert (baz ());
|
||||
|
||||
consteval info
|
||||
qux ()
|
||||
{
|
||||
return ^^qux;
|
||||
}
|
||||
|
||||
constexpr info q = qux ();
|
||||
static_assert (q == ^^qux);
|
||||
|
||||
namespace O
|
||||
{
|
||||
int a[2];
|
||||
auto
|
||||
foo () -> int (&) [access_context::current ().scope () == ^^O ? 2 : 3]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
consteval bool
|
||||
can_do_via (info r)
|
||||
{
|
||||
try { access_context::current ().via (r); }
|
||||
catch (std::meta::exception &) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
struct W;
|
||||
|
||||
static_assert (can_do_via (^^S));
|
||||
static_assert (can_do_via (^^T));
|
||||
static_assert (can_do_via (^^U));
|
||||
static_assert (can_do_via (^^V));
|
||||
static_assert (can_do_via (^^N::S));
|
||||
static_assert (can_do_via (null_reflection));
|
||||
static_assert (!can_do_via (^^W));
|
||||
static_assert (!can_do_via (^^::));
|
||||
static_assert (!can_do_via (^^O));
|
||||
static_assert (!can_do_via (^^bar));
|
||||
static_assert (!can_do_via (^^int));
|
||||
enum E { E0, E1 };
|
||||
static_assert (!can_do_via (^^E));
|
||||
static_assert (!can_do_via (^^E0));
|
||||
static_assert (!can_do_via (reflect_constant (42)));
|
||||
47
gcc/testsuite/g++.dg/reflect/p2996-18.C
Normal file
47
gcc/testsuite/g++.dg/reflect/p2996-18.C
Normal file
@@ -0,0 +1,47 @@
|
||||
// { dg-do compile { target c++26 } }
|
||||
// { dg-additional-options "-freflection" }
|
||||
// Test from [meta.reflection.access.context].
|
||||
|
||||
#include <meta>
|
||||
|
||||
using namespace std::meta;
|
||||
|
||||
struct A {
|
||||
int a = 0;
|
||||
consteval A(int p) : a(p) {}
|
||||
};
|
||||
struct B : A {
|
||||
using A::A;
|
||||
consteval B(int p, int q) : A(p * q) {}
|
||||
info s = access_context::current().scope();
|
||||
};
|
||||
struct C : B { using B::B; };
|
||||
|
||||
struct Agg {
|
||||
consteval bool eq(info rhs = access_context::current().scope()) {
|
||||
return s == rhs;
|
||||
}
|
||||
info s = access_context::current().scope();
|
||||
};
|
||||
|
||||
namespace NS {
|
||||
static_assert(Agg{}.s == access_context::current().scope()); // OK
|
||||
static_assert(Agg{}.eq()); // OK
|
||||
static_assert(B(1).s == ^^B); // OK
|
||||
static_assert(is_constructor(B{1, 2}.s) && parent_of(B{1, 2}.s) == ^^B); // OK
|
||||
static_assert(is_constructor(C{1, 2}.s) && parent_of(C{1, 2}.s) == ^^B); // OK
|
||||
|
||||
auto fn() -> [:is_namespace(access_context::current().scope()) ? ^^int : ^^bool:];
|
||||
static_assert(return_type_of(type_of(^^fn)) == ^^int);
|
||||
// TODO: This doesn't work yet.
|
||||
// static_assert(type_of(^^fn) == ^^auto()->int); // OK
|
||||
|
||||
template<auto R>
|
||||
struct TCls {
|
||||
consteval bool fn()
|
||||
requires (is_type(access_context::current().scope())) {
|
||||
return true; // OK, scope is TCls<R>.
|
||||
}
|
||||
};
|
||||
static_assert(TCls<0>{}.fn()); // OK
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// { dg-do compile { target c++26 } }
|
||||
// { dg-additional-options "-freflection" }
|
||||
// Test std::meta::has_parent.
|
||||
// Test std::meta::parent_of.
|
||||
|
||||
#include <meta>
|
||||
|
||||
|
||||
@@ -87,20 +87,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
// [meta.reflection.exception], class exception
|
||||
class exception : public std::exception {
|
||||
private:
|
||||
//optional<string> what_; // exposition only
|
||||
std::string_view what_;
|
||||
u8string u8what_; // exposition only
|
||||
info from_; // exposition only
|
||||
source_location where_; // exposition only
|
||||
//optional<string> _M_what;
|
||||
std::string_view _M_what;
|
||||
u8string _M_u8what;
|
||||
info _M_from;
|
||||
source_location _M_where;
|
||||
|
||||
public:
|
||||
consteval exception(u8string_view what, info from,
|
||||
source_location where = source_location::current()) noexcept
|
||||
: u8what_{what}, from_{from}, where_{where} { }
|
||||
consteval exception(u8string_view __what, info __from,
|
||||
source_location __where
|
||||
= source_location::current()) noexcept
|
||||
: _M_u8what{__what}, _M_from{__from}, _M_where{__where} { }
|
||||
|
||||
consteval exception(string_view what, info from,
|
||||
source_location where = source_location::current()) noexcept
|
||||
: what_{what}, from_{from}, where_{where} { }
|
||||
consteval exception(string_view __what, info __from,
|
||||
source_location __where
|
||||
= source_location::current()) noexcept
|
||||
: _M_what{__what}, _M_from{__from}, _M_where{__where} { }
|
||||
|
||||
exception(const exception&) = default;
|
||||
exception(exception&&) = default;
|
||||
@@ -108,10 +110,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
exception& operator=(const exception&) = default;
|
||||
exception& operator=(exception&&) = default;
|
||||
|
||||
constexpr const char *what() const noexcept override { return what_.data (); }
|
||||
consteval u8string_view u8what() const noexcept { return u8what_; }
|
||||
consteval info from() const noexcept { return from_; }
|
||||
consteval source_location where() const noexcept { return where_; }
|
||||
constexpr const char *what() const noexcept override
|
||||
{ return _M_what.data (); }
|
||||
consteval u8string_view u8what() const noexcept { return _M_u8what; }
|
||||
consteval info from() const noexcept { return _M_from; }
|
||||
consteval source_location where() const noexcept { return _M_where; }
|
||||
};
|
||||
|
||||
// [meta.reflection.operators], operator representations
|
||||
@@ -266,6 +269,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
consteval info variable_of(info);
|
||||
consteval info return_type_of(info);
|
||||
|
||||
// [meta.reflection.access.context], access control context
|
||||
struct access_context {
|
||||
private:
|
||||
consteval access_context(info __scope, info __designating_class) noexcept
|
||||
: _M_scope{__scope}, _M_designating_class{__designating_class} { }
|
||||
public:
|
||||
access_context() = delete;
|
||||
consteval access_context(const access_context &) = default;
|
||||
consteval access_context(access_context &&) = default;
|
||||
|
||||
consteval info scope() const { return _M_scope; }
|
||||
consteval info designating_class() const { return _M_designating_class; }
|
||||
|
||||
static consteval access_context current() noexcept;
|
||||
static consteval access_context unprivileged() noexcept
|
||||
{ return access_context { ^^::, info {} }; }
|
||||
static consteval access_context unchecked() noexcept
|
||||
{ return access_context { info {}, info {} }; }
|
||||
consteval access_context via(info) const;
|
||||
|
||||
info _M_scope;
|
||||
info _M_designating_class;
|
||||
};
|
||||
|
||||
// [meta.reflection.member.queries], reflection member queries
|
||||
consteval vector<info> enumerators_of(info);
|
||||
|
||||
@@ -501,6 +528,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
// [meta.reflection.annotation], annotation reflection
|
||||
consteval vector<info> annotations_of(info);
|
||||
consteval vector<info> annotations_of_with_type(info, info);
|
||||
|
||||
consteval access_context
|
||||
access_context::via(info __cls) const
|
||||
{
|
||||
if (__cls != info {}
|
||||
&& (!std::meta::is_class_type(__cls)
|
||||
|| !std::meta::is_complete_type(__cls)))
|
||||
{
|
||||
#if __cpp_exceptions
|
||||
throw std::meta::exception(u8"via argument other than null "
|
||||
"or complete class type reflection",
|
||||
^^access_context::via);
|
||||
#else
|
||||
return *this;
|
||||
#endif
|
||||
}
|
||||
return access_context { _M_scope, __cls };
|
||||
}
|
||||
|
||||
} // namespace meta
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
|
||||
Reference in New Issue
Block a user