[libcxx-commits] [libcxx] a061d4d - [libc++] Fix expression-equivalence for `mem_fn` (#111307)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Oct 15 07:33:53 PDT 2024


Author: A. Jiang
Date: 2024-10-15T22:33:50+08:00
New Revision: a061d4d5cedf8f4651a01ea2e8cf98bd8863bf0f

URL: https://github.com/llvm/llvm-project/commit/a061d4d5cedf8f4651a01ea2e8cf98bd8863bf0f
DIFF: https://github.com/llvm/llvm-project/commit/a061d4d5cedf8f4651a01ea2e8cf98bd8863bf0f.diff

LOG: [libc++] Fix expression-equivalence for `mem_fn` (#111307)

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.

Added: 
    libcxx/test/std/utilities/function.objects/func.memfn/mem_fn.pass.cpp

Modified: 
    libcxx/include/__functional/mem_fn.h
    libcxx/include/__functional/weak_result_type.h
    libcxx/include/functional
    libcxx/test/std/utilities/function.objects/func.memfn/member_data.compile.fail.cpp

Removed: 
    libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp
    libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp
    libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp
    libcxx/test/std/utilities/function.objects/func.memfn/member_function_const_volatile.pass.cpp
    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