Index: include/llvm/ADT/Optional.h =================================================================== --- include/llvm/ADT/Optional.h +++ include/llvm/ADT/Optional.h @@ -150,18 +150,43 @@ static const bool value = isPodLike::value; }; -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator==(const Optional &X, const Optional &Y); +template +bool operator==(const Optional &X, const Optional &Y) { + if (X && Y) + return *X == *Y; + return X.hasValue() == Y.hasValue(); +} + +template +bool operator!=(const Optional &X, const Optional &Y) { + return !(X == Y); +} + +template +bool operator<(const Optional &X, const Optional &Y) { + if (X && Y) + return *X < *Y; + return X.hasValue() < Y.hasValue(); +} + +template +bool operator<=(const Optional &X, const Optional &Y) { + return !(Y < X); +} + +template +bool operator>(const Optional &X, const Optional &Y) { + return Y < X; +} + +template +bool operator>=(const Optional &X, const Optional &Y) { + return !(X < Y); +} template bool operator==(const Optional &X, NoneType) { - return !X.hasValue(); + return !X; } template @@ -178,50 +203,86 @@ bool operator!=(NoneType, const Optional &X) { return X != None; } -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator!=(const Optional &X, const Optional &Y); - -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator<(const Optional &X, const Optional &Y); - -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator<=(const Optional &X, const Optional &Y); - -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator>=(const Optional &X, const Optional &Y); - -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator>(const Optional &X, const Optional &Y); + +template bool operator<(const Optional &X, NoneType) { + return false; +} + +template bool operator<(NoneType, const Optional &X) { + return X.hasValue(); +} + +template bool operator<=(const Optional &X, NoneType) { + return !(None < X); +} + +template bool operator<=(NoneType, const Optional &X) { + return !(X < None); +} + +template bool operator>(const Optional &X, NoneType) { + return None < X; +} + +template bool operator>(NoneType, const Optional &X) { + return X < None; +} + +template bool operator>=(const Optional &X, NoneType) { + return None <= X; +} + +template bool operator>=(NoneType, const Optional &X) { + return X <= None; +} + +template bool operator==(const Optional &X, const T &Y) { + return X && *X == Y; +} + +template bool operator==(const T &X, const Optional &Y) { + return Y == X; +} + +template bool operator!=(const Optional &X, const T &Y) { + return !(X == Y); +} + +template bool operator!=(const T &X, const Optional &Y) { + return !(X == Y); +} + +template bool operator<(const Optional &X, const T &Y) { + return !X || *X < Y; +} + +template bool operator<(const T &X, const Optional &Y) { + return Y && X < *Y; +} + +template bool operator<=(const Optional &X, const T &Y) { + return !(Y < X); +} + +template bool operator<=(const T &X, const Optional &Y) { + return !(Y < X); +} + +template bool operator>(const Optional &X, const T &Y) { + return Y < X; +} + +template bool operator>(const T &X, const Optional &Y) { + return Y < X; +} + +template bool operator>=(const Optional &X, const T &Y) { + return !(X < Y); +} + +template bool operator>=(const T &X, const Optional &Y) { + return !(X < Y); +} } // end llvm namespace Index: unittests/ADT/OptionalTest.cpp =================================================================== --- unittests/ADT/OptionalTest.cpp +++ unittests/ADT/OptionalTest.cpp @@ -9,6 +9,7 @@ #include "gtest/gtest.h" #include "llvm/ADT/Optional.h" + using namespace llvm; namespace { @@ -377,17 +378,216 @@ #endif // LLVM_HAS_RVALUE_REFERENCE_THIS -TEST_F(OptionalTest, NoneComparison) { - Optional o; - EXPECT_EQ(o, None); - EXPECT_EQ(None, o); - EXPECT_FALSE(o != None); - EXPECT_FALSE(None != o); - o = 3; - EXPECT_FALSE(o == None); - EXPECT_FALSE(None == o); - EXPECT_TRUE(o != None); - EXPECT_TRUE(None != o); +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 T1 &L, const T2 &R, bool Expected) { + Optional Lhs(L); + Optional Rhs(R); + + 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)); +} + +class EqualityMock { + int Id; + +public: + EqualityMock(int Id) : Id(Id) {} + + static EqualityMock ExpectedLhs, ExpectedRhs; + static bool IsEqual; + + bool operator==(const EqualityMock &Other) const { + EXPECT_NE(-1, ExpectedLhs.Id); + EXPECT_NE(-1, ExpectedRhs.Id); + EXPECT_TRUE((ExpectedLhs.Id == Id && ExpectedRhs.Id == Other.Id) || + (ExpectedLhs.Id == Other.Id && ExpectedRhs.Id == Id)); + return IsEqual; + } +}; + +EqualityMock EqualityMock::ExpectedLhs(-1); +EqualityMock EqualityMock::ExpectedRhs(-1); +bool EqualityMock::IsEqual; + +TEST_F(OptionalTest, OperatorEqual) { + EqualityMock::ExpectedLhs = -1; + EqualityMock::ExpectedRhs = -1; + + CheckRelation(None, None, true); + CheckRelation(None, EqualityMock(1), false); + CheckRelation(EqualityMock(0), None, false); + + EqualityMock::ExpectedLhs = 0; + EqualityMock::ExpectedRhs = 1; + + EqualityMock::IsEqual = false; + CheckRelation(EqualityMock(0), EqualityMock(1), + EqualityMock::IsEqual); + EqualityMock::IsEqual = true; + CheckRelation(EqualityMock(0), EqualityMock(1), + EqualityMock::IsEqual); +} + +TEST_F(OptionalTest, OperatorNotEqual) { + EqualityMock::ExpectedLhs = -1; + EqualityMock::ExpectedRhs = -1; + + CheckRelation(None, None, false); + CheckRelation(None, EqualityMock(1), true); + CheckRelation(EqualityMock(0), None, true); + + EqualityMock::ExpectedLhs = 0; + EqualityMock::ExpectedRhs = 1; + + EqualityMock::IsEqual = false; + CheckRelation(EqualityMock(0), EqualityMock(1), + !EqualityMock::IsEqual); + EqualityMock::IsEqual = true; + CheckRelation(EqualityMock(0), EqualityMock(1), + !EqualityMock::IsEqual); +} + +class ComparisonMock { + int Id; + +public: + ComparisonMock(int Id) : Id(Id) {} + + static ComparisonMock ExpectedLhs, ExpectedRhs; + static bool IsLess; + + bool operator<(const ComparisonMock &Other) const { + EXPECT_NE(-1, ExpectedLhs.Id); + EXPECT_NE(-1, ExpectedRhs.Id); + EXPECT_EQ(ExpectedLhs.Id, Id); + EXPECT_EQ(ExpectedRhs.Id, Other.Id); + return IsLess; + } +}; + +ComparisonMock ComparisonMock::ExpectedLhs(-1); +ComparisonMock ComparisonMock::ExpectedRhs(-1); +bool ComparisonMock::IsLess; + +TEST_F(OptionalTest, OperatorLess) { + ComparisonMock::ExpectedLhs = -1; + ComparisonMock::ExpectedRhs = -1; + + CheckRelation(None, None, false); + CheckRelation(None, ComparisonMock(1), true); + CheckRelation(ComparisonMock(0), None, false); + + ComparisonMock::ExpectedLhs = 0; + ComparisonMock::ExpectedRhs = 1; + + ComparisonMock::IsLess = false; + CheckRelation(ComparisonMock(0), ComparisonMock(1), + ComparisonMock::IsLess); + ComparisonMock::IsLess = true; + CheckRelation(ComparisonMock(0), ComparisonMock(1), + ComparisonMock::IsLess); +} + +TEST_F(OptionalTest, OperatorGreater) { + ComparisonMock::ExpectedLhs = -1; + ComparisonMock::ExpectedRhs = -1; + + CheckRelation(None, None, false); + CheckRelation(None, ComparisonMock(1), false); + CheckRelation(ComparisonMock(0), None, true); + + ComparisonMock::ExpectedLhs = 1; + ComparisonMock::ExpectedRhs = 0; + + ComparisonMock::IsLess = false; + CheckRelation(ComparisonMock(0), ComparisonMock(1), + ComparisonMock::IsLess); + ComparisonMock::IsLess = true; + CheckRelation(ComparisonMock(0), ComparisonMock(1), + ComparisonMock::IsLess); +} + +TEST_F(OptionalTest, OperatorLessEqual) { + ComparisonMock::ExpectedLhs = -1; + ComparisonMock::ExpectedRhs = -1; + + CheckRelation(None, None, true); + CheckRelation(None, ComparisonMock(1), true); + CheckRelation(ComparisonMock(0), None, false); + + ComparisonMock::ExpectedLhs = 1; + ComparisonMock::ExpectedRhs = 0; + + ComparisonMock::IsLess = false; + CheckRelation(ComparisonMock(0), ComparisonMock(1), + !ComparisonMock::IsLess); + ComparisonMock::IsLess = true; + CheckRelation(ComparisonMock(0), ComparisonMock(1), + !ComparisonMock::IsLess); +} + +TEST_F(OptionalTest, OperatorGreaterEqual) { + ComparisonMock::ExpectedLhs = -1; + ComparisonMock::ExpectedRhs = -1; + + CheckRelation(None, None, true); + CheckRelation(None, ComparisonMock(1), false); + CheckRelation(ComparisonMock(0), None, true); + + ComparisonMock::ExpectedLhs = 0; + ComparisonMock::ExpectedRhs = 1; + + ComparisonMock::IsLess = false; + CheckRelation( + ComparisonMock(0), ComparisonMock(1), !ComparisonMock::IsLess); + ComparisonMock::IsLess = true; + CheckRelation( + ComparisonMock(0), ComparisonMock(1), !ComparisonMock::IsLess); } } // end anonymous namespace