[libcxx-commits] [clang] [libcxx] [Clang] Add __builtin_invoke and detect std::invoke as a builtin (PR #116709)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Nov 18 15:15:48 PST 2024
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/116709
`std::invoke` is currently quite heavy compared to a function call, since it involves quite heavy SFINAE. This can be done significantly more efficient by the compiler, since most calls to `std::invoke` are simple function calls and 6 out of the seven overloads for `std::invoke` exist only to support member pointers. Even these boil down to a few relatively simple checks. Furthermore, this results in significantly better code gen at -O0, since it removes multiple levels of function calls.
Some real-world testing with this patch revealed some significant results. For example, instantiating `std::format("Banane")` (and its callees) went down from ~130ms on my system to ~100ms, or with some questionable trickery even down to ~90ms.
>From 025f937646f190978325c58967ccb02828a78bd2 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 18 Nov 2024 16:06:31 +0100
Subject: [PATCH 1/2] [libc++] Define an internal API for std::invoke and
friends
---
libcxx/include/__algorithm/make_projected.h | 12 +++---
libcxx/include/__functional/bind.h | 22 +++++------
libcxx/include/__functional/function.h | 11 ++----
libcxx/include/__functional/hash.h | 2 +-
libcxx/include/__functional/mem_fn.h | 6 +--
.../include/__functional/reference_wrapper.h | 2 +-
libcxx/include/__hash_table | 4 +-
libcxx/include/__tree | 2 +-
libcxx/include/__type_traits/invoke.h | 38 +++++++++++++++++++
libcxx/include/__type_traits/result_of.h | 4 +-
libcxx/include/future | 6 +--
libcxx/include/unordered_map | 4 +-
libcxx/include/unordered_set | 4 +-
13 files changed, 76 insertions(+), 41 deletions(-)
diff --git a/libcxx/include/__algorithm/make_projected.h b/libcxx/include/__algorithm/make_projected.h
index 22cceb4cb2fb85..e2f2e174768ae1 100644
--- a/libcxx/include/__algorithm/make_projected.h
+++ b/libcxx/include/__algorithm/make_projected.h
@@ -37,16 +37,16 @@ struct _ProjectedPred {
: __pred(__pred_arg), __proj(__proj_arg) {}
template <class _Tp>
- typename __invoke_of<_Pred&, decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_Tp>()))>::type
- _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI
- operator()(_Tp&& __v) const {
+ __invoke_result_t<_Pred&, decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_Tp>()))> _LIBCPP_CONSTEXPR
+ _LIBCPP_HIDE_FROM_ABI
+ operator()(_Tp&& __v) const {
return std::__invoke(__pred, std::__invoke(__proj, std::forward<_Tp>(__v)));
}
template <class _T1, class _T2>
- typename __invoke_of<_Pred&,
- decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T1>())),
- decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T2>()))>::type _LIBCPP_CONSTEXPR
+ __invoke_result_t<_Pred&,
+ decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T1>())),
+ decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T2>()))> _LIBCPP_CONSTEXPR
_LIBCPP_HIDE_FROM_ABI
operator()(_T1&& __lhs, _T2&& __rhs) const {
return std::__invoke(
diff --git a/libcxx/include/__functional/bind.h b/libcxx/include/__functional/bind.h
index f82c1517249b16..54ec0062932cbf 100644
--- a/libcxx/include/__functional/bind.h
+++ b/libcxx/include/__functional/bind.h
@@ -82,13 +82,13 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __mu(reference_w
}
template <class _Ti, class... _Uj, size_t... _Indx>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<_Ti&, _Uj...>::type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<_Ti&, _Uj...>
__mu_expand(_Ti& __ti, tuple<_Uj...>& __uj, __tuple_indices<_Indx...>) {
return __ti(std::forward<_Uj>(std::get<_Indx>(__uj))...);
}
template <class _Ti, class... _Uj, __enable_if_t<is_bind_expression<_Ti>::value, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<_Ti&, _Uj...>::type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<_Ti&, _Uj...>
__mu(_Ti& __ti, tuple<_Uj...>& __uj) {
typedef typename __make_tuple_indices<sizeof...(_Uj)>::type __indices;
return std::__mu_expand(__ti, __uj, __indices());
@@ -130,12 +130,12 @@ struct __mu_return_invokable // false
template <class _Ti, class... _Uj>
struct __mu_return_invokable<true, _Ti, _Uj...> {
- typedef typename __invoke_of<_Ti&, _Uj...>::type type;
+ using type = __invoke_result_t<_Ti&, _Uj...>;
};
template <class _Ti, class... _Uj>
struct __mu_return_impl<_Ti, false, true, false, tuple<_Uj...> >
- : public __mu_return_invokable<__invokable<_Ti&, _Uj...>::value, _Ti, _Uj...> {};
+ : public __mu_return_invokable<__is_invocable_v<_Ti&, _Uj...>, _Ti, _Uj...> {};
template <class _Ti, class _TupleUj>
struct __mu_return_impl<_Ti, false, false, true, _TupleUj> {
@@ -168,12 +168,12 @@ struct __is_valid_bind_return {
template <class _Fp, class... _BoundArgs, class _TupleUj>
struct __is_valid_bind_return<_Fp, tuple<_BoundArgs...>, _TupleUj> {
- static const bool value = __invokable<_Fp, typename __mu_return<_BoundArgs, _TupleUj>::type...>::value;
+ static const bool value = __is_invocable_v<_Fp, typename __mu_return<_BoundArgs, _TupleUj>::type...>;
};
template <class _Fp, class... _BoundArgs, class _TupleUj>
struct __is_valid_bind_return<_Fp, const tuple<_BoundArgs...>, _TupleUj> {
- static const bool value = __invokable<_Fp, typename __mu_return<const _BoundArgs, _TupleUj>::type...>::value;
+ static const bool value = __is_invocable_v<_Fp, typename __mu_return<const _BoundArgs, _TupleUj>::type...>;
};
template <class _Fp, class _BoundArgs, class _TupleUj, bool = __is_valid_bind_return<_Fp, _BoundArgs, _TupleUj>::value>
@@ -181,12 +181,12 @@ struct __bind_return;
template <class _Fp, class... _BoundArgs, class _TupleUj>
struct __bind_return<_Fp, tuple<_BoundArgs...>, _TupleUj, true> {
- typedef typename __invoke_of< _Fp&, typename __mu_return< _BoundArgs, _TupleUj >::type... >::type type;
+ using type = __invoke_result_t< _Fp&, typename __mu_return< _BoundArgs, _TupleUj >::type... >;
};
template <class _Fp, class... _BoundArgs, class _TupleUj>
struct __bind_return<_Fp, const tuple<_BoundArgs...>, _TupleUj, true> {
- typedef typename __invoke_of< _Fp&, typename __mu_return< const _BoundArgs, _TupleUj >::type... >::type type;
+ using type = __invoke_result_t< _Fp&, typename __mu_return< const _BoundArgs, _TupleUj >::type... >;
};
template <class _Fp, class _BoundArgs, size_t... _Indx, class _Args>
@@ -256,8 +256,7 @@ class __bind_r : public __bind<_Fp, _BoundArgs...> {
is_void<_Rp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 result_type operator()(_Args&&... __args) {
- typedef __invoke_void_return_wrapper<_Rp> _Invoker;
- return _Invoker::__call(static_cast<base&>(*this), std::forward<_Args>(__args)...);
+ return std::__invoke_r<_Rp>(static_cast<base&>(*this), std::forward<_Args>(__args)...);
}
template <class... _Args,
@@ -266,8 +265,7 @@ class __bind_r : public __bind<_Fp, _BoundArgs...> {
is_void<_Rp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 result_type operator()(_Args&&... __args) const {
- typedef __invoke_void_return_wrapper<_Rp> _Invoker;
- return _Invoker::__call(static_cast<base const&>(*this), std::forward<_Args>(__args)...);
+ return std::__invoke_r<_Rp>(static_cast<base const&>(*this), std::forward<_Args>(__args)...);
}
};
diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 543be05f10f330..5a906f32fb3984 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -166,8 +166,7 @@ class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> {
: __func_(std::move(__f)), __alloc_(std::move(__a)) {}
_LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes&&... __arg) {
- typedef __invoke_void_return_wrapper<_Rp> _Invoker;
- return _Invoker::__call(__func_, std::forward<_ArgTypes>(__arg)...);
+ return std::__invoke_r<_Rp>(__func_, std::forward<_ArgTypes>(__arg)...);
}
_LIBCPP_HIDE_FROM_ABI __alloc_func* __clone() const {
@@ -208,8 +207,7 @@ class __default_alloc_func<_Fp, _Rp(_ArgTypes...)> {
_LIBCPP_HIDE_FROM_ABI explicit __default_alloc_func(const _Target& __f) : __f_(__f) {}
_LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes&&... __arg) {
- typedef __invoke_void_return_wrapper<_Rp> _Invoker;
- return _Invoker::__call(__f_, std::forward<_ArgTypes>(__arg)...);
+ return std::__invoke_r<_Rp>(__f_, std::forward<_ArgTypes>(__arg)...);
}
_LIBCPP_HIDE_FROM_ABI __default_alloc_func* __clone() const {
@@ -834,13 +832,12 @@ class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
__func __f_;
- template <class _Fp,
- bool = _And< _IsNotSame<__remove_cvref_t<_Fp>, function>, __invokable<_Fp, _ArgTypes...> >::value>
+ template <class _Fp, bool = !is_same<__remove_cvref_t<_Fp>, function>::value && __is_invocable_v<_Fp, _ArgTypes...> >
struct __callable;
template <class _Fp>
struct __callable<_Fp, true> {
static const bool value =
- is_void<_Rp>::value || __is_core_convertible<typename __invoke_of<_Fp, _ArgTypes...>::type, _Rp>::value;
+ is_void<_Rp>::value || __is_core_convertible<__invoke_result_t<_Fp, _ArgTypes...>, _Rp>::value;
};
template <class _Fp>
struct __callable<_Fp, false> {
diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index 1f67b6a8377660..28b2635ab12537 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -522,7 +522,7 @@ template <class _Key, class _Hash>
using __check_hash_requirements _LIBCPP_NODEBUG =
integral_constant<bool,
is_copy_constructible<_Hash>::value && is_move_constructible<_Hash>::value &&
- __invokable_r<size_t, _Hash, _Key const&>::value >;
+ __is_invocable_r_v<size_t, _Hash, _Key const&> >;
template <class _Key, class _Hash = hash<_Key> >
using __has_enabled_hash _LIBCPP_NODEBUG =
diff --git a/libcxx/include/__functional/mem_fn.h b/libcxx/include/__functional/mem_fn.h
index f246edb334bb14..83390352eda237 100644
--- a/libcxx/include/__functional/mem_fn.h
+++ b/libcxx/include/__functional/mem_fn.h
@@ -36,14 +36,14 @@ class __mem_fn : public __weak_result_type<_Tp> {
// invoke
template <class... _ArgTypes>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<const _Tp&, _ArgTypes...>::type
- operator()(_ArgTypes&&... __args) const _NOEXCEPT_(__nothrow_invokable<const _Tp&, _ArgTypes...>::value) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<const _Tp&, _ArgTypes...>
+ operator()(_ArgTypes&&... __args) const _NOEXCEPT_(__is_nothrow_invocable_v<const _Tp&, _ArgTypes...>) {
return std::__invoke(__f_, std::forward<_ArgTypes>(__args)...);
}
};
template <class _Rp, class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __mem_fn<_Rp _Tp::*> mem_fn(_Rp _Tp::*__pm) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __mem_fn<_Rp _Tp::*> mem_fn(_Rp _Tp::* __pm) _NOEXCEPT {
return __mem_fn<_Rp _Tp::*>(__pm);
}
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index a4a66a50cf84ca..d6cd6428f22db2 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -57,7 +57,7 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
// invoke
template <class... _ArgTypes>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __invoke_of<type&, _ArgTypes...>::type
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __invoke_result_t<type&, _ArgTypes...>
operator()(_ArgTypes&&... __args) const
#if _LIBCPP_STD_VER >= 17
// Since is_nothrow_invocable requires C++17 LWG3764 is not backported
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index a0c72f4c205413..e7f7bfa957ec76 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -652,9 +652,9 @@ struct __enforce_unordered_container_requirements {
template <class _Key, class _Hash, class _Equal>
#ifndef _LIBCPP_CXX03_LANG
-_LIBCPP_DIAGNOSE_WARNING(!__invokable<_Equal const&, _Key const&, _Key const&>::value,
+_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Equal const&, _Key const&, _Key const&>,
"the specified comparator type does not provide a viable const call operator")
-_LIBCPP_DIAGNOSE_WARNING(!__invokable<_Hash const&, _Key const&>::value,
+_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Hash const&, _Key const&>,
"the specified hash functor does not provide a viable const call operator")
#endif
typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index f6ef21cdaa5b98..11ac55cd97fdbd 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -878,7 +878,7 @@ private:
template <class _Tp, class _Compare>
#ifndef _LIBCPP_CXX03_LANG
-_LIBCPP_DIAGNOSE_WARNING(!__invokable<_Compare const&, _Tp const&, _Tp const&>::value,
+_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Compare const&, _Tp const&, _Tp const&>,
"the specified comparator type does not provide a viable const call operator")
#endif
int __diagnose_non_const_comparator();
diff --git a/libcxx/include/__type_traits/invoke.h b/libcxx/include/__type_traits/invoke.h
index 71db32ae6a3cef..5d64d7cac53080 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -29,6 +29,27 @@
# pragma GCC system_header
#endif
+// This file defines the following libc++-internal API (back-ported to C++03):
+//
+// template <class... Args>
+// decltype(auto) __invoke(Args&&... args) noexcept(noexcept(std::invoke(std::forward<Args>(args...)))) {
+// return std::invoke(std::forward<Args>(args)...);
+// }
+//
+// template <class Ret, class... Args>
+// Ret __invoke_r(Args&&... args) {
+// return std::invoke_r(std::forward<Args>(args)...);
+// }
+//
+// template <class Ret, class Func, class... Args>
+// inline const bool __is_invocable_r_v = is_invocable_r_v<Ret, Func, Args...>;
+//
+// template <class Func, class... Args>
+// inline const bool __is_nothrow_invocable_v = is_nothrow_invocable_v<Func, Args...>;
+//
+// template <class Func, class... Args>
+// using __invoke_result_t = invoke_result_t<Func, Args...>;
+
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _DecayedFp>
@@ -225,6 +246,23 @@ struct __invoke_void_return_wrapper<_Ret, true> {
}
};
+template <class _Func, class... _Args>
+inline const bool __is_invocable_v = __invokable<_Func, _Args...>::value;
+
+template <class _Ret, class _Func, class... _Args>
+inline const bool __is_invocable_r_v = __invokable_r<_Ret, _Func, _Args...>::value;
+
+template <class _Func, class... _Args>
+inline const bool __is_nothrow_invocable_v = __nothrow_invokable<_Func, _Args...>::value;
+
+template <class _Func, class... _Args>
+using __invoke_result_t = typename __invoke_of<_Func, _Args...>::type;
+
+template <class _Ret, class... _Args>
+_Ret __invoke_r(_Args&&... __args) {
+ return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
+}
+
#if _LIBCPP_STD_VER >= 17
// is_invocable
diff --git a/libcxx/include/__type_traits/result_of.h b/libcxx/include/__type_traits/result_of.h
index 73a19447520663..92b845d8efad50 100644
--- a/libcxx/include/__type_traits/result_of.h
+++ b/libcxx/include/__type_traits/result_of.h
@@ -25,7 +25,9 @@ template <class _Callable>
class _LIBCPP_DEPRECATED_IN_CXX17 result_of;
template <class _Fp, class... _Args>
-class _LIBCPP_TEMPLATE_VIS result_of<_Fp(_Args...)> : public __invoke_of<_Fp, _Args...> {};
+class _LIBCPP_TEMPLATE_VIS result_of<_Fp(_Args...)> {
+ using type = __invoke_result_t<_Fp, _Args...>;
+};
# if _LIBCPP_STD_VER >= 14
template <class _Tp>
diff --git a/libcxx/include/future b/libcxx/include/future
index 9f7c95e542fd60..9ebbd012ff03a2 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -1828,7 +1828,7 @@ class _LIBCPP_HIDDEN __async_func {
tuple<_Fp, _Args...> __f_;
public:
- typedef typename __invoke_of<_Fp, _Args...>::type _Rp;
+ using _Rp = __invoke_result_t<_Fp, _Args...>;
_LIBCPP_HIDE_FROM_ABI explicit __async_func(_Fp&& __f, _Args&&... __args)
: __f_(std::move(__f), std::move(__args)...) {}
@@ -1852,7 +1852,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool __does_policy_contain(launch __policy, launch
}
template <class _Fp, class... _Args>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI future<typename __invoke_of<__decay_t<_Fp>, __decay_t<_Args>...>::type>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI future<__invoke_result_t<__decay_t<_Fp>, __decay_t<_Args>...> >
async(launch __policy, _Fp&& __f, _Args&&... __args) {
typedef __async_func<__decay_t<_Fp>, __decay_t<_Args>...> _BF;
typedef typename _BF::_Rp _Rp;
@@ -1877,7 +1877,7 @@ async(launch __policy, _Fp&& __f, _Args&&... __args) {
}
template <class _Fp, class... _Args>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI future<typename __invoke_of<__decay_t<_Fp>, __decay_t<_Args>...>::type>
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI future<__invoke_result_t<__decay_t<_Fp>, __decay_t<_Args>...> >
async(_Fp&& __f, _Args&&... __args) {
return std::async(launch::any, std::forward<_Fp>(__f), std::forward<_Args>(__args)...);
}
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 05aa01a3b7c308..4d2115d4b82f3c 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1839,7 +1839,7 @@ struct __container_traits<unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc> > {
// other than the container's hash function from within an insert or emplace function
// inserting a single element, the insertion has no effect.
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
- __nothrow_invokable<_Hash, const _Key&>::value;
+ __is_nothrow_invocable_v<_Hash, const _Key&>;
};
template <class _Key,
@@ -2539,7 +2539,7 @@ struct __container_traits<unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc> >
// other than the container's hash function from within an insert or emplace function
// inserting a single element, the insertion has no effect.
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
- __nothrow_invokable<_Hash, const _Key&>::value;
+ __is_nothrow_invocable_v<_Hash, const _Key&>;
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 7ab1c651b8c956..dfde1d06ebf948 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -1192,7 +1192,7 @@ struct __container_traits<unordered_set<_Value, _Hash, _Pred, _Alloc> > {
// other than the container's hash function from within an insert or emplace function
// inserting a single element, the insertion has no effect.
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
- __nothrow_invokable<_Hash, const _Value&>::value;
+ __is_nothrow_invocable_v<_Hash, const _Value&>;
};
template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
@@ -1812,7 +1812,7 @@ struct __container_traits<unordered_multiset<_Value, _Hash, _Pred, _Alloc> > {
// other than the container's hash function from within an insert or emplace function
// inserting a single element, the insertion has no effect.
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
- __nothrow_invokable<_Hash, const _Value&>::value;
+ __is_nothrow_invocable_v<_Hash, const _Value&>;
};
_LIBCPP_END_NAMESPACE_STD
>From b72d52b8cbadc460feb813d40433146c2dc9f780 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 1 Oct 2024 11:08:02 +0200
Subject: [PATCH 2/2] [Clang] Add __builtin_invoke and recognize std::invoke as
a builtin
---
clang/include/clang/Basic/BuiltinHeaders.def | 1 +
clang/include/clang/Basic/Builtins.td | 9 +
clang/include/clang/Sema/Sema.h | 9 +
clang/lib/Sema/SemaChecking.cpp | 99 +++++++++++
clang/lib/Sema/SemaDecl.cpp | 4 +
clang/lib/Sema/SemaExprCXX.cpp | 105 ++++++------
clang/test/CodeGenCXX/builtin-invoke.cpp | 80 +++++++++
clang/test/SemaCXX/builtin-invoke.cpp | 136 ++++++++++++++++
libcxx/include/__type_traits/invoke.h | 154 ++++++++++++++----
.../__type_traits/is_core_convertible.h | 9 +
10 files changed, 526 insertions(+), 80 deletions(-)
create mode 100644 clang/test/CodeGenCXX/builtin-invoke.cpp
create mode 100644 clang/test/SemaCXX/builtin-invoke.cpp
diff --git a/clang/include/clang/Basic/BuiltinHeaders.def b/clang/include/clang/Basic/BuiltinHeaders.def
index 8e4a2f9bee9aa0..4b9adceb03d2c2 100644
--- a/clang/include/clang/Basic/BuiltinHeaders.def
+++ b/clang/include/clang/Basic/BuiltinHeaders.def
@@ -17,6 +17,7 @@ HEADER(COMPLEX_H, "complex.h")
HEADER(CTYPE_H, "ctype.h")
HEADER(EMMINTRIN_H, "emmintrin.h")
HEADER(FOUNDATION_NSOBJCRUNTIME_H, "Foundation/NSObjCRuntime.h")
+HEADER(FUNCTIONAL, "functional")
HEADER(IMMINTRIN_H, "immintrin.h")
HEADER(INTRIN_H, "intrin.h")
HEADER(MALLOC_H, "malloc.h")
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 8653474744c58f..ff34c0f27c113d 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4209,6 +4209,15 @@ def MoveIfNsoexcept : CxxLibBuiltin<"utility"> {
let Namespace = "std";
}
+def Invoke : CxxLibBuiltin<"functional"> {
+ // __invoke is used internally by libc++ and libstdc++
+ let Spellings = ["invoke", "__invoke"];
+ let Attributes = [CustomTypeChecking, Constexpr];
+ let Prototype = "void(...)";
+ let Namespace = "std";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
def Annotation : Builtin {
let Spellings = ["__builtin_annotation"];
let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d6f3508a5243f3..50d163e6975613 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2175,6 +2175,8 @@ class Sema final : public SemaBase {
SourceLocation BuiltinLoc,
SourceLocation RParenLoc);
+ ExprResult BuiltinInvoke(CallExpr *TheCall);
+
enum FormatStringType {
FST_Scanf,
FST_Printf,
@@ -14879,11 +14881,18 @@ class Sema final : public SemaBase {
SourceLocation Loc);
QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
SourceLocation Loc);
+
+ QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
+ return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
+ }
+
QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
SourceLocation Loc);
QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
SourceLocation Loc);
+ bool BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT);
+
/// Ensure that the type T is a literal type.
///
/// This routine checks whether the type @p T is a literal type. If @p T is an
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2d4a7cd287b70d..4cf71004aaa1ba 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2246,6 +2246,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return BuiltinShuffleVector(TheCall);
// TheCall will be freed by the smart pointer here, but that's fine, since
// BuiltinShuffleVector guts it, but then doesn't release it.
+ case Builtin::BIinvoke:
+ case Builtin::BI__invoke:
+ case Builtin::BI__builtin_invoke:
+ return BuiltinInvoke(TheCall);
case Builtin::BI__builtin_prefetch:
if (BuiltinPrefetch(TheCall))
return ExprError();
@@ -5178,6 +5182,101 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
BuiltinLoc, RParenLoc);
}
+ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
+ auto Loc = TheCall->getBeginLoc();
+ auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
+ assert(llvm::none_of(Args,
+ [](Expr *Arg) { return Arg->isTypeDependent(); }));
+
+ if (Args.size() == 0) {
+ Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 1 << 0 << 0 << TheCall->getSourceRange();
+ return ExprError();
+ }
+
+ auto FuncT = Args[0]->getType();
+
+ if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
+ if (Args.size() < 2) {
+ Diag(TheCall->getBeginLoc(),
+ diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 2 << 1 << 0 << TheCall->getSourceRange();
+ return ExprError();
+ }
+
+ auto *MemPtrClass = MPT->getClass();
+ auto ObjectT = Args[1]->getType();
+
+
+ if (MPT->isMemberDataPointer() && Args.size() != 2) {
+ Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
+ << 0 << 2 << Args.size() << 0 << TheCall->getSourceRange();
+ return ExprError();
+ }
+
+ ExprResult ObjectArg = [&]() -> ExprResult {
+ // (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of a
+ // class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+ // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+ // (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
+ // and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+ // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+ if (Context.hasSameType(QualType(MemPtrClass, 0),
+ BuiltinRemoveCVRef(ObjectT, Loc)) ||
+ BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
+ BuiltinRemoveCVRef(ObjectT, Loc))) {
+ return Args[1];
+ }
+
+ // (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
+ // a class T and remove_cvref_t<decltype(t1)> is a specialization of
+ // reference_wrapper;
+ if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
+ if (RD->isInStdNamespace() &&
+ RD->getDeclName().getAsString() == "reference_wrapper") {
+ CXXScopeSpec SS;
+ IdentifierInfo *GetName = &Context.Idents.get("get");
+ UnqualifiedId GetID;
+ GetID.setIdentifier(GetName, Loc);
+
+ auto MemExpr = ActOnMemberAccessExpr(
+ getCurScope(), Args[1], Loc, tok::period, SS,
+ /*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
+
+ if (MemExpr.isInvalid())
+ return ExprError();
+
+ return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
+ }
+ }
+
+ // ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
+ // class T and t1 does not satisfy the previous two items;
+
+ return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
+ }();
+
+ if (ObjectArg.isInvalid())
+ return ExprError();
+
+ auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
+ tok::periodstar, ObjectArg.get(), Args[0]);
+ if (BinOp.isInvalid())
+ return ExprError();
+
+ if (MPT->isMemberDataPointer())
+ return BinOp;
+
+ auto *MemCall = new (Context)
+ ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
+
+ return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
+ Args.drop_front(2), TheCall->getRParenLoc());
+ }
+ return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
+ Args.drop_front(), TheCall->getRParenLoc());
+}
+
bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
unsigned NumArgs = TheCall->getNumArgs();
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a36ca61a1bef30..854ee7c42cf2c9 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9746,6 +9746,10 @@ static bool isStdBuiltin(ASTContext &Ctx, FunctionDecl *FD,
return FPT->getNumParams() == 1 && !FPT->isVariadic();
}
+ case Builtin::BIinvoke:
+ case Builtin::BI__invoke:
+ return true;
+
default:
return false;
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 616481d62de887..fe27073bdb9f8e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6057,67 +6057,70 @@ ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc);
}
-static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
- const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
- QualType LhsT = Lhs->getType();
- QualType RhsT = Rhs->getType();
+bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT,
+ QualType RhsT) {
+ // C++0x [meta.rel]p2
+ // Base is a base class of Derived without regard to cv-qualifiers or
+ // Base and Derived are not unions and name the same class type without
+ // regard to cv-qualifiers.
+
+ const RecordType *lhsRecord = LhsT->getAs<RecordType>();
+ const RecordType *rhsRecord = RhsT->getAs<RecordType>();
+ if (!rhsRecord || !lhsRecord) {
+ const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
+ const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
+ if (!LHSObjTy || !RHSObjTy)
+ return false;
- assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
- "Cannot evaluate traits of dependent types");
+ ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
+ ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
+ if (!BaseInterface || !DerivedInterface)
+ return false;
- switch(BTT) {
- case BTT_IsBaseOf: {
- // C++0x [meta.rel]p2
- // Base is a base class of Derived without regard to cv-qualifiers or
- // Base and Derived are not unions and name the same class type without
- // regard to cv-qualifiers.
-
- const RecordType *lhsRecord = LhsT->getAs<RecordType>();
- const RecordType *rhsRecord = RhsT->getAs<RecordType>();
- if (!rhsRecord || !lhsRecord) {
- const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
- const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
- if (!LHSObjTy || !RHSObjTy)
- return false;
+ if (RequireCompleteType(RhsTLoc, RhsT,
+ diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
- ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
- ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
- if (!BaseInterface || !DerivedInterface)
- return false;
+ return BaseInterface->isSuperClassOf(DerivedInterface);
+ }
- if (Self.RequireCompleteType(
- Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type_used_in_type_trait_expr))
- return false;
+ assert(Context.hasSameUnqualifiedType(LhsT, RhsT) ==
+ (lhsRecord == rhsRecord));
- return BaseInterface->isSuperClassOf(DerivedInterface);
- }
+ // Unions are never base classes, and never have base classes.
+ // It doesn't matter if they are complete or not. See PR#41843
+ if (lhsRecord && lhsRecord->getDecl()->isUnion())
+ return false;
+ if (rhsRecord && rhsRecord->getDecl()->isUnion())
+ return false;
+
+ if (lhsRecord == rhsRecord)
+ return true;
- assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
- == (lhsRecord == rhsRecord));
+ // C++0x [meta.rel]p2:
+ // If Base and Derived are class types and are different types
+ // (ignoring possible cv-qualifiers) then Derived shall be a
+ // complete type.
+ if (RequireCompleteType(RhsTLoc, RhsT,
+ diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
- // Unions are never base classes, and never have base classes.
- // It doesn't matter if they are complete or not. See PR#41843
- if (lhsRecord && lhsRecord->getDecl()->isUnion())
- return false;
- if (rhsRecord && rhsRecord->getDecl()->isUnion())
- return false;
+ return cast<CXXRecordDecl>(rhsRecord->getDecl())
+ ->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
+}
- if (lhsRecord == rhsRecord)
- return true;
+static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
+ const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
+ QualType LhsT = Lhs->getType();
+ QualType RhsT = Rhs->getType();
- // C++0x [meta.rel]p2:
- // If Base and Derived are class types and are different types
- // (ignoring possible cv-qualifiers) then Derived shall be a
- // complete type.
- if (Self.RequireCompleteType(
- Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type_used_in_type_trait_expr))
- return false;
+ assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
+ "Cannot evaluate traits of dependent types");
+
+ switch(BTT) {
+ case BTT_IsBaseOf:
+ return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT);
- return cast<CXXRecordDecl>(rhsRecord->getDecl())
- ->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
- }
case BTT_IsVirtualBaseOf: {
const RecordType *BaseRecord = LhsT->getAs<RecordType>();
const RecordType *DerivedRecord = RhsT->getAs<RecordType>();
diff --git a/clang/test/CodeGenCXX/builtin-invoke.cpp b/clang/test/CodeGenCXX/builtin-invoke.cpp
new file mode 100644
index 00000000000000..f5be0166764beb
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-invoke.cpp
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+extern "C" void* memcpy(void*, const void*, decltype(sizeof(int)));
+void func();
+
+namespace std {
+ template <class T>
+ class reference_wrapper {
+ T* ptr;
+
+ public:
+ T& get() { return *ptr; }
+ };
+} // namespace std
+
+struct Callable {
+ void operator()() {}
+
+ void func();
+};
+
+extern "C" void call1() {
+ __builtin_invoke(func);
+ __builtin_invoke(Callable{});
+ __builtin_invoke(memcpy, nullptr, nullptr, 0);
+
+ // CHECK: define dso_local void @call1
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %ref.tmp = alloca %struct.Callable, align 1
+ // CHECK-NEXT: call void @_Z4funcv()
+ // CHECK-NEXT: call void @_ZN8CallableclEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp)
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 null, ptr align 1 null, i64 0, i1 false)
+ // CHECK-NEXT: ret void
+}
+
+extern "C" void call_memptr(std::reference_wrapper<Callable> wrapper) {
+ __builtin_invoke(&Callable::func, wrapper);
+
+ // CHECK: define dso_local void @call_memptr
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %wrapper = alloca %"class.std::reference_wrapper", align 8
+ // CHECK-NEXT: %coerce.dive = getelementptr inbounds nuw %"class.std::reference_wrapper", ptr %wrapper, i32 0, i32 0
+ // CHECK-NEXT: store ptr %wrapper.coerce, ptr %coerce.dive, align 8
+ // CHECK-NEXT: %call = call noundef nonnull align 1 dereferenceable(1) ptr @_ZNSt17reference_wrapperI8CallableE3getEv(ptr noundef nonnull align 8 dereferenceable(8) %wrapper)
+ // CHECK-NEXT: %0 = getelementptr inbounds i8, ptr %call, i64 0
+ // CHECK-NEXT: br i1 false, label %memptr.virtual, label %memptr.nonvirtual
+ // CHECK-EMPTY:
+ // CHECK-NEXT: memptr.virtual:
+ // CHECK-NEXT: %vtable = load ptr, ptr %0, align 8
+ // CHECK-NEXT: %1 = getelementptr i8, ptr %vtable, i64 sub (i64 ptrtoint (ptr @_ZN8Callable4funcEv to i64), i64 1), !nosanitize !2
+ // CHECK-NEXT: %memptr.virtualfn = load ptr, ptr %1, align 8, !nosanitize !2
+ // CHECK-NEXT: br label %memptr.end
+ // CHECK-EMPTY:
+ // CHECK-NEXT: memptr.nonvirtual:
+ // CHECK-NEXT: br label %memptr.end
+ // CHECK-EMPTY:
+ // CHECK-NEXT: memptr.end:
+ // CHECK-NEXT: %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ @_ZN8Callable4funcEv, %memptr.nonvirtual ]
+ // CHECK-NEXT: call void %2(ptr noundef nonnull align 1 dereferenceable(1) %0)
+ // CHECK-NEXT: ret void
+}
+
+namespace std {
+ template <class... Args>
+ auto invoke(Args&&... args) -> decltype(__builtin_invoke(args...));
+} // namespace std
+
+extern "C" void call2() {
+ std::invoke(func);
+ std::invoke(Callable{});
+ std::invoke(memcpy, nullptr, nullptr, 0);
+
+ // CHECK: define dso_local void @call2
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %ref.tmp = alloca %struct.Callable, align 1
+ // CHECK-NEXT: call void @_Z4funcv()
+ // CHECK-NEXT: call void @_ZN8CallableclEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp)
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 null, ptr align 1 null, i64 0, i1 false)
+ // CHECK-NEXT: ret void
+}
diff --git a/clang/test/SemaCXX/builtin-invoke.cpp b/clang/test/SemaCXX/builtin-invoke.cpp
new file mode 100644
index 00000000000000..a022c9886bc742
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-invoke.cpp
@@ -0,0 +1,136 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+
+void func() { // expected-note {{'func' declared here}}
+ __builtin_invoke(); // expected-error {{too few arguments to function call, expected at least 1, have 0}}
+}
+
+void nfunc() noexcept {}
+
+struct S {};
+void argfunc(int, S) {} // expected-note {{'argfunc' declared here}}
+
+struct Callable {
+ void operator()() {}
+
+ void func() {}
+
+ int var;
+};
+
+void* malloc(decltype(sizeof(int)));
+
+template <class T>
+struct pointer_wrapper {
+ T* v;
+
+ T& operator*() {
+ return *v;
+ }
+};
+
+namespace std {
+ template <class T>
+ class reference_wrapper {
+ T* ptr;
+
+ public:
+ reference_wrapper(T& ref) : ptr(&ref) {}
+
+ T& get() { return *ptr; }
+ };
+
+ template <class T>
+ reference_wrapper<T> ref(T& v) {
+ return reference_wrapper<T>(v);
+ }
+} // namespace std
+
+struct InvalidSpecialization1 {
+ void func() {}
+
+ int var;
+};
+
+template <>
+class std::reference_wrapper<InvalidSpecialization1> {
+public:
+ reference_wrapper(InvalidSpecialization1&) {}
+};
+
+struct InvalidSpecialization2 {
+ void func() {}
+
+ int var;
+};
+
+template <>
+class std::reference_wrapper<InvalidSpecialization2> {
+public:
+ reference_wrapper(InvalidSpecialization2&) {}
+
+private:
+ InvalidSpecialization2& get(); // expected-note 2 {{declared private here}}
+};
+
+void call() {
+ __builtin_invoke(func);
+ __builtin_invoke(nfunc);
+ static_assert(!noexcept(__builtin_invoke(func)));
+ static_assert(noexcept(__builtin_invoke(nfunc)));
+ __builtin_invoke(func, 1); // expected-error {{too many arguments to function call, expected 0, have 1}}
+ __builtin_invoke(argfunc, 1); // expected-error {{too few arguments to function call, expected 2, have 1}}
+ __builtin_invoke(Callable{});
+ __builtin_invoke(malloc, 0);
+ __builtin_invoke(__builtin_malloc, 0); // expected-error {{builtin functions must be directly called}}
+
+ // Member functiom pointer
+ __builtin_invoke(&Callable::func); // expected-error {{too few arguments to function call, expected at least 2, have 1}}
+ __builtin_invoke(&Callable::func, 1); // expected-error {{indirection requires pointer operand ('int' invalid)}}
+ __builtin_invoke(&Callable::func, Callable{});
+ __builtin_invoke(&Callable::func, Callable{}, 1); // expected-error {{too many arguments to function call, expected 0, have 1}}
+
+ Callable c;
+ __builtin_invoke(&Callable::func, &c);
+ __builtin_invoke(&Callable::func, std::ref(c));
+ __builtin_invoke(&Callable::func, &c);
+ __builtin_invoke(&Callable::func, &c, 2); // expected-error {{too many arguments to function call, expected 0, have 1}}
+ __builtin_invoke(&Callable::func, pointer_wrapper<Callable>{&c});
+ __builtin_invoke(&Callable::func, pointer_wrapper<Callable>{&c}, 2); // expected-error {{too many arguments to function call, expected 0, have 1}}
+
+ InvalidSpecialization1 is1;
+ InvalidSpecialization2 is2;
+ __builtin_invoke(&InvalidSpecialization1::func, std::ref(is1)); // expected-error {{no member named 'get' in 'std::reference_wrapper<InvalidSpecialization1>'}}
+ __builtin_invoke(&InvalidSpecialization2::func, std::ref(is2)); // expected-error {{'get' is a private member of 'std::reference_wrapper<InvalidSpecialization2>'}}
+
+ // Member data pointer
+ __builtin_invoke(&Callable::var); // expected-error {{too few arguments to function call, expected at least 2, have 1}}
+ __builtin_invoke(&Callable::var, 1); // expected-error {{indirection requires pointer operand ('int' invalid)}}
+ (void)__builtin_invoke(&Callable::var, Callable{});
+ __builtin_invoke(&Callable::var, Callable{}, 1); // expected-error {{too many arguments to function call, expected 2, have 3}}
+
+ (void)__builtin_invoke(&Callable::var, &c);
+ (void)__builtin_invoke(&Callable::var, std::ref(c));
+ (void)__builtin_invoke(&Callable::var, &c);
+ __builtin_invoke(&Callable::var, &c, 2); // expected-error {{too many arguments to function call, expected 2, have 3}}
+ (void)__builtin_invoke(&Callable::var, pointer_wrapper<Callable>{&c});
+ __builtin_invoke(&Callable::var, pointer_wrapper<Callable>{&c}, 2); // expected-error {{too many arguments to function call, expected 2, have 3}}
+
+ __builtin_invoke(&InvalidSpecialization1::var, std::ref(is1)); // expected-error {{no member named 'get' in 'std::reference_wrapper<InvalidSpecialization1>'}}
+ (void)__builtin_invoke(&InvalidSpecialization2::var, std::ref(is2)); // expected-error {{'get' is a private member of 'std::reference_wrapper<InvalidSpecialization2>'}}
+}
+
+[[nodiscard]] int diagnose_discard();
+int no_diagnose_discard();
+
+namespace std {
+ template <class... Args>
+ auto invoke(Args&&... args) -> decltype(__builtin_invoke(args...));
+} // namespace std
+
+void test3() {
+ __builtin_invoke(diagnose_discard); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ __builtin_invoke(no_diagnose_discard);
+
+ std::invoke(diagnose_discard); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::invoke(no_diagnose_discard);
+}
diff --git a/libcxx/include/__type_traits/invoke.h b/libcxx/include/__type_traits/invoke.h
index 5d64d7cac53080..82a070f7c82c55 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -22,6 +22,7 @@
#include <__type_traits/is_same.h>
#include <__type_traits/is_void.h>
#include <__type_traits/nat.h>
+#include <__type_traits/void_t.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
@@ -52,6 +53,99 @@
_LIBCPP_BEGIN_NAMESPACE_STD
+#if __has_builtin(__builtin_invoke)
+
+template <class... _Args>
+using __invoke_result_t = decltype(__builtin_invoke(std::declval<_Args>()...));
+
+template <class... _Args>
+__invoke_result_t<_Args...> __invoke(_Args&&... __args)
+ _NOEXCEPT_(noexcept(__builtin_invoke(std::forward<_Args>(__args)...))) {
+ return __builtin_invoke(std::forward<_Args>(__args)...);
+}
+
+template <class _Void, class... _Args>
+inline const bool __is_invocable_impl = false;
+
+template <class... _Args>
+inline const bool __is_invocable_impl<__void_t<__invoke_result_t<_Args...> >, _Args...> = true;
+
+template <class... _Args>
+inline const bool __is_invocable_v = __is_invocable_impl<void, _Args...>;
+
+template <class _Ret, bool, class... _Args>
+inline const bool __is_invocable_r_impl = false;
+
+template <class _Ret, class... _Args>
+inline const bool __is_invocable_r_impl<_Ret, true, _Args...> =
+ __is_core_convertible<__invoke_result_t<_Args...>, _Ret>::value || is_void<_Ret>::value;
+
+template <class _Ret, class... _Args>
+inline const bool __is_invocable_r_v = __is_invocable_r_impl<_Ret, __is_invocable_v<_Args...>, _Args...>;
+
+template <bool __is_invocable, class... _Args>
+inline const bool __is_nothrow_invocable_impl = false;
+
+template <class... _Args>
+inline const bool __is_nothrow_invocable_impl<true, _Args...> = noexcept(__builtin_invoke(std::declval<_Args>()...));
+
+template <class... _Args>
+inline const bool __is_nothrow_invocable_v = __is_nothrow_invocable_impl<__is_invocable_v<_Args...>, _Args...>;
+
+template <bool __is_invocable, class _Ret, class... _Args>
+inline const bool __is_nothrow_invocable_r_impl = false;
+
+template <class _Ret, class... _Args>
+inline const bool __is_nothrow_invocable_r_impl<true, _Ret, _Args...> =
+ __is_nothrow_core_convertible_v<__invoke_result_t<_Args...>, _Ret> || is_void<_Ret>::value;
+
+template <class _Ret, class... _Args>
+inline const bool __is_nothrow_invocable_r_v =
+ __is_nothrow_invocable_r_impl<__is_nothrow_invocable_v<_Args...>, _Ret, _Args...>;
+
+# if _LIBCPP_STD_VER >= 17
+
+// is_invocable
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_invocable : bool_constant<__is_invocable_v<_Fn, _Args...> > {};
+
+template <class _Ret, class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_invocable_r : bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {};
+
+template <class _Fn, class... _Args>
+inline constexpr bool is_invocable_v = __is_invocable_v<_Fn, _Args...>;
+
+template <class _Ret, class _Fn, class... _Args>
+inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value;
+
+// is_nothrow_invocable
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : bool_constant<__is_nothrow_invocable_v<_Fn, _Args...> > {};
+
+template <class _Ret, class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r
+ : integral_constant<bool, __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>> {};
+
+template <class _Fn, class... _Args>
+inline constexpr bool is_nothrow_invocable_v = __is_nothrow_invocable_v<_Fn, _Args...>;
+
+template <class _Ret, class _Fn, class... _Args>
+inline constexpr bool is_nothrow_invocable_r_v = __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>;
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS invoke_result {
+ using type = __invoke_result_t<_Fn, _Args...>;
+};
+
+template <class _Fn, class... _Args>
+using invoke_result_t = __invoke_result_t<_Fn, _Args...>;
+
+# endif // _LIBCPP_STD_VER >= 17
+
+#else // __has_builtin(__builtin_invoke)
+
template <class _DecayedFp>
struct __member_pointer_class_type {};
@@ -202,21 +296,21 @@ struct __nothrow_invokable_r_imp<true, false, _Ret, _Fp, _Args...> {
template <class _Tp>
static void __test_noexcept(_Tp) _NOEXCEPT;
-#ifdef _LIBCPP_CXX03_LANG
+# ifdef _LIBCPP_CXX03_LANG
static const bool value = false;
-#else
+# else
static const bool value =
noexcept(_ThisT::__test_noexcept<_Ret>(std::__invoke(std::declval<_Fp>(), std::declval<_Args>()...)));
-#endif
+# endif
};
template <class _Ret, class _Fp, class... _Args>
struct __nothrow_invokable_r_imp<true, true, _Ret, _Fp, _Args...> {
-#ifdef _LIBCPP_CXX03_LANG
+# ifdef _LIBCPP_CXX03_LANG
static const bool value = false;
-#else
+# else
static const bool value = noexcept(std::__invoke(std::declval<_Fp>(), std::declval<_Args>()...));
-#endif
+# endif
};
template <class _Ret, class _Fp, class... _Args>
@@ -230,22 +324,6 @@ template <class _Fp, class... _Args>
struct __invoke_of
: public enable_if<__invokable<_Fp, _Args...>::value, typename __invokable_r<void, _Fp, _Args...>::_Result> {};
-template <class _Ret, bool = is_void<_Ret>::value>
-struct __invoke_void_return_wrapper {
- template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) {
- return std::__invoke(std::forward<_Args>(__args)...);
- }
-};
-
-template <class _Ret>
-struct __invoke_void_return_wrapper<_Ret, true> {
- template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) {
- std::__invoke(std::forward<_Args>(__args)...);
- }
-};
-
template <class _Func, class... _Args>
inline const bool __is_invocable_v = __invokable<_Func, _Args...>::value;
@@ -258,12 +336,7 @@ inline const bool __is_nothrow_invocable_v = __nothrow_invokable<_Func, _Args...
template <class _Func, class... _Args>
using __invoke_result_t = typename __invoke_of<_Func, _Args...>::type;
-template <class _Ret, class... _Args>
-_Ret __invoke_r(_Args&&... __args) {
- return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
-}
-
-#if _LIBCPP_STD_VER >= 17
+# if _LIBCPP_STD_VER >= 17
// is_invocable
@@ -301,7 +374,30 @@ struct _LIBCPP_TEMPLATE_VIS invoke_result : __invoke_of<_Fn, _Args...> {};
template <class _Fn, class... _Args>
using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
-#endif // _LIBCPP_STD_VER >= 17
+# endif // _LIBCPP_STD_VER >= 17
+
+#endif // __has_builtin(__builtin_invoke_r)
+
+template <class _Ret, bool = is_void<_Ret>::value>
+struct __invoke_void_return_wrapper {
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) {
+ return std::__invoke(std::forward<_Args>(__args)...);
+ }
+};
+
+template <class _Ret>
+struct __invoke_void_return_wrapper<_Ret, true> {
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) {
+ std::__invoke(std::forward<_Args>(__args)...);
+ }
+};
+
+template <class _Ret, class... _Args>
+_Ret __invoke_r(_Args&&... __args) {
+ return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
+}
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__type_traits/is_core_convertible.h b/libcxx/include/__type_traits/is_core_convertible.h
index 0de177c7771f4a..09cf411e7d7302 100644
--- a/libcxx/include/__type_traits/is_core_convertible.h
+++ b/libcxx/include/__type_traits/is_core_convertible.h
@@ -30,6 +30,15 @@ template <class _Tp, class _Up>
struct __is_core_convertible<_Tp, _Up, decltype(static_cast<void (*)(_Up)>(0)(static_cast<_Tp (*)()>(0)()))>
: public true_type {};
+template <class _Tp, class _Up, bool = true, bool = __is_core_convertible<_Tp, _Up>::value>
+inline const bool __is_nothrow_core_convertible_v = false;
+
+template <class _Tp, class _Up>
+inline const bool __is_nothrow_core_convertible_v<_Tp,
+ _Up,
+ noexcept(static_cast<void (*)(_Up)>(0)(static_cast<_Tp (*)()>(0)())),
+ true> = true;
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_IS_CORE_CONVERTIBLE_H
More information about the libcxx-commits
mailing list