[libcxx-commits] [libcxx] [libc++] Fix expression-equivalence for `mem_fn` (PR #111307)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Oct 12 05:44:23 PDT 2024
================
@@ -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) {
----------------
frederick-vs-ja wrote:
I think SFINAE on pointer to data members is a bit beyond, but still related to the the original examples. When `pm` is a pointer to data member, `std::invoke(pm, x, y)` (or the _`INVOKE`_ operation in the [[func.require]/1](https://eel.is/c++draft/func.require#1)) causes substitutioin failure, and `std::mem_fn(pm)(x, y)` should behave the same.
https://github.com/llvm/llvm-project/pull/111307
More information about the libcxx-commits
mailing list