brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.7 KiB · fdabdca Raw
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