347 lines · cpp
1//===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===//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#include "llvm/ADT/FunctionExtras.h"10#include "CountCopyAndMove.h"11#include "gtest/gtest.h"12 13#include <memory>14 15using namespace llvm;16 17namespace {18 19TEST(UniqueFunctionTest, Basic) {20 unique_function<int(int, int)> Sum = [](int A, int B) { return A + B; };21 EXPECT_EQ(Sum(1, 2), 3);22 23 unique_function<int(int, int)> Sum2 = std::move(Sum);24 EXPECT_EQ(Sum2(1, 2), 3);25 26 unique_function<int(int, int)> Sum3 = [](int A, int B) { return A + B; };27 Sum2 = std::move(Sum3);28 EXPECT_EQ(Sum2(1, 2), 3);29 30 Sum2 = unique_function<int(int, int)>([](int A, int B) { return A + B; });31 EXPECT_EQ(Sum2(1, 2), 3);32 33 // Explicit self-move test.34 *&Sum2 = std::move(Sum2);35 EXPECT_EQ(Sum2(1, 2), 3);36 37 Sum2 = unique_function<int(int, int)>();38 EXPECT_FALSE(Sum2);39 40 // Make sure we can forward through l-value reference parameters.41 unique_function<void(int &)> Inc = [](int &X) { ++X; };42 int X = 42;43 Inc(X);44 EXPECT_EQ(X, 43);45 46 // Make sure we can forward through r-value reference parameters with47 // move-only types.48 unique_function<int(std::unique_ptr<int> &&)> ReadAndDeallocByRef =49 [](std::unique_ptr<int> &&Ptr) {50 int V = *Ptr;51 Ptr.reset();52 return V;53 };54 std::unique_ptr<int> Ptr{new int(13)};55 EXPECT_EQ(ReadAndDeallocByRef(std::move(Ptr)), 13);56 EXPECT_FALSE((bool)Ptr);57 58 // Make sure we can pass a move-only temporary as opposed to a local variable.59 EXPECT_EQ(ReadAndDeallocByRef(std::unique_ptr<int>(new int(42))), 42);60 61 // Make sure we can pass a move-only type by-value.62 unique_function<int(std::unique_ptr<int>)> ReadAndDeallocByVal =63 [](std::unique_ptr<int> Ptr) {64 int V = *Ptr;65 Ptr.reset();66 return V;67 };68 Ptr.reset(new int(13));69 EXPECT_EQ(ReadAndDeallocByVal(std::move(Ptr)), 13);70 EXPECT_FALSE((bool)Ptr);71 72 EXPECT_EQ(ReadAndDeallocByVal(std::unique_ptr<int>(new int(42))), 42);73}74 75TEST(UniqueFunctionTest, Captures) {76 long A = 1, B = 2, C = 3, D = 4, E = 5;77 78 unique_function<long()> Tmp;79 80 unique_function<long()> C1 = [A]() { return A; };81 EXPECT_EQ(C1(), 1);82 Tmp = std::move(C1);83 EXPECT_EQ(Tmp(), 1);84 85 unique_function<long()> C2 = [A, B]() { return A + B; };86 EXPECT_EQ(C2(), 3);87 Tmp = std::move(C2);88 EXPECT_EQ(Tmp(), 3);89 90 unique_function<long()> C3 = [A, B, C]() { return A + B + C; };91 EXPECT_EQ(C3(), 6);92 Tmp = std::move(C3);93 EXPECT_EQ(Tmp(), 6);94 95 unique_function<long()> C4 = [A, B, C, D]() { return A + B + C + D; };96 EXPECT_EQ(C4(), 10);97 Tmp = std::move(C4);98 EXPECT_EQ(Tmp(), 10);99 100 unique_function<long()> C5 = [A, B, C, D, E]() { return A + B + C + D + E; };101 EXPECT_EQ(C5(), 15);102 Tmp = std::move(C5);103 EXPECT_EQ(Tmp(), 15);104}105 106TEST(UniqueFunctionTest, MoveOnly) {107 struct SmallCallable {108 std::unique_ptr<int> A{new int(1)};109 110 int operator()(int B) { return *A + B; }111 };112 unique_function<int(int)> Small = SmallCallable();113 EXPECT_EQ(Small(2), 3);114 unique_function<int(int)> Small2 = std::move(Small);115 EXPECT_EQ(Small2(2), 3);116 117 struct LargeCallable {118 std::unique_ptr<int> A{new int(1)};119 std::unique_ptr<int> B{new int(2)};120 std::unique_ptr<int> C{new int(3)};121 std::unique_ptr<int> D{new int(4)};122 std::unique_ptr<int> E{new int(5)};123 124 int operator()() { return *A + *B + *C + *D + *E; }125 };126 unique_function<int()> Large = LargeCallable();127 EXPECT_EQ(Large(), 15);128 unique_function<int()> Large2 = std::move(Large);129 EXPECT_EQ(Large2(), 15);130}131 132TEST(UniqueFunctionTest, CountForwardingCopies) {133 struct CopyCounter {134 int &CopyCount;135 136 CopyCounter(int &CopyCount) : CopyCount(CopyCount) {}137 CopyCounter(const CopyCounter &Arg) : CopyCount(Arg.CopyCount) {138 ++CopyCount;139 }140 };141 142 unique_function<void(CopyCounter)> ByValF = [](CopyCounter) {};143 int CopyCount = 0;144 ByValF(CopyCounter(CopyCount));145 EXPECT_EQ(1, CopyCount);146 147 CopyCount = 0;148 {149 CopyCounter Counter{CopyCount};150 ByValF(Counter);151 }152 EXPECT_EQ(2, CopyCount);153 154 // Check that we don't generate a copy at all when we can bind a reference all155 // the way down, even if that reference could *in theory* allow copies.156 unique_function<void(const CopyCounter &)> ByRefF = [](const CopyCounter &) {157 };158 CopyCount = 0;159 ByRefF(CopyCounter(CopyCount));160 EXPECT_EQ(0, CopyCount);161 162 CopyCount = 0;163 {164 CopyCounter Counter{CopyCount};165 ByRefF(Counter);166 }167 EXPECT_EQ(0, CopyCount);168 169 // If we use a reference, we can make a stronger guarantee that *no* copy170 // occurs.171 struct Uncopyable {172 Uncopyable() = default;173 Uncopyable(const Uncopyable &) = delete;174 };175 unique_function<void(const Uncopyable &)> UncopyableF =176 [](const Uncopyable &) {};177 UncopyableF(Uncopyable());178 Uncopyable X;179 UncopyableF(X);180}181 182TEST(UniqueFunctionTest, CountForwardingMoves) {183 struct MoveCounter {184 int &MoveCount;185 186 MoveCounter(int &MoveCount) : MoveCount(MoveCount) {}187 MoveCounter(MoveCounter &&Arg) : MoveCount(Arg.MoveCount) { ++MoveCount; }188 };189 190 unique_function<void(MoveCounter)> ByValF = [](MoveCounter) {};191 int MoveCount = 0;192 ByValF(MoveCounter(MoveCount));193 EXPECT_EQ(1, MoveCount);194 195 MoveCount = 0;196 {197 MoveCounter Counter{MoveCount};198 ByValF(std::move(Counter));199 }200 EXPECT_EQ(2, MoveCount);201 202 // Check that when we use an r-value reference we get no spurious copies.203 unique_function<void(MoveCounter &&)> ByRefF = [](MoveCounter &&) {};204 MoveCount = 0;205 ByRefF(MoveCounter(MoveCount));206 EXPECT_EQ(0, MoveCount);207 208 MoveCount = 0;209 {210 MoveCounter Counter{MoveCount};211 ByRefF(std::move(Counter));212 }213 EXPECT_EQ(0, MoveCount);214 215 // If we use an r-value reference we can in fact make a stronger guarantee216 // with an unmovable type.217 struct Unmovable {218 Unmovable() = default;219 Unmovable(Unmovable &&) = delete;220 };221 unique_function<void(const Unmovable &)> UnmovableF = [](const Unmovable &) {222 };223 UnmovableF(Unmovable());224 Unmovable X;225 UnmovableF(X);226}227 228TEST(UniqueFunctionTest, Const) {229 // Can assign from const lambda.230 unique_function<int(int) const> Plus2 = [X(std::make_unique<int>(2))](int Y) {231 return *X + Y;232 };233 EXPECT_EQ(5, Plus2(3));234 235 // Can call through a const ref.236 const auto &Plus2Ref = Plus2;237 EXPECT_EQ(5, Plus2Ref(3));238 239 // Can move-construct and assign.240 unique_function<int(int) const> Plus2A = std::move(Plus2);241 EXPECT_EQ(5, Plus2A(3));242 unique_function<int(int) const> Plus2B;243 Plus2B = std::move(Plus2A);244 EXPECT_EQ(5, Plus2B(3));245 246 // Can convert to non-const function type, but not back.247 unique_function<int(int)> Plus2C = std::move(Plus2B);248 EXPECT_EQ(5, Plus2C(3));249 250 // Overloaded call operator correctly resolved.251 struct ChooseCorrectOverload {252 StringRef operator()() { return "non-const"; }253 StringRef operator()() const { return "const"; }254 };255 unique_function<StringRef()> ChooseMutable = ChooseCorrectOverload();256 ChooseCorrectOverload A;257 EXPECT_EQ("non-const", ChooseMutable());258 EXPECT_EQ("non-const", A());259 unique_function<StringRef() const> ChooseConst = ChooseCorrectOverload();260 const ChooseCorrectOverload &X = A;261 EXPECT_EQ("const", ChooseConst());262 EXPECT_EQ("const", X());263}264 265// Test that overloads on unique_functions are resolved as expected.266std::string returns(StringRef) { return "not a function"; }267std::string returns(unique_function<double()> F) { return "number"; }268std::string returns(unique_function<StringRef()> F) { return "string"; }269 270TEST(UniqueFunctionTest, SFINAE) {271 EXPECT_EQ("not a function", returns("boo!"));272 EXPECT_EQ("number", returns([] { return 42; }));273 EXPECT_EQ("string", returns([] { return "hello"; }));274}275 276// A forward declared type, and a templated type.277class Incomplete;278template <typename T> class Templated { T A; };279 280// Check that we can define unique_function that have references to281// incomplete types, even if those types are templated over an282// incomplete type.283TEST(UniqueFunctionTest, IncompleteTypes) {284 unique_function<void(Templated<Incomplete> &&)>285 IncompleteArgumentRValueReference;286 unique_function<void(Templated<Incomplete> &)>287 IncompleteArgumentLValueReference;288 unique_function<void(Templated<Incomplete> *)> IncompleteArgumentPointer;289 unique_function<Templated<Incomplete> &()> IncompleteResultLValueReference;290 unique_function<Templated<Incomplete> && ()> IncompleteResultRValueReference2;291 unique_function<Templated<Incomplete> *()> IncompleteResultPointer;292}293 294// Incomplete function returning an incomplete type295Incomplete incompleteFunction();296const Incomplete incompleteFunctionConst();297 298// Check that we can assign a callable to a unique_function when the299// callable return value is incomplete.300TEST(UniqueFunctionTest, IncompleteCallableType) {301 unique_function<Incomplete()> IncompleteReturnInCallable{incompleteFunction};302 unique_function<const Incomplete()> IncompleteReturnInCallableConst{303 incompleteFunctionConst};304 unique_function<const Incomplete()> IncompleteReturnInCallableConstConversion{305 incompleteFunction};306}307 308// Define the incomplete function309class Incomplete {};310Incomplete incompleteFunction() { return {}; }311const Incomplete incompleteFunctionConst() { return {}; }312 313// Check that we can store a pointer-sized payload inline in the unique_function.314TEST(UniqueFunctionTest, InlineStorageWorks) {315 // We do assume a couple of implementation details of the unique_function here:316 // - It can store certain small-enough payload inline317 // - Inline storage size is at least >= sizeof(void*)318 void *ptr = nullptr;319 unique_function<void(void *)> UniqueFunctionWithInlineStorage{320 [ptr](void *self) {321 auto mid = reinterpret_cast<uintptr_t>(&ptr);322 auto beg = reinterpret_cast<uintptr_t>(self);323 auto end = reinterpret_cast<uintptr_t>(self) +324 sizeof(unique_function<void(void *)>);325 // Make sure the address of the captured pointer lies somewhere within326 // the unique_function object.327 EXPECT_TRUE(mid >= beg && mid < end);328 }};329 UniqueFunctionWithInlineStorage(&UniqueFunctionWithInlineStorage);330}331 332// Check that the moved-from captured state is properly destroyed during333// move construction/assignment.334TEST(UniqueFunctionTest, MovedFromStateIsDestroyedCorrectly) {335 CountCopyAndMove::ResetCounts();336 {337 unique_function<void()> CapturingFunction{338 [Counter = CountCopyAndMove{}] {}};339 unique_function<void()> CapturingFunctionMoved{340 std::move(CapturingFunction)};341 }342 EXPECT_EQ(CountCopyAndMove::TotalConstructions(),343 CountCopyAndMove::Destructions);344}345 346} // anonymous namespace347