[libcxx-commits] [libcxx] 13f5579 - [libc++] Make std::bind constexpr-friendly
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed May 3 09:27:25 PDT 2023
Author: Louis Dionne
Date: 2023-05-03T12:27:06-04:00
New Revision: 13f5579caeee2c75baf1249b9f64de54f7c361e2
URL: https://github.com/llvm/llvm-project/commit/13f5579caeee2c75baf1249b9f64de54f7c361e2
DIFF: https://github.com/llvm/llvm-project/commit/13f5579caeee2c75baf1249b9f64de54f7c361e2.diff
LOG: [libc++] Make std::bind constexpr-friendly
std::bind is supposed to be constexpr-friendly since C++20 and it was
marked as such in our synopsis. However, the tests were not actually
testing any of it and as it happens, std::bind was not really constexpr
friendly. This fixes the issue and makes sure that at least some of the
tests are running in constexpr mode.
Some tests for std::bind check functions that return void, and those
use global variables. These tests haven't been made constexpr-friendly,
however the coverage added by this patch should be sufficient to get
decent confidence.
Differential Revision: https://reviews.llvm.org/D149295
Added:
libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141.pass.cpp
Modified:
libcxx/include/__functional/bind.h
libcxx/include/__functional/invoke.h
libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_int_0.pass.cpp
libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_lvalue.pass.cpp
libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_rvalue.pass.cpp
libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/nested.pass.cpp
Removed:
libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141_invoke_not_constexpr.pass.cpp
################################################################################
diff --git a/libcxx/include/__functional/bind.h b/libcxx/include/__functional/bind.h
index 790111c5b37fa..1d5e531a2c696 100644
--- a/libcxx/include/__functional/bind.h
+++ b/libcxx/include/__functional/bind.h
@@ -80,7 +80,7 @@ struct is_placeholder<placeholders::__ph<_Np> >
#ifndef _LIBCPP_CXX03_LANG
template <class _Tp, class _Uj>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
_Tp&
__mu(reference_wrapper<_Tp> __t, _Uj&)
{
@@ -88,7 +88,7 @@ __mu(reference_wrapper<_Tp> __t, _Uj&)
}
template <class _Ti, class ..._Uj, size_t ..._Indx>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename __invoke_of<_Ti&, _Uj...>::type
__mu_expand(_Ti& __ti, tuple<_Uj...>& __uj, __tuple_indices<_Indx...>)
{
@@ -96,7 +96,7 @@ __mu_expand(_Ti& __ti, tuple<_Uj...>& __uj, __tuple_indices<_Indx...>)
}
template <class _Ti, class ..._Uj>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename __enable_if_t
<
is_bind_expression<_Ti>::value,
@@ -118,7 +118,7 @@ struct __mu_return2<true, _Ti, _Uj>
};
template <class _Ti, class _Uj>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename enable_if
<
0 < is_placeholder<_Ti>::value,
@@ -131,7 +131,7 @@ __mu(_Ti&, _Uj& __uj)
}
template <class _Ti, class _Uj>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename enable_if
<
!is_bind_expression<_Ti>::value &&
@@ -249,7 +249,7 @@ struct __bind_return<_Fp, const tuple<_BoundArgs...>, _TupleUj, true>
};
template <class _Fp, class _BoundArgs, size_t ..._Indx, class _Args>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
typename __bind_return<_Fp, _BoundArgs, _Args>::type
__apply_functor(_Fp& __f, _BoundArgs& __bound_args, __tuple_indices<_Indx...>,
_Args&& __args)
diff --git a/libcxx/include/__functional/invoke.h b/libcxx/include/__functional/invoke.h
index c4906840f03dd..7f511c86e7c9d 100644
--- a/libcxx/include/__functional/invoke.h
+++ b/libcxx/include/__functional/invoke.h
@@ -474,7 +474,7 @@ template <class _Ret, bool = is_void<_Ret>::value>
struct __invoke_void_return_wrapper
{
template <class ..._Args>
- _LIBCPP_HIDE_FROM_ABI static _Ret __call(_Args&&... __args) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) {
return std::__invoke(std::forward<_Args>(__args)...);
}
};
@@ -483,7 +483,7 @@ template <class _Ret>
struct __invoke_void_return_wrapper<_Ret, true>
{
template <class ..._Args>
- _LIBCPP_HIDE_FROM_ABI static void __call(_Args&&... __args) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) {
std::__invoke(std::forward<_Args>(__args)...);
}
};
diff --git a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141_invoke_not_constexpr.pass.cpp b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141.pass.cpp
similarity index 65%
rename from libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141_invoke_not_constexpr.pass.cpp
rename to libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141.pass.cpp
index eeb87e0db0a8f..f1c5b07aa51b1 100644
--- a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141_invoke_not_constexpr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/PR23141.pass.cpp
@@ -11,9 +11,9 @@
// <functional>
// template<CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
// https://llvm.org/PR23141
#include <functional>
@@ -21,18 +21,23 @@
#include "test_macros.h"
-struct Fun
-{
+struct Fun {
template<typename T, typename U>
- void operator()(T &&, U &&) const
- {
+ TEST_CONSTEXPR_CXX20 void operator()(T &&, U &&) const {
static_assert(std::is_same<U, int &>::value, "");
}
};
-int main(int, char**)
-{
- std::bind(Fun{}, std::placeholders::_1, 42)("hello");
+TEST_CONSTEXPR_CXX20 bool test() {
+ std::bind(Fun{}, std::placeholders::_1, 42)("hello");
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 20
+ static_assert(test());
+#endif
return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_int_0.pass.cpp b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_int_0.pass.cpp
index 4d3f679893d13..37c914467df1f 100644
--- a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_int_0.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_int_0.pass.cpp
@@ -11,9 +11,9 @@
// <functional>
// template<CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
#include <functional>
#include <cassert>
@@ -21,29 +21,25 @@
#include "test_macros.h"
template <class R, class F>
-void
-test(F f, R expected)
-{
+TEST_CONSTEXPR_CXX20
+void test(F f, R expected) {
assert(f() == expected);
}
template <class R, class F>
-void
-test_const(const F& f, R expected)
-{
+TEST_CONSTEXPR_CXX20
+void test_const(const F& f, R expected) {
assert(f() == expected);
}
-int f() {return 1;}
+TEST_CONSTEXPR_CXX20 int f() {return 1;}
-struct A_int_0
-{
- int operator()() {return 4;}
- int operator()() const {return 5;}
+struct A_int_0 {
+ TEST_CONSTEXPR_CXX20 int operator()() {return 4;}
+ TEST_CONSTEXPR_CXX20 int operator()() const {return 5;}
};
-int main(int, char**)
-{
+TEST_CONSTEXPR_CXX20 bool test_all() {
test(std::bind(f), 1);
test(std::bind(&f), 1);
test(std::bind(A_int_0()), 4);
@@ -53,6 +49,14 @@ int main(int, char**)
test(std::bind<int>(&f), 1);
test(std::bind<int>(A_int_0()), 4);
test_const(std::bind<int>(A_int_0()), 5);
+ return true;
+}
+
+int main(int, char**) {
+ test_all();
+#if TEST_STD_VER >= 20
+ static_assert(test_all());
+#endif
- return 0;
+ return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_lvalue.pass.cpp b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_lvalue.pass.cpp
index 49547a22b39a9..82431bb673779 100644
--- a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_lvalue.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_lvalue.pass.cpp
@@ -11,9 +11,9 @@
// <functional>
// template<CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
#include <stdio.h>
@@ -140,27 +140,22 @@ test_void_1()
// 1 arg, return int
-int f_int_1(int i)
-{
+TEST_CONSTEXPR_CXX20 int f_int_1(int i) {
return i + 1;
}
-struct A_int_1
-{
- A_int_1() : data_(5) {}
- int operator()(int i)
- {
+struct A_int_1 {
+ TEST_CONSTEXPR_CXX20 A_int_1() : data_(5) {}
+ TEST_CONSTEXPR_CXX20 int operator()(int i) {
return i - 1;
}
- int mem1() {return 3;}
- int mem2() const {return 4;}
+ TEST_CONSTEXPR_CXX20 int mem1() { return 3; }
+ TEST_CONSTEXPR_CXX20 int mem2() const { return 4; }
int data_;
};
-void
-test_int_1()
-{
+TEST_CONSTEXPR_CXX20 bool test_int_1() {
using namespace std::placeholders;
// function
{
@@ -212,6 +207,8 @@ test_int_1()
std::bind(&A_int_1::data_, _1)(ap) = 7;
assert(std::bind(&A_int_1::data_, _1)(ap) == 7);
}
+
+ return true;
}
// 2 arg, return void
@@ -263,31 +260,45 @@ test_void_2()
}
}
-struct TFENode
-{
- bool foo(unsigned long long) const
- {
+struct ConstQualifiedMemberFunction {
+ TEST_CONSTEXPR_CXX20 bool foo(unsigned long long) const {
return true;
}
};
-void
-test3()
-{
+TEST_CONSTEXPR_CXX20 bool test_const_qualified_member() {
using namespace std;
using namespace std::placeholders;
- const auto f = bind(&TFENode::foo, _1, 0UL);
- const TFENode n = TFENode{};
+ const auto f = bind(&ConstQualifiedMemberFunction::foo, _1, 0UL);
+ const ConstQualifiedMemberFunction n = ConstQualifiedMemberFunction{};
bool b = f(n);
assert(b);
+ return true;
}
-int main(int, char**)
-{
+TEST_CONSTEXPR_CXX20 bool test_many_args() {
+ using namespace std::placeholders;
+ auto f = [](int& a, char&, float&, long&) -> int& { return a; };
+ auto bound = std::bind(f, _4, _3, _2, _1);
+ int a = 3; char b = '2'; float c = 1.0f; long d = 0l;
+ int& result = bound(d, c, b, a);
+ assert(&result == &a);
+ return true;
+}
+
+int main(int, char**) {
test_void_1();
test_int_1();
test_void_2();
- test3();
+ test_const_qualified_member();
+ test_many_args();
+
+ // The other tests are not constexpr-friendly since they need to use a global variable
+#if TEST_STD_VER >= 20
+ static_assert(test_int_1());
+ static_assert(test_const_qualified_member());
+ static_assert(test_many_args());
+#endif
- return 0;
+ return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_rvalue.pass.cpp b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_rvalue.pass.cpp
index cd15727da90df..91ccbcf9eec9e 100644
--- a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_rvalue.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/invoke_rvalue.pass.cpp
@@ -11,9 +11,9 @@
// <functional>
// template<CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
#include <stdio.h>
@@ -130,27 +130,22 @@ test_void_1()
// 1 arg, return int
-int f_int_1(int i)
-{
+TEST_CONSTEXPR_CXX20 int f_int_1(int i) {
return i + 1;
}
-struct A_int_1
-{
- A_int_1() : data_(5) {}
- int operator()(int i)
- {
+struct A_int_1 {
+ TEST_CONSTEXPR_CXX20 A_int_1() : data_(5) {}
+ TEST_CONSTEXPR_CXX20 int operator()(int i) {
return i - 1;
}
- int mem1() {return 3;}
- int mem2() const {return 4;}
+ TEST_CONSTEXPR_CXX20 int mem1() { return 3; }
+ TEST_CONSTEXPR_CXX20 int mem2() const { return 4; }
int data_;
};
-void
-test_int_1()
-{
+TEST_CONSTEXPR_CXX20 bool test_int_1() {
using namespace std::placeholders;
// function
{
@@ -196,6 +191,7 @@ test_int_1()
std::bind(&A_int_1::data_, _1)(&a) = 7;
assert(std::bind(&A_int_1::data_, _1)(&a) == 7);
}
+ return true;
}
// 2 arg, return void
@@ -244,28 +240,39 @@ test_void_2()
}
}
-int f_nested(int i)
-{
+TEST_CONSTEXPR_CXX20 int f_nested(int i) {
return i+1;
}
-int g_nested(int i)
-{
+TEST_CONSTEXPR_CXX20 int g_nested(int i) {
return i*10;
}
-void test_nested()
-{
+TEST_CONSTEXPR_CXX20 bool test_nested() {
using namespace std::placeholders;
assert(std::bind(f_nested, std::bind(g_nested, _1))(3) == 31);
+ return true;
}
-int main(int, char**)
-{
+TEST_CONSTEXPR_CXX20 bool test_many_args() {
+ using namespace std::placeholders;
+ auto f = [](int a, char, float, long) { return a; };
+ auto bound = std::bind(f, _4, _3, _2, _1);
+ assert(bound(0l, 1.0f, '2', 3) == 3);
+ return true;
+}
+
+int main(int, char**) {
test_void_1();
test_int_1();
test_void_2();
test_nested();
- return 0;
+ // The other tests are not constexpr-friendly since they need to use a global variable
+#if TEST_STD_VER >= 20
+ static_assert(test_int_1());
+ static_assert(test_nested());
+#endif
+
+ return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/nested.pass.cpp b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/nested.pass.cpp
index 76e9d84b63fdd..0fe72220efa49 100644
--- a/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/nested.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/bind/func.bind/func.bind.bind/nested.pass.cpp
@@ -11,45 +11,45 @@
// <functional>
// template<CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
// template<Returnable R, CopyConstructible Fn, CopyConstructible... Types>
-// unspecified bind(Fn, Types...);
+// unspecified bind(Fn, Types...); // constexpr since C++20
// https://llvm.org/PR16343
-#include <cmath>
#include <functional>
#include <cassert>
#include "test_macros.h"
-struct power
-{
+struct multiply {
template <typename T>
- T
- operator()(T a, T b)
- {
- return static_cast<T>(std::pow(a, b));
+ TEST_CONSTEXPR_CXX20 T operator()(T a, T b) {
+ return a * b;
}
};
-struct plus_one
-{
+struct plus_one {
template <typename T>
- T
- operator()(T a)
- {
+ TEST_CONSTEXPR_CXX20 T operator()(T a) {
return a + 1;
}
};
-int main(int, char**)
-{
- using std::placeholders::_1;
+TEST_CONSTEXPR_CXX20 bool test() {
+ using std::placeholders::_1;
+ auto g = std::bind(multiply(), 2, _1);
+ assert(g(5) == 10);
+ assert(std::bind(plus_one(), g)(5) == 11);
- auto g = std::bind(power(), 2, _1);
- assert(g(5) == 32);
- assert(std::bind(plus_one(), g)(5) == 33);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 20
+ static_assert(test());
+#endif
return 0;
}
More information about the libcxx-commits
mailing list