463 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++1410 11// template <class F> unspecified not_fn(F&& f);12 13#include <functional>14#include <type_traits>15#include <string>16#include <cassert>17 18#include "test_macros.h"19#include "type_id.h"20#include "callable_types.h"21 22///////////////////////////////////////////////////////////////////////////////23// BOOL TEST TYPES24///////////////////////////////////////////////////////////////////////////////25 26struct EvilBool {27 static int bang_called;28 29 EvilBool(EvilBool const&) = default;30 EvilBool(EvilBool&&) = default;31 32 friend EvilBool operator!(EvilBool const& other) {33 ++bang_called;34 return EvilBool{!other.value};35 }36 37private:38 friend struct MoveOnlyCallable<EvilBool>;39 friend struct CopyCallable<EvilBool>;40 friend struct NoExceptCallable<EvilBool>;41 42 constexpr explicit EvilBool(bool x) : value(x) {}43 EvilBool& operator=(EvilBool const& other) = default;44 45public:46 bool value;47};48 49int EvilBool::bang_called = 0;50 51struct ExplicitBool {52 ExplicitBool(ExplicitBool const&) = default;53 ExplicitBool(ExplicitBool&&) = default;54 55 constexpr explicit operator bool() const { return value; }56 57private:58 friend struct MoveOnlyCallable<ExplicitBool>;59 friend struct CopyCallable<ExplicitBool>;60 61 constexpr explicit ExplicitBool(bool x) : value(x) {}62 constexpr ExplicitBool& operator=(bool x) {63 value = x;64 return *this;65 }66 67 bool value;68};69 70 71struct NoExceptEvilBool {72 NoExceptEvilBool(NoExceptEvilBool const&) = default;73 NoExceptEvilBool(NoExceptEvilBool&&) = default;74 NoExceptEvilBool& operator=(NoExceptEvilBool const& other) = default;75 76 constexpr explicit NoExceptEvilBool(bool x) : value(x) {}77 78 friend NoExceptEvilBool operator!(NoExceptEvilBool const& other) noexcept {79 return NoExceptEvilBool{!other.value};80 }81 82 bool value;83};84 85 86 87TEST_CONSTEXPR_CXX2088bool constructor_tests()89{90 {91 using T = MoveOnlyCallable<bool>;92 T value(true);93 using RetT = decltype(std::not_fn(std::move(value)));94 static_assert(std::is_move_constructible<RetT>::value, "");95 static_assert(!std::is_copy_constructible<RetT>::value, "");96 static_assert(!std::is_move_assignable<RetT>::value, "");97 static_assert(!std::is_copy_assignable<RetT>::value, "");98 auto ret = std::not_fn(std::move(value));99 // test it was moved from100 assert(value.value == false);101 // test that ret() negates the original value 'true'102 assert(ret() == false);103 assert(ret(0, 0.0, "blah") == false);104 // Move ret and test that it was moved from and that ret2 got the105 // original value.106 auto ret2 = std::move(ret);107 assert(ret() == true);108 assert(ret2() == false);109 assert(ret2(42) == false);110 }111 {112 using T = CopyCallable<bool>;113 T value(false);114 using RetT = decltype(std::not_fn(value));115 static_assert(std::is_move_constructible<RetT>::value, "");116 static_assert(std::is_copy_constructible<RetT>::value, "");117 static_assert(!std::is_move_assignable<RetT>::value, "");118 static_assert(!std::is_copy_assignable<RetT>::value, "");119 auto ret = std::not_fn(value);120 // test that value is unchanged (copied not moved)121 assert(value.value == false);122 // test 'ret' has the original value123 assert(ret() == true);124 assert(ret(42, 100) == true);125 // move from 'ret' and check that 'ret2' has the original value.126 auto ret2 = std::move(ret);127 assert(ret() == false);128 assert(ret2() == true);129 assert(ret2("abc") == true);130 // initialize not_fn with rvalue131 auto ret3 = std::not_fn(std::move(value));132 assert(ret(0) == false);133 assert(ret3(0) == true);134 }135 {136 using T = CopyAssignableWrapper;137 T value(true);138 T value2(false);139 using RetT = decltype(std::not_fn(value));140 static_assert(std::is_move_constructible<RetT>::value, "");141 static_assert(std::is_copy_constructible<RetT>::value, "");142 auto ret = std::not_fn(value);143 assert(ret() == false);144 auto ret2 = std::not_fn(value2);145 assert(ret2() == true);146 }147 {148 using T = MoveAssignableWrapper;149 T value(true);150 T value2(false);151 using RetT = decltype(std::not_fn(std::move(value)));152 static_assert(std::is_move_constructible<RetT>::value, "");153 static_assert(!std::is_copy_constructible<RetT>::value, "");154 static_assert(!std::is_copy_assignable<RetT>::value, "");155 auto ret = std::not_fn(std::move(value));156 assert(ret() == false);157 auto ret2 = std::not_fn(std::move(value2));158 assert(ret2() == true);159 }160 return true;161}162 163void return_type_tests()164{165 using std::is_same;166 {167 using T = CopyCallable<bool>;168 auto ret = std::not_fn(T{false});169 static_assert(is_same<decltype(ret()), bool>::value, "");170 static_assert(is_same<decltype(ret("abc")), bool>::value, "");171 assert(ret() == true);172 }173 {174 using T = CopyCallable<ExplicitBool>;175 auto ret = std::not_fn(T{true});176 static_assert(is_same<decltype(ret()), bool>::value, "");177 static_assert(is_same<decltype(ret(std::string("abc"))), bool>::value, "");178 assert(ret() == false);179 }180 {181 using T = CopyCallable<EvilBool>;182 auto ret = std::not_fn(T{false});183 static_assert(is_same<decltype(ret()), EvilBool>::value, "");184 EvilBool::bang_called = 0;185 auto value_ret = ret();186 assert(EvilBool::bang_called == 1);187 assert(value_ret.value == true);188 ret();189 assert(EvilBool::bang_called == 2);190 }191}192 193// Other tests only test using objects with call operators. Test various194// other callable types here.195TEST_CONSTEXPR_CXX20196bool other_callable_types_test()197{198 { // test with function pointer199 auto ret = std::not_fn(returns_true);200 assert(ret() == false);201 }202 { // test with lambda203 auto returns_value = [](bool value) { return value; };204 auto ret = std::not_fn(returns_value);205 assert(ret(true) == false);206 assert(ret(false) == true);207 }208 { // test with pointer to member function209 MemFunCallable mt(true);210 const MemFunCallable mf(false);211 auto ret = std::not_fn(&MemFunCallable::return_value);212 assert(ret(mt) == false);213 assert(ret(mf) == true);214 assert(ret(&mt) == false);215 assert(ret(&mf) == true);216 }217 { // test with pointer to member function218 MemFunCallable mt(true);219 MemFunCallable mf(false);220 auto ret = std::not_fn(&MemFunCallable::return_value_nc);221 assert(ret(mt) == false);222 assert(ret(mf) == true);223 assert(ret(&mt) == false);224 assert(ret(&mf) == true);225 }226 { // test with pointer to member data227 MemFunCallable mt(true);228 const MemFunCallable mf(false);229 auto ret = std::not_fn(&MemFunCallable::value);230 assert(ret(mt) == false);231 assert(ret(mf) == true);232 assert(ret(&mt) == false);233 assert(ret(&mf) == true);234 }235 return true;236}237 238void throws_in_constructor_test()239{240#ifndef TEST_HAS_NO_EXCEPTIONS241 struct ThrowsOnCopy {242 ThrowsOnCopy(ThrowsOnCopy const&) {243 throw 42;244 }245 ThrowsOnCopy() = default;246 bool operator()() const {247 assert(false);248#ifdef TEST_COMPILER_MSVC249 __assume(0);250#else251 __builtin_unreachable();252#endif253 }254 };255 {256 ThrowsOnCopy cp;257 try {258 (void)std::not_fn(cp);259 assert(false);260 } catch (int const& value) {261 assert(value == 42);262 }263 }264#endif265}266 267TEST_CONSTEXPR_CXX20268bool call_operator_sfinae_test() {269 { // wrong number of arguments270 using T = decltype(std::not_fn(returns_true));271 static_assert(std::is_invocable<T>::value, ""); // callable only with no args272 static_assert(!std::is_invocable<T, bool>::value, "");273 }274 { // violates const correctness (member function pointer)275 using T = decltype(std::not_fn(&MemFunCallable::return_value_nc));276 static_assert(std::is_invocable<T, MemFunCallable&>::value, "");277 static_assert(!std::is_invocable<T, const MemFunCallable&>::value, "");278 }279 { // violates const correctness (call object)280 using Obj = CopyCallable<bool>;281 using NCT = decltype(std::not_fn(Obj{true}));282 using CT = const NCT;283 static_assert(std::is_invocable<NCT>::value, "");284 static_assert(!std::is_invocable<CT>::value, "");285 }286 { // returns bad type with no operator!287 auto fn = [](auto x) { return x; };288 using T = decltype(std::not_fn(fn));289 static_assert(std::is_invocable<T, bool>::value, "");290 static_assert(!std::is_invocable<T, std::string>::value, "");291 }292 return true;293}294 295TEST_CONSTEXPR_CXX20296bool call_operator_forwarding_test()297{298 using Fn = ForwardingCallObject;299 Fn::State st;300 auto obj = std::not_fn(Fn{st});301 const auto& c_obj = obj;302 { // test zero args303 obj();304 assert(st.check_call<>(CT_NonConst | CT_LValue));305 std::move(obj)();306 assert(st.check_call<>(CT_NonConst | CT_RValue));307 c_obj();308 assert(st.check_call<>(CT_Const | CT_LValue));309 std::move(c_obj)();310 assert(st.check_call<>(CT_Const | CT_RValue));311 }312 { // test value categories313 int x = 42;314 const int cx = 42;315 obj(x);316 assert(st.check_call<int&>(CT_NonConst | CT_LValue));317 obj(cx);318 assert(st.check_call<const int&>(CT_NonConst | CT_LValue));319 obj(std::move(x));320 assert(st.check_call<int&&>(CT_NonConst | CT_LValue));321 obj(std::move(cx));322 assert(st.check_call<const int&&>(CT_NonConst | CT_LValue));323 obj(42);324 assert(st.check_call<int&&>(CT_NonConst | CT_LValue));325 }326 { // test value categories - rvalue327 int x = 42;328 const int cx = 42;329 std::move(obj)(x);330 assert(st.check_call<int&>(CT_NonConst | CT_RValue));331 std::move(obj)(cx);332 assert(st.check_call<const int&>(CT_NonConst | CT_RValue));333 std::move(obj)(std::move(x));334 assert(st.check_call<int&&>(CT_NonConst | CT_RValue));335 std::move(obj)(std::move(cx));336 assert(st.check_call<const int&&>(CT_NonConst | CT_RValue));337 std::move(obj)(42);338 assert(st.check_call<int&&>(CT_NonConst | CT_RValue));339 }340 { // test value categories - const call341 int x = 42;342 const int cx = 42;343 c_obj(x);344 assert(st.check_call<int&>(CT_Const | CT_LValue));345 c_obj(cx);346 assert(st.check_call<const int&>(CT_Const | CT_LValue));347 c_obj(std::move(x));348 assert(st.check_call<int&&>(CT_Const | CT_LValue));349 c_obj(std::move(cx));350 assert(st.check_call<const int&&>(CT_Const | CT_LValue));351 c_obj(42);352 assert(st.check_call<int&&>(CT_Const | CT_LValue));353 }354 { // test value categories - const call rvalue355 int x = 42;356 const int cx = 42;357 std::move(c_obj)(x);358 assert(st.check_call<int&>(CT_Const | CT_RValue));359 std::move(c_obj)(cx);360 assert(st.check_call<const int&>(CT_Const | CT_RValue));361 std::move(c_obj)(std::move(x));362 assert(st.check_call<int&&>(CT_Const | CT_RValue));363 std::move(c_obj)(std::move(cx));364 assert(st.check_call<const int&&>(CT_Const | CT_RValue));365 std::move(c_obj)(42);366 assert(st.check_call<int&&>(CT_Const | CT_RValue));367 }368 { // test multi arg369 using String = const char *;370 const double y = 3.14;371 String s = "abc";372 obj(42, std::move(y), s, String{"foo"});373 assert((st.check_call<int&&, const double&&, String&, String&&>(CT_NonConst | CT_LValue)));374 std::move(obj)(42, std::move(y), s, String{"foo"});375 assert((st.check_call<int&&, const double&&, String&, String&&>(CT_NonConst | CT_RValue)));376 c_obj(42, std::move(y), s, String{"foo"});377 assert((st.check_call<int&&, const double&&, String&, String&&>(CT_Const | CT_LValue)));378 std::move(c_obj)(42, std::move(y), s, String{"foo"});379 assert((st.check_call<int&&, const double&&, String&, String&&>(CT_Const | CT_RValue)));380 }381 return true;382}383 384TEST_CONSTEXPR_CXX20385bool call_operator_noexcept_test()386{387 {388 using T = ConstCallable<bool>;389 T value(true);390 auto ret = std::not_fn(value);391 static_assert(!noexcept(ret()), "call should not be noexcept");392 auto const& cret = ret;393 static_assert(!noexcept(cret()), "call should not be noexcept");394 }395 {396 using T = NoExceptCallable<bool>;397 T value(true);398 auto ret = std::not_fn(value);399 LIBCPP_STATIC_ASSERT(noexcept(!std::__invoke(value)), "");400#if TEST_STD_VER > 14401 static_assert(noexcept(!std::invoke(value)), "");402#endif403 static_assert(noexcept(ret()), "call should be noexcept");404 auto const& cret = ret;405 static_assert(noexcept(cret()), "call should be noexcept");406 }407 {408 using T = NoExceptCallable<NoExceptEvilBool>;409 T value(true);410 auto ret = std::not_fn(value);411 static_assert(noexcept(ret()), "call should not be noexcept");412 auto const& cret = ret;413 static_assert(noexcept(cret()), "call should not be noexcept");414 }415 {416 using T = NoExceptCallable<EvilBool>;417 T value(true);418 auto ret = std::not_fn(value);419 static_assert(!noexcept(ret()), "call should not be noexcept");420 auto const& cret = ret;421 static_assert(!noexcept(cret()), "call should not be noexcept");422 }423 return true;424}425 426TEST_CONSTEXPR_CXX20427bool test_lwg2767() {428 // See https://cplusplus.github.io/LWG/lwg-defects.html#2767429 struct Abstract { virtual void f() const = 0; };430 struct Derived : public Abstract { void f() const {} };431 struct F { constexpr bool operator()(Abstract&&) { return false; } };432 {433 Derived d;434 Abstract &a = d;435 bool b = std::not_fn(F{})(std::move(a));436 assert(b);437 }438 return true;439}440 441int main(int, char**)442{443 constructor_tests();444 return_type_tests();445 other_callable_types_test();446 throws_in_constructor_test();447 call_operator_sfinae_test(); // somewhat of an extension448 call_operator_forwarding_test();449 call_operator_noexcept_test();450 test_lwg2767();451 452#if TEST_STD_VER >= 20453 static_assert(constructor_tests());454 static_assert(other_callable_types_test());455 static_assert(call_operator_sfinae_test()); // somewhat of an extension456 static_assert(call_operator_forwarding_test());457 static_assert(call_operator_noexcept_test());458 static_assert(test_lwg2767());459#endif460 461 return 0;462}463