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 `__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_","" "`P2255R2 `__","LWG","A type trait to detect reference binding to temporary","February 2022","","" "`P2273R3 `__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0" -"`P2387R3 `__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|" +"`P2387R3 `__","LWG","Pipe support for user-defined range adaptors","February 2022","|Partial| [#note-P2387R3]_","","|ranges|" "`P2440R1 `__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|" "`P2441R2 `__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|" "`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 +_LIBCPP_HIDE_FROM_ABI constexpr auto bind_back(_Fn&& __f, _Args&&... __args) { + static_assert(is_constructible_v, _Fn>, "bind_back requires decay_t to be constructible from F"); + static_assert(is_move_constructible_v>, "bind_back requires decay_t to be move constructible"); + static_assert((is_constructible_v, _Args> && ...), + "bind_back requires all decay_t to be constructible from respective Args"); + static_assert((is_move_constructible_v> && ...), + "bind_back requires all decay_t to be move constructible"); + return __bind_back_t, tuple...>>( + 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 not2(const Predicate& pred); template constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20 +// [func.bind.partial], function templates bind_front and bind_back +template + constexpr unspecified bind_front(F&&, Args&&...); // C++20 +template + constexpr unspecified bind_back(F&&, Args&&...); // C++23 + template struct is_bind_expression; template 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 __cpp_lib_atomic_value_initialization 201911L __cpp_lib_atomic_wait 201907L __cpp_lib_barrier 201907L -__cpp_lib_bind_back 202306L - 202202L // C++23 +__cpp_lib_bind_back 202202L __cpp_lib_bind_front 202306L 201907L // C++20 __cpp_lib_bit_cast 201806L @@ -449,7 +448,7 @@ __cpp_lib_within_lifetime 202306L # 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 #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 + +// + +// template +// constexpr unspecified bind_back(F&& f, Args&&... args); + +#include + +#include +#include +#include +#include + +#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 decltype(auto) dresult(d()); + assert(dresult == 3); + + auto e = std::bind_back(add_rref, n, m); + std::same_as 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 decltype(auto) iresult(i(5)); + assert(iresult == 7); + + auto j = std::bind_back(add_rref, m); + std::same_as 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 a, std::reference_wrapper 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)() == 1); + assert(static_cast(f)() == 2); + assert(static_cast(f)() == 3); + assert(static_cast(f)() == 4); + } + + // Make sure the `bind_back` unspecified-type does not model 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 X { + void operator()() & = delete; + void operator()() const&; + void operator()() &&; + void operator()() const&&; + }; + + using F = decltype(std::bind_back(X{})); + static_assert(!std::invocable); + static_assert(std::invocable); + static_assert(std::invocable); + static_assert(std::invocable); + } + + // 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); + static_assert(std::invocable); + static_assert(!std::invocable); + static_assert(std::invocable); + } + + { // 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); + static_assert(std::invocable); + static_assert(std::invocable); + static_assert(!std::invocable); + } + } + + { // 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); + static_assert(!std::invocable); + } + + { + struct Y { + void operator()(const X&) const; + void operator()(X&&) const = delete; + }; + + using F = decltype(std::bind_back(Y{}, X{})); + static_assert(std::invocable); + static_assert(!std::invocable); + } + } +} + +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 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::value); + static_assert(!std::is_copy_constructible::value); + static_assert(!std::is_move_assignable::value); + static_assert(!std::is_copy_assignable::value); + } + + { // Test move constructor when function is copyable but not assignable. + CopyCallable 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::value); + static_assert(std::is_copy_constructible::value); + static_assert(!std::is_move_assignable::value); + static_assert(!std::is_copy_assignable::value); + } + + { // Test constructors when function is copy assignable. + using F = decltype(std::bind_back(std::declval(), 1)); + static_assert(std::is_move_constructible::value); + static_assert(std::is_copy_constructible::value); + static_assert(std::is_move_assignable::value); + static_assert(std::is_copy_assignable::value); + } + + { // Test constructors when function is move assignable only. + using F = decltype(std::bind_back(std::declval(), 1)); + static_assert(std::is_move_constructible::value); + static_assert(!std::is_copy_constructible::value); + static_assert(std::is_move_assignable::value); + static_assert(!std::is_copy_assignable::value); + } + } + + { // Make sure bind_back's unspecified type's operator() is SFINAE-friendly. + using F = decltype(std::bind_back(std::declval(), 1)); + static_assert(!std::is_invocable::value); + static_assert(std::is_invocable::value); + static_assert(!std::is_invocable::value); + static_assert(!std::is_invocable::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 + +// + +// template +// constexpr unspecified bind_back(F&& f, Args&&... args); + +#include + +#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@-1 {{no matching function for call to 'bind_back'}} + } + + { // Mandates: is_constructible_v, 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 to be constructible from F}} + } + + { // Mandates: is_move_constructible_v> + 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 to be move constructible}} + } + + { // Mandates: (is_constructible_v, 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 to be constructible from respective Args}} + // expected-error@*:* {{no matching constructor for initialization}} + } + + { // Mandates: (is_move_constructible_v> && ...) + 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 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 +#include + +struct MakeTuple { + template + constexpr auto operator()(Args&&... args) const { + return std::make_tuple(std::forward(args)...); + } +}; + +template +struct Elem { + template + constexpr bool operator==(const Elem&) 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 +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 #include +#include #include #include #include @@ -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 decltype(auto) dresult(d()); + assert(dresult == 3); + + auto e = std::bind_front(add_rref, n, m); + std::same_as 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 decltype(auto) iresult(i(5)); + assert(iresult == 7); + + auto j = std::bind_front(add_rref, m); + std::same_as 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",