diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -109,7 +109,7 @@ "`P0980 `__","LWG","Making std::string constexpr","Cologne","","" "`P1004 `__","LWG","Making std::vector constexpr","Cologne","","" "`P1035 `__","LWG","Input Range Adaptors","Cologne","","" -"`P1065 `__","LWG","Constexpr INVOKE","Cologne","","" +"`P1065 `__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0" "`P1135 `__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0" "`P1207 `__","LWG","Movability of Single-pass Iterators","Cologne","","" "`P1208 `__","LWG","Adopt source_location for C++20","Cologne","","" diff --git a/libcxx/include/__functional_base b/libcxx/include/__functional_base --- a/libcxx/include/__functional_base +++ b/libcxx/include/__functional_base @@ -382,20 +382,23 @@ public: // construct/copy/destroy - _LIBCPP_INLINE_VISIBILITY reference_wrapper(type& __f) _NOEXCEPT + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 + reference_wrapper(type& __f) _NOEXCEPT : __f_(_VSTD::addressof(__f)) {} #ifndef _LIBCPP_CXX03_LANG private: reference_wrapper(type&&); public: // = delete; // do not bind to temps #endif // access - _LIBCPP_INLINE_VISIBILITY operator type& () const _NOEXCEPT {return *__f_;} - _LIBCPP_INLINE_VISIBILITY type& get() const _NOEXCEPT {return *__f_;} + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 + operator type& () const _NOEXCEPT {return *__f_;} + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 + type& get() const _NOEXCEPT {return *__f_;} #ifndef _LIBCPP_CXX03_LANG // invoke template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 typename __invoke_of::type operator() (_ArgTypes&&... __args) const { return _VSTD::__invoke(get(), _VSTD::forward<_ArgTypes>(__args)...); @@ -510,7 +513,7 @@ template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 reference_wrapper<_Tp> ref(_Tp& __t) _NOEXCEPT { @@ -518,7 +521,7 @@ } template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 reference_wrapper<_Tp> ref(reference_wrapper<_Tp> __t) _NOEXCEPT { @@ -526,7 +529,7 @@ } template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 reference_wrapper cref(const _Tp& __t) _NOEXCEPT { @@ -534,7 +537,7 @@ } template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 reference_wrapper cref(reference_wrapper<_Tp> __t) _NOEXCEPT { diff --git a/libcxx/include/functional b/libcxx/include/functional --- a/libcxx/include/functional +++ b/libcxx/include/functional @@ -213,7 +213,8 @@ template // deprecated in C++17 binary_negate not2(const Predicate& pred); -template unspecified not_fn(F&& f); // C++17 +template +constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20 template struct is_bind_expression; template struct is_placeholder; @@ -226,11 +227,12 @@ template - unspecified bind(Fn&&, BoundArgs&&...); + constexpr unspecified bind(Fn&&, BoundArgs&&...); // constexpr in C++20 template - unspecified bind(Fn&&, BoundArgs&&...); + constexpr unspecified bind(Fn&&, BoundArgs&&...); // constexpr in C++20 template + constexpr // constexpr in C++20 invoke_result_t invoke(F&& f, Args&&... args) // C++17 noexcept(is_nothrow_invocable_v); @@ -376,7 +378,8 @@ template const_mem_fun_ref_t mem_fun_ref(S (T::*f)() const); // deprecated in C++11, removed in C++17 template const_mem_fun1_ref_t mem_fun_ref(S (T::*f)(A) const); // deprecated in C++11, removed in C++17 -template unspecified mem_fn(R T::*); +template +constexpr unspecified mem_fn(R T::*); // constexpr in C++20 class bad_function_call : public exception @@ -1288,12 +1291,13 @@ type __f_; public: - _LIBCPP_INLINE_VISIBILITY __mem_fn(type __f) _NOEXCEPT : __f_(__f) {} + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 + __mem_fn(type __f) _NOEXCEPT : __f_(__f) {} #ifndef _LIBCPP_CXX03_LANG // invoke template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 typename __invoke_return::type operator() (_ArgTypes&&... __args) const { return _VSTD::__invoke(__f_, _VSTD::forward<_ArgTypes>(__args)...); @@ -1401,7 +1405,7 @@ }; template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 __mem_fn<_Rp _Tp::*> mem_fn(_Rp _Tp::* __pm) _NOEXCEPT { @@ -2873,13 +2877,13 @@ !is_same::type, __bind>::value >::type> - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 explicit __bind(_Gp&& __f, _BA&& ...__bound_args) : __f_(_VSTD::forward<_Gp>(__f)), __bound_args_(_VSTD::forward<_BA>(__bound_args)...) {} template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 typename __bind_return<_Fd, _Td, tuple<_Args&&...> >::type operator()(_Args&& ...__args) { @@ -2888,7 +2892,7 @@ } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 typename __bind_return >::type operator()(_Args&& ...__args) const { @@ -2918,13 +2922,13 @@ !is_same::type, __bind_r>::value >::type> - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 explicit __bind_r(_Gp&& __f, _BA&& ...__bound_args) : base(_VSTD::forward<_Gp>(__f), _VSTD::forward<_BA>(__bound_args)...) {} template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 typename enable_if < is_convertible >::type, @@ -2938,7 +2942,7 @@ } template - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 typename enable_if < is_convertible >::type, @@ -2956,7 +2960,7 @@ struct __is_bind_expression<__bind_r<_Rp, _Fp, _BoundArgs...> > : public true_type {}; template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 __bind<_Fp, _BoundArgs...> bind(_Fp&& __f, _BoundArgs&&... __bound_args) { @@ -2965,7 +2969,7 @@ } template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 __bind_r<_Rp, _Fp, _BoundArgs...> bind(_Fp&& __f, _BoundArgs&&... __bound_args) { @@ -2978,7 +2982,7 @@ #if _LIBCPP_STD_VER > 14 template -invoke_result_t<_Fn, _Args...> +_LIBCPP_CONSTEXPR_AFTER_CXX17 invoke_result_t<_Fn, _Args...> invoke(_Fn&& __f, _Args&&... __args) noexcept(is_nothrow_invocable_v<_Fn, _Args...>) { @@ -2993,21 +2997,21 @@ __not_fn_imp() = delete; template - _LIBCPP_INLINE_VISIBILITY + _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_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_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)...)) @@ -3015,7 +3019,7 @@ template - _LIBCPP_INLINE_VISIBILITY + _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)...)) @@ -3024,17 +3028,17 @@ private: template , __not_fn_imp>::value>> - _LIBCPP_INLINE_VISIBILITY + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 explicit __not_fn_imp(_RawFunc&& __rf) : __fd(_VSTD::forward<_RawFunc>(__rf)) {} template - friend inline _LIBCPP_INLINE_VISIBILITY + friend inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 __not_fn_imp> not_fn(_RawFunc&&); }; template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 __not_fn_imp> not_fn(_RawFunc&& __fn) { return __not_fn_imp>(_VSTD::forward<_RawFunc>(__fn)); } diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -3654,7 +3654,7 @@ template > inline _LIBCPP_INLINE_VISIBILITY -auto +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) _LIBCPP_INVOKE_RETURN((_VSTD::forward<_A0>(__a0).*__f)(_VSTD::forward<_Args>(__args)...)) @@ -3668,7 +3668,7 @@ template > inline _LIBCPP_INLINE_VISIBILITY -auto +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) _LIBCPP_INVOKE_RETURN((__a0.get().*__f)(_VSTD::forward<_Args>(__args)...)) @@ -3682,7 +3682,7 @@ template > inline _LIBCPP_INLINE_VISIBILITY -auto +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) _LIBCPP_INVOKE_RETURN(((*_VSTD::forward<_A0>(__a0)).*__f)(_VSTD::forward<_Args>(__args)...)) @@ -3698,7 +3698,7 @@ template > inline _LIBCPP_INLINE_VISIBILITY -auto +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto __invoke(_Fp&& __f, _A0&& __a0) _LIBCPP_INVOKE_RETURN(_VSTD::forward<_A0>(__a0).*__f) @@ -3712,7 +3712,7 @@ template > inline _LIBCPP_INLINE_VISIBILITY -auto +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto __invoke(_Fp&& __f, _A0&& __a0) _LIBCPP_INVOKE_RETURN(__a0.get().*__f) @@ -3726,7 +3726,7 @@ template > inline _LIBCPP_INLINE_VISIBILITY -auto +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto __invoke(_Fp&& __f, _A0&& __a0) _LIBCPP_INVOKE_RETURN((*_VSTD::forward<_A0>(__a0)).*__f) @@ -3741,7 +3741,7 @@ template inline _LIBCPP_INLINE_VISIBILITY -auto +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto __invoke(_Fp&& __f, _Args&& ...__args) _LIBCPP_INVOKE_RETURN(_VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...)) diff --git a/libcxx/test/std/utilities/function.objects/func.invoke/invoke_constexpr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.invoke/invoke_constexpr.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/func.invoke/invoke_constexpr.pass.cpp @@ -0,0 +1,275 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// constexpr // constexpr in C++20 +// invoke_result_t invoke(F&& f, Args&&... args) +// noexcept(is_nothrow_invocable_v<_Fn, _Args...>); + +/// C++14 [func.def] 20.9.0 +/// (1) The following definitions apply to this Clause: +/// (2) A call signature is the name of a return type followed by a parenthesized +/// comma-separated list of zero or more argument types. +/// (3) A callable type is a function object type (20.9) or a pointer to member. +/// (4) A callable object is an object of a callable type. +/// (5) A call wrapper type is a type that holds a callable object and supports +/// a call operation that forwards to that object. +/// (6) A call wrapper is an object of a call wrapper type. +/// (7) A target object is the callable object held by a call wrapper. + +/// C++14 [func.require] 20.9.1 +/// +/// Define INVOKE (f, t1, t2, ..., tN) as follows: +/// (1.1) - (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of +/// type T or a reference to an object of type T or a reference to an object of a type derived from T; +/// (1.2) - ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of +/// the types described in the previous item; +/// (1.3) - t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a +/// reference to an object of type T or a reference to an object of a type derived from T; +/// (1.4) - (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 is not one of the types +/// described in the previous item; +/// (1.5) - f(t1, t2, ..., tN) in all other cases. + +#include +#include +#include // for std::move +#include + +#include "test_macros.h" + +struct NonCopyable { + constexpr NonCopyable() {} +private: + NonCopyable(NonCopyable const&) = delete; + NonCopyable& operator=(NonCopyable const&) = delete; +}; + +struct TestClass { + constexpr explicit TestClass(int x) : data(x) {} + + constexpr int& operator()(NonCopyable&&) & { return data; } + constexpr int const& operator()(NonCopyable&&) const & { return data; } + + constexpr int&& operator()(NonCopyable&&) && { return std::move(data); } + constexpr int const&& operator()(NonCopyable&&) const && { return std::move(data); } + + int data; +private: + TestClass(TestClass const&) = delete; + TestClass& operator=(TestClass const&) = delete; +}; + +struct DerivedFromTestClass : public TestClass { + constexpr explicit DerivedFromTestClass(int x) : TestClass(x) {} +}; + +static constexpr int data = 42; +constexpr const int& foo(NonCopyable&&) { + return data; +} + +template +constexpr void test_b12(Functor&& f) { + // Create the callable object. + typedef Signature TestClass::*ClassFunc; + ClassFunc func_ptr = &TestClass::operator(); + + // Create the dummy arg. + NonCopyable arg; + + // Check that the deduced return type of invoke is what is expected. + typedef decltype( + std::invoke(func_ptr, std::forward(f), std::move(arg)) + ) DeducedReturnType; + static_assert((std::is_same::value), ""); + + // Check that result_of_t matches Expect. + typedef typename std::result_of::type + ResultOfReturnType; + static_assert((std::is_same::value), ""); + + // Run invoke and check the return value. + DeducedReturnType ret = + std::invoke(func_ptr, std::forward(f), std::move(arg)); + assert(ret == 42); +} + +template +constexpr void test_b34(Functor&& f) { + // Create the callable object. + typedef int TestClass::*ClassFunc; + ClassFunc func_ptr = &TestClass::data; + + // Check that the deduced return type of invoke is what is expected. + typedef decltype( + std::invoke(func_ptr, std::forward(f)) + ) DeducedReturnType; + static_assert((std::is_same::value), ""); + + // Check that result_of_t matches Expect. + typedef typename std::result_of::type + ResultOfReturnType; + static_assert((std::is_same::value), ""); + + // Run invoke and check the return value. + DeducedReturnType ret = + std::invoke(func_ptr, std::forward(f)); + assert(ret == 42); +} + +template +constexpr void test_b5(Functor&& f) { + NonCopyable arg; + + // Check that the deduced return type of invoke is what is expected. + typedef decltype( + std::invoke(std::forward(f), std::move(arg)) + ) DeducedReturnType; + static_assert((std::is_same::value), ""); + + // Check that result_of_t matches Expect. + typedef typename std::result_of::type + ResultOfReturnType; + static_assert((std::is_same::value), ""); + + // Run invoke and check the return value. + DeducedReturnType ret = std::invoke(std::forward(f), std::move(arg)); + assert(ret == 42); +} + +constexpr bool bullet_one_two_tests() { + { + TestClass cl(42); + test_b12(cl); + test_b12(cl); + + test_b12(std::move(cl)); + test_b12(std::move(cl)); + } + { + DerivedFromTestClass cl(42); + test_b12(cl); + test_b12(cl); + + test_b12(std::move(cl)); + test_b12(std::move(cl)); + } + { + TestClass cl_obj(42); + std::reference_wrapper cl(cl_obj); + test_b12(cl); + test_b12(cl); + + test_b12(std::move(cl)); + test_b12(std::move(cl)); + } + { + DerivedFromTestClass cl_obj(42); + std::reference_wrapper cl(cl_obj); + test_b12(cl); + test_b12(cl); + + test_b12(std::move(cl)); + test_b12(std::move(cl)); + } + { + TestClass cl_obj(42); + TestClass *cl = &cl_obj; + test_b12(cl); + test_b12(cl); + } + { + DerivedFromTestClass cl_obj(42); + DerivedFromTestClass *cl = &cl_obj; + test_b12(cl); + test_b12(cl); + } + return true; +} + +constexpr bool bullet_three_four_tests() { + { + typedef TestClass Fn; + Fn cl(42); + test_b34(cl); + test_b34(static_cast(cl)); + + test_b34(static_cast(cl)); + test_b34(static_cast(cl)); + } + { + typedef DerivedFromTestClass Fn; + Fn cl(42); + test_b34(cl); + test_b34(static_cast(cl)); + + test_b34(static_cast(cl)); + test_b34(static_cast(cl)); + } + { + typedef TestClass Fn; + Fn cl(42); + test_b34(std::reference_wrapper(cl)); + test_b34(std::reference_wrapper(cl)); + } + { + typedef DerivedFromTestClass Fn; + Fn cl(42); + test_b34(std::reference_wrapper(cl)); + test_b34(std::reference_wrapper(cl)); + } + { + typedef TestClass Fn; + Fn cl_obj(42); + Fn* cl = &cl_obj; + test_b34(cl); + test_b34(static_cast(cl)); + } + { + typedef DerivedFromTestClass Fn; + Fn cl_obj(42); + Fn* cl = &cl_obj; + test_b34(cl); + test_b34(static_cast(cl)); + } + return true; +} + +constexpr bool bullet_five_tests() { + using FooType = const int&(NonCopyable&&); + { + FooType& fn = foo; + test_b5(fn); + } + { + FooType* fn = foo; + test_b5(fn); + } + { + typedef TestClass Fn; + Fn cl(42); + test_b5(cl); + test_b5(static_cast(cl)); + + test_b5(static_cast(cl)); + test_b5(static_cast(cl)); + } + return true; +} + +int main(int, char**) { + static_assert(bullet_one_two_tests()); + static_assert(bullet_three_four_tests()); + static_assert(bullet_five_tests()); + + return 0; +} diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp --- a/libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.memfn/member_data.pass.cpp @@ -21,7 +21,7 @@ }; template -void +TEST_CONSTEXPR_CXX20 bool test(F f) { { @@ -36,11 +36,16 @@ const F& cf = f; assert(cf(ap) == f(ap)); } + return true; } int main(int, char**) { test(std::mem_fn(&A::data_)); - return 0; +#if TEST_STD_VER >= 20 + static_assert(test(std::mem_fn(&A::data_))); +#endif + + return 0; } diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp --- a/libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.memfn/member_function.pass.cpp @@ -18,13 +18,13 @@ struct A { - char test0() {return 'a';} - char test1(int) {return 'b';} - char test2(int, double) {return 'c';} + TEST_CONSTEXPR char test0() {return 'a';} + TEST_CONSTEXPR char test1(int) {return 'b';} + TEST_CONSTEXPR char test2(int, double) {return 'c';} }; template -void +TEST_CONSTEXPR_CXX20 bool test0(F f) { { @@ -35,10 +35,11 @@ const F& cf = f; assert(cf(ap) == 'a'); } + return true; } template -void +TEST_CONSTEXPR_CXX20 bool test1(F f) { { @@ -49,10 +50,11 @@ const F& cf = f; assert(cf(ap, 2) == 'b'); } + return true; } template -void +TEST_CONSTEXPR_CXX20 bool test2(F f) { { @@ -63,6 +65,7 @@ const F& cf = f; assert(cf(ap, 2, 3.5) == 'c'); } + return true; } int main(int, char**) @@ -74,5 +77,11 @@ static_assert((noexcept(std::mem_fn(&A::test0))), ""); // LWG#2489 #endif - return 0; +#if TEST_STD_VER >= 20 + static_assert(test0(std::mem_fn(&A::test0))); + static_assert(test1(std::mem_fn(&A::test1))); + static_assert(test2(std::mem_fn(&A::test2))); +#endif + + return 0; } diff --git a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp --- a/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.memfn/member_function_const.pass.cpp @@ -18,13 +18,13 @@ struct A { - char test0() const {return 'a';} - char test1(int) const {return 'b';} - char test2(int, double) const {return 'c';} + TEST_CONSTEXPR char test0() const {return 'a';} + TEST_CONSTEXPR char test1(int) const {return 'b';} + TEST_CONSTEXPR char test2(int, double) const {return 'c';} }; template -void +TEST_CONSTEXPR_CXX20 bool test0(F f) { { @@ -37,10 +37,11 @@ const F& cf = f; assert(cf(ap) == 'a'); } + return true; } template -void +TEST_CONSTEXPR_CXX20 bool test1(F f) { { @@ -53,10 +54,11 @@ const F& cf = f; assert(cf(ap, 2) == 'b'); } + return true; } template -void +TEST_CONSTEXPR_CXX20 bool test2(F f) { { @@ -69,6 +71,7 @@ const F& cf = f; assert(cf(ap, 2, 3.5) == 'c'); } + return true; } int main(int, char**) @@ -77,5 +80,11 @@ test1(std::mem_fn(&A::test1)); test2(std::mem_fn(&A::test2)); - return 0; +#if TEST_STD_VER >= 20 + static_assert(test0(std::mem_fn(&A::test0))); + static_assert(test1(std::mem_fn(&A::test1))); + static_assert(test2(std::mem_fn(&A::test2))); +#endif + + 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 @@ -23,50 +23,50 @@ // CALLABLE TEST TYPES /////////////////////////////////////////////////////////////////////////////// -bool returns_true() { return true; } +constexpr bool returns_true() { return true; } template struct MoveOnlyCallable { MoveOnlyCallable(MoveOnlyCallable const&) = delete; - MoveOnlyCallable(MoveOnlyCallable&& other) + constexpr MoveOnlyCallable(MoveOnlyCallable&& other) : value(other.value) { other.value = !other.value; } template - Ret operator()(Args&&...) { return Ret{value}; } + constexpr Ret operator()(Args&&...) { return Ret{value}; } - explicit MoveOnlyCallable(bool x) : value(x) {} + constexpr explicit MoveOnlyCallable(bool x) : value(x) {} Ret value; }; template struct CopyCallable { - CopyCallable(CopyCallable const& other) + constexpr CopyCallable(CopyCallable const& other) : value(other.value) {} - CopyCallable(CopyCallable&& other) + constexpr CopyCallable(CopyCallable&& other) : value(other.value) { other.value = !other.value; } template - Ret operator()(Args&&...) { return Ret{value}; } + constexpr Ret operator()(Args&&...) { return Ret{value}; } - explicit CopyCallable(bool x) : value(x) {} + constexpr explicit CopyCallable(bool x) : value(x) {} Ret value; }; template struct ConstCallable { - ConstCallable(ConstCallable const& other) + constexpr ConstCallable(ConstCallable const& other) : value(other.value) {} - ConstCallable(ConstCallable&& other) + constexpr ConstCallable(ConstCallable&& other) : value(other.value) { other.value = !other.value; } template - Ret operator()(Args&&...) const { return Ret{value}; } + constexpr Ret operator()(Args&&...) const { return Ret{value}; } - explicit ConstCallable(bool x) : value(x) {} + constexpr explicit ConstCallable(bool x) : value(x) {} Ret value; }; @@ -74,51 +74,51 @@ template struct NoExceptCallable { - NoExceptCallable(NoExceptCallable const& other) + constexpr NoExceptCallable(NoExceptCallable const& other) : value(other.value) {} template - Ret operator()(Args&&...) noexcept { return Ret{value}; } + constexpr Ret operator()(Args&&...) noexcept { return Ret{value}; } template - Ret operator()(Args&&...) const noexcept { return Ret{value}; } + constexpr Ret operator()(Args&&...) const noexcept { return Ret{value}; } - explicit NoExceptCallable(bool x) : value(x) {} + constexpr explicit NoExceptCallable(bool x) : value(x) {} Ret value; }; struct CopyAssignableWrapper { - CopyAssignableWrapper(CopyAssignableWrapper const&) = default; - CopyAssignableWrapper(CopyAssignableWrapper&&) = default; - CopyAssignableWrapper& operator=(CopyAssignableWrapper const&) = default; - CopyAssignableWrapper& operator=(CopyAssignableWrapper &&) = default; + constexpr CopyAssignableWrapper(CopyAssignableWrapper const&) = default; + constexpr CopyAssignableWrapper(CopyAssignableWrapper&&) = default; + constexpr CopyAssignableWrapper& operator=(CopyAssignableWrapper const&) = default; + constexpr CopyAssignableWrapper& operator=(CopyAssignableWrapper &&) = default; template - bool operator()(Args&&...) { return value; } + constexpr bool operator()(Args&&...) { return value; } - explicit CopyAssignableWrapper(bool x) : value(x) {} + constexpr explicit CopyAssignableWrapper(bool x) : value(x) {} bool value; }; struct MoveAssignableWrapper { - MoveAssignableWrapper(MoveAssignableWrapper const&) = delete; - MoveAssignableWrapper(MoveAssignableWrapper&&) = default; - MoveAssignableWrapper& operator=(MoveAssignableWrapper const&) = delete; - MoveAssignableWrapper& operator=(MoveAssignableWrapper &&) = default; + constexpr MoveAssignableWrapper(MoveAssignableWrapper const&) = delete; + constexpr MoveAssignableWrapper(MoveAssignableWrapper&&) = default; + constexpr MoveAssignableWrapper& operator=(MoveAssignableWrapper const&) = delete; + constexpr MoveAssignableWrapper& operator=(MoveAssignableWrapper &&) = default; template - bool operator()(Args&&...) { return value; } + constexpr bool operator()(Args&&...) { return value; } - explicit MoveAssignableWrapper(bool x) : value(x) {} + constexpr explicit MoveAssignableWrapper(bool x) : value(x) {} bool value; }; struct MemFunCallable { - explicit MemFunCallable(bool x) : value(x) {} + constexpr explicit MemFunCallable(bool x) : value(x) {} - bool return_value() const { return value; } - bool return_value_nc() { return value; } + constexpr bool return_value() const { return value; } + constexpr bool return_value_nc() { return value; } bool value; }; @@ -135,59 +135,60 @@ } 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 - bool operator()(Args&&...) & { - set_call(CT_NonConst | CT_LValue); + constexpr bool operator()(Args&&...) & { + st_->set_call(CT_NonConst | CT_LValue); return true; } template - bool operator()(Args&&...) const & { - set_call(CT_Const | CT_LValue); + 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 - bool operator()(Args&&...) && { - set_call(CT_NonConst | CT_RValue); + constexpr bool operator()(Args&&...) && { + st_->set_call(CT_NonConst | CT_RValue); return true; } template - bool operator()(Args&&...) const && { - set_call(CT_Const | CT_RValue); + constexpr bool operator()(Args&&...) const && { + st_->set_call(CT_Const | CT_RValue); return true; } - - template - static 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 - static 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; - } - - static CallType last_call_type; - static TypeID const* last_call_args; }; -CallType ForwardingCallObject::last_call_type = CT_None; -TypeID const* ForwardingCallObject::last_call_args = nullptr; - - /////////////////////////////////////////////////////////////////////////////// // BOOL TEST TYPES @@ -209,7 +210,7 @@ friend struct CopyCallable; friend struct NoExceptCallable; - explicit EvilBool(bool x) : value(x) {} + constexpr explicit EvilBool(bool x) : value(x) {} EvilBool& operator=(EvilBool const& other) = default; public: @@ -222,14 +223,14 @@ ExplicitBool(ExplicitBool const&) = default; ExplicitBool(ExplicitBool&&) = default; - explicit operator bool() const { return value; } + constexpr explicit operator bool() const { return value; } private: friend struct MoveOnlyCallable; friend struct CopyCallable; - explicit ExplicitBool(bool x) : value(x) {} - ExplicitBool& operator=(bool x) { + constexpr explicit ExplicitBool(bool x) : value(x) {} + constexpr ExplicitBool& operator=(bool x) { value = x; return *this; } @@ -243,7 +244,7 @@ NoExceptEvilBool(NoExceptEvilBool&&) = default; NoExceptEvilBool& operator=(NoExceptEvilBool const& other) = default; - explicit NoExceptEvilBool(bool x) : value(x) {} + constexpr explicit NoExceptEvilBool(bool x) : value(x) {} friend NoExceptEvilBool operator!(NoExceptEvilBool const& other) noexcept { return NoExceptEvilBool{!other.value}; @@ -254,7 +255,8 @@ -void constructor_tests() +TEST_CONSTEXPR_CXX20 +bool constructor_tests() { { using T = MoveOnlyCallable; @@ -334,6 +336,7 @@ assert(ret() == true); #endif // _LIBCPP_VERSION } + return true; } void return_type_tests() @@ -368,7 +371,8 @@ // Other tests only test using objects with call operators. Test various // other callable types here. -void other_callable_types_test() +TEST_CONSTEXPR_CXX20 +bool other_callable_types_test() { { // test with function pointer auto ret = std::not_fn(returns_true); @@ -407,6 +411,7 @@ assert(ret(&mt) == false); assert(ret(&mf) == true); } + return true; } void throws_in_constructor_test() @@ -438,7 +443,8 @@ #endif } -void call_operator_sfinae_test() { +TEST_CONSTEXPR_CXX20 +bool call_operator_sfinae_test() { { // wrong number of arguments using T = decltype(std::not_fn(returns_true)); static_assert(std::is_invocable::value, ""); // callable only with no args @@ -462,94 +468,100 @@ static_assert(std::is_invocable::value, ""); static_assert(!std::is_invocable::value, ""); } + return true; } -void call_operator_forwarding_test() +TEST_CONSTEXPR_CXX20 +bool call_operator_forwarding_test() { using Fn = ForwardingCallObject; - auto obj = std::not_fn(Fn{}); + Fn::State st; + auto obj = std::not_fn(Fn{st}); const auto& c_obj = obj; { // test zero args obj(); - assert(Fn::check_call<>(CT_NonConst | CT_LValue)); + assert(st.check_call<>(CT_NonConst | CT_LValue)); std::move(obj)(); - assert(Fn::check_call<>(CT_NonConst | CT_RValue)); + assert(st.check_call<>(CT_NonConst | CT_RValue)); c_obj(); - assert(Fn::check_call<>(CT_Const | CT_LValue)); + assert(st.check_call<>(CT_Const | CT_LValue)); std::move(c_obj)(); - assert(Fn::check_call<>(CT_Const | CT_RValue)); + assert(st.check_call<>(CT_Const | CT_RValue)); } { // test value categories int x = 42; const int cx = 42; obj(x); - assert(Fn::check_call(CT_NonConst | CT_LValue)); + assert(st.check_call(CT_NonConst | CT_LValue)); obj(cx); - assert(Fn::check_call(CT_NonConst | CT_LValue)); + assert(st.check_call(CT_NonConst | CT_LValue)); obj(std::move(x)); - assert(Fn::check_call(CT_NonConst | CT_LValue)); + assert(st.check_call(CT_NonConst | CT_LValue)); obj(std::move(cx)); - assert(Fn::check_call(CT_NonConst | CT_LValue)); + assert(st.check_call(CT_NonConst | CT_LValue)); obj(42); - assert(Fn::check_call(CT_NonConst | CT_LValue)); + assert(st.check_call(CT_NonConst | CT_LValue)); } { // test value categories - rvalue int x = 42; const int cx = 42; std::move(obj)(x); - assert(Fn::check_call(CT_NonConst | CT_RValue)); + assert(st.check_call(CT_NonConst | CT_RValue)); std::move(obj)(cx); - assert(Fn::check_call(CT_NonConst | CT_RValue)); + assert(st.check_call(CT_NonConst | CT_RValue)); std::move(obj)(std::move(x)); - assert(Fn::check_call(CT_NonConst | CT_RValue)); + assert(st.check_call(CT_NonConst | CT_RValue)); std::move(obj)(std::move(cx)); - assert(Fn::check_call(CT_NonConst | CT_RValue)); + assert(st.check_call(CT_NonConst | CT_RValue)); std::move(obj)(42); - assert(Fn::check_call(CT_NonConst | CT_RValue)); + assert(st.check_call(CT_NonConst | CT_RValue)); } { // test value categories - const call int x = 42; const int cx = 42; c_obj(x); - assert(Fn::check_call(CT_Const | CT_LValue)); + assert(st.check_call(CT_Const | CT_LValue)); c_obj(cx); - assert(Fn::check_call(CT_Const | CT_LValue)); + assert(st.check_call(CT_Const | CT_LValue)); c_obj(std::move(x)); - assert(Fn::check_call(CT_Const | CT_LValue)); + assert(st.check_call(CT_Const | CT_LValue)); c_obj(std::move(cx)); - assert(Fn::check_call(CT_Const | CT_LValue)); + assert(st.check_call(CT_Const | CT_LValue)); c_obj(42); - assert(Fn::check_call(CT_Const | CT_LValue)); + assert(st.check_call(CT_Const | CT_LValue)); } { // test value categories - const call rvalue int x = 42; const int cx = 42; std::move(c_obj)(x); - assert(Fn::check_call(CT_Const | CT_RValue)); + assert(st.check_call(CT_Const | CT_RValue)); std::move(c_obj)(cx); - assert(Fn::check_call(CT_Const | CT_RValue)); + assert(st.check_call(CT_Const | CT_RValue)); std::move(c_obj)(std::move(x)); - assert(Fn::check_call(CT_Const | CT_RValue)); + assert(st.check_call(CT_Const | CT_RValue)); std::move(c_obj)(std::move(cx)); - assert(Fn::check_call(CT_Const | CT_RValue)); + assert(st.check_call(CT_Const | CT_RValue)); std::move(c_obj)(42); - assert(Fn::check_call(CT_Const | CT_RValue)); + assert(st.check_call(CT_Const | CT_RValue)); } { // test multi arg + using String = const char *; const double y = 3.14; - std::string s = "abc"; - obj(42, std::move(y), s, std::string{"foo"}); - Fn::check_call(CT_NonConst | CT_LValue); - std::move(obj)(42, std::move(y), s, std::string{"foo"}); - Fn::check_call(CT_NonConst | CT_RValue); - c_obj(42, std::move(y), s, std::string{"foo"}); - Fn::check_call(CT_Const | CT_LValue); - std::move(c_obj)(42, std::move(y), s, std::string{"foo"}); - Fn::check_call(CT_Const | CT_RValue); + String s = "abc"; + obj(42, std::move(y), s, String{"foo"}); + assert((st.check_call(CT_NonConst | CT_LValue))); + std::move(obj)(42, std::move(y), s, String{"foo"}); + assert((st.check_call(CT_NonConst | CT_RValue))); + c_obj(42, std::move(y), s, String{"foo"}); + assert((st.check_call(CT_Const | CT_LValue))); + std::move(c_obj)(42, std::move(y), s, String{"foo"}); + assert((st.check_call(CT_Const | CT_RValue))); } + return true; } -void call_operator_noexcept_test() +TEST_CONSTEXPR_CXX20 +bool call_operator_noexcept_test() { { using T = ConstCallable; @@ -587,19 +599,22 @@ auto const& cret = ret; static_assert(!noexcept(cret()), "call should not be noexcept"); } + return true; } -void test_lwg2767() { +TEST_CONSTEXPR_CXX20 +bool test_lwg2767() { // See https://cplusplus.github.io/LWG/lwg-defects.html#2767 struct Abstract { virtual void f() const = 0; }; struct Derived : public Abstract { void f() const {} }; - struct F { bool operator()(Abstract&&) { return false; } }; + struct F { constexpr bool operator()(Abstract&&) { return false; } }; { Derived d; Abstract &a = d; bool b = std::not_fn(F{})(std::move(a)); assert(b); } + return true; } int main(int, char**) @@ -613,5 +628,14 @@ call_operator_noexcept_test(); test_lwg2767(); - return 0; +#if TEST_STD_VER >= 20 + static_assert(constructor_tests()); + static_assert(other_callable_types_test()); + static_assert(call_operator_sfinae_test()); // somewhat of an extension + static_assert(call_operator_forwarding_test()); + static_assert(call_operator_noexcept_test()); + static_assert(test_lwg2767()); +#endif + + return 0; }