Author: Louis Dionne Date: 2023-04-27T10:57:39-04:00 New Revision: 901a22b810784ff09c36ff9d69a3edb3547c6c08
URL: https://github.com/llvm/llvm-project/commit/901a22b810784ff09c36ff9d69a3edb3547c6c08 DIFF: https://github.com/llvm/llvm-project/commit/901a22b810784ff09c36ff9d69a3edb3547c6c08.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; } _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits