//===----------------------------------------------------------------------===// // // 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: -fblocks // ADDITIONAL_COMPILE_FLAGS: -freflection-latest // // // [reflection] // // RUN: %{build} // RUN: %{exec} %t.exe > %t.stdout #include #include #include #include #include #include // ============================ // alexandrescu_lambda_to_tuple // ============================ namespace alexandrescu_lambda_to_tuple { using std::meta::access_context; consteval auto struct_to_tuple_type(std::meta::info type) -> std::meta::info { constexpr auto remove_cvref = [](std::meta::info r) consteval { return substitute(^^std::remove_cvref_t, {r}); }; return substitute(^^std::tuple, nonstatic_data_members_of(type, access_context::unchecked()) | std::views::transform(std::meta::type_of) | std::views::transform(remove_cvref) | std::ranges::to()); } template constexpr auto struct_to_tuple_helper(From const& from) -> To { return To(from.[:members:]...); } template consteval auto get_struct_to_tuple_helper() { using To = [: struct_to_tuple_type(^^From) :]; std::vector args = {^^To, ^^From}; for (auto mem : nonstatic_data_members_of(^^From, access_context::unchecked())) { args.push_back(reflect_constant(mem)); } return extract(substitute(^^struct_to_tuple_helper, args)); } template constexpr auto struct_to_tuple(From const& from) { return get_struct_to_tuple_helper()(from); } void run_test() { struct S { bool i; int j; int k; }; int a = 1; bool b = true; [[maybe_unused]] int c = 32; std::string d = "hello"; auto fn = [=]() { (void) a; (void) b; (void) d; }; // RUN: grep "Lambda state: <1, true, \"hello\">" %t.stdout auto t = struct_to_tuple(fn); std::println(R"(Lambda state: <{}, {}, "{}">)", get<0>(t), get<1>(t), get<2>(t)); } } // namespace alexandrescu_lambda_to_tuple // ======================= // pdimov_sorted_type_list // ======================= namespace pdimov_sorted_type_list { template struct type_list { }; consteval auto sorted_impl(std::vector v) { std::ranges::sort(v, {}, std::meta::alignment_of); return v; } template consteval auto sorted() { using R = typename [:substitute(^^type_list, sorted_impl({^^Ts...})):]; return R{}; } void run_test() { static_assert(typeid(sorted()) == typeid(type_list)); } } // namespace pdimov_sorted_type_list // ======================= // alisdair_universal_swap // ======================= namespace alisdair_universal_swap { template void do_swap_representations(T& lhs, T& rhs) { // This implementation cannot rebind references, and does not handle const // data members --- still need to decide whether we support the latter if constexpr (std::is_class_v) { // This implementation ensures that empty types do nothing template for (constexpr auto base : define_static_array( bases_of(^^T, std::meta::access_context::unchecked()))) { using Base = [:type_of(base):]; do_swap_representations((Base &)lhs, (Base &)rhs); } template for (constexpr auto mem : define_static_array( nonstatic_data_members_of( ^^T, std::meta::access_context::unchecked()))) { do_swap_representations(lhs.[:mem:], rhs.[:mem:]); } } else if constexpr (std::is_array_v) { static_assert(0 < std::rank_v, "cannot swap arrays of unknown bound"); using MemT = std::decay_t; template for (constexpr auto Idx : define_static_array([] { std::vector result; for (size_t idx = 0; idx < std::size(result); ++idx) result.push_back(idx); return result; }())) { do_swap_representations(lhs[Idx], rhs[Idx]); }; } else if constexpr (std::is_scalar_v or std::is_union_v) { // correct for unions without tail padding, including swapping active // element will need compiler magic to eliminate tail padding though // language ensures to not overwrite tail padding for scalars // May be broken if union overloads `operator=` T intrm = lhs; lhs = rhs; rhs = intrm; } else if constexpr (std::is_reference_v) { static_assert(false, "Does not yet rebind references"); } else if constexpr (sizeof(T) > 0) { static_assert(false, "Unexpected type category"); } } void run_test() { std::vector a = {1, 2, 3}; std::vector b = {3, 2, 1}; do_swap_representations(a, b); // RUN: grep "universal-swap: a = \[3, 2, 1]" %t.stdout // RUN: grep "universal-swap: b = \[1, 2, 3]" %t.stdout std::println("universal-swap: a = [{}, {}, {}]", a[0], a[1], a[2]); std::println("universal-swap: b = [{}, {}, {}]", b[0], b[1], b[2]); } } // namespace alisdair_universal_swap // ============================== // std_apply_with_function_splice // ============================== namespace std_apply_with_function_splice { void run_test() { struct S { static void print(std::string_view sv) { std::println("std::apply: {}", sv); } }; // RUN: grep "std::apply: Hello, world!" %t.stdout std::apply([:^^S::print:], std::tuple {"Hello, world!"}); } } // namespace std_apply_with_function_splice // ========================================== // array_with_default_initialized_reflections // ========================================== namespace array_with_default_initialized_reflections { consteval auto fn() { std::array arr = {}; arr[0] = ^^int; return arr; } [[maybe_unused]] constexpr auto rs = fn(); } // namespace array_with_default_initialized_reflections // ====================== // compatible_with_blocks // ====================== namespace compatible_with_blocks { constexpr auto block = std::meta::reflect_constant(^int() { return 4; }); static_assert(type_of(block) == ^^int(^)()); void run_test() { // RUN: grep "block result 1: 4" %t.stdout std::println("block result 1: {}", [:block:]()); // RUN: grep "block result 2: 6" %t.stdout std::println("block result 2: {}", 4 ^^(void){ return 2; }()); } } // namespace compatible_with_blocks // ========================= // expansions_over_std_tuple // ========================= namespace expansions_over_std_tuple { void run_test() { std::tuple t; template for (auto e : t) (void) e; template for (constexpr auto v : std::tuple{1, 2, 3}) (void) v; static constexpr auto my_tuple = std::tuple{1, 2, 3}; template for (constexpr auto v : my_tuple) (void) v; } } // namespace expansions_over_std_tuple template void run_tests() { (..., [:Tests:]::run_test()); } // =============== // barry_alias_bug // =============== namespace barry_alias_bug { template struct Reflection { static constexpr auto value = R; }; template struct Sequence { }; template using get_element_t = [: template_arguments_of(^^Seq)[I] :]; template using get_base_classes_t = [: [] { std::vector args; for (std::meta::info b : bases_of(T::value, std::meta::access_context::unchecked())) { args.push_back(substitute(^^Reflection, {std::meta::reflect_constant(b)})); } return substitute(^^Sequence, args); }() :]; struct B { }; struct D : B { }; constexpr auto b = bases_of(^^D, std::meta::access_context::unchecked())[0]; static_assert(std::same_as< get_element_t<0, get_base_classes_t>>, Reflection>); } // namespace barry_alias_bug int main() { run_tests< ^^alexandrescu_lambda_to_tuple, ^^pdimov_sorted_type_list, ^^alisdair_universal_swap, ^^std_apply_with_function_splice, ^^compatible_with_blocks, ^^expansions_over_std_tuple >(); }