Implement std::meta::{,u8}identifier_of.

This commit is contained in:
Jakub Jelinek
2025-10-10 17:52:12 +02:00
committed by Marek Polacek
parent 2e8b6092f5
commit c80c5aba39
5 changed files with 402 additions and 7 deletions

View File

@@ -2366,7 +2366,7 @@ eval_bit_size_of (location_t loc, const constexpr_ctx *ctx, tree r,
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
-- Otherwise, r represents a data member description (T,N,A,W,NUA); true if
N is not _|_. Otherwise, false. */
static tree
@@ -2461,6 +2461,87 @@ eval_has_identifier (tree r, reflect_kind kind)
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, tree *jump_target,
tree elt_type, tree ret_type)
{
if (eval_has_identifier (r, kind) == boolean_false_node)
return throw_exception (loc, ctx, N_("reflection with has_identifier "
"false"),
r, jump_target);
r = MAYBE_BASELINK_FUNCTIONS (r);
r = OVL_FIRST (r);
const char *name = NULL;
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));
}
// TODO: direct base class relationship and data member description.
else
gcc_unreachable ();
tree str = temp_string_literal (name, elt_type);
if (str == NULL_TREE)
{
if (elt_type == char_type_node)
return throw_exception (loc, ctx, N_("identifier_of not representable"
" in ordinary literal encoding"),
r, jump_target);
else
return throw_exception (loc, ctx, N_("u8identifier_of not representable"
" in UTF-8"),
r, 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);
}
/* Get the reflection of template argument ARG as per
std::meta::template_arguments_of. */
@@ -4675,6 +4756,12 @@ process_metafunction (const constexpr_ctx *ctx, tree call,
tree h1 = REFLECT_EXPR_HANDLE (i1);
return eval_type_order (loc, ctx, h, h1, jump_target);
}
if (id_equal (name, "identifier_of"))
return eval_identifier_of (loc, ctx, h, kind, jump_target, char_type_node,
TREE_TYPE (call));
if (id_equal (name, "u8identifier_of"))
return eval_identifier_of (loc, ctx, h, kind, jump_target,
char8_type_node, TREE_TYPE (call));
not_found:
sorry ("%qE", name);

View File

@@ -4,11 +4,152 @@
#include <meta>
using namespace std::meta;
struct C { };
#if 0
constexpr std::string_view sv = std::meta::identifier_of(^^C);
static_assert(sv == "C");
static_assert(sv.data()[0] == 'C');
static_assert(sv.data()[1] == '\0');
#endif
constexpr std::string_view sv = identifier_of (^^C);
static_assert (sv == "C");
static_assert (sv.data ()[0] == 'C');
static_assert (sv.data ()[1] == '\0');
struct S { };
using T = int;
using U = S;
enum E { E1, E2 };
static_assert (identifier_of (^^T) == std::string_view ("T"));
static_assert (identifier_of (^^S) == std::string_view ("S"));
static_assert (identifier_of (^^U) == std::string_view ("U"));
static_assert (identifier_of (^^std) == std::string_view ("std"));
static_assert (identifier_of (^^std::meta) == std::string_view ("meta"));
static_assert (identifier_of (^^E1) == std::string_view ("E1"));
// TODO: the standard doesn't specify what happens on enumeral types.
// nor annotations nor non-class non-type alias types.
// We probably want identifier_of on enumeral types unless they
// are unnamed, but I think not on other types and not on annotations.
//static_assert (identifier_of (^^E) == std::string_view ("E"));
namespace N {}
namespace NA = N;
static_assert (identifier_of (^^N) == std::string_view ("N"));
static_assert (identifier_of (^^NA) == std::string_view ("NA"));
namespace {
int a;
namespace M {}
static_assert (identifier_of (^^a) == std::string_view ("a"));
static_assert (identifier_of (^^M) == std::string_view ("M"));
}
typedef struct {
int a;
static_assert (identifier_of (^^a) == std::string_view ("a"));
} SV;
static_assert (identifier_of (^^SV) == std::string_view ("SV"));
static_assert (identifier_of (parent_of (^^SV::a)) == std::string_view ("SV"));
template <int N>
struct ST
{
static_assert (identifier_of (^^ST) == std::string_view ("ST"));
};
struct V
{
void foo () { int a; static_assert (identifier_of (parent_of (^^a)) == std::string_view ("foo")); }
template <int N>
void bar () { int a; static_assert (identifier_of (parent_of (^^a)) == std::string_view ("bar")); }
};
int
operator ""_a (const char *)
{
int a;
static_assert (identifier_of (parent_of (^^a)) == std::string_view ("_a"));
return 0;
}
int v;
static_assert (identifier_of (^^V::foo) == std::string_view ("foo"));
static_assert (identifier_of (^^V::bar) == std::string_view ("bar"));
void foo (int);
static_assert (identifier_of (^^foo) == std::string_view ("foo"));
int arr[3];
void
foo (int a)
{
auto [b, c, d] = arr;
static_assert (identifier_of (^^foo) == std::string_view ("foo"));
static_assert (identifier_of (^^a) == std::string_view ("a"));
static_assert (identifier_of (^^b) == std::string_view ("b"));
static_assert (identifier_of (^^c) == std::string_view ("c"));
static_assert (identifier_of (^^d) == std::string_view ("d"));
}
template <int N>
void
bar (int a)
{
auto [...b, c] = arr;
static_assert (identifier_of (^^a) == std::string_view ("a"));
static_assert (identifier_of (^^c) == std::string_view ("c"));
}
void
baz ()
{
auto a = [] {
int a;
static_assert (identifier_of (^^a) == std::string_view ("a"));
static_assert (identifier_of (parent_of (parent_of (parent_of (^^a)))) == std::string_view ("baz"));
};
using t = decltype (a);
static_assert (identifier_of (^^t) == std::string_view ("t"));
}
void qux (int, int b, int c, int d, int);
constexpr auto p0 = parameters_of (^^qux)[0];
constexpr auto p1 = parameters_of (^^qux)[1];
constexpr auto p2 = parameters_of (^^qux)[2];
constexpr auto p3 = parameters_of (^^qux)[3];
constexpr auto p4 = parameters_of (^^qux)[4];
static_assert (identifier_of (p1) == std::string_view ("b"));
static_assert (identifier_of (p2) == std::string_view ("c"));
static_assert (identifier_of (p3) == std::string_view ("d"));
void qux (int a, int, int c, int e, int);
static_assert (identifier_of (p0) == std::string_view ("a"));
static_assert (identifier_of (p1) == std::string_view ("b"));
static_assert (identifier_of (p2) == std::string_view ("c"));
void
qux (int a, int, int, int e, int)
{
static_assert (identifier_of (p0) == std::string_view ("a"));
static_assert (identifier_of (p1) == std::string_view ("b"));
static_assert (identifier_of (p2) == std::string_view ("c"));
static_assert (identifier_of (variable_of (p0)) == std::string_view ("a"));
static_assert (identifier_of (variable_of (p3)) == std::string_view ("e"));
}
void qux (int f, int, int, int, int g);
static_assert (identifier_of (p1) == std::string_view ("b"));
static_assert (identifier_of (p2) == std::string_view ("c"));
static_assert (identifier_of (p4) == std::string_view ("g"));
template <typename... T>
void
freddy (int a, T... b)
{
}
static_assert (identifier_of (parameters_of (^^freddy <int, long, char>)[0]) == std::string_view ("a"));
struct {
int a;
} s;
static_assert (identifier_of (^^s) == std::string_view ("s"));

View File

@@ -0,0 +1,6 @@
// { dg-do compile { target c++26 } }
// { dg-require-iconv "IBM1047" }
// { dg-additional-options "-freflection -fexec-charset=IBM1047" }
// Test std::meta::identifier_of.
#include "identifier_of1.C"

View File

@@ -0,0 +1,159 @@
// { dg-do compile { target c++26 } }
// { dg-additional-options "-freflection" }
// Test std::meta::u8identifier_of.
#include <meta>
using namespace std::meta;
struct C { };
constexpr std::u8string_view sv = std::meta::u8identifier_of (^^C);
static_assert (sv == u8"C");
static_assert (sv.data ()[0] == u8'C');
static_assert (sv.data ()[1] == u8'\0');
struct S { };
using T = int;
using U = S;
enum E { E1, E2 };
static_assert (u8identifier_of (^^T) == std::u8string_view (u8"T"));
static_assert (u8identifier_of (^^S) == std::u8string_view (u8"S"));
static_assert (u8identifier_of (^^U) == std::u8string_view (u8"U"));
static_assert (u8identifier_of (^^std) == std::u8string_view (u8"std"));
static_assert (u8identifier_of (^^std::meta) == std::u8string_view (u8"meta"));
static_assert (u8identifier_of (^^E1) == std::u8string_view (u8"E1"));
// TODO: the standard doesn't specify what happens on enumeral types.
// nor annotations nor non-class non-type alias types.
// We probably want u8identifier_of on enumeral types unless they
// are unnamed, but I think not on other types and not on annotations.
//static_assert (u8identifier_of (^^E) == std::u8string_view (u8"E"));
namespace N {}
namespace NA = N;
static_assert (u8identifier_of (^^N) == std::u8string_view (u8"N"));
static_assert (u8identifier_of (^^NA) == std::u8string_view (u8"NA"));
namespace {
int a;
namespace M {}
static_assert (u8identifier_of (^^a) == std::u8string_view (u8"a"));
static_assert (u8identifier_of (^^M) == std::u8string_view (u8"M"));
}
typedef struct {
int a;
static_assert (u8identifier_of (^^a) == std::u8string_view (u8"a"));
} SV;
static_assert (u8identifier_of (^^SV) == std::u8string_view (u8"SV"));
static_assert (u8identifier_of (parent_of (^^SV::a)) == std::u8string_view (u8"SV"));
template <int N>
struct ST
{
static_assert (u8identifier_of (^^ST) == std::u8string_view (u8"ST"));
};
struct V
{
void foo () { int a; static_assert (u8identifier_of (parent_of (^^a)) == std::u8string_view (u8"foo")); }
template <int N>
void bar () { int a; static_assert (u8identifier_of (parent_of (^^a)) == std::u8string_view (u8"bar")); }
};
int
operator ""_a (const char *)
{
int a;
static_assert (u8identifier_of (parent_of (^^a)) == std::u8string_view (u8"_a"));
return 0;
}
int v;
static_assert (u8identifier_of (^^V::foo) == std::u8string_view (u8"foo"));
static_assert (u8identifier_of (^^V::bar) == std::u8string_view (u8"bar"));
void foo (int);
static_assert (u8identifier_of (^^foo) == std::u8string_view (u8"foo"));
int arr[3];
void
foo (int a)
{
auto [b, c, d] = arr;
static_assert (u8identifier_of (^^foo) == std::u8string_view (u8"foo"));
static_assert (u8identifier_of (^^a) == std::u8string_view (u8"a"));
static_assert (u8identifier_of (^^b) == std::u8string_view (u8"b"));
static_assert (u8identifier_of (^^c) == std::u8string_view (u8"c"));
static_assert (u8identifier_of (^^d) == std::u8string_view (u8"d"));
}
template <int N>
void
bar (int a)
{
auto [...b, c] = arr;
static_assert (u8identifier_of (^^a) == std::u8string_view (u8"a"));
static_assert (u8identifier_of (^^c) == std::u8string_view (u8"c"));
}
void
baz ()
{
auto a = [] {
int a;
static_assert (u8identifier_of (^^a) == std::u8string_view (u8"a"));
static_assert (u8identifier_of (parent_of (parent_of (parent_of (^^a)))) == std::u8string_view (u8"baz"));
};
using t = decltype (a);
static_assert (u8identifier_of (^^t) == std::u8string_view (u8"t"));
}
void qux (int, int b, int c, int d, int);
constexpr auto p0 = parameters_of (^^qux)[0];
constexpr auto p1 = parameters_of (^^qux)[1];
constexpr auto p2 = parameters_of (^^qux)[2];
constexpr auto p3 = parameters_of (^^qux)[3];
constexpr auto p4 = parameters_of (^^qux)[4];
static_assert (u8identifier_of (p1) == std::u8string_view (u8"b"));
static_assert (u8identifier_of (p2) == std::u8string_view (u8"c"));
static_assert (u8identifier_of (p3) == std::u8string_view (u8"d"));
void qux (int a, int, int c, int e, int);
static_assert (u8identifier_of (p0) == std::u8string_view (u8"a"));
static_assert (u8identifier_of (p1) == std::u8string_view (u8"b"));
static_assert (u8identifier_of (p2) == std::u8string_view (u8"c"));
void
qux (int a, int, int, int e, int)
{
static_assert (u8identifier_of (p0) == std::u8string_view (u8"a"));
static_assert (u8identifier_of (p1) == std::u8string_view (u8"b"));
static_assert (u8identifier_of (p2) == std::u8string_view (u8"c"));
static_assert (u8identifier_of (variable_of (p0)) == std::u8string_view (u8"a"));
static_assert (u8identifier_of (variable_of (p3)) == std::u8string_view (u8"e"));
}
void qux (int f, int, int, int, int g);
static_assert (u8identifier_of (p1) == std::u8string_view (u8"b"));
static_assert (u8identifier_of (p2) == std::u8string_view (u8"c"));
static_assert (u8identifier_of (p4) == std::u8string_view (u8"g"));
template <typename... T>
void
freddy (int a, T... b)
{
}
static_assert (u8identifier_of (parameters_of (^^freddy <int, long, char>)[0]) == std::u8string_view (u8"a"));
struct {
int a;
} s;
static_assert (u8identifier_of (^^s) == std::u8string_view (u8"s"));
int qu\u00E6 = 1;
static_assert (u8identifier_of (^^qu\u00E6) == std::u8string_view (u8"qu\N{LATIN SMALL LETTER AE}"));

View File

@@ -167,6 +167,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// [meta.reflection.names], reflection names and locations
consteval bool has_identifier(info);
consteval string_view identifier_of(info);
consteval u8string_view u8identifier_of(info);
consteval source_location source_location_of(info);