[llvm] [libc] Add invoke_result type traits (PR #65750)
Guillaume Chatelet via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 13 04:56:31 PDT 2023
https://github.com/gchatelet updated https://github.com/llvm/llvm-project/pull/65750:
>From 20a79bd606693e50d56939a8ab34c1dd4bdf335a Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Fri, 8 Sep 2023 13:10:05 +0000
Subject: [PATCH 1/3] [libc] add invoke_result type traits
---
libc/src/__support/CPP/type_traits.h | 1 +
.../__support/CPP/type_traits/invoke_result.h | 90 +++++++++++++++++++
.../llvm-project-overlay/libc/BUILD.bazel | 2 +
3 files changed, 93 insertions(+)
create mode 100644 libc/src/__support/CPP/type_traits/invoke_result.h
diff --git a/libc/src/__support/CPP/type_traits.h b/libc/src/__support/CPP/type_traits.h
index 9deb08b221593e1..cec5de1198c96f7 100644
--- a/libc/src/__support/CPP/type_traits.h
+++ b/libc/src/__support/CPP/type_traits.h
@@ -18,6 +18,7 @@
#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_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_result.h b/libc/src/__support/CPP/type_traits/invoke_result.h
new file mode 100644
index 000000000000000..47b0ffb88e57390
--- /dev/null
+++ b/libc/src/__support/CPP/type_traits/invoke_result.h
@@ -0,0 +1,90 @@
+//===-- 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/decay.h"
+#include "src/__support/CPP/type_traits/enable_if.h"
+#include "src/__support/CPP/type_traits/false_type.h"
+#include "src/__support/CPP/type_traits/is_base_of.h"
+#include "src/__support/CPP/type_traits/is_function.h"
+#include "src/__support/CPP/type_traits/true_type.h"
+#include "src/__support/CPP/utility/declval.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 {
+
+// invoke_result
+
+namespace detail {
+template <class T> struct is_reference_wrapper : cpp::false_type {};
+
+// Disable specialization on `cpp::reference_wrapper` as it is not yet
+// implemented.
+
+// template <class U> struct
+// is_reference_wrapper<cpp::reference_wrapper<U>> : cpp::true_type {};
+
+template <class T> struct invoke_impl {
+ template <class F, class... Args>
+ static auto call(F &&f, Args &&...args)
+ -> decltype(cpp::forward<F>(f)(cpp::forward<Args>(args)...));
+};
+
+template <class B, class MT> struct invoke_impl<MT B::*> {
+ template <class T, class Td = cpp::decay_t<T>,
+ class = cpp::enable_if_t<cpp::is_base_of_v<B, Td>>>
+ static auto get(T &&t) -> T &&;
+
+ template <class T, class Td = cpp::decay_t<T>,
+ class = cpp::enable_if_t<is_reference_wrapper<Td>::value>>
+ static auto get(T &&t) -> decltype(t.get());
+
+ template <class T, class Td = cpp::decay_t<T>,
+ class = cpp::enable_if_t<!cpp::is_base_of_v<B, Td>>,
+ class = cpp::enable_if_t<!is_reference_wrapper<Td>::value>>
+ static auto get(T &&t) -> decltype(*cpp::forward<T>(t));
+
+ template <class T, class... Args, class MT1,
+ class = cpp::enable_if_t<cpp::is_function_v<MT1>>>
+ static auto call(MT1 B::*pmf, T &&t, Args &&...args)
+ -> decltype((invoke_impl::get(cpp::forward<T>(t)).*
+ pmf)(cpp::forward<Args>(args)...));
+
+ template <class T>
+ static auto call(MT B::*pmd, T &&t)
+ -> decltype(invoke_impl::get(cpp::forward<T>(t)).*pmd);
+};
+
+template <class F, class... Args, class Fd = typename cpp::decay_t<F>>
+auto INVOKE(F &&f, Args &&...args)
+ -> decltype(invoke_impl<Fd>::call(cpp::forward<F>(f),
+ cpp::forward<Args>(args)...));
+
+template <typename AlwaysVoid, typename, typename...> struct invoke_result {};
+template <typename F, typename... Args>
+struct invoke_result<decltype(void(detail::INVOKE(cpp::declval<F>(),
+ cpp::declval<Args>()...))),
+ F, Args...> {
+ using type =
+ decltype(detail::INVOKE(cpp::declval<F>(), cpp::declval<Args>()...));
+};
+} // namespace detail
+
+template <class F, class... ArgTypes>
+struct invoke_result : detail::invoke_result<void, F, ArgTypes...> {};
+
+template <class F, class... ArgTypes>
+using invoke_result_t = typename invoke_result<F, ArgTypes...>::type;
+
+} // namespace __llvm_libc::cpp
+
+#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 17e4913749d51c6..da9bdc64fb7815d 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -295,6 +295,7 @@ libc_support_library(
"src/__support/CPP/type_traits/enable_if.h",
"src/__support/CPP/type_traits/false_type.h",
"src/__support/CPP/type_traits/integral_constant.h",
+ "src/__support/CPP/type_traits/invoke_result.h",
"src/__support/CPP/type_traits/is_arithmetic.h",
"src/__support/CPP/type_traits/is_array.h",
"src/__support/CPP/type_traits/is_base_of.h",
@@ -333,6 +334,7 @@ libc_support_library(
"src/__support/CPP/type_traits/type_identity.h",
"src/__support/CPP/type_traits/void_t.h",
"src/__support/CPP/utility/declval.h",
+ "src/__support/CPP/utility/forward.h",
],
deps = [
":__support_macros_attributes",
>From d14a049d3c718a126f9f9a738d2aed06433a5712 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 13 Sep 2023 11:39:50 +0000
Subject: [PATCH 2/3] Implement both invoke and invoke_result
---
libc/src/__support/CPP/CMakeLists.txt | 2 +
libc/src/__support/CPP/type_traits.h | 1 +
libc/src/__support/CPP/type_traits/invoke.h | 53 ++++++++++++
.../__support/CPP/type_traits/invoke_result.h | 74 ++--------------
.../src/__support/CPP/type_traits_test.cpp | 86 +++++++++++++++++++
.../llvm-project-overlay/libc/BUILD.bazel | 1 +
6 files changed, 148 insertions(+), 69 deletions(-)
create mode 100644 libc/src/__support/CPP/type_traits/invoke.h
diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index d24c023ec28ebc9..bb330a7b0ac5106 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 cec5de1198c96f7..3de2ca58903184c 100644
--- a/libc/src/__support/CPP/type_traits.h
+++ b/libc/src/__support/CPP/type_traits.h
@@ -18,6 +18,7 @@
#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"
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 000000000000000..39e228d5613f68d
--- /dev/null
+++ b/libc/src/__support/CPP/type_traits/invoke.h
@@ -0,0 +1,53 @@
+//===-- 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"
+
+namespace __llvm_libc::cpp {
+
+namespace detail {
+
+// catch all function type
+template <class FunctionPtrType> struct invoke_dispatcher {
+ template <class... Args>
+ static auto call(FunctionPtrType &&fun, Args &&...args) {
+ return cpp::forward<FunctionPtrType>(fun)(cpp::forward<Args>(args)...);
+ }
+};
+
+// catch pointer to member function
+template <class Class, class FunctionReturnType>
+struct invoke_dispatcher<FunctionReturnType Class::*> {
+ using FunctionPtrType = FunctionReturnType Class::*;
+
+ template <class T, class... Args, class DecayT = cpp::decay_t<T>>
+ static auto call(FunctionPtrType fun, T &&t1, Args &&...args) {
+ if constexpr (cpp::is_base_of_v<Class, DecayT>) {
+ return (cpp::forward<T>(t1).*fun)(cpp::forward<Args>(args)...);
+ } else {
+ return (cpp::forward<T>(*t1).*fun)(cpp::forward<Args>(args)...);
+ }
+ }
+};
+
+} // namespace detail
+
+template <class Function, class... Args>
+auto invoke(Function &&fun, Args &&...args) {
+ return detail::invoke_dispatcher<cpp::decay_t<Function>>::call(
+ cpp::forward<Function>(fun), cpp::forward<Args>(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
index 47b0ffb88e57390..1a071f5cb94836f 100644
--- a/libc/src/__support/CPP/type_traits/invoke_result.h
+++ b/libc/src/__support/CPP/type_traits/invoke_result.h
@@ -8,82 +8,18 @@
#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/decay.h"
-#include "src/__support/CPP/type_traits/enable_if.h"
-#include "src/__support/CPP/type_traits/false_type.h"
-#include "src/__support/CPP/type_traits/is_base_of.h"
-#include "src/__support/CPP/type_traits/is_function.h"
-#include "src/__support/CPP/type_traits/true_type.h"
+#include "src/__support/CPP/type_traits/invoke.h"
#include "src/__support/CPP/utility/declval.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 {
-// invoke_result
-
-namespace detail {
-template <class T> struct is_reference_wrapper : cpp::false_type {};
-
-// Disable specialization on `cpp::reference_wrapper` as it is not yet
-// implemented.
-
-// template <class U> struct
-// is_reference_wrapper<cpp::reference_wrapper<U>> : cpp::true_type {};
-
-template <class T> struct invoke_impl {
- template <class F, class... Args>
- static auto call(F &&f, Args &&...args)
- -> decltype(cpp::forward<F>(f)(cpp::forward<Args>(args)...));
-};
-
-template <class B, class MT> struct invoke_impl<MT B::*> {
- template <class T, class Td = cpp::decay_t<T>,
- class = cpp::enable_if_t<cpp::is_base_of_v<B, Td>>>
- static auto get(T &&t) -> T &&;
-
- template <class T, class Td = cpp::decay_t<T>,
- class = cpp::enable_if_t<is_reference_wrapper<Td>::value>>
- static auto get(T &&t) -> decltype(t.get());
-
- template <class T, class Td = cpp::decay_t<T>,
- class = cpp::enable_if_t<!cpp::is_base_of_v<B, Td>>,
- class = cpp::enable_if_t<!is_reference_wrapper<Td>::value>>
- static auto get(T &&t) -> decltype(*cpp::forward<T>(t));
-
- template <class T, class... Args, class MT1,
- class = cpp::enable_if_t<cpp::is_function_v<MT1>>>
- static auto call(MT1 B::*pmf, T &&t, Args &&...args)
- -> decltype((invoke_impl::get(cpp::forward<T>(t)).*
- pmf)(cpp::forward<Args>(args)...));
-
- template <class T>
- static auto call(MT B::*pmd, T &&t)
- -> decltype(invoke_impl::get(cpp::forward<T>(t)).*pmd);
-};
-
-template <class F, class... Args, class Fd = typename cpp::decay_t<F>>
-auto INVOKE(F &&f, Args &&...args)
- -> decltype(invoke_impl<Fd>::call(cpp::forward<F>(f),
- cpp::forward<Args>(args)...));
-
-template <typename AlwaysVoid, typename, typename...> struct invoke_result {};
-template <typename F, typename... Args>
-struct invoke_result<decltype(void(detail::INVOKE(cpp::declval<F>(),
- cpp::declval<Args>()...))),
- F, Args...> {
+template <class F, class... Args> struct invoke_result {
using type =
- decltype(detail::INVOKE(cpp::declval<F>(), cpp::declval<Args>()...));
+ decltype(cpp::invoke(cpp::declval<F>(), cpp::declval<Args>()...));
};
-} // namespace detail
-
-template <class F, class... ArgTypes>
-struct invoke_result : detail::invoke_result<void, F, ArgTypes...> {};
-template <class F, class... ArgTypes>
-using invoke_result_t = typename invoke_result<F, ArgTypes...>::type;
+template <class F, class... Args>
+using invoke_result_t = typename invoke_result<F, Args...>::type;
} // namespace __llvm_libc::cpp
diff --git a/libc/test/src/__support/CPP/type_traits_test.cpp b/libc/test/src/__support/CPP/type_traits_test.cpp
index cd025f588388d68..3c1a25b5a1033e9 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<int, 4>::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<cpp::invoke_result_t<decltype(&A::apply), A>, void>));
+ EXPECT_TRUE(
+ (cpp::is_same_v<cpp::invoke_result_t<decltype(&A::apply), B>, void>));
+ EXPECT_TRUE(
+ (cpp::is_same_v<cpp::invoke_result_t<decltype(&free_function)>, void>));
+ EXPECT_TRUE(
+ (cpp::is_same_v<cpp::invoke_result_t<decltype(&free_function_return_5)>,
+ int>));
+ EXPECT_TRUE((cpp::is_same_v<
+ cpp::invoke_result_t<decltype(&free_function_passtrough), int>,
+ int>));
+ EXPECT_TRUE(
+ (cpp::is_same_v<
+ cpp::invoke_result_t<decltype(&Delegate::ptr), Delegate, int>, int>));
+ {
+ auto lambda = []() {};
+ EXPECT_TRUE((cpp::is_same_v<cpp::invoke_result_t<decltype(lambda)>, void>));
+ }
+ {
+ auto lambda = []() { return 0; };
+ EXPECT_TRUE((cpp::is_same_v<cpp::invoke_result_t<decltype(lambda)>, int>));
+ }
+ {
+ auto lambda = [](int) -> double { return 0; };
+ EXPECT_TRUE(
+ (cpp::is_same_v<cpp::invoke_result_t<decltype(lambda), int>, double>));
+ }
+}
+
using IntegralAndFloatingTypes =
testing::TypeList<bool, char, short, int, long, long long, unsigned char,
unsigned short, unsigned int, unsigned long,
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index da9bdc64fb7815d..06cabc015decf30 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -295,6 +295,7 @@ libc_support_library(
"src/__support/CPP/type_traits/enable_if.h",
"src/__support/CPP/type_traits/false_type.h",
"src/__support/CPP/type_traits/integral_constant.h",
+ "src/__support/CPP/type_traits/invoke.h",
"src/__support/CPP/type_traits/invoke_result.h",
"src/__support/CPP/type_traits/is_arithmetic.h",
"src/__support/CPP/type_traits/is_array.h",
>From 05b974eda0a043ceb25860ea2f6d45990090a12f Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 13 Sep 2023 11:56:14 +0000
Subject: [PATCH 3/3] Fix documentation
---
libc/src/__support/CPP/type_traits/invoke.h | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/CPP/type_traits/invoke.h b/libc/src/__support/CPP/type_traits/invoke.h
index 39e228d5613f68d..5fb9da17fca672f 100644
--- a/libc/src/__support/CPP/type_traits/invoke.h
+++ b/libc/src/__support/CPP/type_traits/invoke.h
@@ -13,11 +13,14 @@
#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 type
+// Catch all function types.
template <class FunctionPtrType> struct invoke_dispatcher {
template <class... Args>
static auto call(FunctionPtrType &&fun, Args &&...args) {
@@ -25,7 +28,7 @@ template <class FunctionPtrType> struct invoke_dispatcher {
}
};
-// catch pointer to member function
+// Catch pointer to member function types.
template <class Class, class FunctionReturnType>
struct invoke_dispatcher<FunctionReturnType Class::*> {
using FunctionPtrType = FunctionReturnType Class::*;
@@ -33,8 +36,10 @@ struct invoke_dispatcher<FunctionReturnType Class::*> {
template <class T, class... Args, class DecayT = cpp::decay_t<T>>
static auto call(FunctionPtrType fun, T &&t1, Args &&...args) {
if constexpr (cpp::is_base_of_v<Class, DecayT>) {
+ // T is a (possibly cv ref) type.
return (cpp::forward<T>(t1).*fun)(cpp::forward<Args>(args)...);
} else {
+ // T is assumed to be a pointer type.
return (cpp::forward<T>(*t1).*fun)(cpp::forward<Args>(args)...);
}
}
More information about the llvm-commits
mailing list