[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