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,66 @@ } }; -template class OptionalStorage { +template +class OptionalStorage< + T, typename std::enable_if::value, void>::type> { + + T value; + +public: + ~OptionalStorage() = default; + + OptionalStorage() noexcept : value{T::MakeInvalid()} {} + + 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)...) {} + + void reset() noexcept { value = T::MakeInvalid(); } + + bool hasValue() const noexcept { return value.IsValid(); } + + T &getValue() LLVM_LVALUE_FUNCTION noexcept { + assert(value.IsValid()); + return value; + } + T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { + assert(value.IsValid()); + return value; + } +#if LLVM_HAS_RVALUE_REFERENCE_THIS + T &&getValue() && noexcept { + assert(value.IsValid()); + return std::move(value); + } +#endif + + template void emplace(Args &&... args) { + value = T(std::forward(args)...); + } + + OptionalStorage &operator=(T const &y) { + value = y; + return *this; + } + + OptionalStorage &operator=(T &&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 @@ -592,3 +592,29 @@ } } // end anonymous namespace + +class NaturalNumber { +public: + int value; + static NaturalNumber MakeInvalid() { return NaturalNumber{-1}; } + bool IsValid() const { return value >= 0; } +}; + +template <> struct llvm::optional_detail::has_is_valid { + static const bool value = true; +}; + +TEST_F(OptionalTest, TestHasInvalid) { + + static_assert(sizeof(Optional) == sizeof(int), + "no extra bool"); + + Optional x = None; + EXPECT_FALSE(x.hasValue()); + EXPECT_EQ(*reinterpret_cast(&x), -1); + x = NaturalNumber{42}; + EXPECT_TRUE(x.hasValue()); + EXPECT_EQ(x.getValue().value, 42); + x = NaturalNumber{-42}; + EXPECT_FALSE(x.hasValue()); +}