chromium/third_party/abseil-cpp/absl/types/optional_test.cc

// Copyright 2017 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/types/optional.h"

// This test is a no-op when absl::optional is an alias for std::optional.
#if !defined(ABSL_USES_STD_OPTIONAL)

#include <string>
#include <type_traits>
#include <utility>

#include "gtest/gtest.h"
#include "absl/base/config.h"
#include "absl/log/log.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"

#if defined(__cplusplus) && __cplusplus >= 202002L
// In C++20, volatile-qualified return types are deprecated.
#define ABSL_VOLATILE_RETURN_TYPES_DEPRECATED
#endif

// The following types help test an internal compiler error in GCC5 though
// GCC10. The case OptionalTest.InternalCompilerErrorInGcc5ToGcc10 crashes the
// compiler without a workaround. This test case should remain at the beginning
// of the file as the internal compiler error is sensitive to other constructs
// in this file.
template <class T, class...>
using GccIceHelper1 = T;
template <typename T>
struct GccIceHelper2 {};
template <typename T>
class GccIce {
  template <typename U,
            typename SecondTemplateArgHasToExistForSomeReason = void,
            typename DependentType = void,
            typename = std::is_assignable<GccIceHelper1<T, DependentType>&, U>>
  GccIce& operator=(GccIceHelper2<U> const&) {}
};

TEST(OptionalTest, InternalCompilerErrorInGcc5ToGcc10) {
  GccIce<int> instantiate_ice_with_same_type_as_optional;
  static_cast<void>(instantiate_ice_with_same_type_as_optional);
  absl::optional<int> val1;
  absl::optional<int> val2;
  val1 = val2;
}

struct Hashable {};

namespace std {
template <>
struct hash<Hashable> {
  size_t operator()(const Hashable&) { return 0; }
};
}  // namespace std

struct NonHashable {};

namespace {

std::string TypeQuals(std::string&) { return "&"; }
std::string TypeQuals(std::string&&) { return "&&"; }
std::string TypeQuals(const std::string&) { return "c&"; }
std::string TypeQuals(const std::string&&) { return "c&&"; }

struct StructorListener {
  int construct0 = 0;
  int construct1 = 0;
  int construct2 = 0;
  int listinit = 0;
  int copy = 0;
  int move = 0;
  int copy_assign = 0;
  int move_assign = 0;
  int destruct = 0;
  int volatile_copy = 0;
  int volatile_move = 0;
  int volatile_copy_assign = 0;
  int volatile_move_assign = 0;
};

// Suppress MSVC warnings.
// 4521: multiple copy constructors specified
// 4522: multiple assignment operators specified
// We wrote multiple of them to test that the correct overloads are selected.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4521)
#pragma warning(disable : 4522)
#endif
struct Listenable {
  static StructorListener* listener;

  Listenable() { ++listener->construct0; }
  explicit Listenable(int /*unused*/) { ++listener->construct1; }
  Listenable(int /*unused*/, int /*unused*/) { ++listener->construct2; }
  Listenable(std::initializer_list<int> /*unused*/) { ++listener->listinit; }
  Listenable(const Listenable& /*unused*/) { ++listener->copy; }
  Listenable(const volatile Listenable& /*unused*/) {
    ++listener->volatile_copy;
  }
  Listenable(volatile Listenable&& /*unused*/) { ++listener->volatile_move; }
  Listenable(Listenable&& /*unused*/) { ++listener->move; }
  Listenable& operator=(const Listenable& /*unused*/) {
    ++listener->copy_assign;
    return *this;
  }
  Listenable& operator=(Listenable&& /*unused*/) {
    ++listener->move_assign;
    return *this;
  }
  // use void return type instead of volatile T& to work around GCC warning
  // when the assignment's returned reference is ignored.
  void operator=(const volatile Listenable& /*unused*/) volatile {
    ++listener->volatile_copy_assign;
  }
  void operator=(volatile Listenable&& /*unused*/) volatile {
    ++listener->volatile_move_assign;
  }
  ~Listenable() { ++listener->destruct; }
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif

StructorListener* Listenable::listener = nullptr;

struct ConstexprType {
  enum CtorTypes {
    kCtorDefault,
    kCtorInt,
    kCtorInitializerList,
    kCtorConstChar
  };
  constexpr ConstexprType() : x(kCtorDefault) {}
  constexpr explicit ConstexprType(int i) : x(kCtorInt) {}
  constexpr ConstexprType(std::initializer_list<int> il)
      : x(kCtorInitializerList) {}
  constexpr ConstexprType(const char*)  // NOLINT(runtime/explicit)
      : x(kCtorConstChar) {}
  int x;
};

struct Copyable {
  Copyable() {}
  Copyable(const Copyable&) {}
  Copyable& operator=(const Copyable&) { return *this; }
};

struct MoveableThrow {
  MoveableThrow() {}
  MoveableThrow(MoveableThrow&&) {}
  MoveableThrow& operator=(MoveableThrow&&) { return *this; }
};

struct MoveableNoThrow {
  MoveableNoThrow() {}
  MoveableNoThrow(MoveableNoThrow&&) noexcept {}
  MoveableNoThrow& operator=(MoveableNoThrow&&) noexcept { return *this; }
};

struct NonMovable {
  NonMovable() {}
  NonMovable(const NonMovable&) = delete;
  NonMovable& operator=(const NonMovable&) = delete;
  NonMovable(NonMovable&&) = delete;
  NonMovable& operator=(NonMovable&&) = delete;
};

struct NoDefault {
  NoDefault() = delete;
  NoDefault(const NoDefault&) {}
  NoDefault& operator=(const NoDefault&) { return *this; }
};

struct ConvertsFromInPlaceT {
  ConvertsFromInPlaceT(absl::in_place_t) {}  // NOLINT
};

TEST(optionalTest, DefaultConstructor) {
  absl::optional<int> empty;
  EXPECT_FALSE(empty);
  constexpr absl::optional<int> cempty;
  static_assert(!cempty.has_value(), "");
  EXPECT_TRUE(
      std::is_nothrow_default_constructible<absl::optional<int>>::value);
}

TEST(optionalTest, nulloptConstructor) {
  absl::optional<int> empty(absl::nullopt);
  EXPECT_FALSE(empty);
  constexpr absl::optional<int> cempty{absl::nullopt};
  static_assert(!cempty.has_value(), "");
  EXPECT_TRUE((std::is_nothrow_constructible<absl::optional<int>,
                                             absl::nullopt_t>::value));
}

TEST(optionalTest, CopyConstructor) {
  {
    absl::optional<int> empty, opt42 = 42;
    absl::optional<int> empty_copy(empty);
    EXPECT_FALSE(empty_copy);
    absl::optional<int> opt42_copy(opt42);
    EXPECT_TRUE(opt42_copy);
    EXPECT_EQ(42, *opt42_copy);
  }
  {
    absl::optional<const int> empty, opt42 = 42;
    absl::optional<const int> empty_copy(empty);
    EXPECT_FALSE(empty_copy);
    absl::optional<const int> opt42_copy(opt42);
    EXPECT_TRUE(opt42_copy);
    EXPECT_EQ(42, *opt42_copy);
  }
#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  {
    absl::optional<volatile int> empty, opt42 = 42;
    absl::optional<volatile int> empty_copy(empty);
    EXPECT_FALSE(empty_copy);
    absl::optional<volatile int> opt42_copy(opt42);
    EXPECT_TRUE(opt42_copy);
    EXPECT_EQ(42, *opt42_copy);
  }
#endif
  // test copyablility
  EXPECT_TRUE(std::is_copy_constructible<absl::optional<int>>::value);
  EXPECT_TRUE(std::is_copy_constructible<absl::optional<Copyable>>::value);
  EXPECT_FALSE(
      std::is_copy_constructible<absl::optional<MoveableThrow>>::value);
  EXPECT_FALSE(
      std::is_copy_constructible<absl::optional<MoveableNoThrow>>::value);
  EXPECT_FALSE(std::is_copy_constructible<absl::optional<NonMovable>>::value);

  EXPECT_FALSE(
      absl::is_trivially_copy_constructible<absl::optional<Copyable>>::value);
  EXPECT_TRUE(
      absl::is_trivially_copy_constructible<absl::optional<int>>::value);
  EXPECT_TRUE(
      absl::is_trivially_copy_constructible<absl::optional<const int>>::value);
#if !defined(_MSC_VER) && !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  // See defect report "Trivial copy/move constructor for class with volatile
  // member" at
  // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2094
  // A class with non-static data member of volatile-qualified type should still
  // have a trivial copy constructor if the data member is trivial.
  // Also a cv-qualified scalar type should be trivially copyable.
  EXPECT_TRUE(absl::is_trivially_copy_constructible<
              absl::optional<volatile int>>::value);
#endif  // !defined(_MSC_VER) && !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)

  // constexpr copy constructor for trivially copyable types
  {
    constexpr absl::optional<int> o1;
    constexpr absl::optional<int> o2 = o1;
    static_assert(!o2, "");
  }
  {
    constexpr absl::optional<int> o1 = 42;
    constexpr absl::optional<int> o2 = o1;
    static_assert(o2, "");
    static_assert(*o2 == 42, "");
  }
  {
    struct TrivialCopyable {
      constexpr TrivialCopyable() : x(0) {}
      constexpr explicit TrivialCopyable(int i) : x(i) {}
      int x;
    };
    constexpr absl::optional<TrivialCopyable> o1(42);
    constexpr absl::optional<TrivialCopyable> o2 = o1;
    static_assert(o2, "");
    static_assert((*o2).x == 42, "");
#ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG
    EXPECT_TRUE(absl::is_trivially_copy_constructible<
                absl::optional<TrivialCopyable>>::value);
    EXPECT_TRUE(absl::is_trivially_copy_constructible<
                absl::optional<const TrivialCopyable>>::value);
#endif
#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
    EXPECT_FALSE(std::is_copy_constructible<
                 absl::optional<volatile TrivialCopyable>>::value);
#endif  // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  }
}

TEST(optionalTest, MoveConstructor) {
  absl::optional<int> empty, opt42 = 42;
  absl::optional<int> empty_move(std::move(empty));
  EXPECT_FALSE(empty_move);
  absl::optional<int> opt42_move(std::move(opt42));
  EXPECT_TRUE(opt42_move);
  EXPECT_EQ(42, opt42_move);
  // test movability
  EXPECT_TRUE(std::is_move_constructible<absl::optional<int>>::value);
  EXPECT_TRUE(std::is_move_constructible<absl::optional<Copyable>>::value);
  EXPECT_TRUE(std::is_move_constructible<absl::optional<MoveableThrow>>::value);
  EXPECT_TRUE(
      std::is_move_constructible<absl::optional<MoveableNoThrow>>::value);
  EXPECT_FALSE(std::is_move_constructible<absl::optional<NonMovable>>::value);
  // test noexcept
  EXPECT_TRUE(std::is_nothrow_move_constructible<absl::optional<int>>::value);
  EXPECT_EQ(
      absl::default_allocator_is_nothrow::value,
      std::is_nothrow_move_constructible<absl::optional<MoveableThrow>>::value);
  EXPECT_TRUE(std::is_nothrow_move_constructible<
              absl::optional<MoveableNoThrow>>::value);
}

TEST(optionalTest, Destructor) {
  struct Trivial {};

  struct NonTrivial {
    NonTrivial(const NonTrivial&) {}
    NonTrivial& operator=(const NonTrivial&) { return *this; }
    ~NonTrivial() {}
  };

  EXPECT_TRUE(std::is_trivially_destructible<absl::optional<int>>::value);
  EXPECT_TRUE(std::is_trivially_destructible<absl::optional<Trivial>>::value);
  EXPECT_FALSE(
      std::is_trivially_destructible<absl::optional<NonTrivial>>::value);
}

TEST(optionalTest, InPlaceConstructor) {
  constexpr absl::optional<ConstexprType> opt0{absl::in_place_t()};
  static_assert(opt0, "");
  static_assert((*opt0).x == ConstexprType::kCtorDefault, "");
  constexpr absl::optional<ConstexprType> opt1{absl::in_place_t(), 1};
  static_assert(opt1, "");
  static_assert((*opt1).x == ConstexprType::kCtorInt, "");
  constexpr absl::optional<ConstexprType> opt2{absl::in_place_t(), {1, 2}};
  static_assert(opt2, "");
  static_assert((*opt2).x == ConstexprType::kCtorInitializerList, "");

  EXPECT_FALSE((std::is_constructible<absl::optional<ConvertsFromInPlaceT>,
                                      absl::in_place_t>::value));
  EXPECT_FALSE((std::is_constructible<absl::optional<ConvertsFromInPlaceT>,
                                      const absl::in_place_t&>::value));
  EXPECT_TRUE(
      (std::is_constructible<absl::optional<ConvertsFromInPlaceT>,
                             absl::in_place_t, absl::in_place_t>::value));

  EXPECT_FALSE((std::is_constructible<absl::optional<NoDefault>,
                                      absl::in_place_t>::value));
  EXPECT_FALSE((std::is_constructible<absl::optional<NoDefault>,
                                      absl::in_place_t&&>::value));
}

// template<U=T> optional(U&&);
TEST(optionalTest, ValueConstructor) {
  constexpr absl::optional<int> opt0(0);
  static_assert(opt0, "");
  static_assert(*opt0 == 0, "");
  EXPECT_TRUE((std::is_convertible<int, absl::optional<int>>::value));
  // Copy initialization ( = "abc") won't work due to optional(optional&&)
  // is not constexpr. Use list initialization instead. This invokes
  // absl::optional<ConstexprType>::absl::optional<U>(U&&), with U = const char
  // (&) [4], which direct-initializes the ConstexprType value held by the
  // optional via ConstexprType::ConstexprType(const char*).
  constexpr absl::optional<ConstexprType> opt1 = {"abc"};
  static_assert(opt1, "");
  static_assert(ConstexprType::kCtorConstChar == (*opt1).x, "");
  EXPECT_TRUE(
      (std::is_convertible<const char*, absl::optional<ConstexprType>>::value));
  // direct initialization
  constexpr absl::optional<ConstexprType> opt2{2};
  static_assert(opt2, "");
  static_assert(ConstexprType::kCtorInt == (*opt2).x, "");
  EXPECT_FALSE(
      (std::is_convertible<int, absl::optional<ConstexprType>>::value));

  // this invokes absl::optional<int>::optional(int&&)
  // NOTE: this has different behavior than assignment, e.g.
  // "opt3 = {};" clears the optional rather than setting the value to 0
  // According to C++17 standard N4659 [over.ics.list] 16.3.3.1.5, (9.2)- "if
  // the initializer list has no elements, the implicit conversion is the
  // identity conversion", so `optional(int&&)` should be a better match than
  // `optional(optional&&)` which is a user-defined conversion.
  // Note: GCC 7 has a bug with this overload selection when compiled with
  // `-std=c++17`.
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 7 && \
    __cplusplus == 201703L
#define ABSL_GCC7_OVER_ICS_LIST_BUG
#endif
#ifndef ABSL_GCC7_OVER_ICS_LIST_BUG
  constexpr absl::optional<int> opt3({});
  static_assert(opt3, "");
  static_assert(*opt3 == 0, "");
#endif

  // this invokes the move constructor with a default constructed optional
  // because non-template function is a better match than template function.
  absl::optional<ConstexprType> opt4({});
  EXPECT_FALSE(opt4);
}

struct Implicit {};

struct Explicit {};

struct Convert {
  Convert(const Implicit&)  // NOLINT(runtime/explicit)
      : implicit(true), move(false) {}
  Convert(Implicit&&)  // NOLINT(runtime/explicit)
      : implicit(true), move(true) {}
  explicit Convert(const Explicit&) : implicit(false), move(false) {}
  explicit Convert(Explicit&&) : implicit(false), move(true) {}

  bool implicit;
  bool move;
};

struct ConvertFromOptional {
  ConvertFromOptional(const Implicit&)  // NOLINT(runtime/explicit)
      : implicit(true), move(false), from_optional(false) {}
  ConvertFromOptional(Implicit&&)  // NOLINT(runtime/explicit)
      : implicit(true), move(true), from_optional(false) {}
  ConvertFromOptional(
      const absl::optional<Implicit>&)  // NOLINT(runtime/explicit)
      : implicit(true), move(false), from_optional(true) {}
  ConvertFromOptional(absl::optional<Implicit>&&)  // NOLINT(runtime/explicit)
      : implicit(true), move(true), from_optional(true) {}
  explicit ConvertFromOptional(const Explicit&)
      : implicit(false), move(false), from_optional(false) {}
  explicit ConvertFromOptional(Explicit&&)
      : implicit(false), move(true), from_optional(false) {}
  explicit ConvertFromOptional(const absl::optional<Explicit>&)
      : implicit(false), move(false), from_optional(true) {}
  explicit ConvertFromOptional(absl::optional<Explicit>&&)
      : implicit(false), move(true), from_optional(true) {}

  bool implicit;
  bool move;
  bool from_optional;
};

TEST(optionalTest, ConvertingConstructor) {
  absl::optional<Implicit> i_empty;
  absl::optional<Implicit> i(absl::in_place);
  absl::optional<Explicit> e_empty;
  absl::optional<Explicit> e(absl::in_place);
  {
    // implicitly constructing absl::optional<Convert> from
    // absl::optional<Implicit>
    absl::optional<Convert> empty = i_empty;
    EXPECT_FALSE(empty);
    absl::optional<Convert> opt_copy = i;
    EXPECT_TRUE(opt_copy);
    EXPECT_TRUE(opt_copy->implicit);
    EXPECT_FALSE(opt_copy->move);
    absl::optional<Convert> opt_move = absl::optional<Implicit>(absl::in_place);
    EXPECT_TRUE(opt_move);
    EXPECT_TRUE(opt_move->implicit);
    EXPECT_TRUE(opt_move->move);
  }
  {
    // explicitly constructing absl::optional<Convert> from
    // absl::optional<Explicit>
    absl::optional<Convert> empty(e_empty);
    EXPECT_FALSE(empty);
    absl::optional<Convert> opt_copy(e);
    EXPECT_TRUE(opt_copy);
    EXPECT_FALSE(opt_copy->implicit);
    EXPECT_FALSE(opt_copy->move);
    EXPECT_FALSE((std::is_convertible<const absl::optional<Explicit>&,
                                      absl::optional<Convert>>::value));
    absl::optional<Convert> opt_move{absl::optional<Explicit>(absl::in_place)};
    EXPECT_TRUE(opt_move);
    EXPECT_FALSE(opt_move->implicit);
    EXPECT_TRUE(opt_move->move);
    EXPECT_FALSE((std::is_convertible<absl::optional<Explicit>&&,
                                      absl::optional<Convert>>::value));
  }
  {
    // implicitly constructing absl::optional<ConvertFromOptional> from
    // absl::optional<Implicit> via
    // ConvertFromOptional(absl::optional<Implicit>&&) check that
    // ConvertFromOptional(Implicit&&) is NOT called
    static_assert(
        std::is_convertible<absl::optional<Implicit>,
                            absl::optional<ConvertFromOptional>>::value,
        "");
    absl::optional<ConvertFromOptional> opt0 = i_empty;
    EXPECT_TRUE(opt0);
    EXPECT_TRUE(opt0->implicit);
    EXPECT_FALSE(opt0->move);
    EXPECT_TRUE(opt0->from_optional);
    absl::optional<ConvertFromOptional> opt1 = absl::optional<Implicit>();
    EXPECT_TRUE(opt1);
    EXPECT_TRUE(opt1->implicit);
    EXPECT_TRUE(opt1->move);
    EXPECT_TRUE(opt1->from_optional);
  }
  {
    // implicitly constructing absl::optional<ConvertFromOptional> from
    // absl::optional<Explicit> via
    // ConvertFromOptional(absl::optional<Explicit>&&) check that
    // ConvertFromOptional(Explicit&&) is NOT called
    absl::optional<ConvertFromOptional> opt0(e_empty);
    EXPECT_TRUE(opt0);
    EXPECT_FALSE(opt0->implicit);
    EXPECT_FALSE(opt0->move);
    EXPECT_TRUE(opt0->from_optional);
    EXPECT_FALSE(
        (std::is_convertible<const absl::optional<Explicit>&,
                             absl::optional<ConvertFromOptional>>::value));
    absl::optional<ConvertFromOptional> opt1{absl::optional<Explicit>()};
    EXPECT_TRUE(opt1);
    EXPECT_FALSE(opt1->implicit);
    EXPECT_TRUE(opt1->move);
    EXPECT_TRUE(opt1->from_optional);
    EXPECT_FALSE(
        (std::is_convertible<absl::optional<Explicit>&&,
                             absl::optional<ConvertFromOptional>>::value));
  }
}

TEST(optionalTest, StructorBasic) {
  StructorListener listener;
  Listenable::listener = &listener;
  {
    absl::optional<Listenable> empty;
    EXPECT_FALSE(empty);
    absl::optional<Listenable> opt0(absl::in_place);
    EXPECT_TRUE(opt0);
    absl::optional<Listenable> opt1(absl::in_place, 1);
    EXPECT_TRUE(opt1);
    absl::optional<Listenable> opt2(absl::in_place, 1, 2);
    EXPECT_TRUE(opt2);
  }
  EXPECT_EQ(1, listener.construct0);
  EXPECT_EQ(1, listener.construct1);
  EXPECT_EQ(1, listener.construct2);
  EXPECT_EQ(3, listener.destruct);
}

TEST(optionalTest, CopyMoveStructor) {
  StructorListener listener;
  Listenable::listener = &listener;
  absl::optional<Listenable> original(absl::in_place);
  EXPECT_EQ(1, listener.construct0);
  EXPECT_EQ(0, listener.copy);
  EXPECT_EQ(0, listener.move);
  absl::optional<Listenable> copy(original);
  EXPECT_EQ(1, listener.construct0);
  EXPECT_EQ(1, listener.copy);
  EXPECT_EQ(0, listener.move);
  absl::optional<Listenable> move(std::move(original));
  EXPECT_EQ(1, listener.construct0);
  EXPECT_EQ(1, listener.copy);
  EXPECT_EQ(1, listener.move);
}

TEST(optionalTest, ListInit) {
  StructorListener listener;
  Listenable::listener = &listener;
  absl::optional<Listenable> listinit1(absl::in_place, {1});
  absl::optional<Listenable> listinit2(absl::in_place, {1, 2});
  EXPECT_EQ(2, listener.listinit);
}

TEST(optionalTest, AssignFromNullopt) {
  absl::optional<int> opt(1);
  opt = absl::nullopt;
  EXPECT_FALSE(opt);

  StructorListener listener;
  Listenable::listener = &listener;
  absl::optional<Listenable> opt1(absl::in_place);
  opt1 = absl::nullopt;
  EXPECT_FALSE(opt1);
  EXPECT_EQ(1, listener.construct0);
  EXPECT_EQ(1, listener.destruct);

  EXPECT_TRUE((
      std::is_nothrow_assignable<absl::optional<int>, absl::nullopt_t>::value));
  EXPECT_TRUE((std::is_nothrow_assignable<absl::optional<Listenable>,
                                          absl::nullopt_t>::value));
}

TEST(optionalTest, CopyAssignment) {
  const absl::optional<int> empty, opt1 = 1, opt2 = 2;
  absl::optional<int> empty_to_opt1, opt1_to_opt2, opt2_to_empty;

  EXPECT_FALSE(empty_to_opt1);
  empty_to_opt1 = empty;
  EXPECT_FALSE(empty_to_opt1);
  empty_to_opt1 = opt1;
  EXPECT_TRUE(empty_to_opt1);
  EXPECT_EQ(1, empty_to_opt1.value());

  EXPECT_FALSE(opt1_to_opt2);
  opt1_to_opt2 = opt1;
  EXPECT_TRUE(opt1_to_opt2);
  EXPECT_EQ(1, opt1_to_opt2.value());
  opt1_to_opt2 = opt2;
  EXPECT_TRUE(opt1_to_opt2);
  EXPECT_EQ(2, opt1_to_opt2.value());

  EXPECT_FALSE(opt2_to_empty);
  opt2_to_empty = opt2;
  EXPECT_TRUE(opt2_to_empty);
  EXPECT_EQ(2, opt2_to_empty.value());
  opt2_to_empty = empty;
  EXPECT_FALSE(opt2_to_empty);

  EXPECT_FALSE(absl::is_copy_assignable<absl::optional<const int>>::value);
  EXPECT_TRUE(absl::is_copy_assignable<absl::optional<Copyable>>::value);
  EXPECT_FALSE(absl::is_copy_assignable<absl::optional<MoveableThrow>>::value);
  EXPECT_FALSE(
      absl::is_copy_assignable<absl::optional<MoveableNoThrow>>::value);
  EXPECT_FALSE(absl::is_copy_assignable<absl::optional<NonMovable>>::value);

  EXPECT_TRUE(absl::is_trivially_copy_assignable<int>::value);
  EXPECT_TRUE(absl::is_trivially_copy_assignable<volatile int>::value);

  struct Trivial {
    int i;
  };
  struct NonTrivial {
    NonTrivial& operator=(const NonTrivial&) { return *this; }
    int i;
  };

  EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial>::value);
  EXPECT_FALSE(absl::is_copy_assignable<const Trivial>::value);
  EXPECT_FALSE(absl::is_copy_assignable<volatile Trivial>::value);
  EXPECT_TRUE(absl::is_copy_assignable<NonTrivial>::value);
  EXPECT_FALSE(absl::is_trivially_copy_assignable<NonTrivial>::value);

#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  {
    StructorListener listener;
    Listenable::listener = &listener;

    absl::optional<volatile Listenable> empty, set(absl::in_place);
    EXPECT_EQ(1, listener.construct0);
    absl::optional<volatile Listenable> empty_to_empty, empty_to_set,
        set_to_empty(absl::in_place), set_to_set(absl::in_place);
    EXPECT_EQ(3, listener.construct0);
    empty_to_empty = empty;  // no effect
    empty_to_set = set;      // copy construct
    set_to_empty = empty;    // destruct
    set_to_set = set;        // copy assign
    EXPECT_EQ(1, listener.volatile_copy);
    EXPECT_EQ(0, listener.volatile_move);
    EXPECT_EQ(1, listener.destruct);
    EXPECT_EQ(1, listener.volatile_copy_assign);
  }
#endif  // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
}

TEST(optionalTest, MoveAssignment) {
  {
    StructorListener listener;
    Listenable::listener = &listener;

    absl::optional<Listenable> empty1, empty2, set1(absl::in_place),
        set2(absl::in_place);
    EXPECT_EQ(2, listener.construct0);
    absl::optional<Listenable> empty_to_empty, empty_to_set,
        set_to_empty(absl::in_place), set_to_set(absl::in_place);
    EXPECT_EQ(4, listener.construct0);
    empty_to_empty = std::move(empty1);
    empty_to_set = std::move(set1);
    set_to_empty = std::move(empty2);
    set_to_set = std::move(set2);
    EXPECT_EQ(0, listener.copy);
    EXPECT_EQ(1, listener.move);
    EXPECT_EQ(1, listener.destruct);
    EXPECT_EQ(1, listener.move_assign);
  }
#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  {
    StructorListener listener;
    Listenable::listener = &listener;

    absl::optional<volatile Listenable> empty1, empty2, set1(absl::in_place),
        set2(absl::in_place);
    EXPECT_EQ(2, listener.construct0);
    absl::optional<volatile Listenable> empty_to_empty, empty_to_set,
        set_to_empty(absl::in_place), set_to_set(absl::in_place);
    EXPECT_EQ(4, listener.construct0);
    empty_to_empty = std::move(empty1);  // no effect
    empty_to_set = std::move(set1);      // move construct
    set_to_empty = std::move(empty2);    // destruct
    set_to_set = std::move(set2);        // move assign
    EXPECT_EQ(0, listener.volatile_copy);
    EXPECT_EQ(1, listener.volatile_move);
    EXPECT_EQ(1, listener.destruct);
    EXPECT_EQ(1, listener.volatile_move_assign);
  }
#endif  // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  EXPECT_FALSE(absl::is_move_assignable<absl::optional<const int>>::value);
  EXPECT_TRUE(absl::is_move_assignable<absl::optional<Copyable>>::value);
  EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableThrow>>::value);
  EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableNoThrow>>::value);
  EXPECT_FALSE(absl::is_move_assignable<absl::optional<NonMovable>>::value);

  EXPECT_FALSE(
      std::is_nothrow_move_assignable<absl::optional<MoveableThrow>>::value);
  EXPECT_TRUE(
      std::is_nothrow_move_assignable<absl::optional<MoveableNoThrow>>::value);
}

struct NoConvertToOptional {
  // disable implicit conversion from const NoConvertToOptional&
  // to absl::optional<NoConvertToOptional>.
  NoConvertToOptional(const NoConvertToOptional&) = delete;
};

struct CopyConvert {
  CopyConvert(const NoConvertToOptional&);
  CopyConvert& operator=(const CopyConvert&) = delete;
  CopyConvert& operator=(const NoConvertToOptional&);
};

struct CopyConvertFromOptional {
  CopyConvertFromOptional(const NoConvertToOptional&);
  CopyConvertFromOptional(const absl::optional<NoConvertToOptional>&);
  CopyConvertFromOptional& operator=(const CopyConvertFromOptional&) = delete;
  CopyConvertFromOptional& operator=(const NoConvertToOptional&);
  CopyConvertFromOptional& operator=(
      const absl::optional<NoConvertToOptional>&);
};

struct MoveConvert {
  MoveConvert(NoConvertToOptional&&);
  MoveConvert& operator=(const MoveConvert&) = delete;
  MoveConvert& operator=(NoConvertToOptional&&);
};

struct MoveConvertFromOptional {
  MoveConvertFromOptional(NoConvertToOptional&&);
  MoveConvertFromOptional(absl::optional<NoConvertToOptional>&&);
  MoveConvertFromOptional& operator=(const MoveConvertFromOptional&) = delete;
  MoveConvertFromOptional& operator=(NoConvertToOptional&&);
  MoveConvertFromOptional& operator=(absl::optional<NoConvertToOptional>&&);
};

// template <typename U = T> absl::optional<T>& operator=(U&& v);
TEST(optionalTest, ValueAssignment) {
  absl::optional<int> opt;
  EXPECT_FALSE(opt);
  opt = 42;
  EXPECT_TRUE(opt);
  EXPECT_EQ(42, opt.value());
  opt = absl::nullopt;
  EXPECT_FALSE(opt);
  opt = 42;
  EXPECT_TRUE(opt);
  EXPECT_EQ(42, opt.value());
  opt = 43;
  EXPECT_TRUE(opt);
  EXPECT_EQ(43, opt.value());
  opt = {};  // this should clear optional
  EXPECT_FALSE(opt);

  opt = {44};
  EXPECT_TRUE(opt);
  EXPECT_EQ(44, opt.value());

  // U = const NoConvertToOptional&
  EXPECT_TRUE((std::is_assignable<absl::optional<CopyConvert>&,
                                  const NoConvertToOptional&>::value));
  // U = const absl::optional<NoConvertToOptional>&
  EXPECT_TRUE((std::is_assignable<absl::optional<CopyConvertFromOptional>&,
                                  const NoConvertToOptional&>::value));
  // U = const NoConvertToOptional& triggers SFINAE because
  // std::is_constructible_v<MoveConvert, const NoConvertToOptional&> is false
  EXPECT_FALSE((std::is_assignable<absl::optional<MoveConvert>&,
                                   const NoConvertToOptional&>::value));
  // U = NoConvertToOptional
  EXPECT_TRUE((std::is_assignable<absl::optional<MoveConvert>&,
                                  NoConvertToOptional&&>::value));
  // U = const NoConvertToOptional& triggers SFINAE because
  // std::is_constructible_v<MoveConvertFromOptional, const
  // NoConvertToOptional&> is false
  EXPECT_FALSE((std::is_assignable<absl::optional<MoveConvertFromOptional>&,
                                   const NoConvertToOptional&>::value));
  // U = NoConvertToOptional
  EXPECT_TRUE((std::is_assignable<absl::optional<MoveConvertFromOptional>&,
                                  NoConvertToOptional&&>::value));
  // U = const absl::optional<NoConvertToOptional>&
  EXPECT_TRUE(
      (std::is_assignable<absl::optional<CopyConvertFromOptional>&,
                          const absl::optional<NoConvertToOptional>&>::value));
  // U = absl::optional<NoConvertToOptional>
  EXPECT_TRUE(
      (std::is_assignable<absl::optional<MoveConvertFromOptional>&,
                          absl::optional<NoConvertToOptional>&&>::value));
}

// template <typename U> absl::optional<T>& operator=(const absl::optional<U>&
// rhs); template <typename U> absl::optional<T>& operator=(absl::optional<U>&&
// rhs);
TEST(optionalTest, ConvertingAssignment) {
  absl::optional<int> opt_i;
  absl::optional<char> opt_c('c');
  opt_i = opt_c;
  EXPECT_TRUE(opt_i);
  EXPECT_EQ(*opt_c, *opt_i);
  opt_i = absl::optional<char>();
  EXPECT_FALSE(opt_i);
  opt_i = absl::optional<char>('d');
  EXPECT_TRUE(opt_i);
  EXPECT_EQ('d', *opt_i);

  absl::optional<std::string> opt_str;
  absl::optional<const char*> opt_cstr("abc");
  opt_str = opt_cstr;
  EXPECT_TRUE(opt_str);
  EXPECT_EQ(std::string("abc"), *opt_str);
  opt_str = absl::optional<const char*>();
  EXPECT_FALSE(opt_str);
  opt_str = absl::optional<const char*>("def");
  EXPECT_TRUE(opt_str);
  EXPECT_EQ(std::string("def"), *opt_str);

  // operator=(const absl::optional<U>&) with U = NoConvertToOptional
  EXPECT_TRUE(
      (std::is_assignable<absl::optional<CopyConvert>,
                          const absl::optional<NoConvertToOptional>&>::value));
  // operator=(const absl::optional<U>&) with U = NoConvertToOptional
  // triggers SFINAE because
  // std::is_constructible_v<MoveConvert, const NoConvertToOptional&> is false
  EXPECT_FALSE(
      (std::is_assignable<absl::optional<MoveConvert>&,
                          const absl::optional<NoConvertToOptional>&>::value));
  // operator=(absl::optional<U>&&) with U = NoConvertToOptional
  EXPECT_TRUE(
      (std::is_assignable<absl::optional<MoveConvert>&,
                          absl::optional<NoConvertToOptional>&&>::value));
  // operator=(const absl::optional<U>&) with U = NoConvertToOptional triggers
  // SFINAE because std::is_constructible_v<MoveConvertFromOptional, const
  // NoConvertToOptional&> is false. operator=(U&&) with U = const
  // absl::optional<NoConverToOptional>& triggers SFINAE because
  // std::is_constructible<MoveConvertFromOptional,
  // absl::optional<NoConvertToOptional>&&> is true.
  EXPECT_FALSE(
      (std::is_assignable<absl::optional<MoveConvertFromOptional>&,
                          const absl::optional<NoConvertToOptional>&>::value));
}

TEST(optionalTest, ResetAndHasValue) {
  StructorListener listener;
  Listenable::listener = &listener;
  absl::optional<Listenable> opt;
  EXPECT_FALSE(opt);
  EXPECT_FALSE(opt.has_value());
  opt.emplace();
  EXPECT_TRUE(opt);
  EXPECT_TRUE(opt.has_value());
  opt.reset();
  EXPECT_FALSE(opt);
  EXPECT_FALSE(opt.has_value());
  EXPECT_EQ(1, listener.destruct);
  opt.reset();
  EXPECT_FALSE(opt);
  EXPECT_FALSE(opt.has_value());

  constexpr absl::optional<int> empty;
  static_assert(!empty.has_value(), "");
  constexpr absl::optional<int> nonempty(1);
  static_assert(nonempty.has_value(), "");
}

TEST(optionalTest, Emplace) {
  StructorListener listener;
  Listenable::listener = &listener;
  absl::optional<Listenable> opt;
  EXPECT_FALSE(opt);
  opt.emplace(1);
  EXPECT_TRUE(opt);
  opt.emplace(1, 2);
  EXPECT_EQ(1, listener.construct1);
  EXPECT_EQ(1, listener.construct2);
  EXPECT_EQ(1, listener.destruct);

  absl::optional<std::string> o;
  EXPECT_TRUE((std::is_same<std::string&, decltype(o.emplace("abc"))>::value));
  std::string& ref = o.emplace("abc");
  EXPECT_EQ(&ref, &o.value());
}

TEST(optionalTest, ListEmplace) {
  StructorListener listener;
  Listenable::listener = &listener;
  absl::optional<Listenable> opt;
  EXPECT_FALSE(opt);
  opt.emplace({1});
  EXPECT_TRUE(opt);
  opt.emplace({1, 2});
  EXPECT_EQ(2, listener.listinit);
  EXPECT_EQ(1, listener.destruct);

  absl::optional<Listenable> o;
  EXPECT_TRUE((std::is_same<Listenable&, decltype(o.emplace({1}))>::value));
  Listenable& ref = o.emplace({1});
  EXPECT_EQ(&ref, &o.value());
}

TEST(optionalTest, Swap) {
  absl::optional<int> opt_empty, opt1 = 1, opt2 = 2;
  EXPECT_FALSE(opt_empty);
  EXPECT_TRUE(opt1);
  EXPECT_EQ(1, opt1.value());
  EXPECT_TRUE(opt2);
  EXPECT_EQ(2, opt2.value());
  swap(opt_empty, opt1);
  EXPECT_FALSE(opt1);
  EXPECT_TRUE(opt_empty);
  EXPECT_EQ(1, opt_empty.value());
  EXPECT_TRUE(opt2);
  EXPECT_EQ(2, opt2.value());
  swap(opt_empty, opt1);
  EXPECT_FALSE(opt_empty);
  EXPECT_TRUE(opt1);
  EXPECT_EQ(1, opt1.value());
  EXPECT_TRUE(opt2);
  EXPECT_EQ(2, opt2.value());
  swap(opt1, opt2);
  EXPECT_FALSE(opt_empty);
  EXPECT_TRUE(opt1);
  EXPECT_EQ(2, opt1.value());
  EXPECT_TRUE(opt2);
  EXPECT_EQ(1, opt2.value());

  EXPECT_TRUE(noexcept(opt1.swap(opt2)));
  EXPECT_TRUE(noexcept(swap(opt1, opt2)));
}

template <int v>
struct DeletedOpAddr {
  int value = v;
  constexpr DeletedOpAddr() = default;
  constexpr const DeletedOpAddr<v>* operator&() const = delete;  // NOLINT
  DeletedOpAddr<v>* operator&() = delete;                        // NOLINT
};

// The static_assert featuring a constexpr call to operator->() is commented out
// to document the fact that the current implementation of absl::optional<T>
// expects such usecases to be malformed and not compile.
TEST(optionalTest, OperatorAddr) {
  constexpr int v = -1;
  {  // constexpr
    constexpr absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
    static_assert(opt.has_value(), "");
    // static_assert(opt->value == v, "");
    static_assert((*opt).value == v, "");
  }
  {  // non-constexpr
    const absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
    EXPECT_TRUE(opt.has_value());
    EXPECT_TRUE(opt->value == v);
    EXPECT_TRUE((*opt).value == v);
  }
}

TEST(optionalTest, PointerStuff) {
  absl::optional<std::string> opt(absl::in_place, "foo");
  EXPECT_EQ("foo", *opt);
  const auto& opt_const = opt;
  EXPECT_EQ("foo", *opt_const);
  EXPECT_EQ(opt->size(), 3u);
  EXPECT_EQ(opt_const->size(), 3u);

  constexpr absl::optional<ConstexprType> opt1(1);
  static_assert((*opt1).x == ConstexprType::kCtorInt, "");
}

TEST(optionalTest, Value) {
  using O = absl::optional<std::string>;
  using CO = const absl::optional<std::string>;
  using OC = absl::optional<const std::string>;
  O lvalue(absl::in_place, "lvalue");
  CO clvalue(absl::in_place, "clvalue");
  OC lvalue_c(absl::in_place, "lvalue_c");
  EXPECT_EQ("lvalue", lvalue.value());
  EXPECT_EQ("clvalue", clvalue.value());
  EXPECT_EQ("lvalue_c", lvalue_c.value());
  EXPECT_EQ("xvalue", O(absl::in_place, "xvalue").value());
  EXPECT_EQ("xvalue_c", OC(absl::in_place, "xvalue_c").value());
  EXPECT_EQ("cxvalue", CO(absl::in_place, "cxvalue").value());
  EXPECT_EQ("&", TypeQuals(lvalue.value()));
  EXPECT_EQ("c&", TypeQuals(clvalue.value()));
  EXPECT_EQ("c&", TypeQuals(lvalue_c.value()));
  EXPECT_EQ("&&", TypeQuals(O(absl::in_place, "xvalue").value()));
  EXPECT_EQ("c&&", TypeQuals(CO(absl::in_place, "cxvalue").value()));
  EXPECT_EQ("c&&", TypeQuals(OC(absl::in_place, "xvalue_c").value()));

#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  // test on volatile type
  using OV = absl::optional<volatile int>;
  OV lvalue_v(absl::in_place, 42);
  EXPECT_EQ(42, lvalue_v.value());
  EXPECT_EQ(42, OV(42).value());
  EXPECT_TRUE((std::is_same<volatile int&, decltype(lvalue_v.value())>::value));
  EXPECT_TRUE((std::is_same<volatile int&&, decltype(OV(42).value())>::value));
#endif  // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)

  // test exception throw on value()
  absl::optional<int> empty;
#ifdef ABSL_HAVE_EXCEPTIONS
  EXPECT_THROW((void)empty.value(), absl::bad_optional_access);
#else
  EXPECT_DEATH_IF_SUPPORTED((void)empty.value(), "Bad optional access");
#endif

  // test constexpr value()
  constexpr absl::optional<int> o1(1);
  static_assert(1 == o1.value(), "");  // const &
#ifndef _MSC_VER
  using COI = const absl::optional<int>;
  static_assert(2 == COI(2).value(), "");  // const &&
#endif
}

TEST(optionalTest, DerefOperator) {
  using O = absl::optional<std::string>;
  using CO = const absl::optional<std::string>;
  using OC = absl::optional<const std::string>;
  O lvalue(absl::in_place, "lvalue");
  CO clvalue(absl::in_place, "clvalue");
  OC lvalue_c(absl::in_place, "lvalue_c");
  EXPECT_EQ("lvalue", *lvalue);
  EXPECT_EQ("clvalue", *clvalue);
  EXPECT_EQ("lvalue_c", *lvalue_c);
  EXPECT_EQ("xvalue", *O(absl::in_place, "xvalue"));
  EXPECT_EQ("xvalue_c", *OC(absl::in_place, "xvalue_c"));
  EXPECT_EQ("cxvalue", *CO(absl::in_place, "cxvalue"));
  EXPECT_EQ("&", TypeQuals(*lvalue));
  EXPECT_EQ("c&", TypeQuals(*clvalue));
  EXPECT_EQ("&&", TypeQuals(*O(absl::in_place, "xvalue")));
  EXPECT_EQ("c&&", TypeQuals(*CO(absl::in_place, "cxvalue")));
  EXPECT_EQ("c&&", TypeQuals(*OC(absl::in_place, "xvalue_c")));

#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)
  // test on volatile type
  using OV = absl::optional<volatile int>;
  OV lvalue_v(absl::in_place, 42);
  EXPECT_EQ(42, *lvalue_v);
  EXPECT_EQ(42, *OV(42));
  EXPECT_TRUE((std::is_same<volatile int&, decltype(*lvalue_v)>::value));
  EXPECT_TRUE((std::is_same<volatile int&&, decltype(*OV(42))>::value));
#endif  // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED)

  constexpr absl::optional<int> opt1(1);
  static_assert(*opt1 == 1, "");
#if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
  using COI = const absl::optional<int>;
  static_assert(*COI(2) == 2, "");
#endif
}

TEST(optionalTest, ValueOr) {
  absl::optional<double> opt_empty, opt_set = 1.2;
  EXPECT_EQ(42.0, opt_empty.value_or(42));
  EXPECT_EQ(1.2, opt_set.value_or(42));
  EXPECT_EQ(42.0, absl::optional<double>().value_or(42));
  EXPECT_EQ(1.2, absl::optional<double>(1.2).value_or(42));

  constexpr absl::optional<double> copt_empty, copt_set = {1.2};
  static_assert(42.0 == copt_empty.value_or(42), "");
  static_assert(1.2 == copt_set.value_or(42), "");
  using COD = const absl::optional<double>;
  static_assert(42.0 == COD().value_or(42), "");
  static_assert(1.2 == COD(1.2).value_or(42), "");
}

// make_optional cannot be constexpr until C++17
TEST(optionalTest, make_optional) {
  auto opt_int = absl::make_optional(42);
  EXPECT_TRUE((std::is_same<decltype(opt_int), absl::optional<int>>::value));
  EXPECT_EQ(42, opt_int);

  StructorListener listener;
  Listenable::listener = &listener;

  absl::optional<Listenable> opt0 = absl::make_optional<Listenable>();
  EXPECT_EQ(1, listener.construct0);
  absl::optional<Listenable> opt1 = absl::make_optional<Listenable>(1);
  EXPECT_EQ(1, listener.construct1);
  absl::optional<Listenable> opt2 = absl::make_optional<Listenable>(1, 2);
  EXPECT_EQ(1, listener.construct2);
  absl::optional<Listenable> opt3 = absl::make_optional<Listenable>({1});
  absl::optional<Listenable> opt4 = absl::make_optional<Listenable>({1, 2});
  EXPECT_EQ(2, listener.listinit);

  // Constexpr tests on trivially copyable types
  // optional<T> has trivial copy/move ctors when T is trivially copyable.
  // For nontrivial types with constexpr constructors, we need copy elision in
  // C++17 for make_optional to be constexpr.
  {
    constexpr absl::optional<int> c_opt = absl::make_optional(42);
    static_assert(c_opt.value() == 42, "");
  }
  {
    struct TrivialCopyable {
      constexpr TrivialCopyable() : x(0) {}
      constexpr explicit TrivialCopyable(int i) : x(i) {}
      int x;
    };

    constexpr TrivialCopyable v;
    constexpr absl::optional<TrivialCopyable> c_opt0 = absl::make_optional(v);
    static_assert((*c_opt0).x == 0, "");
    constexpr absl::optional<TrivialCopyable> c_opt1 =
        absl::make_optional<TrivialCopyable>();
    static_assert((*c_opt1).x == 0, "");
    constexpr absl::optional<TrivialCopyable> c_opt2 =
        absl::make_optional<TrivialCopyable>(42);
    static_assert((*c_opt2).x == 42, "");
  }
}

template <typename T, typename U>
void optionalTest_Comparisons_EXPECT_LESS(T x, U y) {
  EXPECT_FALSE(x == y);
  EXPECT_TRUE(x != y);
  EXPECT_TRUE(x < y);
  EXPECT_FALSE(x > y);
  EXPECT_TRUE(x <= y);
  EXPECT_FALSE(x >= y);
}

template <typename T, typename U>
void optionalTest_Comparisons_EXPECT_SAME(T x, U y) {
  EXPECT_TRUE(x == y);
  EXPECT_FALSE(x != y);
  EXPECT_FALSE(x < y);
  EXPECT_FALSE(x > y);
  EXPECT_TRUE(x <= y);
  EXPECT_TRUE(x >= y);
}

template <typename T, typename U>
void optionalTest_Comparisons_EXPECT_GREATER(T x, U y) {
  EXPECT_FALSE(x == y);
  EXPECT_TRUE(x != y);
  EXPECT_FALSE(x < y);
  EXPECT_TRUE(x > y);
  EXPECT_FALSE(x <= y);
  EXPECT_TRUE(x >= y);
}

template <typename T, typename U, typename V>
void TestComparisons() {
  absl::optional<T> ae, a2{2}, a4{4};
  absl::optional<U> be, b2{2}, b4{4};
  V v3 = 3;

  // LHS: absl::nullopt, ae, a2, v3, a4
  // RHS: absl::nullopt, be, b2, v3, b4

  // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,absl::nullopt);
  optionalTest_Comparisons_EXPECT_SAME(absl::nullopt, be);
  optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b2);
  // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(absl::nullopt,v3);
  optionalTest_Comparisons_EXPECT_LESS(absl::nullopt, b4);

  optionalTest_Comparisons_EXPECT_SAME(ae, absl::nullopt);
  optionalTest_Comparisons_EXPECT_SAME(ae, be);
  optionalTest_Comparisons_EXPECT_LESS(ae, b2);
  optionalTest_Comparisons_EXPECT_LESS(ae, v3);
  optionalTest_Comparisons_EXPECT_LESS(ae, b4);

  optionalTest_Comparisons_EXPECT_GREATER(a2, absl::nullopt);
  optionalTest_Comparisons_EXPECT_GREATER(a2, be);
  optionalTest_Comparisons_EXPECT_SAME(a2, b2);
  optionalTest_Comparisons_EXPECT_LESS(a2, v3);
  optionalTest_Comparisons_EXPECT_LESS(a2, b4);

  // optionalTest_Comparisons_EXPECT_NOT_TO_WORK(v3,absl::nullopt);
  optionalTest_Comparisons_EXPECT_GREATER(v3, be);
  optionalTest_Comparisons_EXPECT_GREATER(v3, b2);
  optionalTest_Comparisons_EXPECT_SAME(v3, v3);
  optionalTest_Comparisons_EXPECT_LESS(v3, b4);

  optionalTest_Comparisons_EXPECT_GREATER(a4, absl::nullopt);
  optionalTest_Comparisons_EXPECT_GREATER(a4, be);
  optionalTest_Comparisons_EXPECT_GREATER(a4, b2);
  optionalTest_Comparisons_EXPECT_GREATER(a4, v3);
  optionalTest_Comparisons_EXPECT_SAME(a4, b4);
}

struct Int1 {
  Int1() = default;
  Int1(int i) : i(i) {}  // NOLINT(runtime/explicit)
  int i;
};

struct Int2 {
  Int2() = default;
  Int2(int i) : i(i) {}  // NOLINT(runtime/explicit)
  int i;
};

// comparison between Int1 and Int2
constexpr bool operator==(const Int1& lhs, const Int2& rhs) {
  return lhs.i == rhs.i;
}
constexpr bool operator!=(const Int1& lhs, const Int2& rhs) {
  return !(lhs == rhs);
}
constexpr bool operator<(const Int1& lhs, const Int2& rhs) {
  return lhs.i < rhs.i;
}
constexpr bool operator<=(const Int1& lhs, const Int2& rhs) {
  return lhs < rhs || lhs == rhs;
}
constexpr bool operator>(const Int1& lhs, const Int2& rhs) {
  return !(lhs <= rhs);
}
constexpr bool operator>=(const Int1& lhs, const Int2& rhs) {
  return !(lhs < rhs);
}

TEST(optionalTest, Comparisons) {
  TestComparisons<int, int, int>();
  TestComparisons<const int, int, int>();
  TestComparisons<Int1, int, int>();
  TestComparisons<int, Int2, int>();
  TestComparisons<Int1, Int2, int>();

  // compare absl::optional<std::string> with const char*
  absl::optional<std::string> opt_str = "abc";
  const char* cstr = "abc";
  EXPECT_TRUE(opt_str == cstr);
  // compare absl::optional<std::string> with absl::optional<const char*>
  absl::optional<const char*> opt_cstr = cstr;
  EXPECT_TRUE(opt_str == opt_cstr);
  // compare absl::optional<std::string> with absl::optional<absl::string_view>
  absl::optional<absl::string_view> e1;
  absl::optional<std::string> e2;
  EXPECT_TRUE(e1 == e2);
}

TEST(optionalTest, SwapRegression) {
  StructorListener listener;
  Listenable::listener = &listener;

  {
    absl::optional<Listenable> a;
    absl::optional<Listenable> b(absl::in_place);
    a.swap(b);
  }

  EXPECT_EQ(1, listener.construct0);
  EXPECT_EQ(1, listener.move);
  EXPECT_EQ(2, listener.destruct);

  {
    absl::optional<Listenable> a(absl::in_place);
    absl::optional<Listenable> b;
    a.swap(b);
  }

  EXPECT_EQ(2, listener.construct0);
  EXPECT_EQ(2, listener.move);
  EXPECT_EQ(4, listener.destruct);
}

TEST(optionalTest, BigStringLeakCheck) {
  constexpr size_t n = 1 << 16;

  using OS = absl::optional<std::string>;

  OS a;
  OS b = absl::nullopt;
  OS c = std::string(n, 'c');
  std::string sd(n, 'd');
  OS d = sd;
  OS e(absl::in_place, n, 'e');
  OS f;
  f.emplace(n, 'f');

  OS ca(a);
  OS cb(b);
  OS cc(c);
  OS cd(d);
  OS ce(e);

  OS oa;
  OS ob = absl::nullopt;
  OS oc = std::string(n, 'c');
  std::string sod(n, 'd');
  OS od = sod;
  OS oe(absl::in_place, n, 'e');
  OS of;
  of.emplace(n, 'f');

  OS ma(std::move(oa));
  OS mb(std::move(ob));
  OS mc(std::move(oc));
  OS md(std::move(od));
  OS me(std::move(oe));
  OS mf(std::move(of));

  OS aa1;
  OS ab1 = absl::nullopt;
  OS ac1 = std::string(n, 'c');
  std::string sad1(n, 'd');
  OS ad1 = sad1;
  OS ae1(absl::in_place, n, 'e');
  OS af1;
  af1.emplace(n, 'f');

  OS aa2;
  OS ab2 = absl::nullopt;
  OS ac2 = std::string(n, 'c');
  std::string sad2(n, 'd');
  OS ad2 = sad2;
  OS ae2(absl::in_place, n, 'e');
  OS af2;
  af2.emplace(n, 'f');

  aa1 = af2;
  ab1 = ae2;
  ac1 = ad2;
  ad1 = ac2;
  ae1 = ab2;
  af1 = aa2;

  OS aa3;
  OS ab3 = absl::nullopt;
  OS ac3 = std::string(n, 'c');
  std::string sad3(n, 'd');
  OS ad3 = sad3;
  OS ae3(absl::in_place, n, 'e');
  OS af3;
  af3.emplace(n, 'f');

  aa3 = absl::nullopt;
  ab3 = absl::nullopt;
  ac3 = absl::nullopt;
  ad3 = absl::nullopt;
  ae3 = absl::nullopt;
  af3 = absl::nullopt;

  OS aa4;
  OS ab4 = absl::nullopt;
  OS ac4 = std::string(n, 'c');
  std::string sad4(n, 'd');
  OS ad4 = sad4;
  OS ae4(absl::in_place, n, 'e');
  OS af4;
  af4.emplace(n, 'f');

  aa4 = OS(absl::in_place, n, 'a');
  ab4 = OS(absl::in_place, n, 'b');
  ac4 = OS(absl::in_place, n, 'c');
  ad4 = OS(absl::in_place, n, 'd');
  ae4 = OS(absl::in_place, n, 'e');
  af4 = OS(absl::in_place, n, 'f');

  OS aa5;
  OS ab5 = absl::nullopt;
  OS ac5 = std::string(n, 'c');
  std::string sad5(n, 'd');
  OS ad5 = sad5;
  OS ae5(absl::in_place, n, 'e');
  OS af5;
  af5.emplace(n, 'f');

  std::string saa5(n, 'a');
  std::string sab5(n, 'a');
  std::string sac5(n, 'a');
  std::string sad52(n, 'a');
  std::string sae5(n, 'a');
  std::string saf5(n, 'a');

  aa5 = saa5;
  ab5 = sab5;
  ac5 = sac5;
  ad5 = sad52;
  ae5 = sae5;
  af5 = saf5;

  OS aa6;
  OS ab6 = absl::nullopt;
  OS ac6 = std::string(n, 'c');
  std::string sad6(n, 'd');
  OS ad6 = sad6;
  OS ae6(absl::in_place, n, 'e');
  OS af6;
  af6.emplace(n, 'f');

  aa6 = std::string(n, 'a');
  ab6 = std::string(n, 'b');
  ac6 = std::string(n, 'c');
  ad6 = std::string(n, 'd');
  ae6 = std::string(n, 'e');
  af6 = std::string(n, 'f');

  OS aa7;
  OS ab7 = absl::nullopt;
  OS ac7 = std::string(n, 'c');
  std::string sad7(n, 'd');
  OS ad7 = sad7;
  OS ae7(absl::in_place, n, 'e');
  OS af7;
  af7.emplace(n, 'f');

  aa7.emplace(n, 'A');
  ab7.emplace(n, 'B');
  ac7.emplace(n, 'C');
  ad7.emplace(n, 'D');
  ae7.emplace(n, 'E');
  af7.emplace(n, 'F');
}

TEST(optionalTest, MoveAssignRegression) {
  StructorListener listener;
  Listenable::listener = &listener;

  {
    absl::optional<Listenable> a;
    Listenable b;
    a = std::move(b);
  }

  EXPECT_EQ(1, listener.construct0);
  EXPECT_EQ(1, listener.move);
  EXPECT_EQ(2, listener.destruct);
}

TEST(optionalTest, ValueType) {
  EXPECT_TRUE((std::is_same<absl::optional<int>::value_type, int>::value));
  EXPECT_TRUE((std::is_same<absl::optional<std::string>::value_type,
                            std::string>::value));
  EXPECT_FALSE(
      (std::is_same<absl::optional<int>::value_type, absl::nullopt_t>::value));
}

template <typename T>
struct is_hash_enabled_for {
  template <typename U, typename = decltype(std::hash<U>()(std::declval<U>()))>
  static std::true_type test(int);

  template <typename U>
  static std::false_type test(...);

  static constexpr bool value = decltype(test<T>(0))::value;
};

TEST(optionalTest, Hash) {
  std::hash<absl::optional<int>> hash;
  std::set<size_t> hashcodes;
  hashcodes.insert(hash(absl::nullopt));
  for (int i = 0; i < 100; ++i) {
    hashcodes.insert(hash(i));
  }
  EXPECT_GT(hashcodes.size(), 90u);

  static_assert(is_hash_enabled_for<absl::optional<int>>::value, "");
  static_assert(is_hash_enabled_for<absl::optional<Hashable>>::value, "");
  static_assert(
      absl::type_traits_internal::IsHashable<absl::optional<int>>::value, "");
  static_assert(
      absl::type_traits_internal::IsHashable<absl::optional<Hashable>>::value,
      "");
  absl::type_traits_internal::AssertHashEnabled<absl::optional<int>>();
  absl::type_traits_internal::AssertHashEnabled<absl::optional<Hashable>>();

#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
  static_assert(!is_hash_enabled_for<absl::optional<NonHashable>>::value, "");
  static_assert(!absl::type_traits_internal::IsHashable<
                    absl::optional<NonHashable>>::value,
                "");
#endif

  // libstdc++ std::optional is missing remove_const_t, i.e. it's using
  // std::hash<T> rather than std::hash<std::remove_const_t<T>>.
  // Reference: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82262
#ifndef __GLIBCXX__
  static_assert(is_hash_enabled_for<absl::optional<const int>>::value, "");
  static_assert(is_hash_enabled_for<absl::optional<const Hashable>>::value, "");
  std::hash<absl::optional<const int>> c_hash;
  for (int i = 0; i < 100; ++i) {
    EXPECT_EQ(hash(i), c_hash(i));
  }
#endif
}

struct MoveMeNoThrow {
  MoveMeNoThrow() : x(0) {}
  [[noreturn]] MoveMeNoThrow(const MoveMeNoThrow& other) : x(other.x) {
    LOG(FATAL) << "Should not be called.";
  }
  MoveMeNoThrow(MoveMeNoThrow&& other) noexcept : x(other.x) {}
  int x;
};

struct MoveMeThrow {
  MoveMeThrow() : x(0) {}
  MoveMeThrow(const MoveMeThrow& other) : x(other.x) {}
  MoveMeThrow(MoveMeThrow&& other) : x(other.x) {}
  int x;
};

TEST(optionalTest, NoExcept) {
  static_assert(
      std::is_nothrow_move_constructible<absl::optional<MoveMeNoThrow>>::value,
      "");
  static_assert(absl::default_allocator_is_nothrow::value ==
                    std::is_nothrow_move_constructible<
                        absl::optional<MoveMeThrow>>::value,
                "");
  std::vector<absl::optional<MoveMeNoThrow>> v;
  for (int i = 0; i < 10; ++i) v.emplace_back();
}

struct AnyLike {
  AnyLike(AnyLike&&) = default;
  AnyLike(const AnyLike&) = default;

  template <typename ValueType,
            typename T = typename std::decay<ValueType>::type,
            typename std::enable_if<
                !absl::disjunction<
                    std::is_same<AnyLike, T>,
                    absl::negation<std::is_copy_constructible<T>>>::value,
                int>::type = 0>
  AnyLike(ValueType&&) {}  // NOLINT(runtime/explicit)

  AnyLike& operator=(AnyLike&&) = default;
  AnyLike& operator=(const AnyLike&) = default;

  template <typename ValueType,
            typename T = typename std::decay<ValueType>::type>
  typename std::enable_if<
      absl::conjunction<absl::negation<std::is_same<AnyLike, T>>,
                        std::is_copy_constructible<T>>::value,
      AnyLike&>::type
  operator=(ValueType&& /* rhs */) {
    return *this;
  }
};

TEST(optionalTest, ConstructionConstraints) {
  EXPECT_TRUE((std::is_constructible<AnyLike, absl::optional<AnyLike>>::value));

  EXPECT_TRUE(
      (std::is_constructible<AnyLike, const absl::optional<AnyLike>&>::value));

  EXPECT_TRUE((std::is_constructible<absl::optional<AnyLike>, AnyLike>::value));
  EXPECT_TRUE(
      (std::is_constructible<absl::optional<AnyLike>, const AnyLike&>::value));

  EXPECT_TRUE((std::is_convertible<absl::optional<AnyLike>, AnyLike>::value));

  EXPECT_TRUE(
      (std::is_convertible<const absl::optional<AnyLike>&, AnyLike>::value));

  EXPECT_TRUE((std::is_convertible<AnyLike, absl::optional<AnyLike>>::value));
  EXPECT_TRUE(
      (std::is_convertible<const AnyLike&, absl::optional<AnyLike>>::value));

  EXPECT_TRUE(std::is_move_constructible<absl::optional<AnyLike>>::value);
  EXPECT_TRUE(std::is_copy_constructible<absl::optional<AnyLike>>::value);
}

TEST(optionalTest, AssignmentConstraints) {
  EXPECT_TRUE((std::is_assignable<AnyLike&, absl::optional<AnyLike>>::value));
  EXPECT_TRUE(
      (std::is_assignable<AnyLike&, const absl::optional<AnyLike>&>::value));
  EXPECT_TRUE((std::is_assignable<absl::optional<AnyLike>&, AnyLike>::value));
  EXPECT_TRUE(
      (std::is_assignable<absl::optional<AnyLike>&, const AnyLike&>::value));
  EXPECT_TRUE(std::is_move_assignable<absl::optional<AnyLike>>::value);
  EXPECT_TRUE(absl::is_copy_assignable<absl::optional<AnyLike>>::value);
}

#if !defined(__EMSCRIPTEN__)
struct NestedClassBug {
  struct Inner {
    bool dummy = false;
  };
  absl::optional<Inner> value;
};

TEST(optionalTest, InPlaceTSFINAEBug) {
  NestedClassBug b;
  ((void)b);
  using Inner = NestedClassBug::Inner;

  EXPECT_TRUE((std::is_default_constructible<Inner>::value));
  EXPECT_TRUE((std::is_constructible<Inner>::value));
  EXPECT_TRUE(
      (std::is_constructible<absl::optional<Inner>, absl::in_place_t>::value));

  absl::optional<Inner> o(absl::in_place);
  EXPECT_TRUE(o.has_value());
  o.emplace();
  EXPECT_TRUE(o.has_value());
}
#endif  // !defined(__EMSCRIPTEN__)

}  // namespace

#endif  // #if !defined(ABSL_USES_STD_OPTIONAL)