From 5023264dce58aaa46b18404a4578b7a777962758 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 8 Sep 2023 13:10:05 +0000 Subject: [PATCH 1/5] [libc] add invoke / invoke_result type traits --- libc/src/__support/CPP/CMakeLists.txt | 2 + libc/src/__support/CPP/type_traits.h | 2 + libc/src/__support/CPP/type_traits/invoke.h | 58 +++++++++++++ .../__support/CPP/type_traits/invoke_result.h | 26 ++++++ .../src/__support/CPP/type_traits_test.cpp | 86 +++++++++++++++++++ .../llvm-project-overlay/libc/BUILD.bazel | 3 + 6 files changed, 177 insertions(+) create mode 100644 libc/src/__support/CPP/type_traits/invoke.h create mode 100644 libc/src/__support/CPP/type_traits/invoke_result.h diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt index d24c023ec28eb..bb330a7b0ac51 100644 --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -104,6 +104,8 @@ add_header_library( type_traits/enable_if.h type_traits/false_type.h type_traits/integral_constant.h + type_traits/invoke.h + type_traits/invoke_result.h type_traits/is_arithmetic.h type_traits/is_array.h type_traits/is_base_of.h diff --git a/libc/src/__support/CPP/type_traits.h b/libc/src/__support/CPP/type_traits.h index 9deb08b221593..3de2ca5890318 100644 --- a/libc/src/__support/CPP/type_traits.h +++ b/libc/src/__support/CPP/type_traits.h @@ -18,6 +18,8 @@ #include "src/__support/CPP/type_traits/enable_if.h" #include "src/__support/CPP/type_traits/false_type.h" #include "src/__support/CPP/type_traits/integral_constant.h" +#include "src/__support/CPP/type_traits/invoke.h" +#include "src/__support/CPP/type_traits/invoke_result.h" #include "src/__support/CPP/type_traits/is_arithmetic.h" #include "src/__support/CPP/type_traits/is_array.h" #include "src/__support/CPP/type_traits/is_base_of.h" diff --git a/libc/src/__support/CPP/type_traits/invoke.h b/libc/src/__support/CPP/type_traits/invoke.h new file mode 100644 index 0000000000000..e1f661339f349 --- /dev/null +++ b/libc/src/__support/CPP/type_traits/invoke.h @@ -0,0 +1,58 @@ +//===-- invoke type_traits --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H + +#include "src/__support/CPP/type_traits/decay.h" +#include "src/__support/CPP/type_traits/is_base_of.h" +#include "src/__support/CPP/utility/forward.h" + +// BEWARE : this implementation is not fully conformant as it doesn't take +// `cpp::reference_wrapper` into account. + +namespace __llvm_libc::cpp { + +namespace detail { + +// Catch all function types. +template struct invoke_dispatcher { + template + static auto call(FunctionPtrType &&fun, Args &&...args) { + return cpp::forward(fun)(cpp::forward(args)...); + } +}; + +// Catch pointer to member function types. +template +struct invoke_dispatcher { + using FunctionPtrType = FunctionReturnType Class::*; + + template > + static auto call(FunctionPtrType fun, T &&t1, Args &&...args) { + if constexpr (cpp::is_base_of_v) { + // T is a (possibly cv ref) type. + return (cpp::forward(t1).*fun)(cpp::forward(args)...); + } else { + // T is assumed to be a pointer type. + return (*cpp::forward(t1).*fun)(cpp::forward(args)...); + } + } +}; + +} // namespace detail + +template +auto invoke(Function &&fun, Args &&...args) { + return detail::invoke_dispatcher>::call( + cpp::forward(fun), cpp::forward(args)...); +} + +} // namespace __llvm_libc::cpp + +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H diff --git a/libc/src/__support/CPP/type_traits/invoke_result.h b/libc/src/__support/CPP/type_traits/invoke_result.h new file mode 100644 index 0000000000000..1a071f5cb9483 --- /dev/null +++ b/libc/src/__support/CPP/type_traits/invoke_result.h @@ -0,0 +1,26 @@ +//===-- invoke_result type_traits -------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H + +#include "src/__support/CPP/type_traits/invoke.h" +#include "src/__support/CPP/utility/declval.h" + +namespace __llvm_libc::cpp { + +template struct invoke_result { + using type = + decltype(cpp::invoke(cpp::declval(), cpp::declval()...)); +}; + +template +using invoke_result_t = typename invoke_result::type; + +} // namespace __llvm_libc::cpp + +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H diff --git a/libc/test/src/__support/CPP/type_traits_test.cpp b/libc/test/src/__support/CPP/type_traits_test.cpp index cd025f588388d..3c1a25b5a1033 100644 --- a/libc/test/src/__support/CPP/type_traits_test.cpp +++ b/libc/test/src/__support/CPP/type_traits_test.cpp @@ -145,6 +145,92 @@ TEST(LlvmLibcTypeTraitsTest, integral_constant) { EXPECT_EQ((integral_constant::value), 4); } +namespace invoke_detail { + +enum State { INIT = 0, A_APPLY_CALLED, B_APPLY_CALLED }; + +struct A { + State state = INIT; + virtual ~A() {} + virtual void apply() { state = A_APPLY_CALLED; } +}; + +struct B : public A { + virtual ~B() {} + virtual void apply() { state = B_APPLY_CALLED; } +}; + +void free_function() {} +int free_function_return_5() { return 5; } +int free_function_passtrough(int value) { return value; } + +struct Delegate { + int (*ptr)(int) = &free_function_passtrough; +}; + +} // namespace invoke_detail + +TEST(LlvmLibcTypeTraitsTest, invoke) { + using namespace invoke_detail; + { // member function call + A a; + EXPECT_EQ(a.state, INIT); + cpp::invoke(&A::apply, a); + EXPECT_EQ(a.state, A_APPLY_CALLED); + } + { // overriden member function call + B b; + EXPECT_EQ(b.state, INIT); + cpp::invoke(&A::apply, b); + EXPECT_EQ(b.state, B_APPLY_CALLED); + } + { // free function + cpp::invoke(&free_function); + EXPECT_EQ(cpp::invoke(&free_function_return_5), 5); + EXPECT_EQ(cpp::invoke(&free_function_passtrough, 1), 1); + } + { // pointer member function call + Delegate d; + EXPECT_EQ(cpp::invoke(&Delegate::ptr, d, 2), 2); + } + { // lambda + EXPECT_EQ(cpp::invoke([]() -> int { return 2; }), 2); + EXPECT_EQ(cpp::invoke([](int value) -> int { return value; }, 1), 1); + } +} + +TEST(LlvmLibcTypeTraitsTest, invoke_result) { + using namespace invoke_detail; + EXPECT_TRUE( + (cpp::is_same_v, void>)); + EXPECT_TRUE( + (cpp::is_same_v, void>)); + EXPECT_TRUE( + (cpp::is_same_v, void>)); + EXPECT_TRUE( + (cpp::is_same_v, + int>)); + EXPECT_TRUE((cpp::is_same_v< + cpp::invoke_result_t, + int>)); + EXPECT_TRUE( + (cpp::is_same_v< + cpp::invoke_result_t, int>)); + { + auto lambda = []() {}; + EXPECT_TRUE((cpp::is_same_v, void>)); + } + { + auto lambda = []() { return 0; }; + EXPECT_TRUE((cpp::is_same_v, int>)); + } + { + auto lambda = [](int) -> double { return 0; }; + EXPECT_TRUE( + (cpp::is_same_v, double>)); + } +} + using IntegralAndFloatingTypes = testing::TypeList Date: Wed, 13 Sep 2023 13:11:19 +0000 Subject: [PATCH 2/5] Fix missing forwarding reference --- libc/src/__support/CPP/type_traits/invoke.h | 15 ++++++++------- libc/test/src/__support/CPP/type_traits_test.cpp | 3 +++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libc/src/__support/CPP/type_traits/invoke.h b/libc/src/__support/CPP/type_traits/invoke.h index e1f661339f349..0d66e56555946 100644 --- a/libc/src/__support/CPP/type_traits/invoke.h +++ b/libc/src/__support/CPP/type_traits/invoke.h @@ -10,21 +10,22 @@ #define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H #include "src/__support/CPP/type_traits/decay.h" +#include "src/__support/CPP/type_traits/enable_if.h" #include "src/__support/CPP/type_traits/is_base_of.h" +#include "src/__support/CPP/type_traits/is_same.h" #include "src/__support/CPP/utility/forward.h" -// BEWARE : this implementation is not fully conformant as it doesn't take -// `cpp::reference_wrapper` into account. - namespace __llvm_libc::cpp { namespace detail { -// Catch all function types. +// Catch all function and functor types. template struct invoke_dispatcher { - template - static auto call(FunctionPtrType &&fun, Args &&...args) { - return cpp::forward(fun)(cpp::forward(args)...); + template , FunctionPtrType>>> + static auto call(T &&fun, Args &&...args) { + return cpp::forward(fun)(cpp::forward(args)...); } }; diff --git a/libc/test/src/__support/CPP/type_traits_test.cpp b/libc/test/src/__support/CPP/type_traits_test.cpp index 3c1a25b5a1033..04ee3aba68e51 100644 --- a/libc/test/src/__support/CPP/type_traits_test.cpp +++ b/libc/test/src/__support/CPP/type_traits_test.cpp @@ -196,6 +196,9 @@ TEST(LlvmLibcTypeTraitsTest, invoke) { { // lambda EXPECT_EQ(cpp::invoke([]() -> int { return 2; }), 2); EXPECT_EQ(cpp::invoke([](int value) -> int { return value; }, 1), 1); + + const auto lambda = [](int) { return 0; }; + EXPECT_EQ(cpp::invoke(lambda, 1), 0); } } From 74d7f1b76a35967f61dc1a364576b39c11364285 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Wed, 13 Sep 2023 13:42:55 +0000 Subject: [PATCH 3/5] Enforce assumption --- libc/src/__support/CPP/CMakeLists.txt | 1 + .../__support/CPP/type_traits/always_false.h | 25 +++++++++++++++++++ libc/src/__support/CPP/type_traits/invoke.h | 8 ++++-- libc/src/__support/CPP/utility/declval.h | 8 ++---- .../llvm-project-overlay/libc/BUILD.bazel | 1 + 5 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 libc/src/__support/CPP/type_traits/always_false.h diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt index bb330a7b0ac51..a2d3bd7df9e90 100644 --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -95,6 +95,7 @@ add_header_library( type_traits HDRS type_traits.h + type_traits/always_false.h type_traits/add_lvalue_reference.h type_traits/add_pointer.h type_traits/add_rvalue_reference.h diff --git a/libc/src/__support/CPP/type_traits/always_false.h b/libc/src/__support/CPP/type_traits/always_false.h new file mode 100644 index 0000000000000..84be58d16ec8d --- /dev/null +++ b/libc/src/__support/CPP/type_traits/always_false.h @@ -0,0 +1,25 @@ +//===-- convenient static_assert(false) helper ------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_ALWAYS_FALSE_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_ALWAYS_FALSE_H + +#include "src/__support/macros/attributes.h" + +namespace __llvm_libc::cpp { + +// This is technically not part of the standard but it come often enough that +// it's convenient to have around. +// +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html#valid-workaround + +template LIBC_INLINE_VAR constexpr bool always_false = false; + +} // namespace __llvm_libc::cpp + +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_ALWAYS_FALSE_H diff --git a/libc/src/__support/CPP/type_traits/invoke.h b/libc/src/__support/CPP/type_traits/invoke.h index 0d66e56555946..5372a7f60980e 100644 --- a/libc/src/__support/CPP/type_traits/invoke.h +++ b/libc/src/__support/CPP/type_traits/invoke.h @@ -9,9 +9,11 @@ #ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H #define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H +#include "src/__support/CPP/type_traits/always_false.h" #include "src/__support/CPP/type_traits/decay.h" #include "src/__support/CPP/type_traits/enable_if.h" #include "src/__support/CPP/type_traits/is_base_of.h" +#include "src/__support/CPP/type_traits/is_pointer.h" #include "src/__support/CPP/type_traits/is_same.h" #include "src/__support/CPP/utility/forward.h" @@ -39,9 +41,11 @@ struct invoke_dispatcher { if constexpr (cpp::is_base_of_v) { // T is a (possibly cv ref) type. return (cpp::forward(t1).*fun)(cpp::forward(args)...); - } else { - // T is assumed to be a pointer type. + } else if constexpr (cpp::is_pointer_v) { + // T is a pointer type. return (*cpp::forward(t1).*fun)(cpp::forward(args)...); + } else { + static_assert(cpp::always_false); } } }; diff --git a/libc/src/__support/CPP/utility/declval.h b/libc/src/__support/CPP/utility/declval.h index 9261ceb9332d5..21bb973de0dd0 100644 --- a/libc/src/__support/CPP/utility/declval.h +++ b/libc/src/__support/CPP/utility/declval.h @@ -9,17 +9,13 @@ #define LLVM_LIBC_SRC_SUPPORT_CPP_UTILITY_DECLVAL_H #include "src/__support/CPP/type_traits/add_rvalue_reference.h" -#include "src/__support/macros/attributes.h" +#include "src/__support/CPP/type_traits/always_false.h" namespace __llvm_libc::cpp { // declval -namespace detail { -template LIBC_INLINE_VAR constexpr bool always_false = false; -} - template cpp::add_rvalue_reference_t declval() { - static_assert(detail::always_false, + static_assert(cpp::always_false, "declval not allowed in an evaluated context"); } diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 06cabc015decf..80ea75ecd8760 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -289,6 +289,7 @@ libc_support_library( "src/__support/CPP/type_traits/add_lvalue_reference.h", "src/__support/CPP/type_traits/add_pointer.h", "src/__support/CPP/type_traits/add_rvalue_reference.h", + "src/__support/CPP/type_traits/always_false.h", "src/__support/CPP/type_traits/bool_constant.h", "src/__support/CPP/type_traits/conditional.h", "src/__support/CPP/type_traits/decay.h", From 485ecc2040819b0669cf1aa02743412745547c6a Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Thu, 14 Sep 2023 11:38:03 +0000 Subject: [PATCH 4/5] Add tests in the presence of several ref qualifiers --- .../__support/CPP/type_traits/invoke_result.h | 8 +-- .../src/__support/CPP/type_traits_test.cpp | 71 +++++++++++-------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/libc/src/__support/CPP/type_traits/invoke_result.h b/libc/src/__support/CPP/type_traits/invoke_result.h index 1a071f5cb9483..20ebba4e4cf95 100644 --- a/libc/src/__support/CPP/type_traits/invoke_result.h +++ b/libc/src/__support/CPP/type_traits/invoke_result.h @@ -9,14 +9,14 @@ #define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H #include "src/__support/CPP/type_traits/invoke.h" +#include "src/__support/CPP/type_traits/type_identity.h" #include "src/__support/CPP/utility/declval.h" namespace __llvm_libc::cpp { -template struct invoke_result { - using type = - decltype(cpp::invoke(cpp::declval(), cpp::declval()...)); -}; +template +struct invoke_result : cpp::type_identity(), cpp::declval()...))> {}; template using invoke_result_t = typename invoke_result::type; diff --git a/libc/test/src/__support/CPP/type_traits_test.cpp b/libc/test/src/__support/CPP/type_traits_test.cpp index 04ee3aba68e51..e18004fa4bace 100644 --- a/libc/test/src/__support/CPP/type_traits_test.cpp +++ b/libc/test/src/__support/CPP/type_traits_test.cpp @@ -168,6 +168,17 @@ struct Delegate { int (*ptr)(int) = &free_function_passtrough; }; +template struct Tag { + static int value() { return tag; } +}; + +struct Functor { + auto operator()() & { return Tag<0>(); } + auto operator()() const & { return Tag<1>(); } + auto operator()() && { return Tag<2>(); } + auto operator()() const && { return Tag<3>(); } +}; + } // namespace invoke_detail TEST(LlvmLibcTypeTraitsTest, invoke) { @@ -175,62 +186,66 @@ TEST(LlvmLibcTypeTraitsTest, invoke) { { // member function call A a; EXPECT_EQ(a.state, INIT); - cpp::invoke(&A::apply, a); + invoke(&A::apply, a); EXPECT_EQ(a.state, A_APPLY_CALLED); } { // overriden member function call B b; EXPECT_EQ(b.state, INIT); - cpp::invoke(&A::apply, b); + invoke(&A::apply, b); EXPECT_EQ(b.state, B_APPLY_CALLED); } { // free function - cpp::invoke(&free_function); - EXPECT_EQ(cpp::invoke(&free_function_return_5), 5); - EXPECT_EQ(cpp::invoke(&free_function_passtrough, 1), 1); + invoke(&free_function); + EXPECT_EQ(invoke(&free_function_return_5), 5); + EXPECT_EQ(invoke(&free_function_passtrough, 1), 1); } { // pointer member function call Delegate d; - EXPECT_EQ(cpp::invoke(&Delegate::ptr, d, 2), 2); + EXPECT_EQ(invoke(&Delegate::ptr, d, 2), 2); + } + { // Functor with several ref qualifier + Functor f; + const Functor cf; + EXPECT_EQ(invoke(f).value(), 0); + EXPECT_EQ(invoke(cf).value(), 1); + EXPECT_EQ(invoke(move(f)).value(), 2); + EXPECT_EQ(invoke(move(cf)).value(), 3); } { // lambda - EXPECT_EQ(cpp::invoke([]() -> int { return 2; }), 2); - EXPECT_EQ(cpp::invoke([](int value) -> int { return value; }, 1), 1); + EXPECT_EQ(invoke([]() -> int { return 2; }), 2); + EXPECT_EQ(invoke([](int value) -> int { return value; }, 1), 1); const auto lambda = [](int) { return 0; }; - EXPECT_EQ(cpp::invoke(lambda, 1), 0); + EXPECT_EQ(invoke(lambda, 1), 0); } } TEST(LlvmLibcTypeTraitsTest, invoke_result) { using namespace invoke_detail; - EXPECT_TRUE( - (cpp::is_same_v, void>)); - EXPECT_TRUE( - (cpp::is_same_v, void>)); - EXPECT_TRUE( - (cpp::is_same_v, void>)); - EXPECT_TRUE( - (cpp::is_same_v, - int>)); - EXPECT_TRUE((cpp::is_same_v< - cpp::invoke_result_t, - int>)); - EXPECT_TRUE( - (cpp::is_same_v< - cpp::invoke_result_t, int>)); + EXPECT_TRUE((is_same_v, void>)); + EXPECT_TRUE((is_same_v, void>)); + EXPECT_TRUE((is_same_v, void>)); + EXPECT_TRUE((is_same_v, int>)); + EXPECT_TRUE((is_same_v, int>)); + EXPECT_TRUE(( + is_same_v, int>)); + // Functor with several ref qualifier + EXPECT_TRUE((is_same_v, Tag<0>>)); + EXPECT_TRUE((is_same_v, Tag<1>>)); + EXPECT_TRUE((is_same_v, Tag<2>>)); + EXPECT_TRUE((is_same_v, Tag<3>>)); { auto lambda = []() {}; - EXPECT_TRUE((cpp::is_same_v, void>)); + EXPECT_TRUE((is_same_v, void>)); } { auto lambda = []() { return 0; }; - EXPECT_TRUE((cpp::is_same_v, int>)); + EXPECT_TRUE((is_same_v, int>)); } { auto lambda = [](int) -> double { return 0; }; - EXPECT_TRUE( - (cpp::is_same_v, double>)); + EXPECT_TRUE((is_same_v, double>)); } } From e528df8cb325e4da07ca41d898462ed910c527d5 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Thu, 14 Sep 2023 15:36:31 +0000 Subject: [PATCH 5/5] Address comments --- libc/src/__support/CPP/type_traits/invoke.h | 7 +++---- .../src/__support/CPP/type_traits_test.cpp | 21 +++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/libc/src/__support/CPP/type_traits/invoke.h b/libc/src/__support/CPP/type_traits/invoke.h index 5372a7f60980e..e5294ffcba74f 100644 --- a/libc/src/__support/CPP/type_traits/invoke.h +++ b/libc/src/__support/CPP/type_traits/invoke.h @@ -26,7 +26,7 @@ template struct invoke_dispatcher { template , FunctionPtrType>>> - static auto call(T &&fun, Args &&...args) { + static decltype(auto) call(T &&fun, Args &&...args) { return cpp::forward(fun)(cpp::forward(args)...); } }; @@ -37,7 +37,7 @@ struct invoke_dispatcher { using FunctionPtrType = FunctionReturnType Class::*; template > - static auto call(FunctionPtrType fun, T &&t1, Args &&...args) { + static decltype(auto) call(FunctionPtrType fun, T &&t1, Args &&...args) { if constexpr (cpp::is_base_of_v) { // T is a (possibly cv ref) type. return (cpp::forward(t1).*fun)(cpp::forward(args)...); @@ -51,9 +51,8 @@ struct invoke_dispatcher { }; } // namespace detail - template -auto invoke(Function &&fun, Args &&...args) { +decltype(auto) invoke(Function &&fun, Args &&...args) { return detail::invoke_dispatcher>::call( cpp::forward(fun), cpp::forward(args)...); } diff --git a/libc/test/src/__support/CPP/type_traits_test.cpp b/libc/test/src/__support/CPP/type_traits_test.cpp index e18004fa4bace..4e1eb41b73992 100644 --- a/libc/test/src/__support/CPP/type_traits_test.cpp +++ b/libc/test/src/__support/CPP/type_traits_test.cpp @@ -169,7 +169,7 @@ struct Delegate { }; template struct Tag { - static int value() { return tag; } + static constexpr int value = tag; }; struct Functor { @@ -177,6 +177,10 @@ struct Functor { auto operator()() const & { return Tag<1>(); } auto operator()() && { return Tag<2>(); } auto operator()() const && { return Tag<3>(); } + + const Tag<0> &operator()(const Tag<0> &a) { return a; } + const Tag<0> &&operator()(const Tag<0> &&a) { return cpp::move(a); } + Tag<1> operator()(Tag<1> a) { return a; } }; } // namespace invoke_detail @@ -207,10 +211,10 @@ TEST(LlvmLibcTypeTraitsTest, invoke) { { // Functor with several ref qualifier Functor f; const Functor cf; - EXPECT_EQ(invoke(f).value(), 0); - EXPECT_EQ(invoke(cf).value(), 1); - EXPECT_EQ(invoke(move(f)).value(), 2); - EXPECT_EQ(invoke(move(cf)).value(), 3); + EXPECT_EQ(invoke(f).value, 0); + EXPECT_EQ(invoke(cf).value, 1); + EXPECT_EQ(invoke(move(f)).value, 2); + EXPECT_EQ(invoke(move(cf)).value, 3); } { // lambda EXPECT_EQ(invoke([]() -> int { return 2; }), 2); @@ -230,11 +234,16 @@ TEST(LlvmLibcTypeTraitsTest, invoke_result) { EXPECT_TRUE((is_same_v, int>)); EXPECT_TRUE(( is_same_v, int>)); - // Functor with several ref qualifier + // Functor with several ref qualifiers EXPECT_TRUE((is_same_v, Tag<0>>)); EXPECT_TRUE((is_same_v, Tag<1>>)); EXPECT_TRUE((is_same_v, Tag<2>>)); EXPECT_TRUE((is_same_v, Tag<3>>)); + // Functor with several arg qualifiers + EXPECT_TRUE( + (is_same_v &>, const Tag<0> &>)); + EXPECT_TRUE((is_same_v>, const Tag<0> &&>)); + EXPECT_TRUE((is_same_v>, Tag<1>>)); { auto lambda = []() {}; EXPECT_TRUE((is_same_v, void>));