[libcxx-commits] [libcxx] c891704 - [libc++] Implement `bind_back` (#81055)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Apr 9 08:10:24 PDT 2024
Author: Jakub Mazurkiewicz
Date: 2024-04-09T11:10:20-04:00
New Revision: c8917048e3aa2be03b6588b817730abdbce23c85
URL: https://github.com/llvm/llvm-project/commit/c8917048e3aa2be03b6588b817730abdbce23c85
DIFF: https://github.com/llvm/llvm-project/commit/c8917048e3aa2be03b6588b817730abdbce23c85.diff
LOG: [libc++] Implement `bind_back` (#81055)
Implement `std::bind_back` function from P2387R3 "Pipe support for
user-defined range adaptors".
Added:
libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
Modified:
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/Status/Cxx23.rst
libcxx/docs/Status/Cxx23Papers.csv
libcxx/include/__functional/bind_back.h
libcxx/include/functional
libcxx/include/version
libcxx/modules/std/functional.inc
libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp
libcxx/utils/generate_feature_test_macro_components.py
Removed:
################################################################################
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 014ac1c31e6308..3197d2cd1b271c 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``
---------------------------------------------------------- -----------------
@@ -398,8 +398,6 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_atomic_min_max`` *unimplemented*
---------------------------------------------------------- -----------------
- ``__cpp_lib_bind_back`` *unimplemented*
- ---------------------------------------------------------- -----------------
``__cpp_lib_bind_front`` ``202306L``
---------------------------------------------------------- -----------------
``__cpp_lib_bitset`` ``202306L``
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 23d30c8128d71e..b19ff4fdc0f79e 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 80547c5c1f3f57..065db97a0b0b15 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..3c42d4769e8a9f 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>
+_LIBCPP_HIDE_FROM_ABI constexpr auto bind_back(_Fn&& __f, _Args&&... __args) {
+ static_assert(is_constructible_v<decay_t<_Fn>, _Fn>, "bind_back requires decay_t<F> to be constructible from F");
+ static_assert(is_move_constructible_v<decay_t<_Fn>>, "bind_back requires decay_t<F> to be move constructible");
+ static_assert((is_constructible_v<decay_t<_Args>, _Args> && ...),
+ "bind_back requires all decay_t<Args> to be constructible from respective Args");
+ static_assert((is_move_constructible_v<decay_t<_Args>> && ...),
+ "bind_back requires all decay_t<Args> to be move constructible");
+ 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 >= 23
+
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index a2774a48bda0ee..a2476c93ad1b43 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 90dc1b279c6c25..0ed77345baa71d 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -41,8 +41,7 @@ __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_back 202202L <functional>
__cpp_lib_bind_front 202306L <functional>
201907L // C++20
__cpp_lib_bit_cast 201806L <bit>
@@ -449,7 +448,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
@@ -498,8 +497,6 @@ __cpp_lib_within_lifetime 202306L <type_traits>
#if _LIBCPP_STD_VER >= 26
// # define __cpp_lib_associative_heterogeneous_insertion 202306L
// # define __cpp_lib_atomic_min_max 202403L
-# 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
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;
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 fa4d9baa283727..aeb09a30b42591 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,7 +17,6 @@
/* 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]
@@ -337,17 +336,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
@@ -447,17 +440,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++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
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 5055786c2d4589..3ec548f56cea1d 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
@@ -39,7 +39,6 @@
__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]
@@ -4605,17 +4604,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
@@ -6240,17 +6233,11 @@
# 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
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..01a96348d50c50
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.pass.cpp
@@ -0,0 +1,381 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <concepts>
+#include <tuple>
+#include <utility>
+
+#include "callable_types.h"
+#include "types.h"
+
+constexpr void test_basic_bindings() {
+ { // 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>{}));
+ }
+ {
+ auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});
+ assert(f(Elem<10>{}, Elem<11>{}, Elem<12>{}) ==
+ std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<12>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));
+ }
+ }
+
+ { // Basic tests with fundamental types
+ int n = 2;
+ int m = 1;
+ int sum = 0;
+ auto add = [](int x, int y) { return x + y; };
+ auto add_n = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; };
+ auto add_ref = [&](int x, int y) -> int& { return sum = x + y; };
+ auto add_rref = [&](int x, int y) -> int&& { return std::move(sum = x + y); };
+
+ auto a = std::bind_back(add, m, n);
+ assert(a() == 3);
+
+ auto b = std::bind_back(add_n, m, n, m, m, m, m);
+ assert(b() == 7);
+
+ auto c = std::bind_back(add_n, n, m);
+ assert(c(1, 1, 1, 1) == 7);
+
+ auto d = std::bind_back(add_ref, n, m);
+ std::same_as<int&> decltype(auto) dresult(d());
+ assert(dresult == 3);
+
+ auto e = std::bind_back(add_rref, n, m);
+ std::same_as<int&&> decltype(auto) eresult(e());
+ assert(eresult == 3);
+
+ 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(add_n, 1, 1, 1);
+ assert(h(2, 2, 2) == 9);
+
+ auto i = std::bind_back(add_ref, n);
+ std::same_as<int&> decltype(auto) iresult(i(5));
+ assert(iresult == 7);
+
+ auto j = std::bind_back(add_rref, m);
+ std::same_as<int&&> decltype(auto) jresult(j(4));
+ assert(jresult == 5);
+ }
+}
+
+constexpr void test_edge_cases() {
+ { // 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;
+ int j = 2;
+ auto f = std::bind_back(sub, std::ref(i));
+ assert(f(std::ref(j)) == 1);
+ }
+
+ { // Make sure we can call a function that's a pointer to a member function.
+ struct MemberFunction {
+ constexpr int foo(int x, int y) { return x * y; }
+ };
+
+ MemberFunction value;
+ auto fn = std::bind_back(&MemberFunction::foo, 2, 3);
+ assert(fn(value) == 6);
+ }
+
+ { // Make sure we can call a function that's a pointer to a member object.
+ struct MemberObject {
+ int obj;
+ };
+
+ MemberObject value{.obj = 3};
+ auto fn = std::bind_back(&MemberObject::obj);
+ assert(fn(value) == 3);
+ }
+}
+
+constexpr void test_passing_arguments() {
+ { // 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 f = std::bind_back(add, n, 1);
+ n = 100;
+ assert(f() == 3);
+ }
+
+ { // Make sure we pass the bound arguments to the function object
+ // with the right value category.
+ {
+ auto was_copied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; };
+ CopyMoveInfo info;
+ auto f = std::bind_back(was_copied, info);
+ assert(f());
+ }
+
+ {
+ auto was_moved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; };
+ CopyMoveInfo info;
+ auto f = std::bind_back(was_moved, info);
+ assert(std::move(f)());
+ }
+ }
+}
+
+constexpr void test_function_objects() {
+ { // Make sure we call the correctly cv-ref qualified operator()
+ // based on the value category of the bind_back unspecified-type.
+ struct X {
+ constexpr int operator()() & { return 1; }
+ constexpr int operator()() const& { return 2; }
+ constexpr int operator()() && { return 3; }
+ constexpr int operator()() const&& { return 4; }
+ };
+
+ auto f = std::bind_back(X{});
+ using F = decltype(f);
+ assert(static_cast<F&>(f)() == 1);
+ assert(static_cast<const F&>(f)() == 2);
+ assert(static_cast<F&&>(f)() == 3);
+ assert(static_cast<const F&&>(f)() == 4);
+ }
+
+ // Make sure the `bind_back` unspecified-type does not model invocable
+ // when the call would select a
diff erently-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 X {
+ void operator()() & = delete;
+ void operator()() const&;
+ void operator()() &&;
+ void operator()() const&&;
+ };
+
+ using F = decltype(std::bind_back(X{}));
+ static_assert(!std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+
+ // 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 X {
+ void operator()() &;
+ void operator()() const&;
+ void operator()() && = delete;
+ void operator()() const&&;
+ };
+
+ using F = decltype(std::bind_back(X{}));
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(!std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+
+ { // Make sure we delete the const&& overload when the underlying call isn't valid.
+ struct X {
+ void operator()() &;
+ void operator()() const&;
+ void operator()() &&;
+ void operator()() const&& = delete;
+ };
+
+ using F = decltype(std::bind_back(X{}));
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(!std::invocable<const F>);
+ }
+ }
+
+ { // Extra value category tests
+ struct X {};
+
+ {
+ struct Y {
+ void operator()(X&&) const&;
+ void operator()(X&&) && = delete;
+ };
+
+ using F = decltype(std::bind_back(Y{}));
+ static_assert(std::invocable<F&, X>);
+ static_assert(!std::invocable<F, X>);
+ }
+
+ {
+ struct Y {
+ void operator()(const X&) const;
+ void operator()(X&&) const = delete;
+ };
+
+ using F = decltype(std::bind_back(Y{}, X{}));
+ static_assert(std::invocable<F&>);
+ static_assert(!std::invocable<F>);
+ }
+ }
+}
+
+constexpr void test_return_type() {
+ { // Test properties of the constructor of the unspecified-type returned by bind_back.
+ { // Test move constructor when function is move only.
+ MoveOnlyCallable<bool> value(true);
+ auto f = std::bind_back(std::move(value), 1);
+ assert(f());
+ assert(f(1, 2, 3));
+
+ auto f1 = std::move(f);
+ assert(!f());
+ assert(f1());
+ assert(f1(1, 2, 3));
+
+ using F = decltype(f);
+ static_assert(std::is_move_constructible<F>::value);
+ static_assert(!std::is_copy_constructible<F>::value);
+ static_assert(!std::is_move_assignable<F>::value);
+ static_assert(!std::is_copy_assignable<F>::value);
+ }
+
+ { // Test move constructor when function is copyable but not assignable.
+ CopyCallable<bool> value(true);
+ auto f = std::bind_back(value, 1);
+ assert(f());
+ assert(f(1, 2, 3));
+
+ auto f1 = std::move(f);
+ assert(!f());
+ assert(f1());
+ assert(f1(1, 2, 3));
+
+ auto f2 = std::bind_back(std::move(value), 1);
+ assert(f1());
+ assert(f2());
+ assert(f2(1, 2, 3));
+
+ using F = decltype(f);
+ static_assert(std::is_move_constructible<F>::value);
+ static_assert(std::is_copy_constructible<F>::value);
+ static_assert(!std::is_move_assignable<F>::value);
+ static_assert(!std::is_copy_assignable<F>::value);
+ }
+
+ { // Test constructors when function is copy assignable.
+ using F = decltype(std::bind_back(std::declval<CopyAssignableWrapper&>(), 1));
+ static_assert(std::is_move_constructible<F>::value);
+ static_assert(std::is_copy_constructible<F>::value);
+ static_assert(std::is_move_assignable<F>::value);
+ static_assert(std::is_copy_assignable<F>::value);
+ }
+
+ { // Test constructors when function is move assignable only.
+ using F = decltype(std::bind_back(std::declval<MoveAssignableWrapper>(), 1));
+ static_assert(std::is_move_constructible<F>::value);
+ static_assert(!std::is_copy_constructible<F>::value);
+ static_assert(std::is_move_assignable<F>::value);
+ static_assert(!std::is_copy_assignable<F>::value);
+ }
+ }
+
+ { // Make sure bind_back's unspecified type's operator() is SFINAE-friendly.
+ using F = decltype(std::bind_back(std::declval<int (*)(int, int)>(), 1));
+ static_assert(!std::is_invocable<F>::value);
+ static_assert(std::is_invocable<F, int>::value);
+ static_assert(!std::is_invocable<F, void*>::value);
+ static_assert(!std::is_invocable<F, int, int>::value);
+ }
+}
+
+constexpr bool test() {
+ test_basic_bindings();
+ test_edge_cases();
+ test_passing_arguments();
+ test_function_objects();
+ test_return_type();
+
+ 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..eb100c15f580d8
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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"
+
+constexpr int pass(int n) { return n; }
+
+void test() {
+ { // Test calling constexpr function from non-constexpr `bind_back` result
+ auto f1 = std::bind_back(pass, 1);
+ static_assert(f1() == 1); // expected-error {{static assertion expression is not an integral constant expression}}
+ }
+
+ { // Test calling `bind_back` with template function
+ auto f1 = std::bind_back(do_nothing, 2);
+ // expected-error at -1 {{no matching function for call to 'bind_back'}}
+ }
+
+ { // Mandates: is_constructible_v<decay_t<F>, F>
+ struct F {
+ F() = default;
+ F(const F&) = default;
+ F(F&) = delete;
+
+ void operator()() {}
+ };
+
+ F f;
+ auto f1 = std::bind_back(f);
+ // expected-error-re@*:* {{static assertion failed{{.*}}bind_back requires decay_t<F> to be constructible from F}}
+ }
+
+ { // Mandates: is_move_constructible_v<decay_t<F>>
+ struct F {
+ F() = default;
+ F(const F&) = default;
+ F(F&&) = delete;
+
+ void operator()() {}
+ };
+
+ F f;
+ auto f1 = std::bind_back(f);
+ // expected-error-re@*:* {{static assertion failed{{.*}}bind_back requires decay_t<F> to be move constructible}}
+ }
+
+ { // Mandates: (is_constructible_v<decay_t<Args>, Args> && ...)
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&) = delete;
+ };
+
+ Arg x;
+ auto f = std::bind_back([](const Arg&) {}, x);
+ // expected-error-re@*:* {{static assertion failed{{.*}}bind_back requires all decay_t<Args> to be constructible from respective Args}}
+ // expected-error@*:* {{no matching constructor for initialization}}
+ }
+
+ { // Mandates: (is_move_constructible_v<decay_t<Args>> && ...)
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&&) = delete;
+ };
+
+ Arg x;
+ auto f = std::bind_back([](Arg&) {}, x);
+ // expected-error-re@*:* {{static assertion failed{{.*}}bind_back requires all decay_t<Args> to be move constructible}}
+ }
+}
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..76ed4d478baac1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 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==(const Elem<Y>&) const {
+ return X == Y;
+ }
+};
+
+struct CopyMoveInfo {
+ enum { none, copy, move } copy_kind;
+
+ constexpr CopyMoveInfo() : copy_kind(none) {}
+ constexpr CopyMoveInfo(const CopyMoveInfo&) : copy_kind(copy) {}
+ constexpr CopyMoveInfo(CopyMoveInfo&&) : copy_kind(move) {}
+};
+
+template <class T>
+T do_nothing(T t) {
+ return t;
+}
+
+#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.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp
index 6eb4e4a46e82f2..0dee6a95f60a7f 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp
@@ -15,6 +15,7 @@
#include <functional>
#include <cassert>
+#include <concepts>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -142,12 +143,13 @@ 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;
+ int sum = 0;
+ 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 add_ref = [&](int x, int y) -> int& { return sum = x + y; };
+ auto add_rref = [&](int x, int y) -> int&& { return std::move(sum = x + y); };
auto a = std::bind_front(add, m, n);
assert(a() == 3);
@@ -158,6 +160,14 @@ constexpr bool test() {
auto c = std::bind_front(addN, n, m);
assert(c(1, 1, 1, 1) == 7);
+ auto d = std::bind_front(add_ref, n, m);
+ std::same_as<int&> decltype(auto) dresult(d());
+ assert(dresult == 3);
+
+ auto e = std::bind_front(add_rref, n, m);
+ std::same_as<int&&> decltype(auto) eresult(e());
+ assert(eresult == 3);
+
auto f = std::bind_front(add, n);
assert(f(3) == 5);
@@ -166,6 +176,14 @@ constexpr bool test() {
auto h = std::bind_front(addN, 1, 1, 1);
assert(h(2, 2, 2) == 9);
+
+ auto i = std::bind_front(add_ref, n);
+ std::same_as<int&> decltype(auto) iresult(i(5));
+ assert(iresult == 7);
+
+ auto j = std::bind_front(add_rref, m);
+ std::same_as<int&&> decltype(auto) jresult(j(4));
+ assert(jresult == 5);
}
// Make sure we don't treat std::reference_wrapper specially.
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 759e49055be5c4..f2b8d55c0e11b0 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -211,10 +211,9 @@ 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",
More information about the libcxx-commits
mailing list