/* * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2021, Daniel Bertalan * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace AK { namespace Detail { template struct ConditionallyResultType; template struct ConditionallyResultType { using Type = typename T::ResultType; }; template struct ConditionallyResultType { using Type = T; }; } template using ConditionallyResultType = typename Detail::ConditionallyResultType::Type; // NOTE: If you're here because of an internal compiler error in GCC 10.3.0+, // it's because of the following bug: // // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96745 // // Make sure you didn't accidentally make your destructor private before // you start bug hunting. :^) template class Optional; struct OptionalNone { explicit constexpr OptionalNone() = default; }; template> requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { public: using ValueType = T; template V> ALWAYS_INLINE constexpr Self& operator=(V) { static_cast(*this).clear(); return static_cast(*this); } [[nodiscard]] ALWAYS_INLINE constexpr T* ptr() & { return static_cast(*this).has_value() ? __builtin_launder(reinterpret_cast(&static_cast(*this).value())) : nullptr; } [[nodiscard]] ALWAYS_INLINE constexpr T const* ptr() const& { return static_cast(*this).has_value() ? __builtin_launder(reinterpret_cast(&static_cast(*this).value())) : nullptr; } template [[nodiscard]] ALWAYS_INLINE constexpr O value_or(Fallback const& fallback) const& { if (static_cast(*this).has_value()) return static_cast(*this).value(); return fallback; } template requires(!IsLvalueReference && !IsRvalueReference) [[nodiscard]] ALWAYS_INLINE constexpr O value_or(Fallback&& fallback) && { if (static_cast(*this).has_value()) return move(static_cast(*this).value()); return move(fallback); } template [[nodiscard]] ALWAYS_INLINE constexpr O value_or_lazy_evaluated(Callback callback) const { if (static_cast(*this).has_value()) return static_cast(*this).value(); return callback(); } template [[nodiscard]] ALWAYS_INLINE constexpr Optional value_or_lazy_evaluated_optional(Callback callback) const { if (static_cast(*this).has_value()) return static_cast(*this).value(); return callback(); } template [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr try_value_or_lazy_evaluated(Callback callback) const { if (static_cast(*this).has_value()) return static_cast(*this).value(); return TRY(callback()); } template [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr> try_value_or_lazy_evaluated_optional(Callback callback) const { if (static_cast(*this).has_value()) return static_cast(*this).value(); return TRY(callback()); } [[nodiscard]] ALWAYS_INLINE constexpr T const& operator*() const { return static_cast(*this).value(); } [[nodiscard]] ALWAYS_INLINE constexpr T& operator*() { return static_cast(*this).value(); } ALWAYS_INLINE constexpr T const* operator->() const { return &static_cast(*this).value(); } ALWAYS_INLINE constexpr T* operator->() { return &static_cast(*this).value(); } template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) { if constexpr (IsErrorOr) { if (static_cast(*this).has_value()) return OptionalType { TRY(mapper(static_cast(*this).value())) }; return OptionalType {}; } else { if (static_cast(*this).has_value()) return OptionalType { mapper(static_cast(*this).value()) }; return OptionalType {}; } } template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) const { if constexpr (IsErrorOr) { if (static_cast(*this).has_value()) return OptionalType { TRY(mapper(static_cast(*this).value())) }; return OptionalType {}; } else { if (static_cast(*this).has_value()) return OptionalType { mapper(static_cast(*this).value()) }; return OptionalType {}; } } }; template requires(!IsLvalueReference) class [[nodiscard]] Optional : public OptionalBase> { template friend class Optional; static_assert(!IsLvalueReference && !IsRvalueReference); public: using ValueType = T; ALWAYS_INLINE constexpr Optional() { construct_null_if_necessary(); } template V> ALWAYS_INLINE constexpr Optional(V) { construct_null_if_necessary(); } template V> ALWAYS_INLINE constexpr Optional& operator=(V) { clear(); return *this; } AK_MAKE_CONDITIONALLY_COPYABLE(Optional, ); AK_MAKE_CONDITIONALLY_MOVABLE(Optional, ); AK_MAKE_CONDITIONALLY_DESTRUCTIBLE(Optional, ); ALWAYS_INLINE constexpr Optional(Optional const& other) requires(!IsTriviallyCopyConstructible) : m_has_value(other.m_has_value) { if (other.has_value()) construct_at>(&m_storage, other.value()); else construct_null_if_necessary(); } ALWAYS_INLINE constexpr Optional(Optional&& other) requires(!IsTriviallyMoveConstructible) : m_has_value(other.m_has_value) { if (other.has_value()) construct_at>(&m_storage, other.release_value()); else construct_null_if_necessary(); } template requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf && (!IsLvalueReference || IsTriviallyCopyConstructible)) ALWAYS_INLINE explicit constexpr Optional(Optional const& other) : m_has_value(other.has_value()) { if (other.has_value()) construct_at>(&m_storage, other.value()); else construct_null_if_necessary(); } template requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf && (!IsLvalueReference || IsTriviallyMoveConstructible)) ALWAYS_INLINE explicit constexpr Optional(Optional&& other) : m_has_value(other.has_value()) { if (other.has_value()) construct_at>(&m_storage, other.release_value()); else construct_null_if_necessary(); } template requires(!IsSame>) ALWAYS_INLINE explicit(!IsConvertible) constexpr Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) : m_has_value(true) { construct_at>(&m_storage, forward(value)); } ALWAYS_INLINE constexpr Optional& operator=(Optional const& other) requires(!IsTriviallyCopyConstructible || !IsTriviallyDestructible) { if (this != &other) { clear(); m_has_value = other.m_has_value; if (other.has_value()) construct_at>(&m_storage, other.value()); } return *this; } Optional& operator=(Optional&& other) requires(!IsMoveConstructible || !IsDestructible) = delete; // Note: This overload is optional. It exists purely to match the SerenityOS and `std::optional` behaviour. // The only (observable) difference between this overload and the next one is that this one calls the move assignment operator when both `this` and `other` have a value. // The other overload just unconditionally calls the move constructor. ALWAYS_INLINE constexpr Optional& operator=(Optional&& other) requires(IsMoveAssignable && IsMoveConstructible && (!IsTriviallyMoveAssignable || !IsTriviallyMoveConstructible || !IsTriviallyDestructible)) { if (this != &other) { if (has_value() && other.has_value()) { value() = other.release_value(); } else if (has_value()) { value().~T(); m_has_value = false; } else if (other.has_value()) { m_has_value = true; construct_at>(&m_storage, other.release_value()); } } return *this; } // Allow for move constructible but non-move assignable types, such as those containing const or reference fields, // Note: This overload can also handle move assignable types perfectly fine, but the behaviour would be slightly different. ALWAYS_INLINE constexpr Optional& operator=(Optional&& other) requires(!IsMoveAssignable && IsMoveConstructible && (!IsTriviallyMoveConstructible || !IsTriviallyDestructible)) { if (this != &other) { clear(); m_has_value = other.m_has_value; if (other.has_value()) construct_at>(&m_storage, other.release_value()); } return *this; } template requires(!IsOneOfIgnoringCVReference, OptionalNone> && !(IsSame && IsScalar)) // Note: We restrict this to `!IsScalar` to prevent undesired overload resolution for `= {}`. ALWAYS_INLINE constexpr Optional& operator=(U&& value) requires(IsConstructible) { if constexpr (IsAssignable, AddRvalueReference>) { if (m_has_value) m_storage = forward(value); else construct_at>(&m_storage, forward(value)); m_has_value = true; } else { emplace(forward(value)); } return *this; } ALWAYS_INLINE constexpr ~Optional() requires(!IsTriviallyDestructible && IsDestructible) { clear(); } ALWAYS_INLINE constexpr void clear() { if (m_has_value) { value().~T(); m_has_value = false; } } template ALWAYS_INLINE constexpr void emplace(Parameters&&... parameters) { clear(); m_has_value = true; construct_at>(&m_storage, forward(parameters)...); } template ALWAYS_INLINE constexpr void lazy_emplace(Callable callable) { clear(); m_has_value = true; construct_at>(&m_storage, callable()); } [[nodiscard]] ALWAYS_INLINE constexpr bool has_value() const { return m_has_value; } [[nodiscard]] ALWAYS_INLINE constexpr T& value() & { VERIFY(m_has_value); return m_storage; } [[nodiscard]] ALWAYS_INLINE constexpr T const& value() const& { VERIFY(m_has_value); return m_storage; } [[nodiscard]] ALWAYS_INLINE constexpr T value() && { return release_value(); } [[nodiscard]] ALWAYS_INLINE constexpr T release_value() { VERIFY(m_has_value); T released_value = move(value()); value().~T(); m_has_value = false; return released_value; } private: ALWAYS_INLINE constexpr void construct_null_if_necessary(bool should_construct = is_constant_evaluated()) { // OPTIMIZATION: Only construct the `m_null` member when we are constant-evaluating. // Otherwise, this generates an unnecessary zero-fill. #if defined(AK_COMPILER_GCC) // NOTE: GCCs -Wuninitialized warning ends up checking this as well. should_construct = true; #endif if (should_construct) construct_at(&m_null); } union { // FIXME: GCC seems to have an issue with uninitialized unions and non trivial types, // which forces us to have an equally sized trivial null member in the union // to pseudo-initialize the union. struct { u8 _[sizeof(T)]; } m_null; RemoveConst m_storage; }; bool m_has_value { false }; }; template requires(IsLvalueReference) class [[nodiscard]] Optional { AK_MAKE_DEFAULT_COPYABLE(Optional); AK_MAKE_DEFAULT_MOVABLE(Optional); template friend class Optional; template constexpr static bool CanBePlacedInOptional = IsSame, RemoveReference>> && (IsBaseOf, RemoveCVReference> || IsSame, RemoveCVReference>); public: using ValueType = T; ALWAYS_INLINE constexpr Optional() = default; template V> ALWAYS_INLINE constexpr Optional(V) { } template V> ALWAYS_INLINE constexpr Optional& operator=(V) { clear(); return *this; } template ALWAYS_INLINE constexpr Optional(U& value) requires(CanBePlacedInOptional) : m_pointer(&value) { } ALWAYS_INLINE constexpr Optional(RemoveReference& value) : m_pointer(&value) { } template ALWAYS_INLINE constexpr Optional(Optional& other) requires(CanBePlacedInOptional) : m_pointer(other.ptr()) { } template ALWAYS_INLINE constexpr Optional(Optional const& other) requires(CanBePlacedInOptional) : m_pointer(other.ptr()) { } template ALWAYS_INLINE constexpr Optional(Optional&& other) requires(CanBePlacedInOptional) : m_pointer(other.ptr()) { other.m_pointer = nullptr; } template ALWAYS_INLINE constexpr Optional& operator=(Optional& other) requires(CanBePlacedInOptional) { m_pointer = other.ptr(); return *this; } template ALWAYS_INLINE constexpr Optional& operator=(Optional const& other) requires(CanBePlacedInOptional) { m_pointer = other.ptr(); return *this; } template ALWAYS_INLINE constexpr Optional& operator=(Optional&& other) requires(CanBePlacedInOptional && IsLvalueReference) { m_pointer = other.m_pointer; other.m_pointer = nullptr; return *this; } template requires(!IsSame>) ALWAYS_INLINE constexpr Optional& operator=(U& value) requires(CanBePlacedInOptional) { m_pointer = &value; return *this; } // Note: Disallows assignment from a temporary as this does not do any lifetime extension. template requires(!IsSame>) ALWAYS_INLINE consteval Optional& operator=(RemoveReference const&& value) requires(CanBePlacedInOptional) = delete; ALWAYS_INLINE constexpr void clear() { m_pointer = nullptr; } [[nodiscard]] ALWAYS_INLINE constexpr bool has_value() const { return m_pointer != nullptr; } [[nodiscard]] ALWAYS_INLINE RemoveReference* ptr() { return m_pointer; } [[nodiscard]] ALWAYS_INLINE RemoveReference const* ptr() const { return m_pointer; } [[nodiscard]] ALWAYS_INLINE constexpr T value() { VERIFY(m_pointer); return *m_pointer; } [[nodiscard]] ALWAYS_INLINE constexpr AddConstToReferencedType value() const { VERIFY(m_pointer); return *m_pointer; } template requires(IsBaseOf, U>) [[nodiscard]] ALWAYS_INLINE constexpr AddConstToReferencedType value_or(U& fallback) const { if (m_pointer) return value(); return fallback; } // Note that this ends up copying the value. [[nodiscard]] ALWAYS_INLINE constexpr RemoveCVReference value_or(RemoveCVReference fallback) const { if (m_pointer) return value(); return fallback; } [[nodiscard]] ALWAYS_INLINE constexpr T release_value() { return *exchange(m_pointer, nullptr); } ALWAYS_INLINE constexpr AddConstToReferencedType operator*() const { return value(); } ALWAYS_INLINE constexpr T operator*() { return value(); } ALWAYS_INLINE RawPtr>> operator->() const { return &value(); } ALWAYS_INLINE RawPtr> operator->() { return &value(); } // Conversion operators from Optional -> Optional, implicit when T is trivially copyable. ALWAYS_INLINE constexpr operator Optional>() const requires(IsTriviallyCopyable>) { if (has_value()) return Optional>(value()); return {}; } // Conversion operators from Optional -> Optional, explicit when T is not trivially copyable, since this is usually a mistake. ALWAYS_INLINE explicit constexpr operator Optional>() const requires(!IsTriviallyCopyable>) { if (has_value()) return Optional>(value()); return {}; } ALWAYS_INLINE constexpr Optional> copy() const { return static_cast>>(*this); } template [[nodiscard]] ALWAYS_INLINE constexpr T value_or_lazy_evaluated(Callback callback) const { if (m_pointer != nullptr) return value(); return callback(); } template [[nodiscard]] ALWAYS_INLINE constexpr Optional value_or_lazy_evaluated_optional(Callback callback) const { if (m_pointer != nullptr) return value(); return callback(); } template [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr try_value_or_lazy_evaluated(Callback callback) const { if (m_pointer != nullptr) return value(); return TRY(callback()); } template [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr> try_value_or_lazy_evaluated_optional(Callback callback) const { if (m_pointer != nullptr) return value(); return TRY(callback()); } template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) { if constexpr (IsErrorOr) { if (m_pointer != nullptr) return OptionalType { TRY(mapper(value())) }; return OptionalType {}; } else { if (m_pointer != nullptr) return OptionalType { mapper(value()) }; return OptionalType {}; } } template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) const { if constexpr (IsErrorOr) { if (m_pointer != nullptr) return OptionalType { TRY(mapper(value())) }; return OptionalType {}; } else { if (m_pointer != nullptr) return OptionalType { mapper(value()) }; return OptionalType {}; } } private: RemoveReference* m_pointer { nullptr }; }; template ALWAYS_INLINE constexpr bool operator==(Optional const& first, Optional const& second) { return first.has_value() == second.has_value() && (!first.has_value() || first.value() == second.value()); } template ALWAYS_INLINE constexpr bool operator==(Optional const& first, T2 const& second) { return first.has_value() && first.value() == second; } template ALWAYS_INLINE constexpr bool operator==(Optional const& first, OptionalNone) { return !first.has_value(); } } #if USING_AK_GLOBALLY using AK::Optional; using AK::OptionalNone; #endif