diff --git a/libcxx/docs/Cxx2aStatusIssuesStatus.csv b/libcxx/docs/Cxx2aStatusIssuesStatus.csv --- a/libcxx/docs/Cxx2aStatusIssuesStatus.csv +++ b/libcxx/docs/Cxx2aStatusIssuesStatus.csv @@ -147,7 +147,7 @@ "`3158 `__","``tuple(allocator_arg_t, const Alloc&)``\ should be conditionally explicit","Cologne","","" "`3169 `__","``ranges``\ permutation generators discard useful information","Cologne","","" "`3183 `__","Normative permission to specialize Ranges variable templates","Cologne","","" -"`3184 `__","Inconsistencies in ``bind_front``\ wording","Cologne","","" +"`3184 `__","Inconsistencies in ``bind_front``\ wording","Cologne","|Complete|","13.0" "`3185 `__","Uses-allocator construction functions missing ``constexpr``\ and ``noexcept``\ ","Cologne","","" "`3186 `__","``ranges``\ removal, partition, and ``partial_sort_copy``\ algorithms discard useful information","Cologne","","" "`3187 `__","`P0591R4 `__ reverted DR 2586 fixes to ``scoped_allocator_adaptor::construct()``\ ","Cologne","","" diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -51,7 +51,7 @@ "`P1120R0 `__","CWG","Consistency improvements for <=> and other comparison operators","Rapperswil","","" "","","","","","" "`P0318R1 `__","LWG","unwrap_ref_decay and unwrap_reference","San Diego","|Complete|","8.0" -"`P0356R5 `__","LWG","Simplified partial function application","San Diego","* *","" +"`P0356R5 `__","LWG","Simplified partial function application","San Diego","|Complete|","13.0" "`P0357R3 `__","LWG","reference_wrapper for incomplete types","San Diego","|Complete|","8.0" "`P0482R6 `__","CWG","char8_t: A type for UTF-8 characters and strings","San Diego","|In Progress|","" "`P0487R1 `__","LWG","Fixing ``operator>>(basic_istream&, CharT*)``\ (LWG 2499)","San Diego","|Complete|","8.0" @@ -128,7 +128,7 @@ "`P1643 `__","LWG","Add wait/notify to atomic_ref","Cologne","","" "`P1644 `__","LWG","Add wait/notify to atomic","Cologne","","" "`P1650 `__","LWG","Output std::chrono::days with 'd' suffix","Cologne","","" -"`P1651 `__","LWG","bind_front should not unwrap reference_wrapper","Cologne","","" +"`P1651 `__","LWG","bind_front should not unwrap reference_wrapper","Cologne","|Complete|","13.0" "`P1652 `__","LWG","Printf corner cases in std::format","Cologne","","" "`P1661 `__","LWG","Remove dedicated precalculated hash lookup interface","Cologne","|Nothing To Do|","" "`P1754 `__","LWG","Rename concepts to standard_case for C++20, while we still can","Cologne","|In Progress|","" diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -188,7 +188,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_barrier`` ``201907L`` ------------------------------------------------- ----------------- - ``__cpp_lib_bind_front`` *unimplemented* + ``__cpp_lib_bind_front`` ``201907L`` ------------------------------------------------- ----------------- ``__cpp_lib_bit_cast`` *unimplemented* ------------------------------------------------- ----------------- diff --git a/libcxx/include/functional b/libcxx/include/functional --- a/libcxx/include/functional +++ b/libcxx/include/functional @@ -2992,61 +2992,107 @@ return _VSTD::__invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)...); } -template -class _LIBCPP_TEMPLATE_VIS __not_fn_imp { - _DecayFunc __fd; - -public: - __not_fn_imp() = delete; - - template - _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - auto operator()(_Args&& ...__args) & - noexcept(noexcept(!_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...))) - -> decltype( !_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...)) - { return !_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...); } - - template - _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - auto operator()(_Args&& ...__args) && - noexcept(noexcept(!_VSTD::invoke(_VSTD::move(__fd), _VSTD::forward<_Args>(__args)...))) - -> decltype( !_VSTD::invoke(_VSTD::move(__fd), _VSTD::forward<_Args>(__args)...)) - { return !_VSTD::invoke(_VSTD::move(__fd), _VSTD::forward<_Args>(__args)...); } - - template - _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - auto operator()(_Args&& ...__args) const& - noexcept(noexcept(!_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...))) - -> decltype( !_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...)) - { return !_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...); } - +template::value>::type> +struct __perfect_forward_impl; + +template +struct __perfect_forward_impl<_Op, __tuple_types<_Bound...>, __tuple_indices<_Idxs...>> +{ + tuple<_Bound...> __bound_; + + template + _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) & + noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...)) + {return _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...);} + + template + _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) const& + noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...)) + {return _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...);} + + template + _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) && + noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., + _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., + _VSTD::forward<_Args>(__args)...)) + {return _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., + _VSTD::forward<_Args>(__args)...);} + + template + _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) const&& + noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., + _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., + _VSTD::forward<_Args>(__args)...)) + {return _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., + _VSTD::forward<_Args>(__args)...);} + + template>::type, + class = _EnableIf>> + constexpr __perfect_forward_impl(__perfect_forward_impl const& __other) + : __bound_(__other.__bound_) {} + + template>::type, + class = _EnableIf>> + constexpr __perfect_forward_impl(__perfect_forward_impl && __other) + : __bound_(_VSTD::move(__other.__bound_)) {} + + template + explicit constexpr __perfect_forward_impl(_BoundArgs&&... __bound) : + __bound_(_VSTD::forward<_BoundArgs>(__bound)...) { } +}; + +template +using __perfect_forward = + __perfect_forward_impl<_Op, __tuple_types...>>; + +struct __not_fn_op +{ + template + static _LIBCPP_CONSTEXPR_AFTER_CXX17 auto __call(_Args&&... __args) + noexcept(noexcept(!_VSTD::invoke(_VSTD::forward<_Args>(__args)...))) + -> decltype( !_VSTD::invoke(_VSTD::forward<_Args>(__args)...)) + { return !_VSTD::invoke(_VSTD::forward<_Args>(__args)...); } +}; + +template, _Fn> && + is_move_constructible_v<_Fn>>> +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto not_fn(_Fn&& __f) +{ + return __perfect_forward<__not_fn_op, _Fn>(_VSTD::forward<_Fn>(__f)); +} - template - _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - auto operator()(_Args&& ...__args) const&& - noexcept(noexcept(!_VSTD::invoke(_VSTD::move(__fd), _VSTD::forward<_Args>(__args)...))) - -> decltype( !_VSTD::invoke(_VSTD::move(__fd), _VSTD::forward<_Args>(__args)...)) - { return !_VSTD::invoke(_VSTD::move(__fd), _VSTD::forward<_Args>(__args)...); } +#endif // _LIBCPP_STD_VER > 14 -private: - template , __not_fn_imp>::value>> - _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - explicit __not_fn_imp(_RawFunc&& __rf) - : __fd(_VSTD::forward<_RawFunc>(__rf)) {} +#if _LIBCPP_STD_VER > 17 - template - friend inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - __not_fn_imp> not_fn(_RawFunc&&); +struct __bind_front_op +{ + template + constexpr static auto __call(_Args&&... __args) + noexcept(noexcept(_VSTD::invoke(_VSTD::forward<_Args>(__args)...))) + -> decltype( _VSTD::invoke(_VSTD::forward<_Args>(__args)...)) + { return _VSTD::invoke(_VSTD::forward<_Args>(__args)...); } }; -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -__not_fn_imp> not_fn(_RawFunc&& __fn) { - return __not_fn_imp>(_VSTD::forward<_RawFunc>(__fn)); +template, _Fn>, + is_move_constructible>, + is_constructible, _Args>..., + is_move_constructible>... + >::value>> +constexpr auto bind_front(_Fn&& __f, _Args&&... __args) +{ + return __perfect_forward<__bind_front_op, _Fn, _Args...>(_VSTD::forward<_Fn>(__f), + _VSTD::forward<_Args>(__args)...); } -#endif +#endif // _LIBCPP_STD_VER > 17 // struct hash in diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -290,7 +290,7 @@ # if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_barrier) # define __cpp_lib_barrier 201907L # endif -// # define __cpp_lib_bind_front 201907L +# define __cpp_lib_bind_front 201907L // # define __cpp_lib_bit_cast 201806L // # define __cpp_lib_bitops 201907L # define __cpp_lib_bounded_array_traits 201902L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.pass.cpp @@ -174,17 +174,11 @@ #elif TEST_STD_VER == 20 -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should be defined in c++20" -# endif -# if __cpp_lib_bind_front != 201907L -# error "__cpp_lib_bind_front should have the value 201907L in c++20" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_bind_front +# error "__cpp_lib_bind_front should be defined in c++20" +# endif +# if __cpp_lib_bind_front != 201907L +# error "__cpp_lib_bind_front should have the value 201907L in c++20" # endif # if !defined(_LIBCPP_VERSION) @@ -257,17 +251,11 @@ #elif TEST_STD_VER > 20 -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should be defined in c++2b" -# endif -# if __cpp_lib_bind_front != 201907L -# error "__cpp_lib_bind_front should have the value 201907L in c++2b" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_bind_front +# error "__cpp_lib_bind_front should be defined in c++2b" +# endif +# if __cpp_lib_bind_front != 201907L +# error "__cpp_lib_bind_front should have the value 201907L in c++2b" # endif # if !defined(_LIBCPP_VERSION) diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -2268,17 +2268,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should be defined in c++20" -# endif -# if __cpp_lib_bind_front != 201907L -# error "__cpp_lib_bind_front should have the value 201907L in c++20" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_bind_front +# error "__cpp_lib_bind_front should be defined in c++20" +# endif +# if __cpp_lib_bind_front != 201907L +# error "__cpp_lib_bind_front should have the value 201907L in c++20" # endif # if !defined(_LIBCPP_VERSION) @@ -3481,17 +3475,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should be defined in c++2b" -# endif -# if __cpp_lib_bind_front != 201907L -# error "__cpp_lib_bind_front should have the value 201907L in c++2b" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_bind_front -# error "__cpp_lib_bind_front should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_bind_front +# error "__cpp_lib_bind_front should be defined in c++2b" +# endif +# if __cpp_lib_bind_front != 201907L +# error "__cpp_lib_bind_front should have the value 201907L in c++2b" # endif # if !defined(_LIBCPP_VERSION) 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 new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp @@ -0,0 +1,302 @@ +//===----------------------------------------------------------------------===// +// +// 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++98, c++03, c++11, c++14, c++17 + +// functional + +// template constexpr unspecified bind_front(F&&, Args&&...); + +#include + +#include "callable_types.h" +#include "test_macros.h" + +constexpr int add(int a, int b) { return a + b; } + +constexpr int long_test(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; +} + +struct Foo { + int a; + int b; +}; + +struct FooCall { + constexpr Foo operator()(int a, int b) { return Foo{a, b}; } +}; + +struct S { + constexpr bool operator()(int a) { return a == 1; } +}; + +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) {} +}; + +constexpr bool wasCopied(CopyMoveInfo info) { + return info.copy_kind == CopyMoveInfo::copy; +} +constexpr bool wasMoved(CopyMoveInfo info) { + return info.copy_kind == CopyMoveInfo::move; +} + +constexpr void basic_tests() { + int n = 2; + int m = 1; + + auto a = std::bind_front(add, m, n); + assert(a() == 3); + + auto b = std::bind_front(long_test, m, n, m, m, m, m); + assert(b() == 7); + + auto c = std::bind_front(long_test, n, m); + assert(c(1, 1, 1, 1) == 7); + + auto d = std::bind_front(S{}, m); + assert(d()); + + auto f = std::bind_front(add, n); + assert(f(3) == 5); + + auto g = std::bind_front(add, n, 1); + assert(g() == 3); + + auto h = std::bind_front(long_test, 1, 1, 1); + assert(h(2, 2, 2) == 9); + + // Make sure the arg is passed by value. + auto i = std::bind_front(add, n, 1); + n = 100; + assert(i() == 3); + + CopyMoveInfo info; + auto copied = std::bind_front(wasCopied, info); + assert(copied()); + + auto moved = std::bind_front(wasMoved, info); + assert(std::move(moved)()); +} + +struct variadic_fn { + template + constexpr int operator()(Args&&... args) { + return sizeof...(args); + } +}; + +constexpr void test_variadic() { + variadic_fn value; + auto fn = std::bind_front(value, 0, 0, 0); + assert(fn(0, 0, 0) == 6); +} + +struct mutable_callable { + bool should_call_const; + + constexpr bool operator()(int, int) { + assert(!should_call_const); + return true; + } + constexpr bool operator()(int, int) const { + assert(should_call_const); + return true; + } +}; + +constexpr void test_mutable() { + const mutable_callable v1{true}; + const auto fn1 = std::bind_front(v1, 0); + assert(fn1(0)); + + mutable_callable v2{false}; + auto fn2 = std::bind_front(v2, 0); + assert(fn2(0)); +}; + +struct call_member { + constexpr bool member(int, int) { return true; } +}; + +constexpr void test_call_member() { + call_member value; + auto fn = std::bind_front(&call_member::member, value, 0); + assert(fn(0)); +} + +struct no_const_lvalue { + constexpr void operator()(int) && {}; +}; + +constexpr auto make_no_const_lvalue(int x) { + // This is to test that bind_front works when something like the following would not: + // return [nc = no_const_lvalue{}, x] { return nc(x); }; + // Above would not work because it would look for a () const & overload. + return std::bind_front(no_const_lvalue{}, x); +} + +constexpr void test_no_const_lvalue() { make_no_const_lvalue(1)(); } + +constexpr void constructor_tests() { + { + MoveOnlyCallable value(true); + using RetT = decltype(std::bind_front(std::move(value), 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); + + auto ret = std::bind_front(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)); + } + { + CopyCallable value(true); + using RetT = decltype(std::bind_front(value, 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); + + auto ret = std::bind_front(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_front(std::move(value), 1); + assert(!ret()); + assert(ret2()); + assert(ret2(1, 2, 3)); + } + { + CopyAssignableWrapper value(true); + using RetT = decltype(std::bind_front(value, 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); + } + { + MoveAssignableWrapper value(true); + using RetT = decltype(std::bind_front(std::move(value), 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); + } +} + +template +constexpr void test_return(F&& value, Args&&... args) { + auto ret = + std::bind_front(std::forward(value), std::forward(args)...); + static_assert(std::is_same::value); +} + +constexpr void test_return_types() { + test_return(FooCall{}, 1, 2); + test_return(S{}, 1); + test_return(add, 2, 2); +} + +constexpr void test_arg_count() { + using T = decltype(std::bind_front(add, 1)); + static_assert(!std::is_invocable::value); + static_assert(std::is_invocable::value); +} + +template +struct is_bind_frontable { + template + static auto test(int) + -> decltype((void)std::bind_front(std::declval()...), + std::true_type()); + + template + static std::false_type test(...); + + static constexpr bool value = decltype(test(0))::value; +}; + +struct NotCopyMove { + NotCopyMove() = delete; + NotCopyMove(const NotCopyMove&) = delete; + NotCopyMove(NotCopyMove&&) = delete; + void operator()() {} +}; + +struct NonConstCopyConstructible { + explicit NonConstCopyConstructible() {} + NonConstCopyConstructible(NonConstCopyConstructible&) {} +}; + +struct MoveConstructible { + explicit MoveConstructible() {} + MoveConstructible(MoveConstructible&&) {} +}; + +constexpr void test_invocability() { + static_assert(!std::is_constructible_v); + static_assert(!std::is_move_constructible_v); + static_assert(!is_bind_frontable::value); + static_assert(!is_bind_frontable::value); + + static_assert( + !std::is_constructible_v); + static_assert(std::is_move_constructible_v); + static_assert(is_bind_frontable::value); + static_assert( + !is_bind_frontable::value); + + static_assert(std::is_constructible_v); + static_assert(!std::is_move_constructible_v); + static_assert( + !is_bind_frontable::value); + static_assert( + !is_bind_frontable::value); +} + +constexpr bool test() { + basic_tests(); + constructor_tests(); + test_return_types(); + test_arg_count(); + test_variadic(); + test_mutable(); + test_call_member(); + test_no_const_lvalue(); + test_invocability(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} 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 new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// 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++98, c++03, c++11, c++14, c++17 + +// functional + +// template constexpr unspecified bind_front(F&&, Args&&...); + +#include + +#include "test_macros.h" + +constexpr int pass(const int n) { return n; } + +int simple(int n) { return n; } + +template +T do_nothing(T t) { return t; } + +struct NotMoveConst +{ + NotMoveConst(NotMoveConst &&) = delete; + NotMoveConst(NotMoveConst const&) = delete; + + NotMoveConst(int) { } +}; + +void testNotMoveConst(NotMoveConst) { } + +int main(int, char**) +{ + int n = 1; + const int c = 1; + + auto p = std::bind_front(pass, c); + static_assert(p() == 1); // expected-error {{static_assert 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'}} + + return 0; +} diff --git a/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp --- a/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp @@ -17,178 +17,7 @@ #include "test_macros.h" #include "type_id.h" - - -/////////////////////////////////////////////////////////////////////////////// -// CALLABLE TEST TYPES -/////////////////////////////////////////////////////////////////////////////// - -constexpr bool returns_true() { return true; } - -template -struct MoveOnlyCallable { - MoveOnlyCallable(MoveOnlyCallable const&) = delete; - constexpr MoveOnlyCallable(MoveOnlyCallable&& other) - : value(other.value) - { other.value = !other.value; } - - template - constexpr Ret operator()(Args&&...) { return Ret{value}; } - - constexpr explicit MoveOnlyCallable(bool x) : value(x) {} - Ret value; -}; - -template -struct CopyCallable { - constexpr CopyCallable(CopyCallable const& other) - : value(other.value) {} - - constexpr CopyCallable(CopyCallable&& other) - : value(other.value) { other.value = !other.value; } - - template - constexpr Ret operator()(Args&&...) { return Ret{value}; } - - constexpr explicit CopyCallable(bool x) : value(x) {} - Ret value; -}; - - -template -struct ConstCallable { - constexpr ConstCallable(ConstCallable const& other) - : value(other.value) {} - - constexpr ConstCallable(ConstCallable&& other) - : value(other.value) { other.value = !other.value; } - - template - constexpr Ret operator()(Args&&...) const { return Ret{value}; } - - constexpr explicit ConstCallable(bool x) : value(x) {} - Ret value; -}; - - - -template -struct NoExceptCallable { - constexpr NoExceptCallable(NoExceptCallable const& other) - : value(other.value) {} - - template - constexpr Ret operator()(Args&&...) noexcept { return Ret{value}; } - - template - constexpr Ret operator()(Args&&...) const noexcept { return Ret{value}; } - - constexpr explicit NoExceptCallable(bool x) : value(x) {} - Ret value; -}; - -struct CopyAssignableWrapper { - constexpr CopyAssignableWrapper(CopyAssignableWrapper const&) = default; - constexpr CopyAssignableWrapper(CopyAssignableWrapper&&) = default; - constexpr CopyAssignableWrapper& operator=(CopyAssignableWrapper const&) = default; - constexpr CopyAssignableWrapper& operator=(CopyAssignableWrapper &&) = default; - - template - constexpr bool operator()(Args&&...) { return value; } - - constexpr explicit CopyAssignableWrapper(bool x) : value(x) {} - bool value; -}; - - -struct MoveAssignableWrapper { - constexpr MoveAssignableWrapper(MoveAssignableWrapper const&) = delete; - constexpr MoveAssignableWrapper(MoveAssignableWrapper&&) = default; - constexpr MoveAssignableWrapper& operator=(MoveAssignableWrapper const&) = delete; - constexpr MoveAssignableWrapper& operator=(MoveAssignableWrapper &&) = default; - - template - constexpr bool operator()(Args&&...) { return value; } - - constexpr explicit MoveAssignableWrapper(bool x) : value(x) {} - bool value; -}; - -struct MemFunCallable { - constexpr explicit MemFunCallable(bool x) : value(x) {} - - constexpr bool return_value() const { return value; } - constexpr bool return_value_nc() { return value; } - bool value; -}; - -enum CallType : unsigned { - CT_None, - CT_NonConst = 1, - CT_Const = 2, - CT_LValue = 4, - CT_RValue = 8 -}; - -inline constexpr CallType operator|(CallType LHS, CallType RHS) { - return static_cast(static_cast(LHS) | static_cast(RHS)); -} - -struct ForwardingCallObject { - struct State { - CallType last_call_type = CT_None; - TypeID const& (*last_call_args)() = nullptr; - - template - constexpr void set_call(CallType type) { - assert(last_call_type == CT_None); - assert(last_call_args == nullptr); - last_call_type = type; - last_call_args = &makeArgumentID; - } - - template - constexpr bool check_call(CallType type) { - bool result = - last_call_type == type - && last_call_args - && *last_call_args == &makeArgumentID; - last_call_type = CT_None; - last_call_args = nullptr; - return result; - } - }; - - State *st_; - - explicit constexpr ForwardingCallObject(State& st) : st_(&st) {} - - template - constexpr bool operator()(Args&&...) & { - st_->set_call(CT_NonConst | CT_LValue); - return true; - } - - template - constexpr bool operator()(Args&&...) const & { - st_->set_call(CT_Const | CT_LValue); - return true; - } - - // Don't allow the call operator to be invoked as an rvalue. - template - constexpr bool operator()(Args&&...) && { - st_->set_call(CT_NonConst | CT_RValue); - return true; - } - - template - constexpr bool operator()(Args&&...) const && { - st_->set_call(CT_Const | CT_RValue); - return true; - } -}; - +#include "callable_types.h" /////////////////////////////////////////////////////////////////////////////// // BOOL TEST TYPES @@ -298,6 +127,10 @@ assert(ret() == false); assert(ret2() == true); assert(ret2("abc") == true); + // initialize not_fn with rvalue + auto ret3 = std::not_fn(std::move(value)); + assert(ret(0) == false); + assert(ret3(0) == true); } { using T = CopyAssignableWrapper; @@ -306,17 +139,10 @@ using RetT = decltype(std::not_fn(value)); static_assert(std::is_move_constructible::value, ""); static_assert(std::is_copy_constructible::value, ""); - LIBCPP_STATIC_ASSERT(std::is_move_assignable::value, ""); - LIBCPP_STATIC_ASSERT(std::is_copy_assignable::value, ""); auto ret = std::not_fn(value); assert(ret() == false); auto ret2 = std::not_fn(value2); assert(ret2() == true); -#if defined(_LIBCPP_VERSION) - ret = ret2; - assert(ret() == true); - assert(ret2() == true); -#endif // _LIBCPP_VERSION } { using T = MoveAssignableWrapper; @@ -325,16 +151,11 @@ using RetT = decltype(std::not_fn(std::move(value))); static_assert(std::is_move_constructible::value, ""); static_assert(!std::is_copy_constructible::value, ""); - LIBCPP_STATIC_ASSERT(std::is_move_assignable::value, ""); static_assert(!std::is_copy_assignable::value, ""); auto ret = std::not_fn(std::move(value)); assert(ret() == false); auto ret2 = std::not_fn(std::move(value2)); assert(ret2() == true); -#if defined(_LIBCPP_VERSION) - ret = std::move(ret2); - assert(ret() == true); -#endif // _LIBCPP_VERSION } return true; } diff --git a/libcxx/test/support/callable_types.h b/libcxx/test/support/callable_types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/callable_types.h @@ -0,0 +1,186 @@ +//===----------------------------------------------------------------------===// +// +// 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_CALLABLE_TYPES_H +#define TEST_CALLABLE_TYPES_H + +#include "test_macros.h" +#include "type_id.h" + +/////////////////////////////////////////////////////////////////////////////// +// CALLABLE TEST TYPES +/////////////////////////////////////////////////////////////////////////////// + +constexpr bool returns_true() { return true; } + +template +struct MoveOnlyCallable { + MoveOnlyCallable(MoveOnlyCallable const&) = delete; + constexpr MoveOnlyCallable(MoveOnlyCallable&& other) + : value(other.value) + { other.value = !other.value; } + + template + constexpr Ret operator()(Args&&...) { return Ret{value}; } + + constexpr explicit MoveOnlyCallable(bool x) : value(x) {} + Ret value; +}; + +template +struct CopyCallable { + constexpr CopyCallable(CopyCallable const& other) + : value(other.value) {} + + constexpr CopyCallable(CopyCallable&& other) + : value(other.value) { other.value = !other.value; } + + template + constexpr Ret operator()(Args&&...) { return Ret{value}; } + + constexpr explicit CopyCallable(bool x) : value(x) {} + Ret value; +}; + + +template +struct ConstCallable { + constexpr ConstCallable(ConstCallable const& other) + : value(other.value) {} + + constexpr ConstCallable(ConstCallable&& other) + : value(other.value) { other.value = !other.value; } + + template + constexpr Ret operator()(Args&&...) const { return Ret{value}; } + + constexpr explicit ConstCallable(bool x) : value(x) {} + Ret value; +}; + + + +template +struct NoExceptCallable { + constexpr NoExceptCallable(NoExceptCallable const& other) + : value(other.value) {} + + template + constexpr Ret operator()(Args&&...) noexcept { return Ret{value}; } + + template + constexpr Ret operator()(Args&&...) const noexcept { return Ret{value}; } + + constexpr explicit NoExceptCallable(bool x) : value(x) {} + Ret value; +}; + +struct CopyAssignableWrapper { + constexpr CopyAssignableWrapper(CopyAssignableWrapper const&) = default; + constexpr CopyAssignableWrapper(CopyAssignableWrapper&&) = default; + constexpr CopyAssignableWrapper& operator=(CopyAssignableWrapper const&) = default; + constexpr CopyAssignableWrapper& operator=(CopyAssignableWrapper &&) = default; + + template + constexpr bool operator()(Args&&...) { return value; } + + constexpr explicit CopyAssignableWrapper(bool x) : value(x) {} + bool value; +}; + + +struct MoveAssignableWrapper { + constexpr MoveAssignableWrapper(MoveAssignableWrapper const&) = delete; + constexpr MoveAssignableWrapper(MoveAssignableWrapper&&) = default; + constexpr MoveAssignableWrapper& operator=(MoveAssignableWrapper const&) = delete; + constexpr MoveAssignableWrapper& operator=(MoveAssignableWrapper &&) = default; + + template + constexpr bool operator()(Args&&...) { return value; } + + constexpr explicit MoveAssignableWrapper(bool x) : value(x) {} + bool value; +}; + +struct MemFunCallable { + constexpr explicit MemFunCallable(bool x) : value(x) {} + + constexpr bool return_value() const { return value; } + constexpr bool return_value_nc() { return value; } + bool value; +}; + +enum CallType : unsigned { + CT_None, + CT_NonConst = 1, + CT_Const = 2, + CT_LValue = 4, + CT_RValue = 8 +}; + +inline constexpr CallType operator|(CallType LHS, CallType RHS) { + return static_cast(static_cast(LHS) | static_cast(RHS)); +} + +struct ForwardingCallObject { + struct State { + CallType last_call_type = CT_None; + TypeID const& (*last_call_args)() = nullptr; + + template + constexpr void set_call(CallType type) { + assert(last_call_type == CT_None); + assert(last_call_args == nullptr); + last_call_type = type; + last_call_args = &makeArgumentID; + } + + template + constexpr bool check_call(CallType type) { + bool result = + last_call_type == type + && last_call_args + && *last_call_args == &makeArgumentID; + last_call_type = CT_None; + last_call_args = nullptr; + return result; + } + }; + + State *st_; + + explicit constexpr ForwardingCallObject(State& st) : st_(&st) {} + + template + constexpr bool operator()(Args&&...) & { + st_->set_call(CT_NonConst | CT_LValue); + return true; + } + + template + constexpr bool operator()(Args&&...) const & { + st_->set_call(CT_Const | CT_LValue); + return true; + } + + // Don't allow the call operator to be invoked as an rvalue. + template + constexpr bool operator()(Args&&...) && { + st_->set_call(CT_NonConst | CT_RValue); + return true; + } + + template + constexpr bool operator()(Args&&...) const && { + st_->set_call(CT_Const | CT_RValue); + return true; + } +}; + + +#endif // TEST_CALLABLE_TYPES_H diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -123,7 +123,6 @@ "name": "__cpp_lib_bind_front", "values": { "c++20": 201907 }, "headers": ["functional"], - "unimplemented": True, }, { "name": "__cpp_lib_bit_cast", "values": { "c++20": 201806 },