diff --git a/llvm/include/llvm/ADT/Optional.h b/llvm/include/llvm/ADT/Optional.h --- a/llvm/include/llvm/ADT/Optional.h +++ b/llvm/include/llvm/ADT/Optional.h @@ -31,9 +31,12 @@ struct in_place_t {}; +template struct has_is_valid { + const static bool value = false; +}; + /// Storage for any type. -template ::value> -class OptionalStorage { +template class OptionalStorage { union { char empty; T value; @@ -138,7 +141,72 @@ } }; -template class OptionalStorage { +template +class OptionalStorage< + T, typename std::enable_if::value, void>::type> { + + T value; + typedef has_is_valid THelper; + +public: + ~OptionalStorage() = default; + + constexpr OptionalStorage() noexcept : value{THelper::make_invalid()} {} + + OptionalStorage(OptionalStorage const &other) = default; + OptionalStorage(OptionalStorage &&other) = default; + + OptionalStorage &operator=(OptionalStorage const &other) = default; + OptionalStorage &operator=(OptionalStorage &&other) = default; + + template + explicit OptionalStorage(in_place_t, Args &&... args) + : value(std::forward(args)...) { + assert(THelper::is_valid(value)); + } + + void reset() noexcept { value = THelper::make_invalid(); } + + bool hasValue() const noexcept { return THelper::is_valid(value); } + + T &getValue() LLVM_LVALUE_FUNCTION noexcept { + assert(THelper::is_valid(value)); + return value; + } + T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { + assert(THelper::is_valid(value)); + return value; + } +#if LLVM_HAS_RVALUE_REFERENCE_THIS + T &&getValue() && noexcept { + assert(THelper::is_valid(value)); + return std::move(value); + } +#endif + + template void emplace(Args &&... args) { + value = T(std::forward(args)...); + assert(THelper::is_valid(value)); + } + + OptionalStorage &operator=(T const &y) { + assert(THelper::is_valid(y)); + value = y; + return *this; + } + + OptionalStorage &operator=(T &&y) { + assert(THelper::is_valid(y)); + value = std::move(y); + return *this; + } +}; + +template +class OptionalStorage< + T, typename std::enable_if::value && + !has_is_valid::value, + void>::type> { union { char empty; T value; diff --git a/llvm/unittests/ADT/OptionalTest.cpp b/llvm/unittests/ADT/OptionalTest.cpp --- a/llvm/unittests/ADT/OptionalTest.cpp +++ b/llvm/unittests/ADT/OptionalTest.cpp @@ -221,7 +221,7 @@ TEST_F(OptionalTest, Emplace) { MultiArgConstructor::ResetCounts(); Optional A; - + A.emplace(1, 2); EXPECT_TRUE(A.hasValue()); EXPECT_EQ(1, A->x); @@ -592,3 +592,50 @@ } } // end anonymous namespace + +class NaturalNumber { + friend struct llvm::optional_detail::has_is_valid; + int value; + constexpr NaturalNumber() : value(-1) {} + +public: + operator int() const { return value; } + NaturalNumber(int x) : value(x) { assert(value >= 0); } +}; + +template <> struct llvm::optional_detail::has_is_valid { + static const bool value = true; + static bool is_valid(const NaturalNumber &n) { return n.value >= 0; } + static constexpr NaturalNumber make_invalid() { return NaturalNumber(); } +}; + +TEST_F(OptionalTest, TestHasInvalid) { + + constexpr Optional none; + + static_assert(sizeof(Optional) == sizeof(int), + "no extra bool"); + + Optional x = None; + EXPECT_FALSE(x.hasValue()); + EXPECT_TRUE(x == none); + EXPECT_EQ(*reinterpret_cast(&x), -1); + x = NaturalNumber{42}; + EXPECT_TRUE(x.hasValue()); + EXPECT_EQ((int)x.getValue(), 42); + x.reset(); + EXPECT_FALSE(x.hasValue()); + + x.emplace(1234); + EXPECT_TRUE(x.hasValue()); + EXPECT_EQ((int)x.getValue(), 1234); + + x = Optional(4321); + EXPECT_TRUE(x.hasValue()); + EXPECT_EQ((int)x.getValue(), 4321); + + auto y = Optional(0x0dedbeef); + x = y; + EXPECT_TRUE(x.hasValue()); + EXPECT_EQ((int)x.getValue(), (int)0x0dedbeef); +}