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 @@ -27,12 +27,33 @@ class raw_ostream; +// This template may be specialized in order to customize +// how Optional is implemented. If you do specialize this, +// you must do so in the same header that defines T. Otherwise +// there could be some translation units that see your specialized +// OptionalInfo and other translation units that do not. +// +// The only available customization is to set has_is_valid = true, +// and implement is_valid() and make_invalid(). +// This will cause Optional to be represented as: class { T value; } +// None will be represented by an invalid T, as returned by +// make_invalid(). T's representation of invalid values should +// be private and only shared with OptionalInfo +template struct OptionalInfo { + typedef T type; + static const bool has_is_valid = false; + static const bool is_trivially_copyable = + std::is_trivially_copyable::value; + // static bool is_valid(const T &n); + // static constexpr T make_invalid(); +}; + namespace optional_detail { struct in_place_t {}; /// Storage for any type. -template ::value> +template , typename Enable = void> class OptionalStorage { union { char empty; @@ -41,6 +62,8 @@ bool hasVal; public: + static const bool is_generic_storage = true; + ~OptionalStorage() { reset(); } OptionalStorage() noexcept : empty(), hasVal(false) {} @@ -138,7 +161,77 @@ } }; -template class OptionalStorage { +template +class OptionalStorage< + T, OptionalInfo, + typename std::enable_if::has_is_valid, void>::type> { + + T value; + typedef OptionalInfo Info; + +public: + static const bool uses_is_valid = true; + + ~OptionalStorage() = default; + + constexpr OptionalStorage() noexcept : value{Info::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(Info::is_valid(value)); + } + + void reset() noexcept { value = Info::make_invalid(); } + + bool hasValue() const noexcept { return Info::is_valid(value); } + + T &getValue() LLVM_LVALUE_FUNCTION noexcept { + assert(Info::is_valid(value)); + return value; + } + T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { + assert(Info::is_valid(value)); + return value; + } +#if LLVM_HAS_RVALUE_REFERENCE_THIS + T &&getValue() && noexcept { + assert(Info::is_valid(value)); + return std::move(value); + } +#endif + + template void emplace(Args &&... args) { + value = T(std::forward(args)...); + assert(Info::is_valid(value)); + } + + OptionalStorage &operator=(T const &y) { + assert(Info::is_valid(y)); + value = y; + return *this; + } + + OptionalStorage &operator=(T &&y) { + assert(Info::is_valid(y)); + value = std::move(y); + return *this; + } +}; + +template +class OptionalStorage< + T, OptionalInfo, + typename std::enable_if::is_trivially_copyable && + !OptionalInfo::has_is_valid, + void>::type> { + union { char empty; T value; @@ -146,6 +239,8 @@ bool hasVal = false; public: + static const bool uses_trivial_copy = true; + ~OptionalStorage() = default; OptionalStorage() noexcept : empty{} {} @@ -213,11 +308,15 @@ } // namespace optional_detail template class Optional { - optional_detail::OptionalStorage Storage; - public: using value_type = T; + using info_type = OptionalInfo; + using storage_type = optional_detail::OptionalStorage; + +private: + storage_type Storage; +public: constexpr Optional() {} constexpr Optional(NoneType) {} 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,57 @@ } } // end anonymous namespace + +class NaturalNumber { + friend struct llvm::OptionalInfo; + int value; + constexpr NaturalNumber() : value(-1) {} + +public: + operator int() const { return value; } + NaturalNumber(int x) : value(x) { assert(value >= 0); } +}; + +template <> struct llvm::OptionalInfo { + static const bool has_is_valid = true; + static bool is_valid(const NaturalNumber &n) { return n.value >= 0; } + static constexpr NaturalNumber make_invalid() { return NaturalNumber(); } +}; + +static_assert(Optional::storage_type::uses_trivial_copy, + "check Optional"); +static_assert(Optional::storage_type::uses_is_valid, + "check Optional"); +static_assert(Optional>::storage_type::is_generic_storage, + "check Optional"); + +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); +}