[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