//===- llvm/unittest/ADT/OptionalTest.cpp - Optional unit tests -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest-spi.h" #include "gtest/gtest.h" #include using namespace llvm; static_assert(std::is_trivially_copyable>::value, "trivially copyable"); static_assert(std::is_trivially_copyable>>::value, "trivially copyable"); void OptionalWorksInConstexpr() { constexpr auto x1 = Optional(); constexpr Optional x2{}; static_assert(!x1.hasValue() && !x2.hasValue(), "Default construction and hasValue() are contexpr"); constexpr auto y1 = Optional(3); constexpr Optional y2{3}; static_assert(y1.getValue() == y2.getValue() && y1.getValue() == 3, "Construction with value and getValue() are constexpr"); static_assert(Optional{3} >= 2 && Optional{1} < Optional{2}, "Comparisons work in constexpr"); } namespace { struct NonDefaultConstructible { static unsigned CopyConstructions; static unsigned Destructions; static unsigned CopyAssignments; explicit NonDefaultConstructible(int) { } NonDefaultConstructible(const NonDefaultConstructible&) { ++CopyConstructions; } NonDefaultConstructible &operator=(const NonDefaultConstructible&) { ++CopyAssignments; return *this; } ~NonDefaultConstructible() { ++Destructions; } static void ResetCounts() { CopyConstructions = 0; Destructions = 0; CopyAssignments = 0; } }; unsigned NonDefaultConstructible::CopyConstructions = 0; unsigned NonDefaultConstructible::Destructions = 0; unsigned NonDefaultConstructible::CopyAssignments = 0; static_assert( !std::is_trivially_copyable>::value, "not trivially copyable"); TEST(OptionalTest, NonDefaultConstructibleTest) { Optional O; EXPECT_FALSE(O); } TEST(OptionalTest, ResetTest) { NonDefaultConstructible::ResetCounts(); Optional O(NonDefaultConstructible(3)); EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(1u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); O.reset(); EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(1u, NonDefaultConstructible::Destructions); } TEST(OptionalTest, InitializationLeakTest) { NonDefaultConstructible::ResetCounts(); Optional(NonDefaultConstructible(3)); EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(2u, NonDefaultConstructible::Destructions); } TEST(OptionalTest, CopyConstructionTest) { NonDefaultConstructible::ResetCounts(); { Optional A(NonDefaultConstructible(3)); EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(1u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); Optional B(A); EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(0u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); } EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(2u, NonDefaultConstructible::Destructions); } TEST(OptionalTest, ConstructingCopyAssignmentTest) { NonDefaultConstructible::ResetCounts(); { Optional A(NonDefaultConstructible(3)); Optional B; EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(1u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); B = A; EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(0u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); } EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(2u, NonDefaultConstructible::Destructions); } TEST(OptionalTest, CopyingCopyAssignmentTest) { NonDefaultConstructible::ResetCounts(); { Optional A(NonDefaultConstructible(3)); Optional B(NonDefaultConstructible(4)); EXPECT_EQ(2u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(2u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); B = A; EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(1u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(0u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); } EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(2u, NonDefaultConstructible::Destructions); } TEST(OptionalTest, DeletingCopyAssignmentTest) { NonDefaultConstructible::ResetCounts(); { Optional A; Optional B(NonDefaultConstructible(3)); EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(1u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); B = A; EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(1u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); } EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(0u, NonDefaultConstructible::Destructions); } TEST(OptionalTest, NullCopyConstructionTest) { NonDefaultConstructible::ResetCounts(); { Optional A; Optional B; EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(0u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); B = A; EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(0u, NonDefaultConstructible::Destructions); NonDefaultConstructible::ResetCounts(); } EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); EXPECT_EQ(0u, NonDefaultConstructible::Destructions); } TEST(OptionalTest, GetValueOr) { Optional A; EXPECT_EQ(42, A.getValueOr(42)); A = 5; EXPECT_EQ(5, A.getValueOr(42)); } struct MultiArgConstructor { int x, y; MultiArgConstructor(int x, int y) : x(x), y(y) {} explicit MultiArgConstructor(int x, bool positive) : x(x), y(positive ? x : -x) {} MultiArgConstructor(const MultiArgConstructor &) = delete; MultiArgConstructor(MultiArgConstructor &&) = delete; MultiArgConstructor &operator=(const MultiArgConstructor &) = delete; MultiArgConstructor &operator=(MultiArgConstructor &&) = delete; static unsigned Destructions; ~MultiArgConstructor() { ++Destructions; } static void ResetCounts() { Destructions = 0; } }; unsigned MultiArgConstructor::Destructions = 0; static_assert(!std::is_trivially_copyable>::value, "not trivially copyable"); TEST(OptionalTest, Emplace) { MultiArgConstructor::ResetCounts(); Optional A; A.emplace(1, 2); EXPECT_TRUE(A.hasValue()); EXPECT_EQ(1, A->x); EXPECT_EQ(2, A->y); EXPECT_EQ(0u, MultiArgConstructor::Destructions); A.emplace(5, false); EXPECT_TRUE(A.hasValue()); EXPECT_EQ(5, A->x); EXPECT_EQ(-5, A->y); EXPECT_EQ(1u, MultiArgConstructor::Destructions); } struct MoveOnly { static unsigned MoveConstructions; static unsigned Destructions; static unsigned MoveAssignments; int val; explicit MoveOnly(int val) : val(val) { } MoveOnly(MoveOnly&& other) { val = other.val; ++MoveConstructions; } MoveOnly &operator=(MoveOnly&& other) { val = other.val; ++MoveAssignments; return *this; } ~MoveOnly() { ++Destructions; } static void ResetCounts() { MoveConstructions = 0; Destructions = 0; MoveAssignments = 0; } }; unsigned MoveOnly::MoveConstructions = 0; unsigned MoveOnly::Destructions = 0; unsigned MoveOnly::MoveAssignments = 0; static_assert(!std::is_trivially_copyable>::value, "not trivially copyable"); TEST(OptionalTest, MoveOnlyNull) { MoveOnly::ResetCounts(); Optional O; EXPECT_EQ(0u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(0u, MoveOnly::Destructions); } TEST(OptionalTest, MoveOnlyConstruction) { MoveOnly::ResetCounts(); Optional O(MoveOnly(3)); EXPECT_TRUE((bool)O); EXPECT_EQ(3, O->val); EXPECT_EQ(1u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(1u, MoveOnly::Destructions); } TEST(OptionalTest, MoveOnlyMoveConstruction) { Optional A(MoveOnly(3)); MoveOnly::ResetCounts(); Optional B(std::move(A)); EXPECT_TRUE((bool)A); EXPECT_TRUE((bool)B); EXPECT_EQ(3, B->val); EXPECT_EQ(1u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(0u, MoveOnly::Destructions); } TEST(OptionalTest, MoveOnlyAssignment) { MoveOnly::ResetCounts(); Optional O; O = MoveOnly(3); EXPECT_TRUE((bool)O); EXPECT_EQ(3, O->val); EXPECT_EQ(1u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(1u, MoveOnly::Destructions); } TEST(OptionalTest, MoveOnlyInitializingAssignment) { Optional A(MoveOnly(3)); Optional B; MoveOnly::ResetCounts(); B = std::move(A); EXPECT_TRUE((bool)A); EXPECT_TRUE((bool)B); EXPECT_EQ(3, B->val); EXPECT_EQ(1u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(0u, MoveOnly::Destructions); } TEST(OptionalTest, MoveOnlyNullingAssignment) { Optional A; Optional B(MoveOnly(3)); MoveOnly::ResetCounts(); B = std::move(A); EXPECT_FALSE((bool)A); EXPECT_FALSE((bool)B); EXPECT_EQ(0u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(1u, MoveOnly::Destructions); } TEST(OptionalTest, MoveOnlyAssigningAssignment) { Optional A(MoveOnly(3)); Optional B(MoveOnly(4)); MoveOnly::ResetCounts(); B = std::move(A); EXPECT_TRUE((bool)A); EXPECT_TRUE((bool)B); EXPECT_EQ(3, B->val); EXPECT_EQ(0u, MoveOnly::MoveConstructions); EXPECT_EQ(1u, MoveOnly::MoveAssignments); EXPECT_EQ(0u, MoveOnly::Destructions); } struct Immovable { static unsigned Constructions; static unsigned Destructions; int val; explicit Immovable(int val) : val(val) { ++Constructions; } ~Immovable() { ++Destructions; } static void ResetCounts() { Constructions = 0; Destructions = 0; } private: // This should disable all move/copy operations. Immovable(Immovable&& other) = delete; }; unsigned Immovable::Constructions = 0; unsigned Immovable::Destructions = 0; static_assert(!std::is_trivially_copyable>::value, "not trivially copyable"); TEST(OptionalTest, ImmovableEmplace) { Optional A; Immovable::ResetCounts(); A.emplace(4); EXPECT_TRUE((bool)A); EXPECT_EQ(4, A->val); EXPECT_EQ(1u, Immovable::Constructions); EXPECT_EQ(0u, Immovable::Destructions); } #if LLVM_HAS_RVALUE_REFERENCE_THIS TEST(OptionalTest, MoveGetValueOr) { Optional A; MoveOnly::ResetCounts(); EXPECT_EQ(42, std::move(A).getValueOr(MoveOnly(42)).val); EXPECT_EQ(1u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(2u, MoveOnly::Destructions); A = MoveOnly(5); MoveOnly::ResetCounts(); EXPECT_EQ(5, std::move(A).getValueOr(MoveOnly(42)).val); EXPECT_EQ(1u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); EXPECT_EQ(2u, MoveOnly::Destructions); } #endif // LLVM_HAS_RVALUE_REFERENCE_THIS struct EqualTo { template static bool apply(const T &X, const U &Y) { return X == Y; } }; struct NotEqualTo { template static bool apply(const T &X, const U &Y) { return X != Y; } }; struct Less { template static bool apply(const T &X, const U &Y) { return X < Y; } }; struct Greater { template static bool apply(const T &X, const U &Y) { return X > Y; } }; struct LessEqual { template static bool apply(const T &X, const U &Y) { return X <= Y; } }; struct GreaterEqual { template static bool apply(const T &X, const U &Y) { return X >= Y; } }; template void CheckRelation(const Optional &Lhs, const Optional &Rhs, bool Expected) { EXPECT_EQ(Expected, OperatorT::apply(Lhs, Rhs)); if (Lhs) EXPECT_EQ(Expected, OperatorT::apply(*Lhs, Rhs)); else EXPECT_EQ(Expected, OperatorT::apply(None, Rhs)); if (Rhs) EXPECT_EQ(Expected, OperatorT::apply(Lhs, *Rhs)); else EXPECT_EQ(Expected, OperatorT::apply(Lhs, None)); } struct EqualityMock {}; const Optional NoneEq, EqualityLhs((EqualityMock())), EqualityRhs((EqualityMock())); bool IsEqual; bool operator==(const EqualityMock &Lhs, const EqualityMock &Rhs) { EXPECT_EQ(&*EqualityLhs, &Lhs); EXPECT_EQ(&*EqualityRhs, &Rhs); return IsEqual; } TEST(OptionalTest, OperatorEqual) { CheckRelation(NoneEq, NoneEq, true); CheckRelation(NoneEq, EqualityRhs, false); CheckRelation(EqualityLhs, NoneEq, false); IsEqual = false; CheckRelation(EqualityLhs, EqualityRhs, IsEqual); IsEqual = true; CheckRelation(EqualityLhs, EqualityRhs, IsEqual); } TEST(OptionalTest, OperatorNotEqual) { CheckRelation(NoneEq, NoneEq, false); CheckRelation(NoneEq, EqualityRhs, true); CheckRelation(EqualityLhs, NoneEq, true); IsEqual = false; CheckRelation(EqualityLhs, EqualityRhs, !IsEqual); IsEqual = true; CheckRelation(EqualityLhs, EqualityRhs, !IsEqual); } struct InequalityMock {}; const Optional NoneIneq, InequalityLhs((InequalityMock())), InequalityRhs((InequalityMock())); bool IsLess; bool operator<(const InequalityMock &Lhs, const InequalityMock &Rhs) { EXPECT_EQ(&*InequalityLhs, &Lhs); EXPECT_EQ(&*InequalityRhs, &Rhs); return IsLess; } TEST(OptionalTest, OperatorLess) { CheckRelation(NoneIneq, NoneIneq, false); CheckRelation(NoneIneq, InequalityRhs, true); CheckRelation(InequalityLhs, NoneIneq, false); IsLess = false; CheckRelation(InequalityLhs, InequalityRhs, IsLess); IsLess = true; CheckRelation(InequalityLhs, InequalityRhs, IsLess); } TEST(OptionalTest, OperatorGreater) { CheckRelation(NoneIneq, NoneIneq, false); CheckRelation(NoneIneq, InequalityRhs, false); CheckRelation(InequalityLhs, NoneIneq, true); IsLess = false; CheckRelation(InequalityRhs, InequalityLhs, IsLess); IsLess = true; CheckRelation(InequalityRhs, InequalityLhs, IsLess); } TEST(OptionalTest, OperatorLessEqual) { CheckRelation(NoneIneq, NoneIneq, true); CheckRelation(NoneIneq, InequalityRhs, true); CheckRelation(InequalityLhs, NoneIneq, false); IsLess = false; CheckRelation(InequalityRhs, InequalityLhs, !IsLess); IsLess = true; CheckRelation(InequalityRhs, InequalityLhs, !IsLess); } TEST(OptionalTest, OperatorGreaterEqual) { CheckRelation(NoneIneq, NoneIneq, true); CheckRelation(NoneIneq, InequalityRhs, false); CheckRelation(InequalityLhs, NoneIneq, true); IsLess = false; CheckRelation(InequalityLhs, InequalityRhs, !IsLess); IsLess = true; CheckRelation(InequalityLhs, InequalityRhs, !IsLess); } struct ComparableAndStreamable { friend bool operator==(ComparableAndStreamable, ComparableAndStreamable) LLVM_ATTRIBUTE_USED { return true; } friend raw_ostream &operator<<(raw_ostream &OS, ComparableAndStreamable) { return OS << "ComparableAndStreamable"; } static Optional get() { return ComparableAndStreamable(); } }; TEST(OptionalTest, StreamOperator) { auto to_string = [](Optional O) { SmallString<16> S; raw_svector_ostream OS(S); OS << O; return S; }; EXPECT_EQ("ComparableAndStreamable", to_string(ComparableAndStreamable::get())); EXPECT_EQ("None", to_string(None)); } struct Comparable { friend bool operator==(Comparable, Comparable) LLVM_ATTRIBUTE_USED { return true; } static Optional get() { return Comparable(); } }; TEST(OptionalTest, UseInUnitTests) { // Test that we invoke the streaming operators when pretty-printing values in // EXPECT macros. EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, ComparableAndStreamable::get()), "Expected: llvm::None\n" " Which is: None\n" "To be equal to: ComparableAndStreamable::get()\n" " Which is: ComparableAndStreamable"); // Test that it is still possible to compare objects which do not have a // custom streaming operator. EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, Comparable::get()), "object"); } TEST(OptionalTest, HashValue) { // Check that None, false, and true all hash differently. Optional B, B0 = false, B1 = true; EXPECT_NE(hash_value(B0), hash_value(B)); EXPECT_NE(hash_value(B1), hash_value(B)); EXPECT_NE(hash_value(B1), hash_value(B0)); // Check that None, 0, and 1 all hash differently. Optional I, I0 = 0, I1 = 1; EXPECT_NE(hash_value(I0), hash_value(I)); EXPECT_NE(hash_value(I1), hash_value(I)); EXPECT_NE(hash_value(I1), hash_value(I0)); // Check None hash the same way regardless of type. EXPECT_EQ(hash_value(B), hash_value(I)); } } // end anonymous namespace