Files
clang-p2996/libcxx/test/std/experimental/reflection/to-and-from-values.pass.cpp
2025-06-24 11:01:59 -04:00

540 lines
18 KiB
C++

//===----------------------------------------------------------------------===//
//
// Copyright 2024 Bloomberg Finance L.P.
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03 || c++11 || c++14 || c++17 || c++20
// ADDITIONAL_COMPILE_FLAGS: -freflection
// <experimental/reflection>
//
// [reflection]
//
// RUN: %{exec} %t.exe > %t.stdout
#include <meta>
#include <print>
#include <utility>
enum Enum { A = 42 };
enum class EnumCls { A = 42 };
const int ConstVar = 42;
constexpr int ConstexprVar = 42;
void fn() {}
struct Cls {
int k;
void fn();
};
template <int K> struct TCls {
static constexpr int value = K;
};
// =====================
// reflect_constant_results
// =====================
namespace reflect_constant_results {
static_assert([:std::meta::reflect_constant(42):] == 42);
static_assert(type_of(std::meta::reflect_constant(42)) == ^^int);
static_assert([:std::meta::reflect_constant(EnumCls::A):] == EnumCls::A);
static_assert(type_of(std::meta::reflect_constant(EnumCls::A)) == ^^EnumCls);
static_assert([:std::meta::reflect_constant(ConstVar):] == ConstVar);
static_assert(type_of(std::meta::reflect_constant(ConstVar)) == ^^int);
static_assert([:std::meta::reflect_constant(ConstexprVar):] == ConstexprVar);
static_assert(type_of(std::meta::reflect_constant(ConstexprVar)) == ^^int);
static_assert([:std::meta::reflect_constant(&fn):] == &fn);
static_assert(type_of(std::meta::reflect_constant(&fn)) == ^^void(*)());
static_assert([:std::meta::reflect_constant(&Cls::k):] == &Cls::k);
static_assert(type_of(std::meta::reflect_constant(&Cls::k)) == ^^int (Cls::*));
static_assert([:std::meta::reflect_constant(&Cls::fn):] == &Cls::fn);
static_assert(type_of(std::meta::reflect_constant(&Cls::fn)) ==
^^void (Cls::*)());
} // namespace reflect_constant_results
// ======================
// reflect_object_results
// ======================
namespace reflect_object_results {
int NonConstVar;
static_assert(std::meta::reflect_object(NonConstVar) != ^^NonConstVar);
static_assert(type_of(std::meta::reflect_object(NonConstVar)) == ^^int);
static_assert(std::meta::reflect_object(ConstVar) != ^^ConstVar);
static_assert(type_of(std::meta::reflect_object(ConstVar)) == ^^const int);
static_assert(std::meta::reflect_object(ConstexprVar) != ^^ConstexprVar);
static_assert(type_of(std::meta::reflect_object(ConstexprVar)) == ^^const int);
static constexpr std::pair<int, short> p = {1, 2};
static_assert(std::meta::reflect_object(p) != ^^p);
static_assert(std::meta::reflect_object(p.first) != ^^p);
static_assert(type_of(std::meta::reflect_object(p)) ==
^^const std::pair<int, short>);
static_assert(&[:std::meta::reflect_object(p.first):] == &p.first);
static_assert([:std::meta::reflect_object(p.first):] == 1);
static_assert(type_of(std::meta::reflect_object(p.first)) == ^^const int);
static_assert(&[:std::meta::reflect_object(p.second):] == &p.second);
static_assert([:std::meta::reflect_object(p.second):] == 2);
static_assert(type_of(std::meta::reflect_object(p.second)) == ^^const short);
const int &Ref = NonConstVar;
static_assert(std::meta::reflect_object(Ref) != ^^NonConstVar);
static_assert(type_of(std::meta::reflect_object(Ref)) == ^^int);
struct B {};
struct D : B {};
D d;
static_assert(type_of(std::meta::reflect_object(d)) == ^^D);
static_assert(type_of(std::meta::reflect_object(static_cast<B &>(d))) == ^^B);
} // namespace reflect_object_results
// ========================
// reflect_function_results
// ========================
namespace reflect_function_results {
static_assert(std::meta::reflect_function(::fn) == ^^::fn);
static_assert(type_of(std::meta::reflect_function(::fn)) == ^^void());
const auto &Ref = ::fn;
static_assert(std::meta::reflect_function(Ref) == ^^::fn);
static_assert(type_of(std::meta::reflect_function(Ref)) == ^^void());
}
// ===============
// extract_results
// ===============
namespace extract_results {
template <auto Expected>
consteval bool CheckValueIs(std::meta::info R) {
return extract<decltype(Expected)>(R) == Expected;
}
static_assert(CheckValueIs<42>(std::meta::reflect_constant(42)));
static_assert(CheckValueIs<EnumCls::A>(^^EnumCls::A));
static_assert(CheckValueIs<Enum::A>(^^Enum::A));
static_assert(CheckValueIs<42>(^^ConstVar));
static_assert(CheckValueIs<42>(^^ConstexprVar));
static_assert(CheckValueIs<&fn>(^^fn));
static_assert(CheckValueIs<&Cls::k>(^^Cls::k));
static_assert(CheckValueIs<&Cls::fn>(^^Cls::fn));
static_assert(CheckValueIs<42>([]() {
[[maybe_unused]] constexpr int x = 42;
return ^^x;
}()));
[[maybe_unused]] TCls<3> ignored;
static_assert(
CheckValueIs<3>(
static_data_members_of(substitute(^^TCls,
{std::meta::reflect_constant(3)}),
std::meta::access_context::unchecked())[0]));
} // namespace extract_results
// =========
// roundtrip
// =========
namespace roundtrip {
template <typename T>
consteval bool Roundtrip(T value) {
return extract<T>(std::meta::reflect_constant(value)) == value;
}
static_assert(Roundtrip(42));
static_assert(Roundtrip(EnumCls::A));
static_assert(Roundtrip(Enum::A));
static_assert(Roundtrip(ConstVar));
static_assert(Roundtrip(ConstexprVar));
static_assert(Roundtrip(fn));
static_assert(Roundtrip(&Cls::k));
static_assert(Roundtrip(&Cls::fn));
static_assert(Roundtrip([] { }));
} // namespace roundtrip
// =====================
// extract_ref_semantics
// =====================
namespace extract_ref_semantics {
int nonConstGlobal = 1;
const int constGlobal = 2;
static_assert(&extract<int &>(^^nonConstGlobal) == &nonConstGlobal);
static_assert(extract<int &>(^^constGlobal) == 2);
const int &constGlobalRef = constGlobal;
static_assert(&extract<const int &>(^^constGlobalRef) == &constGlobal);
// TODO(P2996): Need to decide whether 'extract' should be legal on locals
// within an immediate function context. It isn't legal as spec'd by P2996R3,
// but we may want to make it work. Not going to sink more time into making
// the commented line below work, until we have a decision.
consteval int myfn([[maybe_unused]] int arg) {
int val = 3;
int &ref = extract<int &>(^^val);
//int &ref2 = extract<int &>(^^ref);
//static_assert(&ref2 == &val);
ref = 4;
return val + extract<int>(^^arg);
}
static_assert(myfn(5) == 9);
consteval const int &returnsRef() { return constGlobal; }
void nonConstFn() {
constexpr auto r = std::meta::reflect_object(returnsRef());
static_assert(extract<const int &>(r) == 2);
}
} // namespace extract_ref_semantics
// ====================
// extract_array_as_ptr
// ====================
namespace extract_array_as_ptr {
constexpr int a[] = {1, 2, 3};
static_assert(extract<const int *>(^^a)[2] == 3);
constexpr int b[3] = {1, 2, 3};
static_assert(extract<const int *>(^^b)[2] == 3);
} // namespace extract_array_as_ptr
// ==============
// constant_of_types
// ==============
namespace constant_of_types {
struct S{};
using Alias1 = int;
constexpr Alias1 a1 = 3;
static_assert(type_of(^^a1) == ^^const int);
static_assert(type_of(constant_of(^^a1)) == ^^int);
using Alias2 = S;
[[maybe_unused]] constexpr Alias2 a2 {};
static_assert(type_of(^^a2) == ^^const S);
static_assert(type_of(constant_of(^^a2)) == ^^const S);
constexpr const int &ref = a1;
static_assert(type_of(constant_of(^^ref)) == ^^int);
constexpr std::pair<std::pair<int, bool>, int> p = {{1, true}, 2};
static_assert(type_of(constant_of(std::meta::reflect_object(p.first))) ==
^^const std::pair<int, bool>);
constexpr int g = 3;
consteval std::meta::info fn() {
[[maybe_unused]] const int &r = g;
static_assert([:constant_of(^^r):] == 3);
return constant_of(^^r);
}
static_assert([:fn():] == 3);
} // namespace constant_of_types
// ======================
// objects_from_variables
// ======================
namespace objects_from_variables {
constexpr int i = 1;
static_assert(object_of(^^i) == std::meta::reflect_object(i));
static_assert(constant_of(^^i) == constant_of(object_of(^^i)));
struct A { const int ci = 0; int nci; };
struct B : A { mutable int i; };
B arr[2];
static_assert(object_of(^^arr) != ^^arr);
static_assert(std::meta::reflect_object(arr) == object_of(^^arr));
static_assert(std::meta::reflect_object(arr[0]) != object_of(^^arr));
static_assert(type_of(object_of(^^arr)) == ^^B[2]);
const A &r = arr[1];
static_assert(object_of(^^r) != ^^r);
static_assert(std::meta::reflect_object(arr[1]) != object_of(^^r));
static_assert(std::meta::reflect_object(static_cast<A&>(arr[1])) ==
object_of(^^r));
static_assert(type_of(^^r) == ^^const A&);
static_assert(type_of(object_of(^^r)) == ^^A);
} // namespace objects_from_variables
// ===================
// values_from_objects
// ===================
namespace values_from_objects {
const int constGlobal = 11;
constexpr auto rref = std::meta::reflect_object(constGlobal);
static_assert(constant_of(^^constGlobal) != ^^constGlobal);
static_assert([:constant_of(^^constGlobal):] == 11);
static_assert([:constant_of(rref):] == 11);
static_assert(constant_of(^^constGlobal) == constant_of(rref));
static_assert(constant_of(^^constGlobal) == std::meta::reflect_constant(11));
enum Enum { A };
[[maybe_unused]] static constexpr Enum e = A;
enum EnumCls { CA };
[[maybe_unused]] static constexpr EnumCls ce = CA;
static_assert(constant_of(^^A) != constant_of(^^CA));
static_assert(constant_of(^^A) != std::meta::reflect_constant(0));
static_assert(constant_of(^^A) == std::meta::reflect_constant(Enum(0)));
static_assert(constant_of(^^A) != std::meta::reflect_constant(EnumCls(0)));
static_assert(constant_of(^^e) != std::meta::reflect_constant(0));
static_assert(constant_of(^^e) == std::meta::reflect_constant(Enum(0)));
static_assert(constant_of(^^e) != std::meta::reflect_constant(EnumCls(0)));
static_assert(constant_of(^^ce) != std::meta::reflect_constant(0));
static_assert(constant_of(^^ce) != std::meta::reflect_constant(Enum(0)));
static_assert(constant_of(^^ce) == std::meta::reflect_constant(EnumCls(0)));
constexpr std::pair<std::pair<int, bool>, int> p = {{1, true}, 2};
constexpr std::meta::info rfirst = std::meta::reflect_object(p.first);
static_assert(is_object(rfirst) && !is_value(rfirst));
static_assert(type_of(rfirst) == ^^const std::pair<int, bool>);
static_assert(rfirst != std::meta::reflect_constant(std::make_pair(1, true)));
constexpr std::meta::info rvfirst = constant_of(rfirst);
static_assert(is_object(rvfirst) && !is_value(rvfirst));
static_assert(type_of(rvfirst) == ^^const std::pair<int, bool>);
static_assert(rvfirst == std::meta::reflect_constant(std::make_pair(1, true)));
static_assert([:rvfirst:].first == 1);
} // namespace values_from_objects
// ======================
// reflect_constant_callable
// ======================
namespace reflect_constant_callable {
template<typename T>
constexpr auto reflectValueCallable =
requires { std::meta::reflect_constant<T>(std::declval<T>()); };
enum class E {};
struct StructuralTypeClass {
int x;
};
struct NonStructuralTypeClass {
mutable int x;
};
struct NonStructuralTypeClass2 {
int f() { return x; };
private:
int x;
};
// scalar types are allowed
static_assert(reflectValueCallable<int>);
static_assert(reflectValueCallable<const int>);
static_assert(reflectValueCallable<int*>);
static_assert(reflectValueCallable<const int*>);
static_assert(reflectValueCallable<const int* const>);
static_assert(reflectValueCallable<E>);
static_assert(reflectValueCallable<int StructuralTypeClass::*>);
static_assert(reflectValueCallable<std::nullptr_t>);
// literal class type with all non-static member vars public & non-mutable
static_assert(reflectValueCallable<StructuralTypeClass>);
// references are not allowed
static_assert(!reflectValueCallable<int&>);
static_assert(!reflectValueCallable<const int&>);
static_assert(!reflectValueCallable<int&&>);
static_assert(!reflectValueCallable<const int&&>);
// non structural class types
static_assert(!reflectValueCallable<NonStructuralTypeClass>);
static_assert(!reflectValueCallable<NonStructuralTypeClass2>);
} // namespace reflect_constant_callable
// ========================
// pointer_object_ambiguity
// ========================
namespace pointer_object_ambiguity {
constexpr int k = 3;
constexpr const int *p = &k;
constexpr auto rp = ^^p;
static_assert(is_variable(rp));
//static_assert(!is_object(rp));
static_assert(!is_value(rp));
constexpr auto ro = object_of(rp);
static_assert(!is_variable(ro));
static_assert(is_object(ro));
static_assert(!is_value(ro));
static_assert(ro == std::meta::reflect_object(p));
static_assert(ro != std::meta::reflect_constant(&k));
constexpr auto rv = constant_of(ro);
static_assert(!is_variable(rv));
static_assert(!is_object(rv));
static_assert(is_value(rv));
static_assert(rv == std::meta::reflect_constant(&k));
static_assert(rp != ro);
static_assert(rp != rv);
static_assert(ro != rv);
} // namespace pointer_object_ambiguity
// ================================
// reflect_constants_of_class_types
// ================================
namespace reflect_constants_of_class_types {
struct S { int m; };
constexpr S s{42};
constexpr auto r1 = std::meta::reflect_constant(s);
constexpr auto r2 = std::meta::reflect_object(s);
static_assert(is_object(r1) && is_object(r2));
static_assert(r1 != r2);
static_assert(r1 == constant_of(r2));
template <auto V> requires (is_class_type(^^decltype(V)))
consteval bool fn() {
return std::meta::reflect_constant(V) == std::meta::reflect_object(V);
}
static_assert(fn<s>());
} // namespace reflect_constants_of_class_types
// ============================
// library_based_implementation
// ============================
namespace library_based_implementation {
namespace for_test {
namespace detail {
template <auto V> auto value_helper() {
return V;
}
template <auto &O> auto &object_helper() {
return O;
}
} // namespace detail
consteval std::meta::info constant_of(std::meta::info R) {
return template_arguments_of(substitute(^^detail::value_helper, {R}))[0];
}
consteval std::meta::info object_of(std::meta::info R) {
if (is_function(R))
throw "not an object";
return template_arguments_of(substitute(^^detail::object_helper, {R}))[0];
}
} // namespace for_test
constexpr int i = 3;
const int &r = i;
static_assert(for_test::constant_of(^^i) == std::meta::reflect_constant(3));
static_assert(for_test::object_of(^^r) == std::meta::object_of(^^i));
} // namespace library_based_implementation
// =======================================
// bb_clang_p2996_issue_67_regression_test
// =======================================
namespace bb_clang_p2996_issue_67_regression_test {
template<class T> struct TCls {};
template <std::size_t Count, std::meta::info... Rs>
struct Cls
{
static void fn()
{
[] <auto... Idxs> (std::index_sequence<Idxs...>) consteval {
(void) (Rs...[Idxs], ...);
}(std::make_index_sequence<Count>());
}
};
void odr_use()
{
Cls<1, std::meta::reflect_constant(0)>::fn();
}
} // namespace bb_clang_p2996_issue_67_regression_test
int main() {
// RUN: grep "call-lambda-value: 1" %t.stdout
extract<void(*)(int)>(
std::meta::reflect_constant(
[](int id) {
std::println("call-lambda-value: {}", id);
}))(1);
constexpr auto l = [](int id) {
std::println("call-lambda-var: {}", id);
};
constexpr auto g = [](auto id) {
std::println("call-generic-lambda-var: {} ({})", id,
display_string_of(type_of(^^id)));
};
// RUN: grep "call-lambda-var: 1" %t.stdout
extract<void(*)(int)>(^^l)(1);
// RUN: grep "call-lambda-var: 2" %t.stdout
extract<decltype(l)>(^^l)(2);
// RUN: grep "call-generic-lambda-var: 3 (int)" %t.stdout
extract<decltype(g)>(^^g)(3);
// RUN: grep "updated-extract-global: 42" %t.stdout
int &ref = extract<int &>(^^extract_ref_semantics::nonConstGlobal);
ref = 42;
std::println("updated-extract-global: {}",
extract_ref_semantics::nonConstGlobal);
// RUN: grep "updated-reflect-result-global: 13" %t.stdout
constexpr auto r = std::meta::reflect_object(
extract_ref_semantics::nonConstGlobal);
static_assert(type_of(r) == ^^int);
[:r:] = 13;
std::println("updated-reflect-result-global: {}",
extract_ref_semantics::nonConstGlobal);
// RUN: grep "splice-value-reflection: 1" %t.stdout
static constexpr std::pair<std::pair<int, bool>, int> p = {{1, true}, 2};
constexpr auto rvfirst = constant_of(std::meta::reflect_object(p.first));
int v = [:rvfirst:].first;
std::println("splice-value-reflection: {}", v);
}