1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220

base / functional / function_ref_unittest.cc [blame]

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/functional/function_ref.h"

#include <stdint.h>

#include <concepts>
#include <optional>

#include "base/compiler_specific.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/functional/function_ref.h"

namespace base {

namespace {

char Func(float) {
  return 'a';
}

}  // namespace

TEST(FunctionRef, Lambda) {
  auto add = [](int a, int b) { return a + b; };

  {
    const FunctionRef<int(int, int)> ref = add;
    EXPECT_EQ(19, ref(17, 2));
  }

  {
    const auto add_const = add;
    const FunctionRef<int(int, int)> ref = add_const;
    EXPECT_EQ(19, ref(17, 2));
  }
}

TEST(FunctionRef, CapturingLambda) {
  int x = 3;
  const auto lambda = [&x] { return x; };
  FunctionRef<int()> ref = lambda;
  EXPECT_EQ(3, ref());
}

TEST(FunctionRef, FunctionPtr) {
  [](FunctionRef<char(float)> ref) { EXPECT_EQ('a', ref(1.0)); }(&Func);
  const FunctionRef<char(float)> ref = +[](float) { return 'a'; };
  EXPECT_EQ('a', ref(1.0f));
}

TEST(FunctionRef, Functor) {
  struct S {
    int operator()(int x) const { return x; }
  };
  const S s;
  const FunctionRef<int(int)> ref = s;
  EXPECT_EQ(17, ref(17));
}

TEST(FunctionRef, Method) {
  struct S {
    int Method() const { return value; }

    const int value;
  };
  const S s(25);
  [&s](FunctionRef<int(const S*)> ref) { EXPECT_EQ(25, ref(&s)); }(&S::Method);
}

// If we construct from another `FunctionRef`, that should work fine, even if
// the input is destroyed before we call the output. In other words, we should
// reference the underlying callable, not the `FunctionRef`.
//
// We construct in a `noinline` function to maximize the chance that ASAN
// notices the use-after-free if we get this wrong.
NOINLINE void ConstructFromLValue(std::optional<FunctionRef<int()>>& ref) {
  const auto return_17 = [] { return 17; };
  FunctionRef<int()> other = return_17;
  ref.emplace(other);
}
NOINLINE void ConstructFromConstLValue(std::optional<FunctionRef<int()>>& ref) {
  const auto return_17 = [] { return 17; };
  const FunctionRef<int()> other = return_17;
  ref.emplace(other);
}
NOINLINE void ConstructFromRValue(std::optional<FunctionRef<int()>>& ref) {
  const auto return_17 = [] { return 17; };
  using Ref = FunctionRef<int()>;
  ref.emplace(Ref(return_17));
}
NOINLINE void ConstructFromConstRValue(std::optional<FunctionRef<int()>>& ref) {
  const auto return_17 = [] { return 17; };
  using Ref = const FunctionRef<int()>;
  ref.emplace(Ref(return_17));
}
TEST(FunctionRef, ConstructionFromOtherFunctionRefObjects) {
  using Ref = FunctionRef<int()>;
  std::optional<Ref> ref;

  ConstructFromLValue(ref);
  EXPECT_EQ(17, (*ref)());

  ConstructFromConstLValue(ref);
  EXPECT_EQ(17, (*ref)());

  ConstructFromRValue(ref);
  EXPECT_EQ(17, (*ref)());

  ConstructFromConstRValue(ref);
  EXPECT_EQ(17, (*ref)());

  // It shouldn't be possible to construct from `FunctionRef` objects with
  // differing signatures, even if they are compatible with `int()`.
  static_assert(!std::constructible_from<Ref, FunctionRef<void()>>);
  static_assert(!std::constructible_from<Ref, FunctionRef<int(int)>>);
  static_assert(!std::constructible_from<Ref, FunctionRef<int64_t()>>);

  // Check again with various qualifiers.
  static_assert(!std::constructible_from<Ref, const FunctionRef<void()>>);
  static_assert(!std::constructible_from<Ref, FunctionRef<void()>&>);
  static_assert(!std::constructible_from<Ref, FunctionRef<void()>&&>);
  static_assert(!std::constructible_from<Ref, const FunctionRef<void()>&>);
  static_assert(!std::constructible_from<Ref, const FunctionRef<void()>&&>);
}

// `FunctionRef` allows functors with convertible return types to be adapted.
TEST(FunctionRef, ConvertibleReturnTypes) {
  {
    const auto lambda = [] { return true; };
    const FunctionRef<int()> ref = lambda;
    EXPECT_EQ(1, ref());
  }

  {
    class Base {};
    class Derived : public Base {};

    const auto lambda = []() -> Derived* { return nullptr; };
    const FunctionRef<const Base*()> ref = lambda;
    EXPECT_EQ(nullptr, ref());
  }
}

TEST(FunctionRef, ConstructionFromInexactMatches) {
  // Lambda.
  const auto lambda = [](int32_t x) { return x; };

  // Capturing lambda.
  const auto capturing_lambda = [&](int32_t x) { return lambda(x); };

  // Function pointer.
  int32_t (*const function_ptr)(int32_t) = +lambda;

  // Functor.
  struct Functor {
    int32_t operator()(int32_t x) const { return x; }
  };
  const Functor functor;

  // Method.
  struct Obj {
    int32_t Method(int32_t x) const { return x; }
  };
  int32_t (Obj::*const method)(int32_t) const = &Obj::Method;

  // Each of the objects above should work for a `FunctionRef` with a
  // convertible return type. In this case, they all return `int32_t`, which
  // should be seamlessly convertible to `int64_t` below.
  static_assert(
      std::constructible_from<FunctionRef<int64_t(int32_t)>, decltype(lambda)>);
  static_assert(std::constructible_from<FunctionRef<int64_t(int32_t)>,
                                        decltype(capturing_lambda)>);
  static_assert(std::constructible_from<FunctionRef<int64_t(int32_t)>,
                                        decltype(function_ptr)>);
  static_assert(std::constructible_from<FunctionRef<int64_t(int32_t)>,
                                        decltype(functor)>);
  static_assert(
      std::constructible_from<FunctionRef<int64_t(const Obj*, int32_t)>,
                              decltype(method)>);

  // It shouldn't be possible to construct a `FunctionRef` from any of the
  // objects above if we discard the return value.
  static_assert(
      !std::constructible_from<FunctionRef<void(int32_t)>, decltype(lambda)>);
  static_assert(!std::constructible_from<FunctionRef<void(int32_t)>,
                                         decltype(capturing_lambda)>);
  static_assert(!std::constructible_from<FunctionRef<void(int32_t)>,
                                         decltype(function_ptr)>);
  static_assert(
      !std::constructible_from<FunctionRef<void(int32_t)>, decltype(functor)>);
  static_assert(!std::constructible_from<FunctionRef<void(const Obj*, int32_t)>,
                                         decltype(method)>);

  // It shouldn't be possible to construct a `FunctionRef` from a pointer to a
  // functor, even with a compatible signature.
  static_assert(!std::constructible_from<FunctionRef<int32_t(int32_t)>,
                                         decltype(&functor)>);
}

TEST(FunctionRef, ConstructionFromAbslFunctionRef) {
  // It shouldn't be possible to construct a `FunctionRef` from an
  // `absl::FunctionRef`, whether the signatures are compatible or not.
  using Ref = FunctionRef<int(int)>;
  static_assert(!std::is_constructible_v<Ref, absl::FunctionRef<void()>>);
  static_assert(!std::is_constructible_v<Ref, absl::FunctionRef<void(int)>>);
  static_assert(!std::is_constructible_v<Ref, absl::FunctionRef<int(int)>>);

  // Check again with various qualifiers.
  using AbslRef = absl::FunctionRef<int(int)>;
  static_assert(!std::is_constructible_v<Ref, const AbslRef>);
  static_assert(!std::is_constructible_v<Ref, AbslRef&>);
  static_assert(!std::is_constructible_v<Ref, AbslRef&&>);
  static_assert(!std::is_constructible_v<Ref, const AbslRef&>);
  static_assert(!std::is_constructible_v<Ref, const AbslRef&&>);
}

}  // namespace base