// Copyright 2022 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "absl/functional/any_invocable.h"

#include <cstddef>
#include <initializer_list>
#include <numeric>
#include <type_traits>

#include "gtest/gtest.h"
#include "absl/base/config.h"
#include "absl/meta/type_traits.h"
#include "absl/utility/utility.h"

static_assert(absl::internal_any_invocable::kStorageSize >= sizeof(void*),
              "These tests assume that the small object storage is at least "
              "the size of a pointer.");

namespace {

// Helper macro used to avoid spelling `noexcept` in language versions older
// than C++17, where it is not part of the type system, in order to avoid
// compilation failures and internal compiler errors.
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) noexcept(noex)
#else
#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex)
#endif

// A dummy type we use when passing qualifiers to metafunctions
struct _ {};

template <class T>
struct Wrapper {
  template <class U,
            class = absl::enable_if_t<std::is_convertible<U, T>::value>>
  Wrapper(U&&);  // NOLINT
};

// This will cause a recursive trait instantiation if the SFINAE checks are
// not ordered correctly for constructibility.
static_assert(std::is_constructible<Wrapper<absl::AnyInvocable<void()>>,
                                    Wrapper<absl::AnyInvocable<void()>>>::value,
              "");

// A metafunction that takes the cv and l-value reference qualifiers that were
// associated with a function type (here passed via qualifiers of an object
// type), and .
template <class Qualifiers, class This>
struct QualifiersForThisImpl {
  static_assert(std::is_object<This>::value, "");
  using type =
      absl::conditional_t<std::is_const<Qualifiers>::value, const This, This>&;
};

template <class Qualifiers, class This>
struct QualifiersForThisImpl<Qualifiers&, This>
    : QualifiersForThisImpl<Qualifiers, This> {};

template <class Qualifiers, class This>
struct QualifiersForThisImpl<Qualifiers&&, This> {
  static_assert(std::is_object<This>::value, "");
  using type =
      absl::conditional_t<std::is_const<Qualifiers>::value, const This, This>&&;
};

template <class Qualifiers, class This>
using QualifiersForThis =
    typename QualifiersForThisImpl<Qualifiers, This>::type;

// A metafunction that takes the cv and l-value reference qualifier of T and
// applies them to U's function type qualifiers.
template <class T, class Fun>
struct GiveQualifiersToFunImpl;

template <class T, class R, class... P>
struct GiveQualifiersToFunImpl<T, R(P...)> {
  using type =
      absl::conditional_t<std::is_const<T>::value, R(P...) const, R(P...)>;
};

template <class T, class R, class... P>
struct GiveQualifiersToFunImpl<T&, R(P...)> {
  using type =
      absl::conditional_t<std::is_const<T>::value, R(P...) const&, R(P...)&>;
};

template <class T, class R, class... P>
struct GiveQualifiersToFunImpl<T&&, R(P...)> {
  using type =
      absl::conditional_t<std::is_const<T>::value, R(P...) const&&, R(P...) &&>;
};

// If noexcept is a part of the type system, then provide the noexcept forms.
#if defined(__cpp_noexcept_function_type)

template <class T, class R, class... P>
struct GiveQualifiersToFunImpl<T, R(P...) noexcept> {
  using type = absl::conditional_t<std::is_const<T>::value,
                                   R(P...) const noexcept, R(P...) noexcept>;
};

template <class T, class R, class... P>
struct GiveQualifiersToFunImpl<T&, R(P...) noexcept> {
  using type =
      absl::conditional_t<std::is_const<T>::value, R(P...) const & noexcept,
                          R(P...) & noexcept>;
};

template <class T, class R, class... P>
struct GiveQualifiersToFunImpl<T&&, R(P...) noexcept> {
  using type =
      absl::conditional_t<std::is_const<T>::value, R(P...) const && noexcept,
                          R(P...) && noexcept>;
};

#endif  // defined(__cpp_noexcept_function_type)

template <class T, class Fun>
using GiveQualifiersToFun = typename GiveQualifiersToFunImpl<T, Fun>::type;

// This is used in template parameters to decide whether or not to use a type
// that fits in the small object optimization storage.
enum class ObjSize { small, large };

// A base type that is used with classes as a means to insert an
// appropriately-sized dummy datamember when Size is ObjSize::large so that the
// user's class type is guaranteed to not fit in small object storage.
template <ObjSize Size>
struct TypeErasedPadding;

template <>
struct TypeErasedPadding<ObjSize::small> {};

template <>
struct TypeErasedPadding<ObjSize::large> {
  char dummy_data[absl::internal_any_invocable::kStorageSize + 1] = {};
};

struct Int {
  Int(int v) noexcept : value(v) {}  // NOLINT
#ifndef _MSC_VER
  Int(Int&&) noexcept {
    // NOTE: Prior to C++17, this not being called requires optimizations to
    //       take place when performing the top-level invocation. In practice,
    //       most supported compilers perform this optimization prior to C++17.
    std::abort();
  }
#else
  Int(Int&& v) noexcept = default;
#endif
  operator int() && noexcept { return value; }  // NOLINT

  int MemberFunctionAdd(int const& b, int c) noexcept {  // NOLINT
    return value + b + c;
  }

  int value;
};

enum class Movable { no, yes, nothrow, trivial };

enum class NothrowCall { no, yes };

enum class Destructible { nothrow, trivial };

enum class ObjAlign : std::size_t {
  normal = absl::internal_any_invocable::kAlignment,
  large = absl::internal_any_invocable::kAlignment * 2,
};

// A function-object template that has knobs for each property that can affect
// how the object is stored in AnyInvocable.
template <Movable Movability, Destructible Destructibility, class Qual,
          NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment>
struct add;

#define ABSL_INTERNALS_ADD(qual)                                              \
  template <NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment>  \
  struct alignas(static_cast<std::size_t>(Alignment))                         \
      add<Movable::trivial, Destructible::trivial, _ qual, CallExceptionSpec, \
          Size, Alignment> : TypeErasedPadding<Size> {                        \
    explicit add(int state_init) : state(state_init) {}                       \
    explicit add(std::initializer_list<int> state_init, int tail)             \
        : state(std::accumulate(std::begin(state_init), std::end(state_init), \
                                0) +                                          \
                tail) {}                                                      \
    add(add&& other) = default; /*NOLINT*/                                    \
    Int operator()(int a, int b, int c) qual                                  \
        ABSL_INTERNAL_NOEXCEPT_SPEC(CallExceptionSpec == NothrowCall::yes) {  \
      return state + a + b + c;                                               \
    }                                                                         \
    int state;                                                                \
  };                                                                          \
                                                                              \
  template <NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment>  \
  struct alignas(static_cast<std::size_t>(Alignment))                         \
      add<Movable::trivial, Destructible::nothrow, _ qual, CallExceptionSpec, \
          Size, Alignment> : TypeErasedPadding<Size> {                        \
    explicit add(int state_init) : state(state_init) {}                       \
    explicit add(std::initializer_list<int> state_init, int tail)             \
        : state(std::accumulate(std::begin(state_init), std::end(state_init), \
                                0) +                                          \
                tail) {}                                                      \
    ~add() noexcept {}                                                        \
    add(add&& other) = default; /*NOLINT*/                                    \
    Int operator()(int a, int b, int c) qual                                  \
        ABSL_INTERNAL_NOEXCEPT_SPEC(CallExceptionSpec == NothrowCall::yes) {  \
      return state + a + b + c;                                               \
    }                                                                         \
    int state;                                                                \
  }

// Explicitly specify an empty argument.
// MSVC (at least up to _MSC_VER 1931, if not beyond) warns that
// ABSL_INTERNALS_ADD() is an undefined zero-arg overload.
#define ABSL_INTERNALS_NOARG
ABSL_INTERNALS_ADD(ABSL_INTERNALS_NOARG);
#undef ABSL_INTERNALS_NOARG

ABSL_INTERNALS_ADD(const);
ABSL_INTERNALS_ADD(&);
ABSL_INTERNALS_ADD(const&);
ABSL_INTERNALS_ADD(&&);       // NOLINT
ABSL_INTERNALS_ADD(const&&);  // NOLINT

#undef ABSL_INTERNALS_ADD

template <Destructible Destructibility, class Qual,
          NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment>
struct add<Movable::no, Destructibility, Qual, CallExceptionSpec, Size,
           Alignment> : private add<Movable::trivial, Destructibility, Qual,
                                    CallExceptionSpec, Size, Alignment> {
  using Base = add<Movable::trivial, Destructibility, Qual, CallExceptionSpec,
                   Size, Alignment>;

  explicit add(int state_init) : Base(state_init) {}

  explicit add(std::initializer_list<int> state_init, int tail)
      : Base(state_init, tail) {}

  add(add&&) = delete;

  using Base::operator();
  using Base::state;
};

template <Destructible Destructibility, class Qual,
          NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment>
struct add<Movable::yes, Destructibility, Qual, CallExceptionSpec, Size,
           Alignment> : private add<Movable::trivial, Destructibility, Qual,
                                    CallExceptionSpec, Size, Alignment> {
  using Base = add<Movable::trivial, Destructibility, Qual, CallExceptionSpec,
                   Size, Alignment>;

  explicit add(int state_init) : Base(state_init) {}

  explicit add(std::initializer_list<int> state_init, int tail)
      : Base(state_init, tail) {}

  add(add&& other) noexcept(false) : Base(other.state) {}  // NOLINT

  using Base::operator();
  using Base::state;
};

template <Destructible Destructibility, class Qual,
          NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment>
struct add<Movable::nothrow, Destructibility, Qual, CallExceptionSpec, Size,
           Alignment> : private add<Movable::trivial, Destructibility, Qual,
                                    CallExceptionSpec, Size, Alignment> {
  using Base = add<Movable::trivial, Destructibility, Qual, CallExceptionSpec,
                   Size, Alignment>;

  explicit add(int state_init) : Base(state_init) {}

  explicit add(std::initializer_list<int> state_init, int tail)
      : Base(state_init, tail) {}

  add(add&& other) noexcept : Base(other.state) {}

  using Base::operator();
  using Base::state;
};

// Actual non-member functions rather than function objects
Int add_function(Int&& a, int b, int c) noexcept { return a.value + b + c; }

Int mult_function(Int&& a, int b, int c) noexcept { return a.value * b * c; }

Int square_function(Int const&& a) noexcept { return a.value * a.value; }

template <class Sig>
using AnyInvocable = absl::AnyInvocable<Sig>;

// Instantiations of this template contains all of the compile-time parameters
// for a given instantiation of the AnyInvocable test suite.
template <Movable Movability, Destructible Destructibility, class Qual,
          NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment>
struct TestParams {
  static constexpr Movable kMovability = Movability;
  static constexpr Destructible kDestructibility = Destructibility;
  using Qualifiers = Qual;
  static constexpr NothrowCall kCallExceptionSpec = CallExceptionSpec;
  static constexpr bool kIsNoexcept = kCallExceptionSpec == NothrowCall::yes;
  static constexpr bool kIsRvalueQualified =
      std::is_rvalue_reference<Qual>::value;
  static constexpr ObjSize kSize = Size;
  static constexpr ObjAlign kAlignment = Alignment;

  // These types are used when testing with member object pointer Invocables
  using UnqualifiedUnaryFunType = int(Int const&&)
      ABSL_INTERNAL_NOEXCEPT_SPEC(CallExceptionSpec == NothrowCall::yes);
  using UnaryFunType = GiveQualifiersToFun<Qualifiers, UnqualifiedUnaryFunType>;
  using MemObjPtrType = int(Int::*);
  using UnaryAnyInvType = AnyInvocable<UnaryFunType>;
  using UnaryThisParamType = QualifiersForThis<Qualifiers, UnaryAnyInvType>;

  template <class T>
  static UnaryThisParamType ToUnaryThisParam(T&& fun) {
    return static_cast<UnaryThisParamType>(fun);
  }

  // This function type intentionally uses 3 "kinds" of parameter types.
  //     - A user-defined type
  //     - A reference type
  //     - A scalar type
  //
  // These were chosen because internal forwarding takes place on parameters
  // differently depending based on type properties (scalars are forwarded by
  // value).
  using ResultType = Int;
  using AnyInvocableFunTypeNotNoexcept = Int(Int, const int&, int);
  using UnqualifiedFunType =
      typename std::conditional<kIsNoexcept, Int(Int, const int&, int) noexcept,
                                Int(Int, const int&, int)>::type;
  using FunType = GiveQualifiersToFun<Qualifiers, UnqualifiedFunType>;
  using MemFunPtrType =
      typename std::conditional<kIsNoexcept,
                                Int (Int::*)(const int&, int) noexcept,
                                Int (Int::*)(const int&, int)>::type;
  using AnyInvType = AnyInvocable<FunType>;
  using AddType = add<kMovability, kDestructibility, Qualifiers,
                      kCallExceptionSpec, kSize, kAlignment>;
  using ThisParamType = QualifiersForThis<Qualifiers, AnyInvType>;

  template <class T>
  static ThisParamType ToThisParam(T&& fun) {
    return static_cast<ThisParamType>(fun);
  }

  // These typedefs are used when testing void return type covariance.
  using UnqualifiedVoidFunType =
      typename std::conditional<kIsNoexcept,
                                void(Int, const int&, int) noexcept,
                                void(Int, const int&, int)>::type;
  using VoidFunType = GiveQualifiersToFun<Qualifiers, UnqualifiedVoidFunType>;
  using VoidAnyInvType = AnyInvocable<VoidFunType>;
  using VoidThisParamType = QualifiersForThis<Qualifiers, VoidAnyInvType>;

  template <class T>
  static VoidThisParamType ToVoidThisParam(T&& fun) {
    return static_cast<VoidThisParamType>(fun);
  }

  using CompatibleAnyInvocableFunType =
      absl::conditional_t<std::is_rvalue_reference<Qual>::value,
                          GiveQualifiersToFun<const _&&, UnqualifiedFunType>,
                          GiveQualifiersToFun<const _&, UnqualifiedFunType>>;

  using CompatibleAnyInvType = AnyInvocable<CompatibleAnyInvocableFunType>;

  using IncompatibleInvocable =
      absl::conditional_t<std::is_rvalue_reference<Qual>::value,
                          GiveQualifiersToFun<_&, UnqualifiedFunType>(_::*),
                          GiveQualifiersToFun<_&&, UnqualifiedFunType>(_::*)>;
};

// Given a member-pointer type, this metafunction yields the target type of the
// pointer, not including the class-type. It is used to verify that the function
// call operator of AnyInvocable has the proper signature, corresponding to the
// function type that the user provided.
template <class MemberPtrType>
struct MemberTypeOfImpl;

template <class Class, class T>
struct MemberTypeOfImpl<T(Class::*)> {
  using type = T;
};

template <class MemberPtrType>
using MemberTypeOf = typename MemberTypeOfImpl<MemberPtrType>::type;

template <class T, class = void>
struct IsMemberSwappableImpl : std::false_type {
  static constexpr bool kIsNothrow = false;
};

template <class T>
struct IsMemberSwappableImpl<
    T, absl::void_t<decltype(std::declval<T&>().swap(std::declval<T&>()))>>
    : std::true_type {
  static constexpr bool kIsNothrow =
      noexcept(std::declval<T&>().swap(std::declval<T&>()));
};

template <class T>
using IsMemberSwappable = IsMemberSwappableImpl<T>;

template <class T>
using IsNothrowMemberSwappable =
    std::integral_constant<bool, IsMemberSwappableImpl<T>::kIsNothrow>;

template <class T>
class AnyInvTestBasic : public ::testing::Test {};

TYPED_TEST_SUITE_P(AnyInvTestBasic);

TYPED_TEST_P(AnyInvTestBasic, DefaultConstruction) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun;

  EXPECT_FALSE(static_cast<bool>(fun));

  EXPECT_TRUE(std::is_nothrow_default_constructible<AnyInvType>::value);
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionNullptr) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun = nullptr;

  EXPECT_FALSE(static_cast<bool>(fun));

  EXPECT_TRUE(
      (std::is_nothrow_constructible<AnyInvType, std::nullptr_t>::value));
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionNullFunctionPtr) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType;

  UnqualifiedFunType* const null_fun_ptr = nullptr;
  AnyInvType fun = null_fun_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionNullMemberFunctionPtr) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using MemFunPtrType = typename TypeParam::MemFunPtrType;

  const MemFunPtrType null_mem_fun_ptr = nullptr;
  AnyInvType fun = null_mem_fun_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionNullMemberObjectPtr) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;
  using MemObjPtrType = typename TypeParam::MemObjPtrType;

  const MemObjPtrType null_mem_obj_ptr = nullptr;
  UnaryAnyInvType fun = null_mem_obj_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionMemberFunctionPtr) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun = &Int::MemberFunctionAdd;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionMemberObjectPtr) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;

  UnaryAnyInvType fun = &Int::value;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13));
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionFunctionReferenceDecay) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun = add_function;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionCompatibleAnyInvocableEmpty) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType;

  CompatibleAnyInvType other;
  AnyInvType fun = std::move(other);

  EXPECT_FALSE(static_cast<bool>(other));  // NOLINT
  EXPECT_EQ(other, nullptr);               // NOLINT
  EXPECT_EQ(nullptr, other);               // NOLINT

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, ConstructionCompatibleAnyInvocableNonempty) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType;

  CompatibleAnyInvType other = &add_function;
  AnyInvType fun = std::move(other);

  EXPECT_FALSE(static_cast<bool>(other));  // NOLINT
  EXPECT_EQ(other, nullptr);               // NOLINT
  EXPECT_EQ(nullptr, other);               // NOLINT

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestBasic, ConversionToBool) {
  using AnyInvType = typename TypeParam::AnyInvType;

  {
    AnyInvType fun;

    // This tests contextually-convertible-to-bool.
    EXPECT_FALSE(fun ? true : false);  // NOLINT

    // Make sure that the conversion is not implicit.
    EXPECT_TRUE(
        (std::is_nothrow_constructible<bool, const AnyInvType&>::value));
    EXPECT_FALSE((std::is_convertible<const AnyInvType&, bool>::value));
  }

  {
    AnyInvType fun = &add_function;

    // This tests contextually-convertible-to-bool.
    EXPECT_TRUE(fun ? true : false);  // NOLINT
  }
}

TYPED_TEST_P(AnyInvTestBasic, Invocation) {
  using AnyInvType = typename TypeParam::AnyInvType;

  using FunType = typename TypeParam::FunType;
  using AnyInvCallType = MemberTypeOf<decltype(&AnyInvType::operator())>;

  // Make sure the function call operator of AnyInvocable always has the
  // type that was specified via the template argument.
  EXPECT_TRUE((std::is_same<AnyInvCallType, FunType>::value));

  AnyInvType fun = &add_function;

  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceConstruction) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType fun(absl::in_place_type<AddType>, 5);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceConstructionInitializerList) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType fun(absl::in_place_type<AddType>, {1, 2, 3, 4}, 5);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(39, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceNullFunPtrConstruction) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType;

  AnyInvType fun(absl::in_place_type<UnqualifiedFunType*>, nullptr);

  // In-place construction does not lead to empty.
  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceNullFunPtrConstructionValueInit) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType;

  AnyInvType fun(absl::in_place_type<UnqualifiedFunType*>);

  // In-place construction does not lead to empty.
  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemFunPtrConstruction) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using MemFunPtrType = typename TypeParam::MemFunPtrType;

  AnyInvType fun(absl::in_place_type<MemFunPtrType>, nullptr);

  // In-place construction does not lead to empty.
  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemFunPtrConstructionValueInit) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using MemFunPtrType = typename TypeParam::MemFunPtrType;

  AnyInvType fun(absl::in_place_type<MemFunPtrType>);

  // In-place construction does not lead to empty.
  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemObjPtrConstruction) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;
  using MemObjPtrType = typename TypeParam::MemObjPtrType;

  UnaryAnyInvType fun(absl::in_place_type<MemObjPtrType>, nullptr);

  // In-place construction does not lead to empty.
  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemObjPtrConstructionValueInit) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;
  using MemObjPtrType = typename TypeParam::MemObjPtrType;

  UnaryAnyInvType fun(absl::in_place_type<MemObjPtrType>);

  // In-place construction does not lead to empty.
  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, InPlaceVoidCovarianceConstruction) {
  using VoidAnyInvType = typename TypeParam::VoidAnyInvType;
  using AddType = typename TypeParam::AddType;

  VoidAnyInvType fun(absl::in_place_type<AddType>, 5);

  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromEmpty) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType source_fun;
  AnyInvType fun(std::move(source_fun));

  EXPECT_FALSE(static_cast<bool>(fun));

  EXPECT_TRUE(std::is_nothrow_move_constructible<AnyInvType>::value);
}

TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromNonEmpty) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType source_fun(absl::in_place_type<AddType>, 5);
  AnyInvType fun(std::move(source_fun));

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

  EXPECT_TRUE(std::is_nothrow_move_constructible<AnyInvType>::value);
}

TYPED_TEST_P(AnyInvTestBasic, ComparisonWithNullptrEmpty) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun;

  EXPECT_TRUE(fun == nullptr);
  EXPECT_TRUE(nullptr == fun);

  EXPECT_FALSE(fun != nullptr);
  EXPECT_FALSE(nullptr != fun);
}

TYPED_TEST_P(AnyInvTestBasic, ComparisonWithNullptrNonempty) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType fun(absl::in_place_type<AddType>, 5);

  EXPECT_FALSE(fun == nullptr);
  EXPECT_FALSE(nullptr == fun);

  EXPECT_TRUE(fun != nullptr);
  EXPECT_TRUE(nullptr != fun);
}

TYPED_TEST_P(AnyInvTestBasic, ResultType) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using ExpectedResultType = typename TypeParam::ResultType;

  EXPECT_TRUE((std::is_same<typename AnyInvType::result_type,
                            ExpectedResultType>::value));
}

template <class T>
class AnyInvTestCombinatoric : public ::testing::Test {};

TYPED_TEST_SUITE_P(AnyInvTestCombinatoric);

TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignEmptyEmptyLhsRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType source_fun;
  AnyInvType fun;

  fun = std::move(source_fun);

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignEmptyLhsNonemptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType source_fun(absl::in_place_type<AddType>, 5);
  AnyInvType fun;

  fun = std::move(source_fun);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignNonemptyEmptyLhsRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType source_fun;
  AnyInvType fun(absl::in_place_type<AddType>, 5);

  fun = std::move(source_fun);

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignNonemptyLhsNonemptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType source_fun(absl::in_place_type<AddType>, 5);
  AnyInvType fun(absl::in_place_type<AddType>, 20);

  fun = std::move(source_fun);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric, SelfMoveAssignEmpty) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType source_fun;
  source_fun = std::move(source_fun);

  // This space intentionally left blank.
}

TYPED_TEST_P(AnyInvTestCombinatoric, SelfMoveAssignNonempty) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType source_fun(absl::in_place_type<AddType>, 5);
  source_fun = std::move(source_fun);

  // This space intentionally left blank.
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullptrEmptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun;
  fun = nullptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullFunctionPtrEmptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType;

  UnqualifiedFunType* const null_fun_ptr = nullptr;
  AnyInvType fun;
  fun = null_fun_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberFunctionPtrEmptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using MemFunPtrType = typename TypeParam::MemFunPtrType;

  const MemFunPtrType null_mem_fun_ptr = nullptr;
  AnyInvType fun;
  fun = null_mem_fun_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberObjectPtrEmptyLhs) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;
  using MemObjPtrType = typename TypeParam::MemObjPtrType;

  const MemObjPtrType null_mem_obj_ptr = nullptr;
  UnaryAnyInvType fun;
  fun = null_mem_obj_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberFunctionPtrEmptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun;
  fun = &Int::MemberFunctionAdd;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberObjectPtrEmptyLhs) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;

  UnaryAnyInvType fun;
  fun = &Int::value;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignFunctionReferenceDecayEmptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun;
  fun = add_function;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric,
             AssignCompatibleAnyInvocableEmptyLhsEmptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType;

  CompatibleAnyInvType other;
  AnyInvType fun;
  fun = std::move(other);

  EXPECT_FALSE(static_cast<bool>(other));  // NOLINT
  EXPECT_EQ(other, nullptr);               // NOLINT
  EXPECT_EQ(nullptr, other);               // NOLINT

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric,
             AssignCompatibleAnyInvocableEmptyLhsNonemptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType;

  CompatibleAnyInvType other = &add_function;
  AnyInvType fun;
  fun = std::move(other);

  EXPECT_FALSE(static_cast<bool>(other));  // NOLINT

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullptrNonemptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun = &mult_function;
  fun = nullptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullFunctionPtrNonemptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType;

  UnqualifiedFunType* const null_fun_ptr = nullptr;
  AnyInvType fun = &mult_function;
  fun = null_fun_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberFunctionPtrNonemptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using MemFunPtrType = typename TypeParam::MemFunPtrType;

  const MemFunPtrType null_mem_fun_ptr = nullptr;
  AnyInvType fun = &mult_function;
  fun = null_mem_fun_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberObjectPtrNonemptyLhs) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;
  using MemObjPtrType = typename TypeParam::MemObjPtrType;

  const MemObjPtrType null_mem_obj_ptr = nullptr;
  UnaryAnyInvType fun = &square_function;
  fun = null_mem_obj_ptr;

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberFunctionPtrNonemptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun = &mult_function;
  fun = &Int::MemberFunctionAdd;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberObjectPtrNonemptyLhs) {
  using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType;

  UnaryAnyInvType fun = &square_function;
  fun = &Int::value;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13));
}

TYPED_TEST_P(AnyInvTestCombinatoric, AssignFunctionReferenceDecayNonemptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  AnyInvType fun = &mult_function;
  fun = add_function;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric,
             AssignCompatibleAnyInvocableNonemptyLhsEmptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType;

  CompatibleAnyInvType other;
  AnyInvType fun = &mult_function;
  fun = std::move(other);

  EXPECT_FALSE(static_cast<bool>(other));  // NOLINT
  EXPECT_EQ(other, nullptr);               // NOLINT
  EXPECT_EQ(nullptr, other);               // NOLINT

  EXPECT_FALSE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestCombinatoric,
             AssignCompatibleAnyInvocableNonemptyLhsNonemptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType;

  CompatibleAnyInvType other = &add_function;
  AnyInvType fun = &mult_function;
  fun = std::move(other);

  EXPECT_FALSE(static_cast<bool>(other));  // NOLINT

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value);
}

TYPED_TEST_P(AnyInvTestCombinatoric, SwapEmptyLhsEmptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;

  // Swap idiom
  {
    AnyInvType fun;
    AnyInvType other;

    using std::swap;
    swap(fun, other);

    EXPECT_FALSE(static_cast<bool>(fun));
    EXPECT_FALSE(static_cast<bool>(other));

    EXPECT_TRUE(
        absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value);
  }

  // Member swap
  {
    AnyInvType fun;
    AnyInvType other;

    fun.swap(other);

    EXPECT_FALSE(static_cast<bool>(fun));
    EXPECT_FALSE(static_cast<bool>(other));

    EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value);
  }
}

TYPED_TEST_P(AnyInvTestCombinatoric, SwapEmptyLhsNonemptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  // Swap idiom
  {
    AnyInvType fun;
    AnyInvType other(absl::in_place_type<AddType>, 5);

    using std::swap;
    swap(fun, other);

    EXPECT_TRUE(static_cast<bool>(fun));
    EXPECT_FALSE(static_cast<bool>(other));

    EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

    EXPECT_TRUE(
        absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value);
  }

  // Member swap
  {
    AnyInvType fun;
    AnyInvType other(absl::in_place_type<AddType>, 5);

    fun.swap(other);

    EXPECT_TRUE(static_cast<bool>(fun));
    EXPECT_FALSE(static_cast<bool>(other));

    EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

    EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value);
  }
}

TYPED_TEST_P(AnyInvTestCombinatoric, SwapNonemptyLhsEmptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  // Swap idiom
  {
    AnyInvType fun(absl::in_place_type<AddType>, 5);
    AnyInvType other;

    using std::swap;
    swap(fun, other);

    EXPECT_FALSE(static_cast<bool>(fun));
    EXPECT_TRUE(static_cast<bool>(other));

    EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value);

    EXPECT_TRUE(
        absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value);
  }

  // Member swap
  {
    AnyInvType fun(absl::in_place_type<AddType>, 5);
    AnyInvType other;

    fun.swap(other);

    EXPECT_FALSE(static_cast<bool>(fun));
    EXPECT_TRUE(static_cast<bool>(other));

    EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value);

    EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value);
  }
}

TYPED_TEST_P(AnyInvTestCombinatoric, SwapNonemptyLhsNonemptyRhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  // Swap idiom
  {
    AnyInvType fun(absl::in_place_type<AddType>, 5);
    AnyInvType other(absl::in_place_type<AddType>, 6);

    using std::swap;
    swap(fun, other);

    EXPECT_TRUE(static_cast<bool>(fun));
    EXPECT_TRUE(static_cast<bool>(other));

    EXPECT_EQ(30, TypeParam::ToThisParam(fun)(7, 8, 9).value);
    EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value);

    EXPECT_TRUE(
        absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value);
  }

  // Member swap
  {
    AnyInvType fun(absl::in_place_type<AddType>, 5);
    AnyInvType other(absl::in_place_type<AddType>, 6);

    fun.swap(other);

    EXPECT_TRUE(static_cast<bool>(fun));
    EXPECT_TRUE(static_cast<bool>(other));

    EXPECT_EQ(30, TypeParam::ToThisParam(fun)(7, 8, 9).value);
    EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value);

    EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value);
  }
}

template <class T>
class AnyInvTestMovable : public ::testing::Test {};

TYPED_TEST_SUITE_P(AnyInvTestMovable);

TYPED_TEST_P(AnyInvTestMovable, ConversionConstructionUserDefinedType) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType fun(AddType(5));

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
}

TYPED_TEST_P(AnyInvTestMovable, ConversionConstructionVoidCovariance) {
  using VoidAnyInvType = typename TypeParam::VoidAnyInvType;
  using AddType = typename TypeParam::AddType;

  VoidAnyInvType fun(AddType(5));

  EXPECT_TRUE(static_cast<bool>(fun));
}

TYPED_TEST_P(AnyInvTestMovable, ConversionAssignUserDefinedTypeEmptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType fun;
  fun = AddType(5);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
}

TYPED_TEST_P(AnyInvTestMovable, ConversionAssignUserDefinedTypeNonemptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AnyInvType fun = &add_function;
  fun = AddType(5);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
}

TYPED_TEST_P(AnyInvTestMovable, ConversionAssignVoidCovariance) {
  using VoidAnyInvType = typename TypeParam::VoidAnyInvType;
  using AddType = typename TypeParam::AddType;

  VoidAnyInvType fun;
  fun = AddType(5);

  EXPECT_TRUE(static_cast<bool>(fun));
}

template <class T>
class AnyInvTestNoexceptFalse : public ::testing::Test {};

TYPED_TEST_SUITE_P(AnyInvTestNoexceptFalse);

TYPED_TEST_P(AnyInvTestNoexceptFalse, ConversionConstructionConstraints) {
  using AnyInvType = typename TypeParam::AnyInvType;

  EXPECT_TRUE((std::is_constructible<
               AnyInvType,
               typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value));
  EXPECT_FALSE((
      std::is_constructible<AnyInvType,
                            typename TypeParam::IncompatibleInvocable>::value));
}

TYPED_TEST_P(AnyInvTestNoexceptFalse, ConversionAssignConstraints) {
  using AnyInvType = typename TypeParam::AnyInvType;

  EXPECT_TRUE((std::is_assignable<
               AnyInvType&,
               typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value));
  EXPECT_FALSE(
      (std::is_assignable<AnyInvType&,
                          typename TypeParam::IncompatibleInvocable>::value));
}

template <class T>
class AnyInvTestNoexceptTrue : public ::testing::Test {};

TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue);

TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionConstructionConstraints) {
#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L
  GTEST_SKIP() << "Noexcept was not part of the type system before C++17.";
#else
  using AnyInvType = typename TypeParam::AnyInvType;

// TODO(b/217761454): Fix this and re-enable for MSVC.
#ifndef _MSC_VER
  EXPECT_FALSE((std::is_constructible<
                AnyInvType,
                typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value));
#endif
  EXPECT_FALSE((
      std::is_constructible<AnyInvType,
                            typename TypeParam::IncompatibleInvocable>::value));
#endif
}

TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionAssignConstraints) {
#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L
  GTEST_SKIP() << "Noexcept was not part of the type system before C++17.";
#else
  using AnyInvType = typename TypeParam::AnyInvType;

// TODO(b/217761454): Fix this and re-enable for MSVC.
#ifndef _MSC_VER
  EXPECT_FALSE((std::is_assignable<
                AnyInvType&,
                typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value));
#endif
  EXPECT_FALSE(
      (std::is_assignable<AnyInvType&,
                          typename TypeParam::IncompatibleInvocable>::value));
#endif
}

template <class T>
class AnyInvTestNonRvalue : public ::testing::Test {};

TYPED_TEST_SUITE_P(AnyInvTestNonRvalue);

TYPED_TEST_P(AnyInvTestNonRvalue, ConversionConstructionReferenceWrapper) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AddType add(4);
  AnyInvType fun = std::ref(add);
  add.state = 5;

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
}

TYPED_TEST_P(AnyInvTestNonRvalue, NonMoveableResultType) {
#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L
  GTEST_SKIP() << "Copy/move elision was not standard before C++17";
#else
  // Define a result type that cannot be copy- or move-constructed.
  struct Result {
    int x;

    explicit Result(const int x_in) : x(x_in) {}
    Result(Result&&) = delete;
  };

  static_assert(!std::is_move_constructible<Result>::value, "");
  static_assert(!std::is_copy_constructible<Result>::value, "");

  // Assumption check: it should nevertheless be possible to use functors that
  // return a Result struct according to the language rules.
  const auto return_17 = []() noexcept { return Result(17); };
  EXPECT_EQ(17, return_17().x);

  // Just like plain functors, it should work fine to use an AnyInvocable that
  // returns the non-moveable type.
  using UnqualifiedFun =
      absl::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>;

  using Fun =
      GiveQualifiersToFun<typename TypeParam::Qualifiers, UnqualifiedFun>;

  AnyInvocable<Fun> any_inv(return_17);
  EXPECT_EQ(17, any_inv().x);
#endif
}

TYPED_TEST_P(AnyInvTestNonRvalue, ConversionAssignReferenceWrapperEmptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AddType add(4);
  AnyInvType fun;
  fun = std::ref(add);
  add.state = 5;
  EXPECT_TRUE(
      (std::is_nothrow_assignable<AnyInvType&,
                                  std::reference_wrapper<AddType>>::value));

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
}

TYPED_TEST_P(AnyInvTestNonRvalue, ConversionAssignReferenceWrapperNonemptyLhs) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  AddType add(4);
  AnyInvType fun = &mult_function;
  fun = std::ref(add);
  add.state = 5;
  EXPECT_TRUE(
      (std::is_nothrow_assignable<AnyInvType&,
                                  std::reference_wrapper<AddType>>::value));

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value);

  EXPECT_TRUE(static_cast<bool>(fun));
  EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value);
}

template <class T>
class AnyInvTestRvalue : public ::testing::Test {};

TYPED_TEST_SUITE_P(AnyInvTestRvalue);

TYPED_TEST_P(AnyInvTestRvalue, ConversionConstructionReferenceWrapper) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  EXPECT_FALSE((
      std::is_convertible<std::reference_wrapper<AddType>, AnyInvType>::value));
}

TYPED_TEST_P(AnyInvTestRvalue, NonMoveableResultType) {
#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L
  GTEST_SKIP() << "Copy/move elision was not standard before C++17";
#else
  // Define a result type that cannot be copy- or move-constructed.
  struct Result {
    int x;

    explicit Result(const int x_in) : x(x_in) {}
    Result(Result&&) = delete;
  };

  static_assert(!std::is_move_constructible<Result>::value, "");
  static_assert(!std::is_copy_constructible<Result>::value, "");

  // Assumption check: it should nevertheless be possible to use functors that
  // return a Result struct according to the language rules.
  const auto return_17 = []() noexcept { return Result(17); };
  EXPECT_EQ(17, return_17().x);

  // Just like plain functors, it should work fine to use an AnyInvocable that
  // returns the non-moveable type.
  using UnqualifiedFun =
      absl::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>;

  using Fun =
      GiveQualifiersToFun<typename TypeParam::Qualifiers, UnqualifiedFun>;

  EXPECT_EQ(17, AnyInvocable<Fun>(return_17)().x);
#endif
}

TYPED_TEST_P(AnyInvTestRvalue, ConversionAssignReferenceWrapper) {
  using AnyInvType = typename TypeParam::AnyInvType;
  using AddType = typename TypeParam::AddType;

  EXPECT_FALSE((
      std::is_assignable<AnyInvType&, std::reference_wrapper<AddType>>::value));
}

// NOTE: This test suite originally attempted to enumerate all possible
// combinations of type properties but the build-time started getting too large.
// Instead, it is now assumed that certain parameters are orthogonal and so
// some combinations are elided.

// A metafunction to form a TypeList of all cv and non-rvalue ref combinations,
// coupled with all of the other explicitly specified parameters.
template <Movable Mov, Destructible Dest, NothrowCall CallExceptionSpec,
          ObjSize Size, ObjAlign Align>
using NonRvalueQualifiedTestParams = ::testing::Types<               //
    TestParams<Mov, Dest, _, CallExceptionSpec, Size, Align>,        //
    TestParams<Mov, Dest, const _, CallExceptionSpec, Size, Align>,  //
    TestParams<Mov, Dest, _&, CallExceptionSpec, Size, Align>,       //
    TestParams<Mov, Dest, const _&, CallExceptionSpec, Size, Align>>;

// A metafunction to form a TypeList of const and non-const rvalue ref
// qualifiers, coupled with all of the other explicitly specified parameters.
template <Movable Mov, Destructible Dest, NothrowCall CallExceptionSpec,
          ObjSize Size, ObjAlign Align>
using RvalueQualifiedTestParams = ::testing::Types<
    TestParams<Mov, Dest, _&&, CallExceptionSpec, Size, Align>,       //
    TestParams<Mov, Dest, const _&&, CallExceptionSpec, Size, Align>  //
    >;

// All qualifier combinations and a noexcept function type
using TestParameterListNonRvalueQualifiersNothrowCall =
    NonRvalueQualifiedTestParams<Movable::trivial, Destructible::trivial,
                                 NothrowCall::yes, ObjSize::small,
                                 ObjAlign::normal>;
using TestParameterListRvalueQualifiersNothrowCall =
    RvalueQualifiedTestParams<Movable::trivial, Destructible::trivial,
                              NothrowCall::yes, ObjSize::small,
                              ObjAlign::normal>;

// All qualifier combinations and a non-noexcept function type
using TestParameterListNonRvalueQualifiersCallMayThrow =
    NonRvalueQualifiedTestParams<Movable::trivial, Destructible::trivial,
                                 NothrowCall::no, ObjSize::small,
                                 ObjAlign::normal>;
using TestParameterListRvalueQualifiersCallMayThrow =
    RvalueQualifiedTestParams<Movable::trivial, Destructible::trivial,
                              NothrowCall::no, ObjSize::small,
                              ObjAlign::normal>;

// Lists of various cases that should lead to remote storage
using TestParameterListRemoteMovable = ::testing::Types<
    // "Normal" aligned types that are large and have trivial destructors
    TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>,  //
    TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>,  //
    TestParams<Movable::yes, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>,  //
    TestParams<Movable::yes, Destructible::trivial, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>,  //

    // Same as above but with non-trivial destructors
    TestParams<Movable::trivial, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>,  //
    TestParams<Movable::nothrow, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>,  //
    TestParams<Movable::yes, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>,  //
    TestParams<Movable::yes, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>  //

// Dynamic memory allocation for over-aligned data was introduced in C++17.
// See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r4.html
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
    // Types that must use remote storage because of a large alignment.
    ,
    TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::large>,  //
    TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::large>,  //
    TestParams<Movable::trivial, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::small, ObjAlign::large>,  //
    TestParams<Movable::nothrow, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::small, ObjAlign::large>  //
#endif
    >;
using TestParameterListRemoteNonMovable = ::testing::Types<
    // "Normal" aligned types that are large and have trivial destructors
    TestParams<Movable::no, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>,  //
    TestParams<Movable::no, Destructible::trivial, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>,  //
    // Same as above but with non-trivial destructors
    TestParams<Movable::no, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>,  //
    TestParams<Movable::no, Destructible::nothrow, _, NothrowCall::no,
               ObjSize::large, ObjAlign::normal>  //
    >;

// Parameters that lead to local storage
using TestParameterListLocal = ::testing::Types<
    // Types that meet the requirements and have trivial destructors
    TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>,  //
    TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>,  //

    // Same as above but with non-trivial destructors
    TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>,  //
    TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no,
               ObjSize::small, ObjAlign::normal>  //
    >;

// All of the tests that are run for every possible combination of types.
REGISTER_TYPED_TEST_SUITE_P(
    AnyInvTestBasic, DefaultConstruction, ConstructionNullptr,
    ConstructionNullFunctionPtr, ConstructionNullMemberFunctionPtr,
    ConstructionNullMemberObjectPtr, ConstructionMemberFunctionPtr,
    ConstructionMemberObjectPtr, ConstructionFunctionReferenceDecay,
    ConstructionCompatibleAnyInvocableEmpty,
    ConstructionCompatibleAnyInvocableNonempty, InPlaceConstruction,
    ConversionToBool, Invocation, InPlaceConstructionInitializerList,
    InPlaceNullFunPtrConstruction, InPlaceNullFunPtrConstructionValueInit,
    InPlaceNullMemFunPtrConstruction, InPlaceNullMemFunPtrConstructionValueInit,
    InPlaceNullMemObjPtrConstruction, InPlaceNullMemObjPtrConstructionValueInit,
    InPlaceVoidCovarianceConstruction, MoveConstructionFromEmpty,
    MoveConstructionFromNonEmpty, ComparisonWithNullptrEmpty,
    ComparisonWithNullptrNonempty, ResultType);

INSTANTIATE_TYPED_TEST_SUITE_P(
    NonRvalueCallMayThrow, AnyInvTestBasic,
    TestParameterListNonRvalueQualifiersCallMayThrow);
INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestBasic,
                               TestParameterListRvalueQualifiersCallMayThrow);

INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestBasic,
                               TestParameterListRemoteMovable);
INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestBasic,
                               TestParameterListRemoteNonMovable);

INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestBasic, TestParameterListLocal);

INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestBasic,
                               TestParameterListNonRvalueQualifiersNothrowCall);
INSTANTIATE_TYPED_TEST_SUITE_P(CallNothrowRvalue, AnyInvTestBasic,
                               TestParameterListRvalueQualifiersNothrowCall);

// Tests for functions that take two operands.
REGISTER_TYPED_TEST_SUITE_P(
    AnyInvTestCombinatoric, MoveAssignEmptyEmptyLhsRhs,
    MoveAssignEmptyLhsNonemptyRhs, MoveAssignNonemptyEmptyLhsRhs,
    MoveAssignNonemptyLhsNonemptyRhs, SelfMoveAssignEmpty,
    SelfMoveAssignNonempty, AssignNullptrEmptyLhs,
    AssignNullFunctionPtrEmptyLhs, AssignNullMemberFunctionPtrEmptyLhs,
    AssignNullMemberObjectPtrEmptyLhs, AssignMemberFunctionPtrEmptyLhs,
    AssignMemberObjectPtrEmptyLhs, AssignFunctionReferenceDecayEmptyLhs,
    AssignCompatibleAnyInvocableEmptyLhsEmptyRhs,
    AssignCompatibleAnyInvocableEmptyLhsNonemptyRhs, AssignNullptrNonemptyLhs,
    AssignNullFunctionPtrNonemptyLhs, AssignNullMemberFunctionPtrNonemptyLhs,
    AssignNullMemberObjectPtrNonemptyLhs, AssignMemberFunctionPtrNonemptyLhs,
    AssignMemberObjectPtrNonemptyLhs, AssignFunctionReferenceDecayNonemptyLhs,
    AssignCompatibleAnyInvocableNonemptyLhsEmptyRhs,
    AssignCompatibleAnyInvocableNonemptyLhsNonemptyRhs, SwapEmptyLhsEmptyRhs,
    SwapEmptyLhsNonemptyRhs, SwapNonemptyLhsEmptyRhs,
    SwapNonemptyLhsNonemptyRhs);

INSTANTIATE_TYPED_TEST_SUITE_P(
    NonRvalueCallMayThrow, AnyInvTestCombinatoric,
    TestParameterListNonRvalueQualifiersCallMayThrow);
INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestCombinatoric,
                               TestParameterListRvalueQualifiersCallMayThrow);

INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestCombinatoric,
                               TestParameterListRemoteMovable);
INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestCombinatoric,
                               TestParameterListRemoteNonMovable);

INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestCombinatoric,
                               TestParameterListLocal);

INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestCombinatoric,
                               TestParameterListNonRvalueQualifiersNothrowCall);
INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestCombinatoric,
                               TestParameterListRvalueQualifiersNothrowCall);

REGISTER_TYPED_TEST_SUITE_P(AnyInvTestMovable,
                            ConversionConstructionUserDefinedType,
                            ConversionConstructionVoidCovariance,
                            ConversionAssignUserDefinedTypeEmptyLhs,
                            ConversionAssignUserDefinedTypeNonemptyLhs,
                            ConversionAssignVoidCovariance);

INSTANTIATE_TYPED_TEST_SUITE_P(
    NonRvalueCallMayThrow, AnyInvTestMovable,
    TestParameterListNonRvalueQualifiersCallMayThrow);
INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestMovable,
                               TestParameterListRvalueQualifiersCallMayThrow);

INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestMovable,
                               TestParameterListRemoteMovable);

INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestMovable,
                               TestParameterListLocal);

INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestMovable,
                               TestParameterListNonRvalueQualifiersNothrowCall);
INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestMovable,
                               TestParameterListRvalueQualifiersNothrowCall);

REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptFalse,
                            ConversionConstructionConstraints,
                            ConversionAssignConstraints);

INSTANTIATE_TYPED_TEST_SUITE_P(
    NonRvalueCallMayThrow, AnyInvTestNoexceptFalse,
    TestParameterListNonRvalueQualifiersCallMayThrow);
INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestNoexceptFalse,
                               TestParameterListRvalueQualifiersCallMayThrow);

INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestNoexceptFalse,
                               TestParameterListRemoteMovable);
INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestNoexceptFalse,
                               TestParameterListRemoteNonMovable);

INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestNoexceptFalse,
                               TestParameterListLocal);

REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue,
                            ConversionConstructionConstraints,
                            ConversionAssignConstraints);

INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestNoexceptTrue,
                               TestParameterListNonRvalueQualifiersNothrowCall);
INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestNoexceptTrue,
                               TestParameterListRvalueQualifiersNothrowCall);

REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNonRvalue,
                            ConversionConstructionReferenceWrapper,
                            NonMoveableResultType,
                            ConversionAssignReferenceWrapperEmptyLhs,
                            ConversionAssignReferenceWrapperNonemptyLhs);

INSTANTIATE_TYPED_TEST_SUITE_P(
    NonRvalueCallMayThrow, AnyInvTestNonRvalue,
    TestParameterListNonRvalueQualifiersCallMayThrow);

INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestNonRvalue,
                               TestParameterListRemoteMovable);
INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestNonRvalue,
                               TestParameterListRemoteNonMovable);

INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestNonRvalue,
                               TestParameterListLocal);

INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestNonRvalue,
                               TestParameterListNonRvalueQualifiersNothrowCall);

REGISTER_TYPED_TEST_SUITE_P(AnyInvTestRvalue,
                            ConversionConstructionReferenceWrapper,
                            NonMoveableResultType,
                            ConversionAssignReferenceWrapper);

INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestRvalue,
                               TestParameterListRvalueQualifiersCallMayThrow);

INSTANTIATE_TYPED_TEST_SUITE_P(CallNothrowRvalue, AnyInvTestRvalue,
                               TestParameterListRvalueQualifiersNothrowCall);

// Minimal SFINAE testing for platforms where we can't run the tests, but we can
// build binaries for.
static_assert(
    std::is_convertible<void (*)(), absl::AnyInvocable<void() &&>>::value, "");
static_assert(!std::is_convertible<void*, absl::AnyInvocable<void() &&>>::value,
              "");

#undef ABSL_INTERNAL_NOEXCEPT_SPEC

}  // namespace
