-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[libc++] Fix expression-equivalence for mem_fn
#111307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-libcxx Author: A. Jiang (frederick-vs-ja) ChangesPreviously, SFINAE constraints and exception specification propagation were missing in the return type of libc++'s This PR adds the missed stuffs. Fixes #86043. Drive-by changes:
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:
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]
|
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.
8ed4a8f
to
9c73637
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! A few questions but this LGTM.
const F& cf = f; | ||
assert(cf(ap) == 'a'); | ||
|
||
#if TEST_STD_VER >= 17 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you only checking for noexcept-ness in >= C++17?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In earlier modes, the stored member function pointer couldn't propagate noexcept-ness, because noexcept-specifier is made a part of type only since C++17 via P0012R1.
I'm not sure whether the behavioral change of the C++ core language should be reflected here.
struct is_callable : is_callable_impl<void, Func, Args...>::type {}; | ||
|
||
template <class F> | ||
void test_sfinae_data(F) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you confirm that those are the tests you added for the issue you fixed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) causes substitutioin failure, and std::mem_fn(pm)(x, y)
should behave the same.
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 llvm#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.
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:
__invoke_return
,mem_fn
into one.