diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index eec06cd5887..0febbbb5e57 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -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);
diff --git a/gcc/testsuite/g++.dg/reflect/identifier_of1.C b/gcc/testsuite/g++.dg/reflect/identifier_of1.C
index fd5add1f683..c7eb89a611a 100644
--- a/gcc/testsuite/g++.dg/reflect/identifier_of1.C
+++ b/gcc/testsuite/g++.dg/reflect/identifier_of1.C
@@ -4,11 +4,152 @@
#include
+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
+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
+ 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
+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
+void
+freddy (int a, T... b)
+{
+}
+
+static_assert (identifier_of (parameters_of (^^freddy )[0]) == std::string_view ("a"));
+
+struct {
+ int a;
+} s;
+
+static_assert (identifier_of (^^s) == std::string_view ("s"));
diff --git a/gcc/testsuite/g++.dg/reflect/identifier_of2.C b/gcc/testsuite/g++.dg/reflect/identifier_of2.C
new file mode 100644
index 00000000000..115382fb5a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/identifier_of2.C
@@ -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"
diff --git a/gcc/testsuite/g++.dg/reflect/u8identifier_of1.C b/gcc/testsuite/g++.dg/reflect/u8identifier_of1.C
new file mode 100644
index 00000000000..23a4a3515b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/u8identifier_of1.C
@@ -0,0 +1,159 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// Test std::meta::u8identifier_of.
+
+#include
+
+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
+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
+ 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
+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
+void
+freddy (int a, T... b)
+{
+}
+
+static_assert (u8identifier_of (parameters_of (^^freddy )[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}"));
diff --git a/libstdc++-v3/include/std/meta b/libstdc++-v3/include/std/meta
index b70ac7f8b42..9a31b1561c8 100644
--- a/libstdc++-v3/include/std/meta
+++ b/libstdc++-v3/include/std/meta
@@ -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);