[libcxx-commits] [libcxx] [libc++] Fix expression-equivalence for `mem_fn` (PR #111307)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Oct 6 11:16:28 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: A. Jiang (frederick-vs-ja)
<details>
<summary>Changes</summary>
Previously, SFINAE constraints and exception specification propagation were missing in the return type of libc++'s `std::mem_fn`. The requirements on expression-equivalence (or even plain "equivalent" in pre-C++20 specification) in [func.memfn] are actually requiring them.
This PR adds the missed stuffs. Fixes #<!-- -->86043.
Drive-by changes:
- removing no longer used `__invoke_return`,
- updating synopsis comments in several files, and
- merging several test files for `mem_fn` into one.
---
Patch is 33.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111307.diff
10 Files Affected:
- (modified) libcxx/include/__functional/mem_fn.h (+2-4)
- (modified) libcxx/include/__functional/weak_result_type.h (-5)
- (modified) libcxx/include/functional (+1-1)
- (added) libcxx/test/std/utilities/function.objects/func.memfn/mem_fn.pass.cpp (+741)
- (modified) libcxx/test/std/utilities/function.objects/func.memfn/member_data.compile.fail.cpp (+1-1)
- (removed) libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp (-51)
- (removed) libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp (-87)
- (removed) libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp (-90)
- (removed) libcxx/test/std/utilities/function.objects/func.memfn/member_function_const_volatile.pass.cpp (-81)
- (removed) libcxx/test/std/utilities/function.objects/func.memfn/member_function_volatile.pass.cpp (-81)
``````````diff
diff --git a/libcxx/include/__functional/mem_fn.h b/libcxx/include/__functional/mem_fn.h
index 58dbdf871d747b..f246edb334bb14 100644
--- a/libcxx/include/__functional/mem_fn.h
+++ b/libcxx/include/__functional/mem_fn.h
@@ -36,10 +36,8 @@ class __mem_fn : public __weak_result_type<_Tp> {
// invoke
template <class... _ArgTypes>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-
- typename __invoke_return<type, _ArgTypes...>::type
- operator()(_ArgTypes&&... __args) const {
+ _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) {
return std::__invoke(__f_, std::forward<_ArgTypes>(__args)...);
}
};
diff --git a/libcxx/include/__functional/weak_result_type.h b/libcxx/include/__functional/weak_result_type.h
index 793775a2903e6f..233d86009a2017 100644
--- a/libcxx/include/__functional/weak_result_type.h
+++ b/libcxx/include/__functional/weak_result_type.h
@@ -221,11 +221,6 @@ struct __weak_result_type<_Rp (_Cp::*)(_A1, _A2, _A3...) const volatile> {
#endif
};
-template <class _Tp, class... _Args>
-struct __invoke_return {
- typedef decltype(std::__invoke(std::declval<_Tp>(), std::declval<_Args>()...)) type;
-};
-
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FUNCTIONAL_WEAK_RESULT_TYPE_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 3d39f654ddb08a..489d82b2d43ab9 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -395,7 +395,7 @@ const_mem_fun_ref_t<S,T> mem_fun_ref(S (T::*f)() const);
template <class S, class T, class A>
const_mem_fun1_ref_t<S,T,A> mem_fun_ref(S (T::*f)(A) const); // deprecated in C++11, removed in C++17
-template<class R, class T> constexpr unspecified mem_fn(R T::*); // constexpr in C++20
+template<class R, class T> constexpr unspecified mem_fn(R T::*) noexcept; // constexpr in C++20
class bad_function_call
: public exception
diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/mem_fn.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/mem_fn.pass.cpp
new file mode 100644
index 00000000000000..a5d2eae6ff11ca
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.memfn/mem_fn.pass.cpp
@@ -0,0 +1,741 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <functional>
+
+// template<class R, class T> constexpr unspecified mem_fn(R T::*) noexcept; // constexpr in C++20
+
+#include <functional>
+#include <cassert>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+struct A {
+ double data_;
+
+ TEST_CONSTEXPR_CXX14 char test0() { return 'a'; }
+ TEST_CONSTEXPR_CXX14 char test1(int) { return 'b'; }
+ TEST_CONSTEXPR_CXX14 char test2(int, double) { return 'c'; }
+
+ TEST_CONSTEXPR_CXX14 char test0_nothrow() TEST_NOEXCEPT { return 'd'; }
+ TEST_CONSTEXPR_CXX14 char test1_nothrow(int) TEST_NOEXCEPT { return 'e'; }
+ TEST_CONSTEXPR_CXX14 char test2_nothrow(int, double) TEST_NOEXCEPT { return 'f'; }
+
+ TEST_CONSTEXPR char test_c0() const { return 'a'; }
+ TEST_CONSTEXPR char test_c1(int) const { return 'b'; }
+ TEST_CONSTEXPR char test_c2(int, double) const { return 'c'; }
+
+ TEST_CONSTEXPR char test_c0_nothrow() const TEST_NOEXCEPT { return 'd'; }
+ TEST_CONSTEXPR char test_c1_nothrow(int) const TEST_NOEXCEPT { return 'e'; }
+ TEST_CONSTEXPR char test_c2_nothrow(int, double) const TEST_NOEXCEPT { return 'f'; }
+
+ char test_v0() volatile { return 'a'; }
+ char test_v1(int) volatile { return 'b'; }
+ char test_v2(int, double) volatile { return 'c'; }
+
+ char test_v0_nothrow() volatile TEST_NOEXCEPT { return 'd'; }
+ char test_v1_nothrow(int) volatile TEST_NOEXCEPT { return 'e'; }
+ char test_v2_nothrow(int, double) volatile TEST_NOEXCEPT { return 'f'; }
+
+ char test_cv0() const volatile { return 'a'; }
+ char test_cv1(int) const volatile { return 'b'; }
+ char test_cv2(int, double) const volatile { return 'c'; }
+
+ char test_cv0_nothrow() const volatile TEST_NOEXCEPT { return 'd'; }
+ char test_cv1_nothrow(int) const volatile TEST_NOEXCEPT { return 'e'; }
+ char test_cv2_nothrow(int, double) const volatile TEST_NOEXCEPT { return 'f'; }
+};
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_data(F f) {
+ A a = {0.0};
+ f(a) = 5;
+ assert(a.data_ == 5);
+ A* ap = &a;
+ f(ap) = 6;
+ assert(a.data_ == 6);
+ const A* cap = ap;
+ assert(f(cap) == f(ap));
+ const F& cf = f;
+ assert(cf(ap) == f(ap));
+
+#if TEST_STD_VER >= 11
+ static_assert(noexcept(f(a)), "");
+ static_assert(noexcept(f(ap)), "");
+ static_assert(noexcept(f(cap)), "");
+ static_assert(noexcept(cf(ap)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'a');
+ A* ap = &a;
+ assert(f(ap) == 'a');
+ const F& cf = f;
+ assert(cf(ap) == 'a');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a)), "");
+ static_assert(!noexcept(f(ap)), "");
+ static_assert(!noexcept(cf(ap)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'b');
+ A* ap = &a;
+ assert(f(ap, 2) == 'b');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'b');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0)), "");
+ static_assert(!noexcept(f(ap, 1)), "");
+ static_assert(!noexcept(cf(ap, 2)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'c');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'c');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'c');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0, 0.0)), "");
+ static_assert(!noexcept(f(ap, 1, 2)), "");
+ static_assert(!noexcept(cf(ap, 2, 3.5)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_noexcept_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'd');
+ A* ap = &a;
+ assert(f(ap) == 'd');
+ const F& cf = f;
+ assert(cf(ap) == 'd');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a)), "");
+ static_assert(noexcept(f(ap)), "");
+ static_assert(noexcept(cf(ap)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_noexcept_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'e');
+ A* ap = &a;
+ assert(f(ap, 2) == 'e');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'e');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0)), "");
+ static_assert(noexcept(f(ap, 1)), "");
+ static_assert(noexcept(cf(ap, 2)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_noexcept_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'f');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'f');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'f');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0, 0.0)), "");
+ static_assert(noexcept(f(ap, 1, 2)), "");
+ static_assert(noexcept(cf(ap, 2, 3.5)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_const_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'a');
+ A* ap = &a;
+ assert(f(ap) == 'a');
+ const A* cap = &a;
+ assert(f(cap) == 'a');
+ const F& cf = f;
+ assert(cf(ap) == 'a');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a)), "");
+ static_assert(!noexcept(f(ap)), "");
+ static_assert(!noexcept(f(cap)), "");
+ static_assert(!noexcept(cf(ap)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_const_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'b');
+ A* ap = &a;
+ assert(f(ap, 2) == 'b');
+ const A* cap = &a;
+ assert(f(cap, 2) == 'b');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'b');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0)), "");
+ static_assert(!noexcept(f(ap, 1)), "");
+ static_assert(!noexcept(f(cap, 2)), "");
+ static_assert(!noexcept(cf(ap, 3)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_const_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'c');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'c');
+ const A* cap = &a;
+ assert(f(cap, 2, 3.5) == 'c');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'c');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0, 0.0)), "");
+ static_assert(!noexcept(f(ap, 1, 2)), "");
+ static_assert(!noexcept(f(cap, 2, 3.5)), "");
+ static_assert(!noexcept(cf(ap, 3, 17.29)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_const_noexcept_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'd');
+ A* ap = &a;
+ assert(f(ap) == 'd');
+ const A* cap = &a;
+ assert(f(cap) == 'd');
+ const F& cf = f;
+ assert(cf(ap) == 'd');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a)), "");
+ static_assert(noexcept(f(ap)), "");
+ static_assert(noexcept(f(cap)), "");
+ static_assert(noexcept(cf(ap)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_const_noexcept_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'e');
+ A* ap = &a;
+ assert(f(ap, 2) == 'e');
+ const A* cap = &a;
+ assert(f(cap, 2) == 'e');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'e');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0)), "");
+ static_assert(noexcept(f(ap, 1)), "");
+ static_assert(noexcept(f(cap, 2)), "");
+ static_assert(noexcept(cf(ap, 3)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+TEST_CONSTEXPR_CXX20 bool test_const_noexcept_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'f');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'f');
+ const A* cap = &a;
+ assert(f(cap, 2, 3.5) == 'f');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'f');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0, 0.0)), "");
+ static_assert(noexcept(f(ap, 1, 2)), "");
+ static_assert(noexcept(f(cap, 2, 3.5)), "");
+ static_assert(noexcept(cf(ap, 3, 17.29)), "");
+#endif
+
+ return true;
+}
+
+template <class F>
+void test_volatile_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'a');
+ A* ap = &a;
+ assert(f(ap) == 'a');
+ volatile A* cap = &a;
+ assert(f(cap) == 'a');
+ const F& cf = f;
+ assert(cf(ap) == 'a');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a)), "");
+ static_assert(!noexcept(f(ap)), "");
+ static_assert(!noexcept(f(cap)), "");
+ static_assert(!noexcept(cf(ap)), "");
+#endif
+}
+
+template <class F>
+void test_volatile_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'b');
+ A* ap = &a;
+ assert(f(ap, 2) == 'b');
+ volatile A* cap = &a;
+ assert(f(cap, 2) == 'b');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'b');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0)), "");
+ static_assert(!noexcept(f(ap, 1)), "");
+ static_assert(!noexcept(f(cap, 2)), "");
+ static_assert(!noexcept(cf(ap, 3)), "");
+#endif
+}
+
+template <class F>
+void test_volatile_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'c');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'c');
+ volatile A* cap = &a;
+ assert(f(cap, 2, 3.5) == 'c');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'c');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0, 0.0)), "");
+ static_assert(!noexcept(f(ap, 1, 2)), "");
+ static_assert(!noexcept(f(cap, 2, 3.5)), "");
+ static_assert(!noexcept(cf(ap, 3, 17.29)), "");
+#endif
+}
+
+template <class F>
+void test_volatile_noexcept_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'd');
+ A* ap = &a;
+ assert(f(ap) == 'd');
+ volatile A* cap = &a;
+ assert(f(cap) == 'd');
+ const F& cf = f;
+ assert(cf(ap) == 'd');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a)), "");
+ static_assert(noexcept(f(ap)), "");
+ static_assert(noexcept(f(cap)), "");
+ static_assert(noexcept(cf(ap)), "");
+#endif
+}
+
+template <class F>
+void test_volatile_noexcept_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'e');
+ A* ap = &a;
+ assert(f(ap, 2) == 'e');
+ volatile A* cap = &a;
+ assert(f(cap, 2) == 'e');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'e');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0)), "");
+ static_assert(noexcept(f(ap, 1)), "");
+ static_assert(noexcept(f(cap, 2)), "");
+ static_assert(noexcept(cf(ap, 3)), "");
+#endif
+}
+
+template <class F>
+void test_volatile_noexcept_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'f');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'f');
+ volatile A* cap = &a;
+ assert(f(cap, 2, 3.5) == 'f');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'f');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0, 0.0)), "");
+ static_assert(noexcept(f(ap, 1, 2)), "");
+ static_assert(noexcept(f(cap, 2, 3.5)), "");
+ static_assert(noexcept(cf(ap, 3, 17.29)), "");
+#endif
+}
+
+template <class F>
+void test_const_volatile_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'a');
+ A* ap = &a;
+ assert(f(ap) == 'a');
+ const volatile A* cap = &a;
+ assert(f(cap) == 'a');
+ const F& cf = f;
+ assert(cf(ap) == 'a');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a)), "");
+ static_assert(!noexcept(f(ap)), "");
+ static_assert(!noexcept(f(cap)), "");
+ static_assert(!noexcept(cf(ap)), "");
+#endif
+}
+
+template <class F>
+void test_const_volatile_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'b');
+ A* ap = &a;
+ assert(f(ap, 2) == 'b');
+ const volatile A* cap = &a;
+ assert(f(cap, 2) == 'b');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'b');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0)), "");
+ static_assert(!noexcept(f(ap, 1)), "");
+ static_assert(!noexcept(f(cap, 2)), "");
+ static_assert(!noexcept(cf(ap, 3)), "");
+#endif
+}
+
+template <class F>
+void test_const_volatile_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'c');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'c');
+ const volatile A* cap = &a;
+ assert(f(cap, 2, 3.5) == 'c');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'c');
+
+#if TEST_STD_VER >= 17
+ static_assert(!noexcept(f(a, 0, 0.0)), "");
+ static_assert(!noexcept(f(ap, 1, 2)), "");
+ static_assert(!noexcept(f(cap, 2, 3.5)), "");
+ static_assert(!noexcept(cf(ap, 3, 17.29)), "");
+#endif
+}
+
+template <class F>
+void test_const_volatile_noexcept_fun0(F f) {
+ A a = {};
+ assert(f(a) == 'd');
+ A* ap = &a;
+ assert(f(ap) == 'd');
+ const volatile A* cap = &a;
+ assert(f(cap) == 'd');
+ const F& cf = f;
+ assert(cf(ap) == 'd');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a)), "");
+ static_assert(noexcept(f(ap)), "");
+ static_assert(noexcept(f(cap)), "");
+ static_assert(noexcept(cf(ap)), "");
+#endif
+}
+
+template <class F>
+void test_const_volatile_noexcept_fun1(F f) {
+ A a = {};
+ assert(f(a, 1) == 'e');
+ A* ap = &a;
+ assert(f(ap, 2) == 'e');
+ const volatile A* cap = &a;
+ assert(f(cap, 2) == 'e');
+ const F& cf = f;
+ assert(cf(ap, 2) == 'e');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0)), "");
+ static_assert(noexcept(f(ap, 1)), "");
+ static_assert(noexcept(f(cap, 2)), "");
+ static_assert(noexcept(cf(ap, 3)), "");
+#endif
+}
+
+template <class F>
+void test_const_volatile_noexcept_fun2(F f) {
+ A a = {};
+ assert(f(a, 1, 2) == 'f');
+ A* ap = &a;
+ assert(f(ap, 2, 3.5) == 'f');
+ const volatile A* cap = &a;
+ assert(f(cap, 2, 3.5) == 'f');
+ const F& cf = f;
+ assert(cf(ap, 2, 3.5) == 'f');
+
+#if TEST_STD_VER >= 17
+ static_assert(noexcept(f(a, 0, 0.0)), "");
+ static_assert(noexcept(f(ap, 1, 2)), "");
+ static_assert(noexcept(f(cap, 2, 3.5)), "");
+ static_assert(noexcept(cf(ap, 3, 17.29)), "");
+#endif
+}
+
+#if TEST_STD_VER >= 11
+template <class V, class Func, class... Args>
+struct is_callable_impl : std::false_type {};
+
+template <class Func, class... Args>
+struct is_callable_impl<decltype((void)std::declval<Func>()(std::declval<Args>()...)), Func, Args...> : std::true_type {
+};
+
+template <class Func, class... Args>
+struct is_callable : is_callable_impl<void, Func, Args...>::type {};
+
+template <class F>
+void test_sfinae_data(F f) {
+ static_assert(is_callable<F, A>::value, "");
+ static_assert(is_callable<F, const A>::value, "");
+ static_assert(is_callable<F, A&>::value, "");
+ static_assert(is_callable<F, const A&>::value, "");
+ static_assert(is_callable<F, A*>::value, "");
+ static_assert(is_callable<F, const A*>::value, "");
+
+ static_assert(!is_callable<F, A, char>::value, "");
+ static_assert(!is_callable<F, const A, char>::value, "");
+ static_assert(!is_callable<F, A&, char>::value, "");
+ static_assert(!is_callable<F, const A&, char>::value, "");
+ static_assert(!is_callable<F, A*, char>::value, "");
+ static_assert(!is_callable<F, const A*, char>::value, "");
+}
+
+template <class F>
+void test_sfinae_fun0(F f) {
+ static_assert(is_callable<F, A>::value, "");
+ static_assert(is_callable<F, A&>::value, "");
+ static_assert(is_callable<F, A*>::value, "");
+
+ static_assert(!is_callable<F, const A>::value, "");
+ static_assert(!is_callable<F, const A&>::value, "");
+ static_assert(!is_callable<F, const A*>::value, "");
+
+ static_assert(!is_callable<F, volatile A>::value, "");
+ static_assert(!is_callable<F, volatile A&>::value, "");
+ static_assert(!is_callable<F, volatile A*>::value, "");
+
+ static_assert(!is_callable<F, const volatile A>::value, "");
+ static_assert(!is_callable<F, const volatile A&>::value, "");
+ static_assert(!is_callable<F, const volatile A*>::value, "");
+
+ static_assert(!is_callable<F, A, int>::value, "");
+ static_assert(!is_callable<F, A&, int>::value, "");
+ static_assert(!is_callable<F, A*, int>::value, "");
+}
+
+template <class F>
+void test_sfinae_fun1(F f) {
+ static_assert(is_callable<F, A, int>::value, "");
+ static_assert(is_callable<F, A&, int>::value, "");
+ static_assert(is_callable<F, A*, int>::value, "");
+
+ static_assert(!is_callable<F, A>::value, "");
+ static_assert(!is_callable<F, A&>::value, "");
+ static_assert(!is_callable<F, A*>::value, "");
+}
+
+template <class F>
+void test_sfinae_const_fun0(F f) {
+ static_assert(is_callable<F, A>::value, "");
+ static_assert(is_callable<F, A&>::value, "");
+ static_assert(is_callable<F, A*>::value, "");
+
+ static_assert(is_callable<F, const A>::value, "");
+ static_assert(is_callable<F, const A&>::value, "");
+ static_assert(is_callable<F, const A*>::value, "");
+
+ static_assert(!is_callable<F, volatile A>::value, "");
+ static_assert(!is_callable<F, volatile A&>::value, "");
+ static_assert(!is_callable<F, volatile A*>::value, "");
+
+ static_assert(!is_callable<F, const volatile A>::value, "");
+ static_assert(!is_callable<F, const volatile A&>::value, "");
+ static_assert(!is_callable<F, const volatile A*>::value, "");
+}
+
+template <class F>
+void test_sfinae_volatile_fun0(F f) {
+ static_assert(is_callable<F, A>::value, "");
+ static_assert(is_callable<F, A&>::value, "");
+ static_assert(is_callable<F, A*>::value, "");
+
+ static_assert(!is_callable<F, const A>::value, "");
+ static_assert(!is_callable<F, const A&>::value, "");
+ static_assert(!is_callable<F, const A*>::value, "");
+
+ static_assert(is_callable<F, volatile A>::value, "");
+ static_assert(is_callable<F, volatile A&>::value, "");
+ static_assert(is_callable<F, volatile A*>::value, "");
+
+ static_assert(!is_callable<F, const volatile A>::value, "");
+ static_assert(!is_callable<F, const volatile A&>::value, "");
+ static_assert(!is_callable<F, const volatile A*>::value, "");
+}
+
+template <class F>
+void test_sfinae_const_volatile_fun0(F f) {
+ static_assert(is_callable<F, A>::value, "");
+ static_assert(is_callable<F, A&>::value, "");
+ static_assert(is_callable<F, A*>::value, "");
+
+ static_assert(is_callable<F, const A>::value, "");
+ static_assert(is_callable<F, const A&>::value, "");
+ static_assert(is_callable<F, const A*>::value, "");
+
+ static_assert(is_callable<F, volatile A>::value, "");
+ static_assert(is_callable<F, volatile A&>::value, "");
+ static_assert(is_callable<F, volatile A*>::value, "");
+
+ static_assert(is_callable<F, const volatile A>::value, "");
+ static_assert(is...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/111307
More information about the libcxx-commits
mailing list