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,89 +32,452 @@ // in_place that can be used in the constructor. inline 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 { + OptionalStorageBase() = default; + ~OptionalStorageBase() = default; + + template + LIBC_INLINE constexpr OptionalStorageBase(in_place_t, ArgTypes &&...Args) + : storage(in_place, forward(Args)...), in_use(true) {} + + // Constructor used by OptionalStorageBase copy constructor when the + // contained value is not trivially copy constructible. + LIBC_INLINE constexpr OptionalStorageBase(bool, + const OptionalStorageBase &other) { + if (other.in_use) + this->construct(other); + } + + // Constructor used by OptionalStorageBase move constructor when the + // contained value is not trivially move constructible. + LIBC_INLINE constexpr OptionalStorageBase(bool, OptionalStorageBase &&other) { + if (other.in_use) + this->construct(other); + } + + // Copy constructor is only used to when the contained value is + // trivially copy constructible. + LIBC_INLINE constexpr OptionalStorageBase(const OptionalStorageBase &) = + default; + + // Move constructor is only used to when the contained value is + // trivially copy constructible. + LIBC_INLINE constexpr OptionalStorageBase(OptionalStorageBase &&) = default; + + LIBC_INLINE constexpr OptionalStorageBase & + operator=(const OptionalStorageBase &) = default; + + LIBC_INLINE constexpr OptionalStorageBase & + operator=(OptionalStorageBase &&) = default; + + // used to perform non-trivial copy assignment. + LIBC_INLINE constexpr void copy_assign(const OptionalStorageBase &other) { + if (this->in_use && other.in_use) + this->storage.stored_value = other.storage.stored_value; + else { + if (this->other.in_use) + this->construct(other); + else + this->reset(); + } + } + + // used to perform non-trivial move assignment. + LIBC_INLINE constexpr void move_assign(OptionalStorageBase &&other) { + if (this->in_use && other.in_use) + this->storage.stored_value = move(other.storage.stored_value); + else { + if (this->other.in_use) + this->construct(other); + else + this->reset(); + } + } + + template ::value> + 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)...) {} + }; + + template struct OptionalStorage { union { char empty; U stored_value; }; - bool in_use; public: - LIBC_INLINE ~OptionalStorage() { reset(); } + LIBC_INLINE ~OptionalStorage() {} - LIBC_INLINE constexpr OptionalStorage() : empty(), in_use(false) {} + 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; - } + using stored_type = remove_const_t; + OptionalStorage storage; + bool in_use = false; - LIBC_INLINE constexpr bool has_value() const { return in_use; } + template constexpr void construct(Args &&...args) { + construct_at(&this->storage.stored_value, forward(args)...); + this->in_use = true; + } - 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); } - }; + LIBC_INLINE constexpr void destroy() { + in_use = false; + storage.stored_value.~stored_type(); + } + + LIBC_INLINE constexpr void reset() { + if (in_use) + destroy(); + } + + LIBC_INLINE constexpr T &stored_value() { return this->storage.stored_value; } + + LIBC_INLINE constexpr const T &stored_value() const { + return this->storage.stored_value; + } +}; + +// Class template that manages the storage for optionals. +template , + bool /*_HasTrivialCopy */ = is_trivially_copy_assignable_v + &&is_trivially_copy_constructible_v, + bool /*_HasTrivialMove */ = is_trivially_move_assignable_v + &&is_trivially_move_constructible_v> +struct OptionalStorage; + +// storage for potentially-constexpr optionals (trivial copy/move/destroy). +template +struct OptionalStorage : OptionalStorageBase { + using OptionalStorageBase::OptionalStorageBase; + OptionalStorage() = default; +}; + +// storage for optionals with non-trivial copy construction/assignment. +template +struct OptionalStorage : OptionalStorageBase { + using OptionalStorageBase::OptionalStorageBase; + + OptionalStorage() = default; + ~OptionalStorage() = default; + OptionalStorage(const OptionalStorage &) = default; + OptionalStorage(OptionalStorage &&) = default; + OptionalStorage &operator=(OptionalStorage &&) = default; + + // Non-trivial copy assignment. + LIBC_INLINE constexpr OptionalStorage & + operator=(const OptionalStorage &other) { + this->copy_assign(other); + return *this; + } +}; + +// storage for optionals with non-trivial move construction/assignment. +template +struct OptionalStorage : OptionalStorageBase { + using OptionalStorageBase::OptionalStorageBase; + + OptionalStorage() = default; + ~OptionalStorage() = default; + OptionalStorage(const OptionalStorage &) = default; + OptionalStorage(OptionalStorage &&) = default; + OptionalStorage &operator=(const OptionalStorage &) = default; + + // Non-trivial move assignment. + LIBC_INLINE constexpr OptionalStorage &operator=(OptionalStorage &&other) { + this->move_assign(move(other)); + return *this; + } +}; + +// storage for optionals with non-trivial copy and move assignment. +template +struct OptionalStorage : OptionalStorageBase { + using OptionalStorageBase::OptionalStorageBase; + + OptionalStorage() = default; + ~OptionalStorage() = default; + OptionalStorage(const OptionalStorage &) = default; + OptionalStorage(OptionalStorage &&) = default; + + // Non-trivial copy assignment. + LIBC_INLINE constexpr OptionalStorage & + operator=(const OptionalStorage &other) { + this->copy_assign(other); + return *this; + } + + // Non-trivial move assignment. + LIBC_INLINE constexpr OptionalStorage &operator=(OptionalStorage &&other) { + this->move_assign(move(other)); + return *this; + } +}; + +// storage for optionals with non-trivial destructors. +template +struct OptionalStorage + : OptionalStorage { + // Base class implements all the constructors and assignment operators: + using OptionalStorage::OptionalStorage; + OptionalStorage() = default; + OptionalStorage(const OptionalStorage &) = default; + OptionalStorage(OptionalStorage &&) = default; + OptionalStorage &operator=(const OptionalStorage &) = default; + OptionalStorage &operator=(OptionalStorage &&) = default; + + // Destructor needs to destroy the contained value: + LIBC_INLINE ~OptionalStorage() { this->reset(); } +}; + +// Common base class for OptionalBase to avoid repeating these +// member functions in each specialization. +template class OptionalBaseImpl { +protected: + using StoredType = remove_const_t; + + // The construct operation has !in_use as a precondition + // while destruct has in_use as a precondition. + template + LIBC_INLINE constexpr void _construct(Args &&...args) { + static_cast(this)->storage.construct(forward(args)...); + } + + // reset is a 'safe' operation with no precondition. + LIBC_INLINE constexpr void _reset() { + static_cast(this)->storage.reset(); + } + + LIBC_INLINE constexpr bool _in_use() const { + return static_cast(this)->storage.in_use; + } + + // The stored_value operations have in_use as a precondition. + LIBC_INLINE constexpr T &_stored_value() { + return static_cast(this)->storage.stored_value(); + } + + LIBC_INLINE constexpr const T &_stored_value() const { + return static_cast(this)->storage.stored_value(); + } +}; + +/** + * Class template that provides copy/move constructors of optional. + * + * Such a separate base class template is necessary in order to + * conditionally make copy/move constructors trivial. + * + * When the contained value is trivially copy/move constructible, + * the copy/move constructors of OptionalBase will invoke the + * trivial copy/move constructor of OptionalStorage. Otherwise, + * they will invoke OptionalStorage(bool, const OptionalStorage&) + * or OptionalStorage(bool, OptionalStorage&&) to initialize + * the contained value, if copying/moving an engaged optional. + * + * Whether the other special members are trivial is determined by the + * OptionalStorage specialization used for the storage member. + * + */ +template , + bool = is_trivially_move_constructible_v> +struct OptionalBase : OptionalBaseImpl> { + // Constructors for disengaged optionals. + LIBC_INLINE constexpr OptionalBase() = default; + + // Constructors for engaged optionals. + template , bool> = false> + LIBC_INLINE constexpr explicit OptionalBase(in_place_t, Args &&...args) + : storage(in_place, forward(args)...) {} + + // Copy and move constructors. + LIBC_INLINE constexpr OptionalBase(const OptionalBase &other) + : storage(other.storage.in_use, other.storage) {} + + LIBC_INLINE constexpr OptionalBase(OptionalBase &&other) + : storage(other.storage.in_use, move(other.storage)) {} + + // Assignment operators. + OptionalBase &operator=(const OptionalBase &) = default; + OptionalBase &operator=(OptionalBase &&) = default; + + OptionalStorage storage; +}; + +template +struct OptionalBase : OptionalBaseImpl> { + // Constructors for disengaged optionals. + LIBC_INLINE constexpr OptionalBase() = default; + + // Constructors for engaged optionals. + template , bool> = false> + LIBC_INLINE constexpr explicit OptionalBase(in_place_t, Args &&...args) + : storage(in_place, forward(args)...) {} + + // Copy and move constructors. + LIBC_INLINE constexpr OptionalBase(const OptionalBase &other) + : storage(other.storage.in_use, other.storage) {} + + LIBC_INLINE constexpr OptionalBase(OptionalBase &&other) = default; + + // Assignment operators. + OptionalBase &operator=(const OptionalBase &) = default; + OptionalBase &operator=(OptionalBase &&) = default; + + OptionalStorage storage; +}; + +template +struct OptionalBase : OptionalBaseImpl> { + // Constructors for disengaged optionals. + LIBC_INLINE constexpr OptionalBase() = default; + + // Constructors for engaged optionals. + template , bool> = false> + LIBC_INLINE constexpr explicit OptionalBase(in_place_t, Args &&...args) + : storage(in_place, forward(args)...) {} + + // Copy and move constructors. + LIBC_INLINE constexpr OptionalBase(const OptionalBase &other) = default; + + LIBC_INLINE constexpr OptionalBase(OptionalBase &&other) + : storage(other.storage.in_use, move(other.storage)) {} + + // Assignment operators. + OptionalBase &operator=(const OptionalBase &) = default; + OptionalBase &operator=(OptionalBase &&) = default; + + OptionalStorage storage; +}; + +template +struct OptionalBase : OptionalBaseImpl> { + // Constructors for disengaged optionals. + LIBC_INLINE constexpr OptionalBase() = default; + + // Constructors for engaged optionals. + template , bool> = false> + LIBC_INLINE constexpr explicit OptionalBase(in_place_t, Args &&...args) + : storage(in_place, forward(args)...) {} + + // Copy and move constructors. + LIBC_INLINE constexpr OptionalBase(const OptionalBase &other) = default; + LIBC_INLINE constexpr OptionalBase(OptionalBase &&other) = default; + + // Assignment operators. + OptionalBase &operator=(const OptionalBase &) = default; + OptionalBase &operator=(OptionalBase &&) = default; OptionalStorage storage; +}; + +template +class optional + : private OptionalBase, + private _Enable_copy_move< + // Copy constructor. + is_copy_constructible_v, + // Copy assignment. + __and_, is_copy_assignable>::value, + // Move constructor. + is_move_constructible_v, + // Move assignment. + __and_, is_move_assignable>::value, + // Unique tag type. + optional> { + static_assert(!is_same_v, nullopt_t>); + static_assert(!is_same_v, in_place_t>); + static_assert(!is_reference_v); + +private: + using _Base = OptionalBase; + + // 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) {} + using value_type = T; - LIBC_INLINE constexpr optional(const T &t) : storage(in_place, t) {} - LIBC_INLINE constexpr optional(const optional &) = default; + LIBC_INLINE constexpr optional() {} - LIBC_INLINE constexpr optional(T &&t) : storage(in_place, move(t)) {} - LIBC_INLINE constexpr optional(optional &&O) = default; + LIBC_INLINE constexpr optional(nullopt_t) {} - template - LIBC_INLINE constexpr optional(in_place_t, ArgTypes &&...Args) - : storage(in_place, forward(Args)...) {} + // Converting constructors for engaged optionals. + template , __not_tag, is_constructible, + is_convertible> = true> + LIBC_INLINE constexpr optional(T2 &&t) : _Base(in_place, forward(t)) {} - LIBC_INLINE optional &operator=(T &&t) { - storage = move(t); + // Assignment operators. + LIBC_INLINE constexpr optional &operator=(nullopt_t) { + this->_reset(); return *this; } - LIBC_INLINE optional &operator=(optional &&) = default; - LIBC_INLINE static constexpr optional create(const T *t) { - return t ? optional(*t) : optional(); + // Destructor is implicit, implemented in OptionalBase. + + // Observers. + LIBC_INLINE constexpr const T *operator->() const { + return &this->_stored_value(); } - LIBC_INLINE optional &operator=(const T &t) { - storage = t; - return *this; + LIBC_INLINE constexpr T *operator->() { return &this->_stored_value(); } + + LIBC_INLINE constexpr const T &operator*() const & { + return this->_stored_value(); } - LIBC_INLINE optional &operator=(const optional &) = default; - LIBC_INLINE void reset() { storage.reset(); } + LIBC_INLINE constexpr T &operator*() & { return this->_stored_value(); } + + LIBC_INLINE constexpr T &&operator*() && { + return move(this->_stored_value()); + } + + LIBC_INLINE constexpr const T &&operator*() const && { + return move(this->_stored_value()); + } + + LIBC_INLINE constexpr explicit operator bool() const { + return this->_in_use(); + } + + LIBC_INLINE constexpr bool has_value() const { return this->_in_use(); } + + LIBC_INLINE constexpr const T &value() const & { + return this->_stored_value(); + } - LIBC_INLINE constexpr const T &value() const & { return storage.value(); } - LIBC_INLINE T &value() & { return storage.value(); } + LIBC_INLINE constexpr T &value() & { return this->_stored_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 const T &operator*() const & { return value(); } - LIBC_INLINE T &operator*() & { return value(); } + LIBC_INLINE constexpr T &&value() && { return move(this->_stored_value()); } - template LIBC_INLINE constexpr T value_or(U &&value) const & { - return has_value() ? value() : forward(value); + LIBC_INLINE constexpr const T &&value() const && { + return move(this->_stored_value()); } - LIBC_INLINE T &&value() && { return move(storage.value()); } - LIBC_INLINE T &&operator*() && { return move(storage.value()); } + LIBC_INLINE constexpr void reset() { this->_reset(); } }; } // 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 @@ -12,9 +12,7 @@ namespace __llvm_libc { namespace cpp { -template struct type_identity { - using type = T; -}; +template struct type_identity { using type = T; }; template struct enable_if; template struct enable_if : type_identity {}; @@ -28,22 +26,16 @@ using true_type = cpp::integral_constant; using false_type = cpp::integral_constant; -template -struct is_trivially_copyable - : public integral_constant {}; - -template -struct is_trivially_constructible - : integral_constant {}; +template using bool_constant = integral_constant; template struct is_same : cpp::false_type {}; template struct is_same : cpp::true_type {}; template inline constexpr bool is_same_v = is_same::value; -template struct is_const : cpp::false_type {}; -template struct is_const : cpp::true_type {}; -template inline constexpr bool is_const_v = is_const::value; +template struct is_const : cpp::false_type {}; +template struct is_const : cpp::true_type {}; +template inline constexpr bool is_const_v = is_const::value; template struct remove_cv : type_identity {}; template struct remove_cv : type_identity {}; @@ -51,6 +43,10 @@ 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 {}; @@ -163,10 +159,37 @@ template using conditional_t = typename conditional::type; +template struct __or_; + +template +struct __or_ : public conditional::type {}; + +template +struct __or_ + : public 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 inline constexpr bool __and_v = __and_::value; + template struct is_void : is_same::type> {}; template inline constexpr bool is_void_v = is_void::value; -template T declval(); +template T declval(); // Compile time checks on implicit conversions. namespace details { @@ -183,6 +206,146 @@ 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_assignable : bool_constant<__is_assignable(T, U)> {}; + +template +struct is_trivially_copyable + : public integral_constant {}; + +template +struct is_trivially_constructible + : integral_constant {}; + +template +inline constexpr bool + is_trivially_constructible_v = __is_trivially_constructible(T, Args...); + +template +struct is_trivially_destructible + : public integral_constant {}; + +template +inline constexpr bool is_trivially_destructible_v = + is_trivially_destructible::value; + +template +using __add_lvalue_reference_t = __add_lvalue_reference(T); + +template struct is_reference : bool_constant<__is_reference(T)> {}; + +template inline constexpr bool is_reference_v = __is_reference(T); + +template struct __is_block : false_type {}; + +template struct __is_nullptr_t_impl : public false_type {}; +template <> struct __is_nullptr_t_impl : public true_type {}; + +template +struct __is_nullptr_t : public __is_nullptr_t_impl> {}; + +template +struct is_member_pointer : bool_constant<__is_member_pointer(T)> {}; + +template +struct is_scalar + : public integral_constant< + bool, is_arithmetic::value || is_member_pointer::value || + is_pointer::value || __is_nullptr_t::value || + __is_block::value || is_enum::value> {}; + +template using __decay_t = __decay(T); + +template struct decay { using type = __decay_t; }; + +template +struct is_copy_constructible + : public integral_constant< + bool, __is_constructible(T, __add_lvalue_reference_t)> {}; + +template +inline constexpr bool is_copy_constructible_v = is_copy_constructible::value; + +template +struct is_trivially_copy_assignable + : public integral_constant, + __add_lvalue_reference_t)> {}; + +template +inline constexpr bool is_trivially_copy_assignable_v = + is_trivially_copy_assignable::value; + +template +struct is_copy_assignable + : public integral_constant, + __add_lvalue_reference_t)> {}; + +template +inline constexpr bool is_copy_assignable_v = is_copy_assignable::value; + +template +struct is_trivially_copy_constructible + : public integral_constant)> {}; + +template +inline constexpr bool is_trivially_copy_constructible_v = + is_trivially_copy_constructible::value; + +template +struct is_trivially_move_assignable + : public integral_constant, + __add_rvalue_reference(T))> {}; + +template +inline constexpr bool is_trivially_move_assignable_v = + is_trivially_move_assignable::value; + +template +struct is_move_assignable + : public integral_constant, + __add_rvalue_reference(T))> {}; + +template +inline constexpr bool is_move_assignable_v = is_move_assignable::value; + +template +struct is_trivially_move_constructible + : public integral_constant {}; + +template +inline constexpr bool is_trivially_move_constructible_v = + is_trivially_move_constructible::value; + +template +struct is_move_constructible + : public integral_constant {}; + +template +inline constexpr bool is_move_constructible_v = is_move_constructible::value; + +template +struct _Enable_copy_move {}; + } // 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 @@ -323,7 +323,7 @@ return remainder; } if (other == 0) { - return nullopt; + return {}; } UInt 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);