/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/util/functional.h" #include "mongo/unittest/unittest.h" /** * Note that tests in this file are deliberately outside the mongodb namespace to ensure that * deduction works appropriately via adl. I.e. this set of tests doesn't follow our usual * convention, needn't be considered prevailing local style, but should be left alone moving * forward. */ namespace { // These are important to ensure that function_ref is passed in registers in ABIs that we care // about. There is no way to query this directly, and if there was, we would need to exclude windows // anyway because it never splits single objects across registers. static_assert(std::is_trivially_copyable_v>); static_assert(sizeof(mongo::function_ref) == (2 * sizeof(void*))); template struct FuncObj; template struct FuncObj { Ret operator()(Args...); }; template auto makeFuncObj() -> FuncObj; template auto makeFuncPtr() -> Sig*; #define TEST_DEDUCTION_GUIDE(sig) \ static_assert(std::is_same_v())), \ mongo::unique_function>); \ static_assert(std::is_same_v())), \ mongo::unique_function>) TEST_DEDUCTION_GUIDE(void()); TEST_DEDUCTION_GUIDE(void(int)); TEST_DEDUCTION_GUIDE(void(int&)); TEST_DEDUCTION_GUIDE(void(int&&)); TEST_DEDUCTION_GUIDE(void(int, double)); TEST_DEDUCTION_GUIDE(void(int&, double)); TEST_DEDUCTION_GUIDE(void(int&&, double)); TEST_DEDUCTION_GUIDE(int*()); TEST_DEDUCTION_GUIDE(int*(int)); TEST_DEDUCTION_GUIDE(int*(int&)); TEST_DEDUCTION_GUIDE(int*(int&&)); TEST_DEDUCTION_GUIDE(int*(int, double)); TEST_DEDUCTION_GUIDE(int*(int&, double)); TEST_DEDUCTION_GUIDE(int*(int&&, double)); template struct RunDetection { ~RunDetection() { itRan = false; } RunDetection(const RunDetection&) = delete; RunDetection& operator=(const RunDetection&) = delete; RunDetection() { itRan = false; } static bool itRan; }; template bool RunDetection::itRan = false; void markRunDetection0() { RunDetection<0>::itRan = true; } TEST(UniqueFunctionTest, construct_simple_unique_function_from_lambda) { // Implicit construction { RunDetection<0> runDetection; mongo::unique_function uf = [] { RunDetection<0>::itRan = true; }; uf(); ASSERT_TRUE(runDetection.itRan); } // Explicit construction { RunDetection<0> runDetection; mongo::unique_function uf{[] { RunDetection<0>::itRan = true; }}; uf(); ASSERT_TRUE(runDetection.itRan); } } TEST(FunctionRefTest, construct_simple_function_ref_from_lambda) { // Implicit construction { RunDetection<0> runDetection; [](mongo::function_ref fr) { // fr(); }([] { RunDetection<0>::itRan = true; }); ASSERT_TRUE(runDetection.itRan); } // Explicit construction { RunDetection<0> runDetection; mongo::function_ref([] { RunDetection<0>::itRan = true; })(); ASSERT_TRUE(runDetection.itRan); } } TEST(UniqueFunctionTest, works_with_function_name) { RunDetection<0> runDetection; mongo::unique_function uf = markRunDetection0; uf(); ASSERT_TRUE(runDetection.itRan); } TEST(UniqueFunctionTest, works_with_function_pointer) { RunDetection<0> runDetection; mongo::unique_function uf = &markRunDetection0; uf(); ASSERT_TRUE(runDetection.itRan); } TEST(FunctionRefTest, works_with_function_name) { RunDetection<0> runDetection; mongo::function_ref fr = markRunDetection0; fr(); ASSERT_TRUE(runDetection.itRan); } TEST(FunctionRefTest, works_with_function_pointer) { RunDetection<0> runDetection; mongo::function_ref fr = &markRunDetection0; fr(); ASSERT_TRUE(runDetection.itRan); } TEST(UniqueFunctionTest, returns_value) { ASSERT_EQ(mongo::unique_function([] { return 42; })(), 42); } TEST(FunctionRefTest, returns_value) { ASSERT_EQ(mongo::function_ref([] { return 42; })(), 42); } TEST(UniqueFunctionTest, takes_arguments) { RunDetection<0> runDetection; mongo::unique_function([](int a, int b) { ASSERT_EQ(a, 1); ASSERT_EQ(b, 2); RunDetection<0>::itRan = true; })(1, 2); ASSERT_TRUE(runDetection.itRan); } TEST(FunctionRefTest, takes_arguments) { RunDetection<0> runDetection; mongo::function_ref([](int a, int b) { ASSERT_EQ(a, 1); ASSERT_EQ(b, 2); RunDetection<0>::itRan = true; })(1, 2); ASSERT_TRUE(runDetection.itRan); } TEST(UniqueFunctionTest, returns_reference) { struct Immobile { Immobile() = default; Immobile(Immobile&&) = delete; }; Immobile object; ASSERT_EQ(&(mongo::unique_function([&]() -> Immobile& { return object; })()), &object); } TEST(FunctionRefTest, returns_reference) { struct Immobile { Immobile() = default; Immobile(Immobile&&) = delete; }; Immobile object; ASSERT_EQ(&(mongo::function_ref([&]() -> Immobile& { return object; })()), &object); } TEST(UniqueFunctionTest, assign_simple_unique_function_from_lambda) { // Implicit construction RunDetection<0> runDetection; mongo::unique_function uf; uf = [] { RunDetection<0>::itRan = true; }; uf(); ASSERT_TRUE(runDetection.itRan); } static_assert(!std::is_default_constructible_v>); static_assert(std::is_nothrow_move_assignable_v>); static_assert(std::is_nothrow_copy_assignable_v>); static_assert(std::is_assignable_v, void (*)()>); static_assert(std::is_assignable_v, void (&)()>); static_assert(!std::is_assignable_v, std::function>); TEST(UniqueFunctionTest, reassign_simple_unique_function_from_lambda) { // Implicit construction RunDetection<0> runDetection0; RunDetection<1> runDetection1; mongo::unique_function uf = [] { RunDetection<0>::itRan = true; }; uf = [] { RunDetection<1>::itRan = true; }; uf(); ASSERT_FALSE(runDetection0.itRan); ASSERT_TRUE(runDetection1.itRan); } TEST(FunctionRefTest, reassign_simple_function_ref_from_decayed_lambda) { // Implicit construction RunDetection<0> runDetection; mongo::function_ref fr = +[] { }; fr = +[] { RunDetection<0>::itRan = true; }; fr(); ASSERT_TRUE(runDetection.itRan); } TEST(FunctionRefTest, reassign_simple_function_ref_from_function_ref) { // Implicit construction RunDetection<0> runDetection0; RunDetection<1> runDetection1; [](mongo::function_ref fr0, mongo::function_ref fr1) { fr0 = fr1; fr0(); }([] { RunDetection<0>::itRan = true; }, [] { RunDetection<1>::itRan = true; }); ASSERT_FALSE(runDetection0.itRan); ASSERT_TRUE(runDetection1.itRan); } TEST(UniqueFunctionTest, accepts_a_functor_that_is_move_only) { struct Checker {}; mongo::unique_function uf = [checkerPtr = std::make_unique()] { }; mongo::unique_function uf2 = std::move(uf); uf = std::move(uf2); } TEST(FunctionRefTest, accepts_a_functor_that_is_immobile) { struct Immobile { Immobile() = default; Immobile(Immobile&&) = delete; void operator()() { RunDetection<0>::itRan = true; } }; { RunDetection<0> runDetection0; [](mongo::function_ref func) { // func(); }(Immobile()); ASSERT_TRUE(runDetection0.itRan); } { RunDetection<0> runDetection0; Immobile immobile; [](mongo::function_ref func) { // func(); }(immobile); ASSERT_TRUE(runDetection0.itRan); } } TEST(UniqueFunctionTest, dtor_releases_functor_object_and_does_not_call_function) { RunDetection<0> runDetection0; RunDetection<1> runDetection1; struct Checker { ~Checker() { RunDetection<0>::itRan = true; } }; { mongo::unique_function uf = [checkerPtr = std::make_unique()] { RunDetection<1>::itRan = true; }; ASSERT_FALSE(runDetection0.itRan); ASSERT_FALSE(runDetection1.itRan); } ASSERT_TRUE(runDetection0.itRan); ASSERT_FALSE(runDetection1.itRan); } TEST(UniqueFunctionTest, comparison_checks) { mongo::unique_function uf; // Using true/false assertions, as we're testing the actual operators and commutativity here. ASSERT_TRUE(uf == nullptr); ASSERT_TRUE(nullptr == uf); ASSERT_FALSE(uf != nullptr); ASSERT_FALSE(nullptr != uf); uf = [] { }; ASSERT_FALSE(uf == nullptr); ASSERT_FALSE(nullptr == uf); ASSERT_TRUE(uf != nullptr); ASSERT_TRUE(nullptr != uf); uf = nullptr; ASSERT_TRUE(uf == nullptr); ASSERT_TRUE(nullptr == uf); ASSERT_FALSE(uf != nullptr); ASSERT_FALSE(nullptr != uf); } TEST(UniqueFunctionTest, simple_instantiations) { mongo::unique_function a; mongo::unique_function x = []() -> int { return 42; }; x = []() -> int { return 42; }; } TEST(FunctionRefTest, simple_instantiations) { mongo::function_ref([]() -> int { return 42; }); } namespace conversion_checking { template using fr = mongo::function_ref; template using uf = mongo::unique_function; template using sf = std::function; // Check expected `is_convertible_v` traits (which also checks if this kind of conversion will // compile correctly too. TEST(UniqueFunctionTest, convertibility_tests) { // TODO when on C++17, see if the new MSVC can handle these `std::isconvertible` static assertions. #ifndef _MSC_VER // Note that `mongo::unique_function` must never convert to `std::function` in any of the // following cases. // No arguments, return variants // Same return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); // Convertible return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); // Incompatible return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); // Argument consistency, with return variants // Same return type, same arguments MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); // Convertible return type, same arguments MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); // Incompatible return type, same arguments MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); // Extra arguments, with return variants // Same return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); // Convertible return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); // Incompatible return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); // Argument conversions, with return variants // Same return type, Convertible argument MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); // Convertible return type, with convertible arguments MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(std::is_convertible_v, uf>); MONGO_STATIC_ASSERT( !std::is_convertible_v, sf>); MONGO_STATIC_ASSERT( std::is_convertible_v, uf>); MONGO_STATIC_ASSERT( std::is_convertible_v, uf>); // Incompatible return type, with convertible arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); struct X {}; struct Y {}; // Incompatible argument conversions, with return variants // Same return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); // Convertible return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); // Incompatible return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, uf>); #endif } // function_ref is convertible to and from a std::function with a compatible signature. TEST(FunctionRefTest, convertibility_tests) { // No arguments, return variants // Same return type MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); // Convertible return type MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); // Incompatible return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); // Argument consistency, with return variants // Same return type, same arguments MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); // Convertible return type, same arguments MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); // Incompatible return type, same arguments MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); // Extra arguments, with return variants // Same return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); // Convertible return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); // Incompatible return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); // Argument conversions, with return variants // Same return type, Convertible argument MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); // Convertible return type, with convertible arguments MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(std::is_convertible_v, fr>); MONGO_STATIC_ASSERT( std::is_convertible_v, sf>); MONGO_STATIC_ASSERT( std::is_convertible_v, fr>); MONGO_STATIC_ASSERT( std::is_convertible_v, fr>); // Incompatible return type, with convertible arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); struct X {}; struct Y {}; // Incompatible argument conversions, with return variants // Same return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); // Convertible return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); // Incompatible return type MONGO_STATIC_ASSERT(!std::is_convertible_v, sf>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); MONGO_STATIC_ASSERT(!std::is_convertible_v, fr>); } } // namespace conversion_checking template bool accept(std::function arg, U) { return false; } template >, void>> bool accept(T arg, U) { return true; } TEST(UniqueFunctionTest, functionDominanceExample) { mongo::unique_function uf = [] { }; ASSERT_TRUE(accept(std::move(uf), nullptr)); } // Enable these tests to manually verify that we get warnings (which are promoted to errors). // Note: because the warning is from inside the template instantiations, it usually won't show up // with clangd, you need to do an actual build. #if 0 TEST(UniqueFunctionTest, WarnWhenIgnoringStatus) { mongo::unique_function([] { return mongo::Status::OK(); })(); } TEST(FunctionRefTest, WarnWhenIgnoringStatus) { mongo::function_ref([] { return mongo::Status::OK(); })(); } #endif } // namespace