[libcxx-commits] [libcxx] [libc++] Implement `bind_back` (PR #81055)

Jakub Mazurkiewicz via libcxx-commits libcxx-commits at lists.llvm.org
Mon Feb 19 16:47:13 PST 2024


https://github.com/JMazurkiewicz updated https://github.com/llvm/llvm-project/pull/81055

>From 10b30bd2bab11cbad7a91c2bee243038db1ee10e Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Thu, 8 Feb 2024 00:27:45 +0100
Subject: [PATCH 1/6] [libc++] Implement `bind_back`

* Partially implement P2387R3: "Pipe support for user-defined range adaptors"
* Refactor `bind_front` tests
* Add `types.h` header with common types for `bind_(front|back)` tests
* Fix value of `__cpp_lib_bind_front` FTM in C++26 mode
---
 libcxx/docs/FeatureTestMacroTable.rst         |   6 +-
 libcxx/docs/Status/Cxx23.rst                  |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/__functional/bind_back.h       |  14 +
 libcxx/include/functional                     |   6 +
 libcxx/include/version                        |  12 +-
 .../functional.version.compile.pass.cpp       |  38 +-
 .../version.version.compile.pass.cpp          |  38 +-
 .../func.bind.partial/bind_back.pass.cpp      | 356 ++++++++++++++++++
 .../func.bind.partial/bind_back.verify.cpp    |  32 ++
 .../bind_front.pass.cpp                       | 127 ++-----
 .../func.bind.partial/bind_front.verify.cpp   |  32 ++
 .../func.bind.partial/types.h                 |  74 ++++
 .../func.bind_front/bind_front.verify.cpp     |  45 ---
 .../generate_feature_test_macro_components.py |   5 +-
 15 files changed, 585 insertions(+), 203 deletions(-)
 create mode 100644 libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
 rename libcxx/test/std/utilities/function.objects/{func.bind_front => func.bind.partial}/bind_front.pass.cpp (76%)
 create mode 100644 libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.verify.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
 delete mode 100644 libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index a5c6fa22cec06c..a3be1b9e71fc3f 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -308,7 +308,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_associative_heterogeneous_erasure``     *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_bind_back``                             *unimplemented*
+    ``__cpp_lib_bind_back``                             ``202202L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_byteswap``                              ``202110L``
     --------------------------------------------------- -----------------
@@ -392,10 +392,6 @@ Status
     ---------------------------------------------------------------------
     ``__cpp_lib_associative_heterogeneous_insertion``   *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_bind_back``                             *unimplemented*
-    --------------------------------------------------- -----------------
-    ``__cpp_lib_bind_front``                            ``202306L``
-    --------------------------------------------------- -----------------
     ``__cpp_lib_bitset``                                ``202306L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_copyable_function``                     *unimplemented*
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 3e6e33f08c7ccf..a3a0d8b81e88f9 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -43,6 +43,7 @@ Paper Status
    .. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented.
    .. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
       clang doesn't issue a diagnostic for deprecated using template declarations.
+   .. [#note-P2387R3] P2387R3: ``bind_back`` only
    .. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
    .. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
    .. [#note-P2770R0] P2770R0: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb415ed8c031fa..aebc2ffe5b067a 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -45,7 +45,7 @@
 "`P1413R3 <https://wg21.link/P1413R3>`__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_",""
 "`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
 "`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
-"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|"
+"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","|Partial| [#note-P2387R3]_","","|ranges|"
 "`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
 "`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|"
 "`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
diff --git a/libcxx/include/__functional/bind_back.h b/libcxx/include/__functional/bind_back.h
index ce26d3b70630f3..2baab3e252d3d1 100644
--- a/libcxx/include/__functional/bind_back.h
+++ b/libcxx/include/__functional/bind_back.h
@@ -62,6 +62,20 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __bind_back(_Fn&& __f, _Args&&... __args) n
       std::forward<_Fn>(__f), std::forward_as_tuple(std::forward<_Args>(__args)...));
 }
 
+#  if _LIBCPP_STD_VER >= 23
+template <class _Fn, class... _Args>
+  requires is_constructible_v<decay_t<_Fn>, _Fn> && is_move_constructible_v<decay_t<_Fn>> &&
+           (is_constructible_v<decay_t<_Args>, _Args> && ...) && (is_move_constructible_v<decay_t<_Args>> && ...)
+_LIBCPP_HIDE_FROM_ABI constexpr auto bind_back(_Fn&& __f, _Args&&... __args) noexcept(
+    noexcept(__bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(
+        std::forward<_Fn>(__f), std::forward_as_tuple(std::forward<_Args>(__args)...))))
+    -> decltype(__bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(
+        std::forward<_Fn>(__f), std::forward_as_tuple(std::forward<_Args>(__args)...))) {
+  return __bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(
+      std::forward<_Fn>(__f), std::forward_as_tuple(std::forward<_Args>(__args)...));
+}
+#  endif // _LIBCPP_STD_VER >= 20
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index fd99e11fb18180..d72ebb2310c5ae 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -207,6 +207,12 @@ binary_negate<Predicate> not2(const Predicate& pred);
 template <class F>
 constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20
 
+// [func.bind.partial], function templates bind_front and bind_back
+template<class F, class... Args>
+  constexpr unspecified bind_front(F&&, Args&&...); // C++20
+template<class F, class... Args>
+  constexpr unspecified bind_back(F&&, Args&&...);  // C++23
+
 template<class T> struct is_bind_expression;
 template<class T> struct is_placeholder;
 
diff --git a/libcxx/include/version b/libcxx/include/version
index b18927a2bc38c2..5dc73becc943c6 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -40,10 +40,8 @@ __cpp_lib_atomic_shared_ptr                             201711L <atomic>
 __cpp_lib_atomic_value_initialization                   201911L <atomic> <memory>
 __cpp_lib_atomic_wait                                   201907L <atomic>
 __cpp_lib_barrier                                       201907L <barrier>
-__cpp_lib_bind_back                                     202306L <functional>
-                                                        202202L // C++23
-__cpp_lib_bind_front                                    202306L <functional>
-                                                        201907L // C++20
+__cpp_lib_bind_back                                     202202L <functional>
+__cpp_lib_bind_front                                    201907L <functional>
 __cpp_lib_bit_cast                                      201806L <bit>
 __cpp_lib_bitops                                        201907L <bit>
 __cpp_lib_bitset                                        202306L <bitset>
@@ -439,7 +437,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # define __cpp_lib_adaptor_iterator_pair_constructor    202106L
 # define __cpp_lib_allocate_at_least                    202302L
 // # define __cpp_lib_associative_heterogeneous_erasure    202110L
-// # define __cpp_lib_bind_back                            202202L
+# define __cpp_lib_bind_back                            202202L
 # define __cpp_lib_byteswap                             202110L
 # define __cpp_lib_constexpr_bitset                     202207L
 # define __cpp_lib_constexpr_charconv                   202207L
@@ -485,10 +483,6 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 
 #if _LIBCPP_STD_VER >= 26
 // # define __cpp_lib_associative_heterogeneous_insertion  202306L
-# undef  __cpp_lib_bind_back
-// # define __cpp_lib_bind_back                            202306L
-# undef  __cpp_lib_bind_front
-# define __cpp_lib_bind_front                           202306L
 # define __cpp_lib_bitset                               202306L
 // # define __cpp_lib_copyable_function                    202306L
 // # define __cpp_lib_debugging                            202311L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index 72c96c62b64c45..db4c183544caa2 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -17,9 +17,7 @@
 
 /*  Constant                           Value
     __cpp_lib_bind_back                202202L [C++23]
-                                       202306L [C++26]
     __cpp_lib_bind_front               201907L [C++20]
-                                       202306L [C++26]
     __cpp_lib_boyer_moore_searcher     201603L [C++17]
     __cpp_lib_constexpr_functional     201907L [C++20]
     __cpp_lib_copyable_function        202306L [C++26]
@@ -320,17 +318,11 @@
 
 #elif TEST_STD_VER == 23
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should be defined in c++23"
-#   endif
-#   if __cpp_lib_bind_back != 202202L
-#     error "__cpp_lib_bind_back should have the value 202202L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_bind_back
+#   error "__cpp_lib_bind_back should be defined in c++23"
+# endif
+# if __cpp_lib_bind_back != 202202L
+#   error "__cpp_lib_bind_back should have the value 202202L in c++23"
 # endif
 
 # ifndef __cpp_lib_bind_front
@@ -426,24 +418,18 @@
 
 #elif TEST_STD_VER > 23
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should be defined in c++26"
-#   endif
-#   if __cpp_lib_bind_back != 202306L
-#     error "__cpp_lib_bind_back should have the value 202306L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_bind_back
+#   error "__cpp_lib_bind_back should be defined in c++26"
+# endif
+# if __cpp_lib_bind_back != 202202L
+#   error "__cpp_lib_bind_back should have the value 202202L in c++26"
 # endif
 
 # ifndef __cpp_lib_bind_front
 #   error "__cpp_lib_bind_front should be defined in c++26"
 # endif
-# if __cpp_lib_bind_front != 202306L
-#   error "__cpp_lib_bind_front should have the value 202306L in c++26"
+# if __cpp_lib_bind_front != 201907L
+#   error "__cpp_lib_bind_front should have the value 201907L in c++26"
 # endif
 
 # ifndef __cpp_lib_boyer_moore_searcher
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 14271308624e65..18aaadaeacc4ea 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -38,9 +38,7 @@
     __cpp_lib_atomic_wait                            201907L [C++20]
     __cpp_lib_barrier                                201907L [C++20]
     __cpp_lib_bind_back                              202202L [C++23]
-                                                     202306L [C++26]
     __cpp_lib_bind_front                             201907L [C++20]
-                                                     202306L [C++26]
     __cpp_lib_bit_cast                               201806L [C++20]
     __cpp_lib_bitops                                 201907L [C++20]
     __cpp_lib_bitset                                 202306L [C++26]
@@ -4465,17 +4463,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should be defined in c++23"
-#   endif
-#   if __cpp_lib_bind_back != 202202L
-#     error "__cpp_lib_bind_back should have the value 202202L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_bind_back
+#   error "__cpp_lib_bind_back should be defined in c++23"
+# endif
+# if __cpp_lib_bind_back != 202202L
+#   error "__cpp_lib_bind_back should have the value 202202L in c++23"
 # endif
 
 # ifndef __cpp_lib_bind_front
@@ -6053,24 +6045,18 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should be defined in c++26"
-#   endif
-#   if __cpp_lib_bind_back != 202306L
-#     error "__cpp_lib_bind_back should have the value 202306L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_bind_back
-#     error "__cpp_lib_bind_back should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_bind_back
+#   error "__cpp_lib_bind_back should be defined in c++26"
+# endif
+# if __cpp_lib_bind_back != 202202L
+#   error "__cpp_lib_bind_back should have the value 202202L in c++26"
 # endif
 
 # ifndef __cpp_lib_bind_front
 #   error "__cpp_lib_bind_front should be defined in c++26"
 # endif
-# if __cpp_lib_bind_front != 202306L
-#   error "__cpp_lib_bind_front should have the value 202306L in c++26"
+# if __cpp_lib_bind_front != 201907L
+#   error "__cpp_lib_bind_front should have the value 201907L in c++26"
 # endif
 
 # ifndef __cpp_lib_bit_cast
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
new file mode 100644
index 00000000000000..aac1bd63a54cbe
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
@@ -0,0 +1,356 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <functional>
+
+// template<class F, class... Args>
+//   constexpr unspecified bind_back(F&& f, Args&&... args);
+
+#include <functional>
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+
+#include "callable_types.h"
+#include "types.h"
+
+template <class Fn, class... Args>
+concept back_bindable =
+    requires(Fn&& fn, Args&&... args) { std::bind_back(std::forward<Fn>(fn), std::forward<Args>(args)...); };
+
+constexpr bool test() {
+  // Bind arguments, call without arguments
+  {
+    {
+      auto f = std::bind_back(MakeTuple{});
+      assert(f() == std::make_tuple());
+    }
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{});
+      assert(f() == std::make_tuple(Elem<1>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});
+      assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});
+      assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));
+    }
+  }
+
+  // Bind no arguments, call with arguments
+  {
+    {
+      auto f = std::bind_back(MakeTuple{});
+      assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{});
+      assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{});
+      assert(f(Elem<1>{}, Elem<2>{}, Elem<3>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));
+    }
+  }
+
+  // Bind arguments, call with arguments
+  {
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{});
+      assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});
+      assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});
+      assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));
+    }
+
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{});
+      assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});
+      assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}));
+    }
+    {
+      auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});
+      assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));
+    }
+  }
+
+  // Basic tests with fundamental types
+  {
+    int n     = 2;
+    int m     = 1;
+    auto add  = [](int x, int y) { return x + y; };
+    auto addN = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; };
+
+    auto a = std::bind_back(add, m, n);
+    assert(a() == 3);
+
+    auto b = std::bind_back(addN, m, n, m, m, m, m);
+    assert(b() == 7);
+
+    auto c = std::bind_back(addN, n, m);
+    assert(c(1, 1, 1, 1) == 7);
+
+    auto f = std::bind_back(add, n);
+    assert(f(3) == 5);
+
+    auto g = std::bind_back(add, n, 1);
+    assert(g() == 3);
+
+    auto h = std::bind_back(addN, 1, 1, 1);
+    assert(h(2, 2, 2) == 9);
+  }
+
+  // Make sure we don't treat std::reference_wrapper specially.
+  {
+    auto sub = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) { return a.get() - b.get(); };
+    int i = 1, j = 2;
+    auto f = std::bind_back(sub, std::ref(i));
+    assert(f(std::ref(j)) == 2 - 1);
+  }
+
+  // Make sure we can call a function that's a pointer to a member function.
+  {
+    struct MemberFunction {
+      constexpr bool foo(int, int) { return true; }
+    };
+    MemberFunction value;
+    auto fn = std::bind_back(&MemberFunction::foo, 0, 0);
+    assert(fn(value));
+  }
+
+  // Make sure that we copy the bound arguments into the unspecified-type.
+  {
+    auto add = [](int x, int y) { return x + y; };
+    int n    = 2;
+    auto i   = std::bind_back(add, n, 1);
+    n        = 100;
+    assert(i() == 3);
+  }
+
+  // Make sure we pass the bound arguments to the function object
+  // with the right value category.
+  {
+    {
+      auto wasCopied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; };
+      CopyMoveInfo info;
+      auto copied = std::bind_back(wasCopied, info);
+      assert(copied());
+    }
+
+    {
+      auto wasMoved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; };
+      CopyMoveInfo info;
+      auto moved = std::bind_back(wasMoved, info);
+      assert(std::move(moved)());
+    }
+  }
+
+  // Make sure we call the correctly cv-ref qualified operator() based on the
+  // value category of the bind_back unspecified-type.
+  {
+    struct F {
+      constexpr int operator()() & { return 1; }
+      constexpr int operator()() const& { return 2; }
+      constexpr int operator()() && { return 3; }
+      constexpr int operator()() const&& { return 4; }
+    };
+    auto x  = std::bind_back(F{});
+    using X = decltype(x);
+    assert(static_cast<X&>(x)() == 1);
+    assert(static_cast<X const&>(x)() == 2);
+    assert(static_cast<X&&>(x)() == 3);
+    assert(static_cast<X const&&>(x)() == 4);
+  }
+
+  // Make sure the bind_back unspecified-type is NOT invocable when the call would select a
+  // differently-qualified operator().
+  //
+  // For example, if the call to `operator()() &` is ill-formed, the call to the unspecified-type
+  // should be ill-formed and not fall back to the `operator()() const&` overload.
+  { // Make sure we delete the & overload when the underlying call isn't valid
+    {
+      struct F {
+        void operator()() & = delete;
+        void operator()() const&;
+        void operator()() &&;
+        void operator()() const&&;
+      };
+      using X = decltype(std::bind_back(F{}));
+      static_assert(!std::is_invocable_v<X&>);
+      static_assert(std::is_invocable_v<X const&>);
+      static_assert(std::is_invocable_v<X>);
+      static_assert(std::is_invocable_v<X const>);
+    }
+
+    // There's no way to make sure we delete the const& overload when the underlying call isn't valid,
+    // so we can't check this one.
+
+    // Make sure we delete the && overload when the underlying call isn't valid
+    {
+      struct F {
+        void operator()() &;
+        void operator()() const&;
+        void operator()() && = delete;
+        void operator()() const&&;
+      };
+      using X = decltype(std::bind_back(F{}));
+      static_assert(std::is_invocable_v<X&>);
+      static_assert(std::is_invocable_v<X const&>);
+      static_assert(!std::is_invocable_v<X>);
+      static_assert(std::is_invocable_v<X const>);
+    }
+
+    // Make sure we delete the const&& overload when the underlying call isn't valid
+    {
+      struct F {
+        void operator()() &;
+        void operator()() const&;
+        void operator()() &&;
+        void operator()() const&& = delete;
+      };
+      using X = decltype(std::bind_back(F{}));
+      static_assert(std::is_invocable_v<X&>);
+      static_assert(std::is_invocable_v<X const&>);
+      static_assert(std::is_invocable_v<X>);
+      static_assert(!std::is_invocable_v<X const>);
+    }
+  }
+
+  // Some examples by Tim Song
+  {
+    {
+      struct T {};
+      struct F {
+        void operator()(T&&) const&;
+        void operator()(T&&) && = delete;
+      };
+      using X = decltype(std::bind_back(F{}));
+      static_assert(!std::is_invocable_v<X, T>);
+    }
+
+    {
+      struct T {};
+      struct F {
+        void operator()(T const&) const;
+        void operator()(T&&) const = delete;
+      };
+      using X = decltype(std::bind_back(F{}, T{}));
+      static_assert(!std::is_invocable_v<X>);
+    }
+  }
+
+  // Test properties of the constructor of the unspecified-type returned by bind_back.
+  {
+    {
+      MoveOnlyCallable<bool> value(true);
+      auto ret = std::bind_back(std::move(value), 1);
+      assert(ret());
+      assert(ret(1, 2, 3));
+
+      auto ret1 = std::move(ret);
+      assert(!ret());
+      assert(ret1());
+      assert(ret1(1, 2, 3));
+
+      using RetT = decltype(ret);
+      static_assert(std::is_move_constructible<RetT>::value);
+      static_assert(!std::is_copy_constructible<RetT>::value);
+      static_assert(!std::is_move_assignable<RetT>::value);
+      static_assert(!std::is_copy_assignable<RetT>::value);
+    }
+    {
+      CopyCallable<bool> value(true);
+      auto ret = std::bind_back(value, 1);
+      assert(ret());
+      assert(ret(1, 2, 3));
+
+      auto ret1 = std::move(ret);
+      assert(ret1());
+      assert(ret1(1, 2, 3));
+
+      auto ret2 = std::bind_back(std::move(value), 1);
+      assert(!ret());
+      assert(ret2());
+      assert(ret2(1, 2, 3));
+
+      using RetT = decltype(ret);
+      static_assert(std::is_move_constructible<RetT>::value);
+      static_assert(std::is_copy_constructible<RetT>::value);
+      static_assert(!std::is_move_assignable<RetT>::value);
+      static_assert(!std::is_copy_assignable<RetT>::value);
+    }
+    {
+      CopyAssignableWrapper value(true);
+      using RetT = decltype(std::bind_back(value, 1));
+
+      static_assert(std::is_move_constructible<RetT>::value);
+      static_assert(std::is_copy_constructible<RetT>::value);
+      static_assert(std::is_move_assignable<RetT>::value);
+      static_assert(std::is_copy_assignable<RetT>::value);
+    }
+    {
+      MoveAssignableWrapper value(true);
+      using RetT = decltype(std::bind_back(std::move(value), 1));
+
+      static_assert(std::is_move_constructible<RetT>::value);
+      static_assert(!std::is_copy_constructible<RetT>::value);
+      static_assert(std::is_move_assignable<RetT>::value);
+      static_assert(!std::is_copy_assignable<RetT>::value);
+    }
+  }
+
+  // Make sure bind_back is SFINAE friendly
+  {
+    static_assert(!std::is_constructible_v<NotCopyMove, NotCopyMove&>);
+    static_assert(!std::is_move_constructible_v<NotCopyMove>);
+    static_assert(!back_bindable<NotCopyMove>);
+    static_assert(!back_bindable<NotCopyMove&>);
+
+    auto takeAnything = [](auto&&...) {};
+    static_assert(!std::is_constructible_v<MoveConstructible, MoveConstructible&>);
+    static_assert(std::is_move_constructible_v<MoveConstructible>);
+    static_assert(back_bindable<decltype(takeAnything), MoveConstructible>);
+    static_assert(!back_bindable<decltype(takeAnything), MoveConstructible&>);
+
+    static_assert(std::is_constructible_v<NonConstCopyConstructible, NonConstCopyConstructible&>);
+    static_assert(!std::is_move_constructible_v<NonConstCopyConstructible>);
+    static_assert(!back_bindable<decltype(takeAnything), NonConstCopyConstructible&>);
+    static_assert(!back_bindable<decltype(takeAnything), NonConstCopyConstructible>);
+  }
+
+  // Make sure bind_back's unspecified type's operator() is SFINAE-friendly
+  {
+    using T = decltype(std::bind_back(std::declval<int (*)(int, int)>(), 1));
+    static_assert(!std::is_invocable<T>::value);
+    static_assert(std::is_invocable<T, int>::value);
+    static_assert(!std::is_invocable<T, void*>::value);
+    static_assert(!std::is_invocable<T, int, int>::value);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
new file mode 100644
index 00000000000000..03e82cefdcdede
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <functional>
+
+// template<class F, class... Args>
+//   constexpr unspecified bind_back(F&& f, Args&&... args);
+
+#include <functional>
+
+#include "types.h"
+
+void f() {
+  int n       = 1;
+  const int c = 1;
+
+  auto p = std::bind_back(pass, c);
+  static_assert(p() == 1); // expected-error {{static assertion expression is not an integral constant expression}}
+
+  auto d = std::bind_back(do_nothing, n); // expected-error {{no matching function for call to 'bind_back'}}
+
+  auto t = std::bind_back( // expected-error {{no matching function for call to 'bind_back'}}
+      testNotMoveConst,
+      NotMoveConst(0));
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
similarity index 76%
rename from libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
index 6eb4e4a46e82f2..ae53e2fa277b50 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
@@ -11,7 +11,7 @@
 // functional
 
 // template <class F, class... Args>
-// constexpr unspecified bind_front(F&&, Args&&...);
+//   constexpr unspecified bind_front(F&&, Args&&...);
 
 #include <functional>
 #include <cassert>
@@ -20,21 +20,12 @@
 #include <utility>
 
 #include "callable_types.h"
-#include "test_macros.h"
+#include "types.h"
 
-struct CopyMoveInfo {
-  enum { none, copy, move } copy_kind;
-
-  constexpr CopyMoveInfo() : copy_kind(none) {}
-  constexpr CopyMoveInfo(CopyMoveInfo const&) : copy_kind(copy) {}
-  constexpr CopyMoveInfo(CopyMoveInfo&&) : copy_kind(move) {}
-};
-
-template <class ...Args>
+template <class... Args>
 struct is_bind_frontable {
-  template <class ...LocalArgs>
-  static auto test(int)
-      -> decltype((void)std::bind_front(std::declval<LocalArgs>()...), std::true_type());
+  template <class... LocalArgs>
+  static auto test(int) -> decltype((void)std::bind_front(std::declval<LocalArgs>()...), std::true_type());
 
   template <class...>
   static std::false_type test(...);
@@ -42,38 +33,6 @@ struct is_bind_frontable {
   static constexpr bool value = decltype(test<Args...>(0))::value;
 };
 
-struct NotCopyMove {
-  NotCopyMove() = delete;
-  NotCopyMove(const NotCopyMove&) = delete;
-  NotCopyMove(NotCopyMove&&) = delete;
-  template <class ...Args>
-  void operator()(Args&& ...) const { }
-};
-
-struct NonConstCopyConstructible {
-  explicit NonConstCopyConstructible() {}
-  NonConstCopyConstructible(NonConstCopyConstructible&) {}
-};
-
-struct MoveConstructible {
-  explicit MoveConstructible() {}
-  MoveConstructible(MoveConstructible&&) {}
-};
-
-struct MakeTuple {
-  template <class ...Args>
-  constexpr auto operator()(Args&& ...args) const {
-    return std::make_tuple(std::forward<Args>(args)...);
-  }
-};
-
-template <int X>
-struct Elem {
-  template <int Y>
-  constexpr bool operator==(Elem<Y> const&) const
-  { return X == Y; }
-};
-
 constexpr bool test() {
   // Bind arguments, call without arguments
   {
@@ -142,12 +101,10 @@ constexpr bool test() {
 
   // Basic tests with fundamental types
   {
-    int n = 2;
-    int m = 1;
-    auto add = [](int x, int y) { return x + y; };
-    auto addN = [](int a, int b, int c, int d, int e, int f) {
-      return a + b + c + d + e + f;
-    };
+    int n     = 2;
+    int m     = 1;
+    auto add  = [](int x, int y) { return x + y; };
+    auto addN = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; };
 
     auto a = std::bind_front(add, m, n);
     assert(a() == 3);
@@ -170,9 +127,7 @@ constexpr bool test() {
 
   // Make sure we don't treat std::reference_wrapper specially.
   {
-    auto add = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) {
-      return a.get() + b.get();
-    };
+    auto add = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) { return a.get() + b.get(); };
     int i = 1, j = 2;
     auto f = std::bind_front(add, std::ref(i));
     assert(f(std::ref(j)) == 3);
@@ -191,9 +146,9 @@ constexpr bool test() {
   // Make sure that we copy the bound arguments into the unspecified-type.
   {
     auto add = [](int x, int y) { return x + y; };
-    int n = 2;
-    auto i = std::bind_front(add, n, 1);
-    n = 100;
+    int n    = 2;
+    auto i   = std::bind_front(add, n, 1);
+    n        = 100;
     assert(i() == 3);
   }
 
@@ -201,18 +156,14 @@ constexpr bool test() {
   // with the right value category.
   {
     {
-      auto wasCopied = [](CopyMoveInfo info) {
-        return info.copy_kind == CopyMoveInfo::copy;
-      };
+      auto wasCopied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; };
       CopyMoveInfo info;
       auto copied = std::bind_front(wasCopied, info);
       assert(copied());
     }
 
     {
-      auto wasMoved = [](CopyMoveInfo info) {
-        return info.copy_kind == CopyMoveInfo::move;
-      };
+      auto wasMoved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; };
       CopyMoveInfo info;
       auto moved = std::bind_front(wasMoved, info);
       assert(std::move(moved)());
@@ -228,7 +179,7 @@ constexpr bool test() {
       constexpr int operator()() && { return 3; }
       constexpr int operator()() const&& { return 4; }
     };
-    auto x = std::bind_front(F{});
+    auto x  = std::bind_front(F{});
     using X = decltype(x);
     assert(static_cast<X&>(x)() == 1);
     assert(static_cast<X const&>(x)() == 2);
@@ -252,9 +203,9 @@ constexpr bool test() {
       };
       using X = decltype(std::bind_front(F{}));
       static_assert(!std::is_invocable_v<X&>);
-      static_assert( std::is_invocable_v<X const&>);
-      static_assert( std::is_invocable_v<X>);
-      static_assert( std::is_invocable_v<X const>);
+      static_assert(std::is_invocable_v<X const&>);
+      static_assert(std::is_invocable_v<X>);
+      static_assert(std::is_invocable_v<X const>);
     }
 
     // There's no way to make sure we delete the const& overload when the underlying call isn't valid,
@@ -269,10 +220,10 @@ constexpr bool test() {
         void operator()() const&&;
       };
       using X = decltype(std::bind_front(F{}));
-      static_assert( std::is_invocable_v<X&>);
-      static_assert( std::is_invocable_v<X const&>);
+      static_assert(std::is_invocable_v<X&>);
+      static_assert(std::is_invocable_v<X const&>);
       static_assert(!std::is_invocable_v<X>);
-      static_assert( std::is_invocable_v<X const>);
+      static_assert(std::is_invocable_v<X const>);
     }
 
     // Make sure we delete the const&& overload when the underlying call isn't valid
@@ -284,9 +235,9 @@ constexpr bool test() {
         void operator()() const&& = delete;
       };
       using X = decltype(std::bind_front(F{}));
-      static_assert( std::is_invocable_v<X&>);
-      static_assert( std::is_invocable_v<X const&>);
-      static_assert( std::is_invocable_v<X>);
+      static_assert(std::is_invocable_v<X&>);
+      static_assert(std::is_invocable_v<X const&>);
+      static_assert(std::is_invocable_v<X>);
       static_assert(!std::is_invocable_v<X const>);
     }
   }
@@ -294,9 +245,9 @@ constexpr bool test() {
   // Some examples by Tim Song
   {
     {
-      struct T { };
+      struct T {};
       struct F {
-        void operator()(T&&) const &;
+        void operator()(T&&) const&;
         void operator()(T&&) && = delete;
       };
       using X = decltype(std::bind_front(F{}));
@@ -304,7 +255,7 @@ constexpr bool test() {
     }
 
     {
-      struct T { };
+      struct T {};
       struct F {
         void operator()(T const&) const;
         void operator()(T&&) const = delete;
@@ -328,7 +279,7 @@ constexpr bool test() {
       assert(ret1(1, 2, 3));
 
       using RetT = decltype(ret);
-      static_assert( std::is_move_constructible<RetT>::value);
+      static_assert(std::is_move_constructible<RetT>::value);
       static_assert(!std::is_copy_constructible<RetT>::value);
       static_assert(!std::is_move_assignable<RetT>::value);
       static_assert(!std::is_copy_assignable<RetT>::value);
@@ -349,8 +300,8 @@ constexpr bool test() {
       assert(ret2(1, 2, 3));
 
       using RetT = decltype(ret);
-      static_assert( std::is_move_constructible<RetT>::value);
-      static_assert( std::is_copy_constructible<RetT>::value);
+      static_assert(std::is_move_constructible<RetT>::value);
+      static_assert(std::is_copy_constructible<RetT>::value);
       static_assert(!std::is_move_assignable<RetT>::value);
       static_assert(!std::is_copy_assignable<RetT>::value);
     }
@@ -367,9 +318,9 @@ constexpr bool test() {
       MoveAssignableWrapper value(true);
       using RetT = decltype(std::bind_front(std::move(value), 1));
 
-      static_assert( std::is_move_constructible<RetT>::value);
+      static_assert(std::is_move_constructible<RetT>::value);
       static_assert(!std::is_copy_constructible<RetT>::value);
-      static_assert( std::is_move_assignable<RetT>::value);
+      static_assert(std::is_move_assignable<RetT>::value);
       static_assert(!std::is_copy_assignable<RetT>::value);
     }
   }
@@ -381,13 +332,13 @@ constexpr bool test() {
     static_assert(!is_bind_frontable<NotCopyMove>::value);
     static_assert(!is_bind_frontable<NotCopyMove&>::value);
 
-    auto takeAnything = [](auto&& ...) { };
+    auto takeAnything = [](auto&&...) {};
     static_assert(!std::is_constructible_v<MoveConstructible, MoveConstructible&>);
-    static_assert( std::is_move_constructible_v<MoveConstructible>);
-    static_assert( is_bind_frontable<decltype(takeAnything), MoveConstructible>::value);
+    static_assert(std::is_move_constructible_v<MoveConstructible>);
+    static_assert(is_bind_frontable<decltype(takeAnything), MoveConstructible>::value);
     static_assert(!is_bind_frontable<decltype(takeAnything), MoveConstructible&>::value);
 
-    static_assert( std::is_constructible_v<NonConstCopyConstructible, NonConstCopyConstructible&>);
+    static_assert(std::is_constructible_v<NonConstCopyConstructible, NonConstCopyConstructible&>);
     static_assert(!std::is_move_constructible_v<NonConstCopyConstructible>);
     static_assert(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible&>::value);
     static_assert(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible>::value);
@@ -395,9 +346,9 @@ constexpr bool test() {
 
   // Make sure bind_front's unspecified type's operator() is SFINAE-friendly
   {
-    using T = decltype(std::bind_front(std::declval<int(*)(int, int)>(), 1));
+    using T = decltype(std::bind_front(std::declval<int (*)(int, int)>(), 1));
     static_assert(!std::is_invocable<T>::value);
-    static_assert( std::is_invocable<T, int>::value);
+    static_assert(std::is_invocable<T, int>::value);
     static_assert(!std::is_invocable<T, void*>::value);
     static_assert(!std::is_invocable<T, int, int>::value);
   }
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.verify.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.verify.cpp
new file mode 100644
index 00000000000000..ddcb78a39aa7a4
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <functional>
+
+// template <class F, class... Args>
+//   constexpr unspecified bind_front(F&&, Args&&...);
+
+#include <functional>
+
+#include "types.h"
+
+void f() {
+  int n       = 1;
+  const int c = 1;
+
+  auto p = std::bind_front(pass, c);
+  static_assert(p() == 1); // expected-error {{static assertion expression is not an integral constant expression}}
+
+  auto d = std::bind_front(do_nothing, n); // expected-error {{no matching function for call to 'bind_front'}}
+
+  auto t = std::bind_front( // expected-error {{no matching function for call to 'bind_front'}}
+      testNotMoveConst,
+      NotMoveConst(0));
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h b/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
new file mode 100644
index 00000000000000..a1f9c7a3d28a08
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_UTILITIES_FUNCTION_OBJECTS_FUNC_BIND_PARTIAL_TYPES_H
+#define TEST_STD_UTILITIES_FUNCTION_OBJECTS_FUNC_BIND_PARTIAL_TYPES_H
+
+#include <tuple>
+#include <utility>
+
+struct CopyMoveInfo {
+  enum { none, copy, move } copy_kind;
+
+  constexpr CopyMoveInfo() : copy_kind(none) {}
+  constexpr CopyMoveInfo(CopyMoveInfo const&) : copy_kind(copy) {}
+  constexpr CopyMoveInfo(CopyMoveInfo&&) : copy_kind(move) {}
+};
+
+struct NotCopyMove {
+  NotCopyMove()                   = delete;
+  NotCopyMove(const NotCopyMove&) = delete;
+  NotCopyMove(NotCopyMove&&)      = delete;
+  template <class... Args>
+  void operator()(Args&&...) const {}
+};
+
+struct NonConstCopyConstructible {
+  explicit NonConstCopyConstructible() {}
+  NonConstCopyConstructible(NonConstCopyConstructible&) {}
+};
+
+struct MoveConstructible {
+  explicit MoveConstructible() {}
+  MoveConstructible(MoveConstructible&&) {}
+};
+
+struct MakeTuple {
+  template <class... Args>
+  constexpr auto operator()(Args&&... args) const {
+    return std::make_tuple(std::forward<Args>(args)...);
+  }
+};
+
+template <int X>
+struct Elem {
+  template <int Y>
+  constexpr bool operator==(Elem<Y> const&) const {
+    return X == Y;
+  }
+};
+
+struct NotMoveConst {
+  NotMoveConst(NotMoveConst&&)      = delete;
+  NotMoveConst(NotMoveConst const&) = delete;
+
+  NotMoveConst(int) {}
+};
+
+constexpr int pass(const int n) { return n; }
+
+inline int simple(int n) { return n; }
+
+template <class T>
+T do_nothing(T t) {
+  return t;
+}
+
+inline void testNotMoveConst(NotMoveConst) {}
+
+#endif // TEST_STD_UTILITIES_FUNCTION_OBJECTS_FUNC_BIND_PARTIAL_TYPES_H
diff --git a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp
deleted file mode 100644
index 5100259bd5b299..00000000000000
--- a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// functional
-
-// template <class F, class... Args>
-// constexpr unspecified bind_front(F&&, Args&&...);
-
-#include <functional>
-
-constexpr int pass(const int n) { return n; }
-
-int simple(int n) { return n; }
-
-template<class T>
-T do_nothing(T t) { return t; }
-
-struct NotMoveConst
-{
-    NotMoveConst(NotMoveConst &&) = delete;
-    NotMoveConst(NotMoveConst const&) = delete;
-
-    NotMoveConst(int) { }
-};
-
-void testNotMoveConst(NotMoveConst) { }
-
-void f() {
-    int n = 1;
-    const int c = 1;
-
-    auto p = std::bind_front(pass, c);
-    static_assert(p() == 1); // expected-error {{static assertion expression is not an integral constant expression}}
-
-    auto d = std::bind_front(do_nothing, n); // expected-error {{no matching function for call to 'bind_front'}}
-
-    auto t = std::bind_front(testNotMoveConst, NotMoveConst(0)); // expected-error {{no matching function for call to 'bind_front'}}
-}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index cc1fc50ce41233..5ac94d5e18c683 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -205,16 +205,15 @@ def add_version_header(tc):
             "name": "__cpp_lib_bind_back",
             "values": {
                 "c++23": 202202,
-                "c++26": 202306,  # P2714R1 Bind front and back to NTTP callables
+                # "c++26": 202306,  # P2714R1 Bind front and back to NTTP callables
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_bind_front",
             "values": {
                 "c++20": 201907,
-                "c++26": 202306,  # P2714R1 Bind front and back to NTTP callables
+                # "c++26": 202306,  # P2714R1 Bind front and back to NTTP callables
             },
             "headers": ["functional"],
         },

>From 37521e1c716170e6b5d2894b325cd0161112f4b0 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Thu, 8 Feb 2024 01:16:47 +0100
Subject: [PATCH 2/6] Try to fix C++ modules

---
 libcxx/modules/std/functional.inc | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 1148944a9d2fee..ddc7d023ee6dc2 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -56,8 +56,10 @@ export namespace std {
   using std::not_fn;
 
   // [func.bind.partial], function templates bind_front and bind_back
-  // using std::bind_back;
   using std::bind_front;
+#if _LIBCPP_STD_VER >= 23
+  using std::bind_back;
+#endif
 
   // [func.bind], bind
   using std::is_bind_expression;

>From b06ab610ff69fbf570d952e79dbdd8b04c1d3da5 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Mon, 19 Feb 2024 21:37:12 +0100
Subject: [PATCH 3/6] Move unrelated static assertions to `types.h`

---
 .../func.bind.partial/bind_back.pass.cpp                 | 6 ------
 .../func.bind.partial/bind_front.pass.cpp                | 6 ------
 .../utilities/function.objects/func.bind.partial/types.h | 9 +++++++++
 3 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
index aac1bd63a54cbe..6433dc8de140e1 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
@@ -319,19 +319,13 @@ constexpr bool test() {
 
   // Make sure bind_back is SFINAE friendly
   {
-    static_assert(!std::is_constructible_v<NotCopyMove, NotCopyMove&>);
-    static_assert(!std::is_move_constructible_v<NotCopyMove>);
     static_assert(!back_bindable<NotCopyMove>);
     static_assert(!back_bindable<NotCopyMove&>);
 
     auto takeAnything = [](auto&&...) {};
-    static_assert(!std::is_constructible_v<MoveConstructible, MoveConstructible&>);
-    static_assert(std::is_move_constructible_v<MoveConstructible>);
     static_assert(back_bindable<decltype(takeAnything), MoveConstructible>);
     static_assert(!back_bindable<decltype(takeAnything), MoveConstructible&>);
 
-    static_assert(std::is_constructible_v<NonConstCopyConstructible, NonConstCopyConstructible&>);
-    static_assert(!std::is_move_constructible_v<NonConstCopyConstructible>);
     static_assert(!back_bindable<decltype(takeAnything), NonConstCopyConstructible&>);
     static_assert(!back_bindable<decltype(takeAnything), NonConstCopyConstructible>);
   }
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
index ae53e2fa277b50..9cdc7378549808 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
@@ -327,19 +327,13 @@ constexpr bool test() {
 
   // Make sure bind_front is SFINAE friendly
   {
-    static_assert(!std::is_constructible_v<NotCopyMove, NotCopyMove&>);
-    static_assert(!std::is_move_constructible_v<NotCopyMove>);
     static_assert(!is_bind_frontable<NotCopyMove>::value);
     static_assert(!is_bind_frontable<NotCopyMove&>::value);
 
     auto takeAnything = [](auto&&...) {};
-    static_assert(!std::is_constructible_v<MoveConstructible, MoveConstructible&>);
-    static_assert(std::is_move_constructible_v<MoveConstructible>);
     static_assert(is_bind_frontable<decltype(takeAnything), MoveConstructible>::value);
     static_assert(!is_bind_frontable<decltype(takeAnything), MoveConstructible&>::value);
 
-    static_assert(std::is_constructible_v<NonConstCopyConstructible, NonConstCopyConstructible&>);
-    static_assert(!std::is_move_constructible_v<NonConstCopyConstructible>);
     static_assert(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible&>::value);
     static_assert(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible>::value);
   }
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h b/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
index a1f9c7a3d28a08..493de23694e322 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
@@ -28,16 +28,25 @@ struct NotCopyMove {
   void operator()(Args&&...) const {}
 };
 
+static_assert(!std::is_constructible_v<NotCopyMove, NotCopyMove&>);
+static_assert(!std::is_move_constructible_v<NotCopyMove>);
+
 struct NonConstCopyConstructible {
   explicit NonConstCopyConstructible() {}
   NonConstCopyConstructible(NonConstCopyConstructible&) {}
 };
 
+static_assert(std::is_constructible_v<NonConstCopyConstructible, NonConstCopyConstructible&>);
+static_assert(!std::is_move_constructible_v<NonConstCopyConstructible>);
+
 struct MoveConstructible {
   explicit MoveConstructible() {}
   MoveConstructible(MoveConstructible&&) {}
 };
 
+static_assert(!std::is_constructible_v<MoveConstructible, MoveConstructible&>);
+static_assert(std::is_move_constructible_v<MoveConstructible>);
+
 struct MakeTuple {
   template <class... Args>
   constexpr auto operator()(Args&&... args) const {

>From eba6fe17228ffa5b341900c1f4efe08fef368a92 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Mon, 19 Feb 2024 21:38:47 +0100
Subject: [PATCH 4/6] Make `bind_back` not SFINAE friendly (as standard says)

---
 libcxx/include/__functional/bind_back.h            | 14 ++++++--------
 .../func.bind.partial/bind_back.pass.cpp           | 13 -------------
 .../func.bind.partial/bind_back.verify.cpp         | 10 ++++++++++
 3 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/libcxx/include/__functional/bind_back.h b/libcxx/include/__functional/bind_back.h
index 2baab3e252d3d1..6869d86e644afe 100644
--- a/libcxx/include/__functional/bind_back.h
+++ b/libcxx/include/__functional/bind_back.h
@@ -64,17 +64,15 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __bind_back(_Fn&& __f, _Args&&... __args) n
 
 #  if _LIBCPP_STD_VER >= 23
 template <class _Fn, class... _Args>
-  requires is_constructible_v<decay_t<_Fn>, _Fn> && is_move_constructible_v<decay_t<_Fn>> &&
-           (is_constructible_v<decay_t<_Args>, _Args> && ...) && (is_move_constructible_v<decay_t<_Args>> && ...)
-_LIBCPP_HIDE_FROM_ABI constexpr auto bind_back(_Fn&& __f, _Args&&... __args) noexcept(
-    noexcept(__bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(
-        std::forward<_Fn>(__f), std::forward_as_tuple(std::forward<_Args>(__args)...))))
-    -> decltype(__bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(
-        std::forward<_Fn>(__f), std::forward_as_tuple(std::forward<_Args>(__args)...))) {
+_LIBCPP_HIDE_FROM_ABI constexpr auto bind_back(_Fn&& __f, _Args&&... __args) {
+  static_assert(
+      is_constructible_v<decay_t<_Fn>, _Fn> && is_move_constructible_v<decay_t<_Fn>> &&
+          (is_constructible_v<decay_t<_Args>, _Args> && ...) && (is_move_constructible_v<decay_t<_Args>> && ...),
+      "Requirements of `bind_back` function are not satisfied");
   return __bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(
       std::forward<_Fn>(__f), std::forward_as_tuple(std::forward<_Args>(__args)...));
 }
-#  endif // _LIBCPP_STD_VER >= 20
+#  endif // _LIBCPP_STD_VER >= 23
 
 #endif // _LIBCPP_STD_VER >= 20
 
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
index 6433dc8de140e1..89aec3414c6354 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
@@ -317,19 +317,6 @@ constexpr bool test() {
     }
   }
 
-  // Make sure bind_back is SFINAE friendly
-  {
-    static_assert(!back_bindable<NotCopyMove>);
-    static_assert(!back_bindable<NotCopyMove&>);
-
-    auto takeAnything = [](auto&&...) {};
-    static_assert(back_bindable<decltype(takeAnything), MoveConstructible>);
-    static_assert(!back_bindable<decltype(takeAnything), MoveConstructible&>);
-
-    static_assert(!back_bindable<decltype(takeAnything), NonConstCopyConstructible&>);
-    static_assert(!back_bindable<decltype(takeAnything), NonConstCopyConstructible>);
-  }
-
   // Make sure bind_back's unspecified type's operator() is SFINAE-friendly
   {
     using T = decltype(std::bind_back(std::declval<int (*)(int, int)>(), 1));
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
index 03e82cefdcdede..94b0001699723a 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
@@ -29,4 +29,14 @@ void f() {
   auto t = std::bind_back( // expected-error {{no matching function for call to 'bind_back'}}
       testNotMoveConst,
       NotMoveConst(0));
+
+  auto takeAnything = [](auto&&...) {};
+  MoveConstructible mc;
+  auto mc1 = std::bind_back(takeAnything, mc); // expected-error {{static assertion failed}}
+
+  NonConstCopyConstructible nccc;
+  auto nccc1 = std::bind_back(takeAnything, nccc); // expected-error {{static assertion failed}}
+  auto nccc2 = std::bind_back(                     // expected-error {{static assertion failed}}
+      takeAnything,
+      NonConstCopyConstructible{});
 }

>From 532d644a92b4aeb46613d825eb9ed4d2be81f75e Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Mon, 19 Feb 2024 22:35:11 +0100
Subject: [PATCH 5/6] Use `LIBCPP_STATIC_ASSERT` in `bind_front` SFINAE tests

---
 .../func.bind.partial/bind_front.pass.cpp          | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
index 9cdc7378549808..9f18abc66c0537 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp
@@ -327,15 +327,15 @@ constexpr bool test() {
 
   // Make sure bind_front is SFINAE friendly
   {
-    static_assert(!is_bind_frontable<NotCopyMove>::value);
-    static_assert(!is_bind_frontable<NotCopyMove&>::value);
+    LIBCPP_STATIC_ASSERT(!is_bind_frontable<NotCopyMove>::value);
+    LIBCPP_STATIC_ASSERT(!is_bind_frontable<NotCopyMove&>::value);
 
-    auto takeAnything = [](auto&&...) {};
-    static_assert(is_bind_frontable<decltype(takeAnything), MoveConstructible>::value);
-    static_assert(!is_bind_frontable<decltype(takeAnything), MoveConstructible&>::value);
+    [[maybe_unused]] auto takeAnything = [](auto&&...) {};
+    LIBCPP_STATIC_ASSERT(is_bind_frontable<decltype(takeAnything), MoveConstructible>::value);
+    LIBCPP_STATIC_ASSERT(!is_bind_frontable<decltype(takeAnything), MoveConstructible&>::value);
 
-    static_assert(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible&>::value);
-    static_assert(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible>::value);
+    LIBCPP_STATIC_ASSERT(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible&>::value);
+    LIBCPP_STATIC_ASSERT(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible>::value);
   }
 
   // Make sure bind_front's unspecified type's operator() is SFINAE-friendly

>From 5d8dbd6b3a1c472d12142fddee4f8b8f74acb7c0 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Tue, 20 Feb 2024 01:46:42 +0100
Subject: [PATCH 6/6] Fix `bind_back.verify.cpp` test

---
 .../func.bind.partial/bind_back.verify.cpp    | 20 ++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
index 94b0001699723a..cfcc5f26609646 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
@@ -26,17 +26,23 @@ void f() {
 
   auto d = std::bind_back(do_nothing, n); // expected-error {{no matching function for call to 'bind_back'}}
 
-  auto t = std::bind_back( // expected-error {{no matching function for call to 'bind_back'}}
-      testNotMoveConst,
-      NotMoveConst(0));
+  auto t = std::
+      bind_back( // expected-error-re@*:* {{static assertion failed{{.*}}Requirements of `bind_back` function are not satisfied}}
+          testNotMoveConst, // expected-error@*:* {{no matching constructor for initialization}}
+          NotMoveConst(0));
 
   auto takeAnything = [](auto&&...) {};
   MoveConstructible mc;
-  auto mc1 = std::bind_back(takeAnything, mc); // expected-error {{static assertion failed}}
+  auto mc1 = std::bind_back(
+      takeAnything, // expected-error-re@*:* {{static assertion failed{{.*}}Requirements of `bind_back` function are not satisfied}}
+      mc); // expected-error@*:* {{no matching constructor for initialization}}
 
   NonConstCopyConstructible nccc;
-  auto nccc1 = std::bind_back(takeAnything, nccc); // expected-error {{static assertion failed}}
-  auto nccc2 = std::bind_back(                     // expected-error {{static assertion failed}}
+  auto nccc1 = std::bind_back(
       takeAnything,
-      NonConstCopyConstructible{});
+      nccc); // expected-error-re@*:* {{static assertion failed{{.*}}Requirements of `bind_back` function are not satisfied}}
+  auto nccc2 = std::
+      bind_back( // expected-error-re@*:* {{static assertion failed{{.*}}Requirements of `bind_back` function are not satisfied}}
+          takeAnything,
+          NonConstCopyConstructible{});
 }



More information about the libcxx-commits mailing list