diff --git a/libc/src/__support/CPP/optional.h b/libc/src/__support/CPP/optional.h --- a/libc/src/__support/CPP/optional.h +++ b/libc/src/__support/CPP/optional.h @@ -32,41 +32,105 @@ // in_place that can be used in the constructor. LIBC_INLINE_VAR constexpr in_place_t in_place{}; -// This is very simple implementation of the std::optional class. It makes -// several assumptions that the underlying type is trivially constructable, -// copyable, or movable. -template class optional { - template class OptionalStorage { +template struct OptionalStorageBase { + template ::value> + struct OptionalStorage { union { char empty; U stored_value; }; - bool in_use; - public: - LIBC_INLINE ~OptionalStorage() { reset(); } + LIBC_INLINE constexpr OptionalStorage() : empty() {} + template + LIBC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args) + : stored_value(forward(args)...) {} + }; - LIBC_INLINE constexpr OptionalStorage() : empty(), in_use(false) {} + template struct OptionalStorage { + union { + char empty; + U stored_value; + }; + LIBC_INLINE constexpr OptionalStorage() : empty() {} template LIBC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args) - : stored_value(forward(args)...), in_use(true) {} + : stored_value(forward(args)...) {} - LIBC_INLINE void reset() { - if (in_use) - stored_value.~U(); - in_use = false; - } + LIBC_INLINE ~OptionalStorage() {} + }; - LIBC_INLINE constexpr bool has_value() const { return in_use; } + using stored_type = remove_const_t; + OptionalStorage storage; + bool in_use = false; - LIBC_INLINE U &value() & { return stored_value; } - LIBC_INLINE constexpr U const &value() const & { return stored_value; } - LIBC_INLINE U &&value() && { return move(stored_value); } - }; +public: + LIBC_INLINE ~OptionalStorageBase() = default; + LIBC_INLINE constexpr OptionalStorageBase() = default; + template + LIBC_INLINE constexpr OptionalStorageBase(in_place_t, ArgTypes &&...Args) + : storage(in_place, forward(Args)...), in_use(true) {} + + LIBC_INLINE constexpr void reset() { + if (in_use) + storage.stored_value.~stored_type(); + in_use = false; + } + + LIBC_INLINE constexpr bool has_value() const { return in_use; } + + LIBC_INLINE constexpr T &value() & { return this->storage.stored_value; } + LIBC_INLINE constexpr T const &value() const & { + return this->storage.stored_value; + } + LIBC_INLINE constexpr T &&value() && { + return move(this->storage.stored_value); + } +}; + +// Class template that manages the storage for optionals. +template > +struct OptionalStorage : OptionalStorageBase { + using OptionalStorageBase::OptionalStorageBase; + LIBC_INLINE constexpr OptionalStorage() = default; +}; + +// storage for optionals with non-trivial destructors. +template +struct OptionalStorage : OptionalStorageBase { + using OptionalStorageBase::OptionalStorageBase; + LIBC_INLINE constexpr OptionalStorage() = default; + LIBC_INLINE constexpr OptionalStorage(const OptionalStorage &) = default; + LIBC_INLINE constexpr OptionalStorage(OptionalStorage &&) = default; + LIBC_INLINE constexpr OptionalStorage & + operator=(const OptionalStorage &) = default; + LIBC_INLINE constexpr OptionalStorage & + operator=(OptionalStorage &&) = default; + + // Destructor needs to destroy the contained value: + LIBC_INLINE ~OptionalStorage() { this->reset(); } +}; + +// This is very simple implementation of the std::optional class. It makes +// several assumptions that the underlying type is trivially copyable, or +// movable. +template class optional { OptionalStorage storage; + static_assert(!is_same_v, nullopt_t>); + static_assert(!is_same_v, in_place_t>); + static_assert(!is_reference_v); + + // SFINAE helpers + template + using __not_self = __not_>>; + template + using __not_tag = __not_>>; + template + using _Requires = enable_if_t<__and_<_Cond...>::value, bool>; + public: LIBC_INLINE constexpr optional() = default; LIBC_INLINE constexpr optional(nullopt_t) {} @@ -77,44 +141,48 @@ LIBC_INLINE constexpr optional(T &&t) : storage(in_place, move(t)) {} LIBC_INLINE constexpr optional(optional &&O) = default; - template + // Constructors for engaged optionals. + template , bool> = false> LIBC_INLINE constexpr optional(in_place_t, ArgTypes &&...Args) : storage(in_place, forward(Args)...) {} - LIBC_INLINE optional &operator=(T &&t) { - storage = move(t); + template , __not_tag, is_constructible, + is_convertible> = true> + LIBC_INLINE constexpr optional(T2 &&t) : optional(in_place, forward(t)) {} + + // Assignment operators. + LIBC_INLINE constexpr optional &operator=(nullopt_t) { + reset(); return *this; } - LIBC_INLINE optional &operator=(optional &&) = default; - - LIBC_INLINE static constexpr optional create(const T *t) { - return t ? optional(*t) : optional(); + LIBC_INLINE constexpr optional &operator=(T &&t) { + storage = move(t); + return *this; } + LIBC_INLINE constexpr optional &operator=(optional &&) = default; - LIBC_INLINE optional &operator=(const T &t) { + LIBC_INLINE constexpr optional &operator=(const T &t) { storage = t; return *this; } - LIBC_INLINE optional &operator=(const optional &) = default; + LIBC_INLINE constexpr optional &operator=(const optional &) = default; - LIBC_INLINE void reset() { storage.reset(); } + LIBC_INLINE constexpr void reset() { storage.reset(); } LIBC_INLINE constexpr const T &value() const & { return storage.value(); } - LIBC_INLINE T &value() & { return storage.value(); } + LIBC_INLINE constexpr T &value() & { return storage.value(); } LIBC_INLINE constexpr explicit operator bool() const { return has_value(); } LIBC_INLINE constexpr bool has_value() const { return storage.has_value(); } LIBC_INLINE constexpr const T *operator->() const { return &storage.value(); } - LIBC_INLINE T *operator->() { return &storage.value(); } + LIBC_INLINE constexpr T *operator->() { return &storage.value(); } LIBC_INLINE constexpr const T &operator*() const & { return value(); } - LIBC_INLINE T &operator*() & { return value(); } - - template LIBC_INLINE constexpr T value_or(U &&value) const & { - return has_value() ? value() : forward(value); - } + LIBC_INLINE constexpr T &operator*() & { return value(); } - LIBC_INLINE T &&value() && { return move(storage.value()); } - LIBC_INLINE T &&operator*() && { return move(storage.value()); } + LIBC_INLINE constexpr T &&value() && { return move(storage.value()); } + LIBC_INLINE constexpr T &&operator*() && { return move(storage.value()); } }; } // namespace cpp diff --git a/libc/src/__support/CPP/type_traits.h b/libc/src/__support/CPP/type_traits.h --- a/libc/src/__support/CPP/type_traits.h +++ b/libc/src/__support/CPP/type_traits.h @@ -30,6 +30,8 @@ using true_type = cpp::integral_constant; using false_type = cpp::integral_constant; +template using bool_constant = integral_constant; + template struct is_trivially_copyable : public integral_constant {}; @@ -54,14 +56,16 @@ template struct remove_cv : type_identity {}; template using remove_cv_t = typename remove_cv::type; +template struct remove_const : type_identity {}; +template struct remove_const : type_identity {}; +template using remove_const_t = typename remove_const::type; + template struct remove_reference : type_identity {}; template struct remove_reference : type_identity {}; template struct remove_reference : type_identity {}; template using remove_reference_t = typename remove_reference::type; -template struct add_rvalue_reference : type_identity {}; - template struct remove_cvref { using type = remove_cv_t>; }; @@ -171,6 +175,22 @@ template using conditional_t = typename conditional::type; +template struct __and_; + +template <> struct __and_<> : public true_type {}; + +template struct __and_ : public T1 {}; + +template +struct __and_ : public conditional_t {}; + +template +struct __and_ + : public conditional_t, T1> {}; + +template +struct __not_ : public integral_constant {}; + template struct is_void : is_same::type> {}; template @@ -194,6 +214,29 @@ details::void_t( declval()))>> = true; +template +struct is_constructible + : public integral_constant {}; + +template +inline constexpr bool is_constructible_v = __is_constructible(T, Args...); + +template +struct is_convertible + : public integral_constant {}; + +template +struct is_trivially_destructible + : public integral_constant {}; + +template +inline constexpr bool is_trivially_destructible_v = + is_trivially_destructible::value; + +template struct is_reference : bool_constant<__is_reference(T)> {}; + +template inline constexpr bool is_reference_v = __is_reference(T); + } // namespace cpp } // namespace __llvm_libc diff --git a/libc/src/__support/UInt.h b/libc/src/__support/UInt.h --- a/libc/src/__support/UInt.h +++ b/libc/src/__support/UInt.h @@ -376,7 +376,7 @@ return remainder; } if (other == 0) { - return nullopt; + return {}; } BigInt quotient(0); diff --git a/libc/test/src/__support/CPP/optional_test.cpp b/libc/test/src/__support/CPP/optional_test.cpp --- a/libc/test/src/__support/CPP/optional_test.cpp +++ b/libc/test/src/__support/CPP/optional_test.cpp @@ -43,14 +43,13 @@ // For this test case, the destructor increments the pointed-to value. int holding = 1; optional Complicated(&holding); - // Destructor was run once as part of copying the object. - ASSERT_EQ(holding, 2); - // Destructor was run a second time as part of destruction. + ASSERT_EQ(holding, 1); + // Destructor was run once as part of destruction. Complicated.reset(); - ASSERT_EQ(holding, 3); - // Destructor was not run a third time as the object is already destroyed. + ASSERT_EQ(holding, 2); + // Destructor was not run a second time as the object is already destroyed. Complicated.reset(); - ASSERT_EQ(holding, 3); + ASSERT_EQ(holding, 2); // Test that assigning an optional to another works when set optional Trivial3(12);