[libcxx-commits] [libcxx] [libc++] Fix expression-equivalence for `mem_fn` (PR #111307)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Oct 6 11:31:26 PDT 2024
https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/111307
>From 9c73637f0e03d0cd84dec27bf798dfad487a0e5f Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 7 Oct 2024 02:27:31 +0800
Subject: [PATCH] [libc++] Fix expression-equivalence for `mem_fn`
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.
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.
---
libcxx/include/__functional/mem_fn.h | 6 +-
.../include/__functional/weak_result_type.h | 5 -
libcxx/include/functional | 2 +-
.../func.memfn/mem_fn.pass.cpp | 741 ++++++++++++++++++
.../func.memfn/member_data.compile.fail.cpp | 2 +-
.../func.memfn/member_data.pass.cpp | 51 --
.../func.memfn/member_function.pass.cpp | 87 --
.../func.memfn/member_function_const.pass.cpp | 90 ---
.../member_function_const_volatile.pass.cpp | 81 --
.../member_function_volatile.pass.cpp | 81 --
10 files changed, 745 insertions(+), 401 deletions(-)
create mode 100644 libcxx/test/std/utilities/function.objects/func.memfn/mem_fn.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.memfn/member_function_const_volatile.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.memfn/member_function_volatile.pass.cpp
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..14f0e1194cc9b4
--- /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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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, "");
+}
+#endif
+
+int main(int, char**) {
+ test_data(std::mem_fn(&A::data_));
+
+ test_fun0(std::mem_fn(&A::test0));
+ test_fun1(std::mem_fn(&A::test1));
+ test_fun2(std::mem_fn(&A::test2));
+
+ test_noexcept_fun0(std::mem_fn(&A::test0_nothrow));
+ test_noexcept_fun1(std::mem_fn(&A::test1_nothrow));
+ test_noexcept_fun2(std::mem_fn(&A::test2_nothrow));
+
+ test_const_fun0(std::mem_fn(&A::test_c0));
+ test_const_fun1(std::mem_fn(&A::test_c1));
+ test_const_fun2(std::mem_fn(&A::test_c2));
+
+ test_const_noexcept_fun0(std::mem_fn(&A::test_c0_nothrow));
+ test_const_noexcept_fun1(std::mem_fn(&A::test_c1_nothrow));
+ test_const_noexcept_fun2(std::mem_fn(&A::test_c2_nothrow));
+
+ test_volatile_fun0(std::mem_fn(&A::test_v0));
+ test_volatile_fun1(std::mem_fn(&A::test_v1));
+ test_volatile_fun2(std::mem_fn(&A::test_v2));
+
+ test_volatile_noexcept_fun0(std::mem_fn(&A::test_v0_nothrow));
+ test_volatile_noexcept_fun1(std::mem_fn(&A::test_v1_nothrow));
+ test_volatile_noexcept_fun2(std::mem_fn(&A::test_v2_nothrow));
+
+ test_const_volatile_fun0(std::mem_fn(&A::test_cv0));
+ test_const_volatile_fun1(std::mem_fn(&A::test_cv1));
+ test_const_volatile_fun2(std::mem_fn(&A::test_cv2));
+
+ test_const_volatile_noexcept_fun0(std::mem_fn(&A::test_cv0_nothrow));
+ test_const_volatile_noexcept_fun1(std::mem_fn(&A::test_cv1_nothrow));
+ test_const_volatile_noexcept_fun2(std::mem_fn(&A::test_cv2_nothrow));
+
+#if TEST_STD_VER >= 11
+ // LWG2489
+ static_assert((noexcept(std::mem_fn(&A::data_))), "");
+ static_assert((noexcept(std::mem_fn(&A::test0))), "");
+ static_assert((noexcept(std::mem_fn(&A::test0_nothrow))), "");
+
+ test_sfinae_data(std::mem_fn(&A::data_));
+
+ test_sfinae_fun0(std::mem_fn(&A::test0));
+ test_sfinae_fun0(std::mem_fn(&A::test0_nothrow));
+
+ test_sfinae_const_fun0(std::mem_fn(&A::test_c0));
+ test_sfinae_const_fun0(std::mem_fn(&A::test_c0_nothrow));
+
+ test_sfinae_volatile_fun0(std::mem_fn(&A::test_v0));
+ test_sfinae_volatile_fun0(std::mem_fn(&A::test_v0_nothrow));
+
+ test_sfinae_const_volatile_fun0(std::mem_fn(&A::test_cv0));
+ test_sfinae_const_volatile_fun0(std::mem_fn(&A::test_cv0_nothrow));
+
+ test_sfinae_fun1(std::mem_fn(&A::test1));
+ test_sfinae_fun1(std::mem_fn(&A::test1_nothrow));
+#endif
+
+#if TEST_STD_VER >= 20
+ static_assert(test_data(std::mem_fn(&A::data_)));
+
+ static_assert(test_fun0(std::mem_fn(&A::test0)));
+ static_assert(test_fun1(std::mem_fn(&A::test1)));
+ static_assert(test_fun2(std::mem_fn(&A::test2)));
+
+ static_assert(test_const_fun0(std::mem_fn(&A::test_c0)));
+ static_assert(test_const_fun1(std::mem_fn(&A::test_c1)));
+ static_assert(test_const_fun2(std::mem_fn(&A::test_c2)));
+
+ static_assert(test_noexcept_fun0(std::mem_fn(&A::test0_nothrow)));
+ static_assert(test_noexcept_fun1(std::mem_fn(&A::test1_nothrow)));
+ static_assert(test_noexcept_fun2(std::mem_fn(&A::test2_nothrow)));
+
+ static_assert(test_const_noexcept_fun0(std::mem_fn(&A::test_c0_nothrow)));
+ static_assert(test_const_noexcept_fun1(std::mem_fn(&A::test_c1_nothrow)));
+ static_assert(test_const_noexcept_fun2(std::mem_fn(&A::test_c2_nothrow)));
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_data.compile.fail.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_data.compile.fail.cpp
index f68b019b80b996..5325f1fb26b2cd 100644
--- a/libcxx/test/std/utilities/function.objects/func.memfn/member_data.compile.fail.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.memfn/member_data.compile.fail.cpp
@@ -9,7 +9,7 @@
// <functional>
-// template<Returnable R, class T> unspecified mem_fn(R T::* pm);
+// template<class R, class T> constexpr unspecified mem_fn(R T::*) noexcept; // constexpr in C++20
#include <functional>
#include <cassert>
diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp
deleted file mode 100644
index d6cd06cd8cd096..00000000000000
--- a/libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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<Returnable R, class T> unspecified mem_fn(R T::* pm);
-
-#include <functional>
-#include <cassert>
-
-#include "test_macros.h"
-
-struct A
-{
- double data_;
-};
-
-template <class F>
-TEST_CONSTEXPR_CXX20 bool
-test(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));
- }
- return true;
-}
-
-int main(int, char**)
-{
- test(std::mem_fn(&A::data_));
-
-#if TEST_STD_VER >= 20
- static_assert(test(std::mem_fn(&A::data_)));
-#endif
-
- return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp
deleted file mode 100644
index 88d4108e10ac0a..00000000000000
--- a/libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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<Returnable R, class T, CopyConstructible... Args>
-// unspecified mem_fn(R (T::* pm)(Args...));
-
-#include <functional>
-#include <cassert>
-
-#include "test_macros.h"
-
-struct A
-{
- TEST_CONSTEXPR_CXX14 char test0() {return 'a';}
- TEST_CONSTEXPR_CXX14 char test1(int) {return 'b';}
- TEST_CONSTEXPR_CXX14 char test2(int, double) {return 'c';}
-};
-
-template <class F>
-TEST_CONSTEXPR_CXX20 bool
-test0(F f)
-{
- {
- A a;
- assert(f(a) == 'a');
- A* ap = &a;
- assert(f(ap) == 'a');
- const F& cf = f;
- assert(cf(ap) == 'a');
- }
- return true;
-}
-
-template <class F>
-TEST_CONSTEXPR_CXX20 bool
-test1(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');
- }
- return true;
-}
-
-template <class F>
-TEST_CONSTEXPR_CXX20 bool
-test2(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');
- }
- return true;
-}
-
-int main(int, char**)
-{
- test0(std::mem_fn(&A::test0));
- test1(std::mem_fn(&A::test1));
- test2(std::mem_fn(&A::test2));
-#if TEST_STD_VER >= 11
- static_assert((noexcept(std::mem_fn(&A::test0))), ""); // LWG#2489
-#endif
-
-#if TEST_STD_VER >= 20
- static_assert(test0(std::mem_fn(&A::test0)));
- static_assert(test1(std::mem_fn(&A::test1)));
- static_assert(test2(std::mem_fn(&A::test2)));
-#endif
-
- return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp
deleted file mode 100644
index 057998c7e6e555..00000000000000
--- a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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<Returnable R, class T, CopyConstructible... Args>
-// unspecified mem_fn(R (T::* pm)(Args...) const);
-
-#include <functional>
-#include <cassert>
-
-#include "test_macros.h"
-
-struct A
-{
- TEST_CONSTEXPR char test0() const {return 'a';}
- TEST_CONSTEXPR char test1(int) const {return 'b';}
- TEST_CONSTEXPR char test2(int, double) const {return 'c';}
-};
-
-template <class F>
-TEST_CONSTEXPR_CXX20 bool
-test0(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');
- }
- return true;
-}
-
-template <class F>
-TEST_CONSTEXPR_CXX20 bool
-test1(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');
- }
- return true;
-}
-
-template <class F>
-TEST_CONSTEXPR_CXX20 bool
-test2(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');
- }
- return true;
-}
-
-int main(int, char**)
-{
- test0(std::mem_fn(&A::test0));
- test1(std::mem_fn(&A::test1));
- test2(std::mem_fn(&A::test2));
-
-#if TEST_STD_VER >= 20
- static_assert(test0(std::mem_fn(&A::test0)));
- static_assert(test1(std::mem_fn(&A::test1)));
- static_assert(test2(std::mem_fn(&A::test2)));
-#endif
-
- return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const_volatile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const_volatile.pass.cpp
deleted file mode 100644
index 175f2de7005562..00000000000000
--- a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const_volatile.pass.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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<Returnable R, class T, CopyConstructible... Args>
-// unspecified mem_fn(R (T::* pm)(Args...) const volatile);
-
-#include <functional>
-#include <cassert>
-
-#include "test_macros.h"
-
-struct A
-{
- char test0() const volatile {return 'a';}
- char test1(int) const volatile {return 'b';}
- char test2(int, double) const volatile {return 'c';}
-};
-
-template <class F>
-void
-test0(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');
- }
-}
-
-template <class F>
-void
-test1(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');
- }
-}
-
-template <class F>
-void
-test2(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');
- }
-}
-
-int main(int, char**)
-{
- test0(std::mem_fn(&A::test0));
- test1(std::mem_fn(&A::test1));
- test2(std::mem_fn(&A::test2));
-
- return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_volatile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_function_volatile.pass.cpp
deleted file mode 100644
index 7f27c2f1d99edc..00000000000000
--- a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_volatile.pass.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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<Returnable R, class T, CopyConstructible... Args>
-// unspecified mem_fn(R (T::* pm)(Args...) volatile);
-
-#include <functional>
-#include <cassert>
-
-#include "test_macros.h"
-
-struct A
-{
- char test0() volatile {return 'a';}
- char test1(int) volatile {return 'b';}
- char test2(int, double) volatile {return 'c';}
-};
-
-template <class F>
-void
-test0(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');
- }
-}
-
-template <class F>
-void
-test1(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');
- }
-}
-
-template <class F>
-void
-test2(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');
- }
-}
-
-int main(int, char**)
-{
- test0(std::mem_fn(&A::test0));
- test1(std::mem_fn(&A::test1));
- test2(std::mem_fn(&A::test2));
-
- return 0;
-}
More information about the libcxx-commits
mailing list