Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++] Implement bind_back #81055

Merged
merged 12 commits into from
Apr 9, 2024

Conversation

JMazurkiewicz
Copy link
Contributor

@JMazurkiewicz JMazurkiewicz commented Feb 7, 2024

Implement std::bind_back function from P2387R3 "Pipe support for user-defined range adaptors".

@JMazurkiewicz JMazurkiewicz requested a review from a team as a code owner February 7, 2024 23:41
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 7, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 7, 2024

@llvm/pr-subscribers-libcxx

Author: Jakub Mazurkiewicz (JMazurkiewicz)

Changes
  • 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

Patch is 43.54 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81055.diff

15 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-5)
  • (modified) libcxx/docs/Status/Cxx23.rst (+1)
  • (modified) libcxx/docs/Status/Cxx23Papers.csv (+1-1)
  • (modified) libcxx/include/__functional/bind_back.h (+14)
  • (modified) libcxx/include/functional (+6)
  • (modified) libcxx/include/version (+3-9)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp (+12-26)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+12-26)
  • (added) libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp (+356)
  • (added) libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp (+32)
  • (renamed) libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.pass.cpp (+39-88)
  • (added) libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.verify.cpp (+32)
  • (added) libcxx/test/std/utilities/function.objects/func.bind.partial/types.h (+74)
  • (removed) libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp (-45)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+2-3)
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;
+    ...
[truncated]

Implement `std::bind_back` function from P2387R3 "Pipe support for user-defined range adaptors".
@var-const var-const self-assigned this Mar 8, 2024
@var-const var-const added the ranges Issues related to `<ranges>` label Mar 8, 2024
@ldionne ldionne assigned ldionne and unassigned var-const Mar 12, 2024
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch! I have some comments but this is looking pretty good.

auto p = std::bind_back(pass, 1);
static_assert(p() == 1); // expected-error {{static assertion expression is not an integral constant expression}}

auto d = std::bind_back(do_nothing, 2); // expected-error {{no matching function for call to 'bind_back'}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this test checking? You can't pass an overloaded function as an argument. Am I missing something, or this test isn't testing anything from the library?

Copy link
Contributor Author

@JMazurkiewicz JMazurkiewicz Mar 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test checks if there exists some (incorrect) overload of std::bind_back. I'm going to add comment with clarification (commit 50d4023).

You can't pass an overloaded function as an argument.

You can do something like this:

namespace std {
    template<class F, class... Args>
    auto bind_back(F&&, Args&&...);

    void bind_back(int (*)(int));
}

template<class T>
T do_nothing(T t) {
    return t;
}

void use() {
    std::bind_back(do_nothing);
}

BTW, I've copied this test from bind_front.verify.cpp:

auto d = std::bind_front(do_nothing, n); // expected-error {{no matching function for call to 'bind_front'}}


void test() {
{ // Various failures
auto p = std::bind_back(pass, 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's fundamental that pass is an actual function (and not a function object), I would mention it in a comment and I would define pass in this test file so we can see its definition immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, commits: 66632ba, 5d68323

#include "callable_types.h"
#include "types.h"

constexpr void test_basic_bindings() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any test that we can return e.g. a reference from the function and it won't get decayed? I couldn't find any, but maybe I missed them.

If we don't have any, we should also add a similar test for bind_front while we're at it, it's pretty easy to test.

Copy link
Contributor Author

@JMazurkiewicz JMazurkiewicz Mar 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@JMazurkiewicz

This comment was marked as outdated.

@ldionne
Copy link
Member

ldionne commented Apr 9, 2024

Thanks for the patch! I'll merge now since it's green.

@ldionne ldionne merged commit c891704 into llvm:main Apr 9, 2024
52 checks passed
@JMazurkiewicz JMazurkiewicz deleted the libcxx/functional/bind_back branch April 9, 2024 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. ranges Issues related to `<ranges>`
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants