diff --git a/AK/Concepts.h b/AK/Concepts.h index 3dda34c887f0..7a6286385e4a 100644 --- a/AK/Concepts.h +++ b/AK/Concepts.h @@ -45,6 +45,9 @@ concept OneOf = IsOneOf; template concept OneOfIgnoringCV = IsOneOfIgnoringCV; +template +concept OneOfIgnoringCVReference = IsOneOfIgnoringCVReference; + template typename S> concept SpecializationOf = IsSpecializationOf; @@ -185,6 +188,7 @@ using AK::Concepts::IteratorFunction; using AK::Concepts::IteratorPairWith; using AK::Concepts::OneOf; using AK::Concepts::OneOfIgnoringCV; +using AK::Concepts::OneOfIgnoringCVReference; using AK::Concepts::SameAs; using AK::Concepts::Signed; using AK::Concepts::SpecializationOf; diff --git a/AK/FlyString.h b/AK/FlyString.h index 7bf52e82d154..f6c72cda764d 100644 --- a/AK/FlyString.h +++ b/AK/FlyString.h @@ -85,19 +85,19 @@ class FlyString { private: friend class Optional; - explicit FlyString(nullptr_t) + explicit constexpr FlyString(nullptr_t) : m_data(nullptr) { } - explicit FlyString(Detail::StringBase data) + explicit constexpr FlyString(Detail::StringBase data) : m_data(move(data)) { } Detail::StringBase m_data; - bool is_invalid() const { return m_data.raw(Badge {}) == 0; } + constexpr bool is_invalid() const { return m_data.raw(Badge {}) == 0; } }; void did_destroy_fly_string_data(Badge, Detail::StringData const&); @@ -110,38 +110,38 @@ class Optional : public OptionalBase { public: using ValueType = FlyString; - Optional() = default; + constexpr Optional() = default; template V> - Optional(V) { } + constexpr Optional(V) { } - Optional(Optional const& other) + constexpr Optional(Optional const& other) { if (other.has_value()) m_value = other.m_value; } - Optional(Optional&& other) - : m_value(other.m_value) + constexpr Optional(Optional&& other) + : m_value(move(other.m_value)) { } template requires(!IsSame>) - explicit(!IsConvertible) Optional(U&& value) + explicit(!IsConvertible) constexpr Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) : m_value(forward(value)) { } template V> - Optional& operator=(V) + constexpr Optional& operator=(V) { clear(); return *this; } - Optional& operator=(Optional const& other) + constexpr Optional& operator=(Optional const& other) { if (this != &other) { clear(); @@ -150,7 +150,7 @@ class Optional : public OptionalBase { return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { clear(); @@ -159,37 +159,37 @@ class Optional : public OptionalBase { return *this; } - void clear() + constexpr void clear() { m_value = FlyString(nullptr); } - [[nodiscard]] bool has_value() const + [[nodiscard]] constexpr bool has_value() const { return !m_value.is_invalid(); } - [[nodiscard]] FlyString& value() & + [[nodiscard]] constexpr FlyString& value() & { VERIFY(has_value()); return m_value; } - [[nodiscard]] FlyString const& value() const& + [[nodiscard]] constexpr FlyString const& value() const& { VERIFY(has_value()); return m_value; } - [[nodiscard]] FlyString value() && + [[nodiscard]] constexpr FlyString value() && { return release_value(); } - [[nodiscard]] FlyString release_value() + [[nodiscard]] constexpr FlyString release_value() { VERIFY(has_value()); - FlyString released_value = m_value; + FlyString released_value = move(m_value); clear(); return released_value; } diff --git a/AK/Noncopyable.h b/AK/Noncopyable.h index a2a7e7759373..e5b0fcdc180b 100644 --- a/AK/Noncopyable.h +++ b/AK/Noncopyable.h @@ -26,14 +26,14 @@ public: \ c(c const&) = default; \ c& operator=(c const&) = default -#define AK_MAKE_CONDITIONALLY_NONMOVABLE(c, ...) \ -public: \ - c(c&&) \ - requires(!(AK::Detail::IsMoveConstructible __VA_ARGS__)) \ - = delete; \ - c& operator=(c&&) \ - requires(!(AK::Detail::IsMoveConstructible __VA_ARGS__) \ - || !(AK::Detail::IsDestructible __VA_ARGS__)) \ +#define AK_MAKE_CONDITIONALLY_NONMOVABLE(c, ...) \ +public: \ + c(c&&) \ + requires(!(AK::Detail::IsMoveConstructible __VA_ARGS__)) \ + = delete; \ + c& operator=(c&&) \ + requires(!((AK::Detail::IsMoveConstructible __VA_ARGS__) || (AK::Detail::IsMoveAssignable __VA_ARGS__)) \ + || !(AK::Detail::IsDestructible __VA_ARGS__)) \ = delete #define AK_MAKE_CONDITIONALLY_MOVABLE(c, T) \ @@ -41,14 +41,14 @@ public: \ c(c&&) = default; \ c& operator=(c&&) = default -#define AK_MAKE_CONDITIONALLY_NONCOPYABLE(c, ...) \ -public: \ - c(c const&) \ - requires(!(AK::Detail::IsCopyConstructible __VA_ARGS__)) \ - = delete; \ - c& operator=(c const&) \ - requires(!(AK::Detail::IsCopyConstructible __VA_ARGS__) \ - || !(AK::Detail::IsDestructible __VA_ARGS__)) \ +#define AK_MAKE_CONDITIONALLY_NONCOPYABLE(c, ...) \ +public: \ + c(c const&) \ + requires(!(AK::Detail::IsCopyConstructible __VA_ARGS__)) \ + = delete; \ + c& operator=(c const&) \ + requires(!((AK::Detail::IsCopyConstructible __VA_ARGS__) || (AK::Detail::IsCopyAssignable __VA_ARGS__)) \ + || !(AK::Detail::IsDestructible __VA_ARGS__)) \ = delete #define AK_MAKE_CONDITIONALLY_COPYABLE(c, ...) \ diff --git a/AK/Optional.h b/AK/Optional.h index 97aa5523aff9..f66f785fcd67 100644 --- a/AK/Optional.h +++ b/AK/Optional.h @@ -46,7 +46,7 @@ template class Optional; struct OptionalNone { - explicit OptionalNone() = default; + explicit constexpr OptionalNone() = default; }; template> @@ -55,24 +55,24 @@ requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { using ValueType = T; template V> - Self& operator=(V) + ALWAYS_INLINE constexpr Self& operator=(V) { static_cast(*this).clear(); return static_cast(*this); } - [[nodiscard]] ALWAYS_INLINE T* ptr() & + [[nodiscard]] ALWAYS_INLINE constexpr T* ptr() & { return static_cast(*this).has_value() ? __builtin_launder(reinterpret_cast(&static_cast(*this).value())) : nullptr; } - [[nodiscard]] ALWAYS_INLINE T const* ptr() const& + [[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 O value_or(Fallback const& fallback) const& + [[nodiscard]] ALWAYS_INLINE constexpr O value_or(Fallback const& fallback) const& { if (static_cast(*this).has_value()) return static_cast(*this).value(); @@ -81,7 +81,7 @@ requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { template requires(!IsLvalueReference && !IsRvalueReference) - [[nodiscard]] ALWAYS_INLINE O value_or(Fallback&& fallback) && + [[nodiscard]] ALWAYS_INLINE constexpr O value_or(Fallback&& fallback) && { if (static_cast(*this).has_value()) return move(static_cast(*this).value()); @@ -89,7 +89,7 @@ requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { } template - [[nodiscard]] ALWAYS_INLINE O value_or_lazy_evaluated(Callback callback) const + [[nodiscard]] ALWAYS_INLINE constexpr O value_or_lazy_evaluated(Callback callback) const { if (static_cast(*this).has_value()) return static_cast(*this).value(); @@ -97,7 +97,7 @@ requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { } template - [[nodiscard]] ALWAYS_INLINE Optional value_or_lazy_evaluated_optional(Callback callback) const + [[nodiscard]] ALWAYS_INLINE constexpr Optional value_or_lazy_evaluated_optional(Callback callback) const { if (static_cast(*this).has_value()) return static_cast(*this).value(); @@ -105,7 +105,7 @@ requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { } template - [[nodiscard]] ALWAYS_INLINE ErrorOr try_value_or_lazy_evaluated(Callback callback) const + [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr try_value_or_lazy_evaluated(Callback callback) const { if (static_cast(*this).has_value()) return static_cast(*this).value(); @@ -113,21 +113,21 @@ requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { } template - [[nodiscard]] ALWAYS_INLINE ErrorOr> try_value_or_lazy_evaluated_optional(Callback callback) const + [[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 T const& operator*() const { return static_cast(*this).value(); } - [[nodiscard]] ALWAYS_INLINE T& operator*() { return static_cast(*this).value(); } + [[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 T const* operator->() const { return &static_cast(*this).value(); } - ALWAYS_INLINE 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 Conditional, OptionalType> map(F&& mapper) + ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) { if constexpr (IsErrorOr) { if (static_cast(*this).has_value()) @@ -142,7 +142,7 @@ requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { } template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> - ALWAYS_INLINE Conditional, OptionalType> map(F&& mapper) const + ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) const { if constexpr (IsErrorOr) { if (static_cast(*this).has_value()) @@ -167,94 +167,152 @@ requires(!IsLvalueReference) class [[nodiscard]] Optional : public Optiona public: using ValueType = T; - ALWAYS_INLINE Optional() = default; + ALWAYS_INLINE constexpr Optional() + { + construct_null_if_necessary(); + } template V> - Optional(V) { } + ALWAYS_INLINE constexpr Optional(V) + { + construct_null_if_necessary(); + } template V> - Optional& operator=(V) + ALWAYS_INLINE constexpr Optional& operator=(V) { clear(); return *this; } AK_MAKE_CONDITIONALLY_COPYABLE(Optional, ); - AK_MAKE_CONDITIONALLY_NONMOVABLE(Optional, ); + AK_MAKE_CONDITIONALLY_MOVABLE(Optional, ); AK_MAKE_CONDITIONALLY_DESTRUCTIBLE(Optional, ); - ALWAYS_INLINE Optional(Optional const& other) + ALWAYS_INLINE constexpr Optional(Optional const& other) requires(!IsTriviallyCopyConstructible) : m_has_value(other.m_has_value) { if (other.has_value()) - new (&m_storage) T(other.value()); + construct_at>(&m_storage, other.value()); + else + construct_null_if_necessary(); } - ALWAYS_INLINE Optional(Optional&& other) + ALWAYS_INLINE constexpr Optional(Optional&& other) + requires(!IsTriviallyMoveConstructible) : m_has_value(other.m_has_value) { if (other.has_value()) - new (&m_storage) T(other.release_value()); + construct_at>(&m_storage, other.release_value()); + else + construct_null_if_necessary(); } template - requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf && (!IsLvalueReference || IsTriviallyCopyConstructible)) ALWAYS_INLINE explicit Optional(Optional const& other) + requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf && (!IsLvalueReference || IsTriviallyCopyConstructible)) ALWAYS_INLINE explicit constexpr Optional(Optional const& other) : m_has_value(other.has_value()) { if (other.has_value()) - new (&m_storage) T(other.value()); + construct_at>(&m_storage, other.value()); + else + construct_null_if_necessary(); } template - requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf && (!IsLvalueReference || IsTriviallyMoveConstructible)) ALWAYS_INLINE explicit Optional(Optional&& other) + requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf && (!IsLvalueReference || IsTriviallyMoveConstructible)) ALWAYS_INLINE explicit constexpr Optional(Optional&& other) : m_has_value(other.has_value()) { if (other.has_value()) - new (&m_storage) T(other.release_value()); + construct_at>(&m_storage, other.release_value()); + else + construct_null_if_necessary(); } template requires(!IsSame>) - ALWAYS_INLINE explicit(!IsConvertible) Optional(U&& value) + ALWAYS_INLINE explicit(!IsConvertible) constexpr Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) : m_has_value(true) { - new (&m_storage) T(forward(value)); + construct_at>(&m_storage, forward(value)); } - ALWAYS_INLINE Optional& operator=(Optional const& other) + 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()) { - new (&m_storage) T(other.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; } - ALWAYS_INLINE Optional& operator=(Optional&& other) + // 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()) { - new (&m_storage) T(other.release_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 ~Optional() + ALWAYS_INLINE constexpr ~Optional() requires(!IsTriviallyDestructible && IsDestructible) { clear(); } - ALWAYS_INLINE void clear() + ALWAYS_INLINE constexpr void clear() { if (m_has_value) { value().~T(); @@ -263,41 +321,41 @@ requires(!IsLvalueReference) class [[nodiscard]] Optional : public Optiona } template - ALWAYS_INLINE void emplace(Parameters&&... parameters) + ALWAYS_INLINE constexpr void emplace(Parameters&&... parameters) { clear(); m_has_value = true; - new (&m_storage) T(forward(parameters)...); + construct_at>(&m_storage, forward(parameters)...); } template - ALWAYS_INLINE void lazy_emplace(Callable callable) + ALWAYS_INLINE constexpr void lazy_emplace(Callable callable) { clear(); m_has_value = true; - new (&m_storage) T { callable() }; + construct_at>(&m_storage, callable()); } - [[nodiscard]] ALWAYS_INLINE bool has_value() const { return m_has_value; } + [[nodiscard]] ALWAYS_INLINE constexpr bool has_value() const { return m_has_value; } - [[nodiscard]] ALWAYS_INLINE T& value() & + [[nodiscard]] ALWAYS_INLINE constexpr T& value() & { VERIFY(m_has_value); - return *__builtin_launder(reinterpret_cast(&m_storage)); + return m_storage; } - [[nodiscard]] ALWAYS_INLINE T const& value() const& + [[nodiscard]] ALWAYS_INLINE constexpr T const& value() const& { VERIFY(m_has_value); - return *__builtin_launder(reinterpret_cast(&m_storage)); + return m_storage; } - [[nodiscard]] ALWAYS_INLINE T value() && + [[nodiscard]] ALWAYS_INLINE constexpr T value() && { return release_value(); } - [[nodiscard]] ALWAYS_INLINE T release_value() + [[nodiscard]] ALWAYS_INLINE constexpr T release_value() { VERIFY(m_has_value); T released_value = move(value()); @@ -307,7 +365,27 @@ requires(!IsLvalueReference) class [[nodiscard]] Optional : public Optiona } private: - alignas(T) u8 m_storage[sizeof(T)]; + 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 }; }; @@ -325,46 +403,46 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { public: using ValueType = T; - ALWAYS_INLINE Optional() = default; + ALWAYS_INLINE constexpr Optional() = default; template V> - Optional(V) { } + ALWAYS_INLINE constexpr Optional(V) { } template V> - Optional& operator=(V) + ALWAYS_INLINE constexpr Optional& operator=(V) { clear(); return *this; } template - ALWAYS_INLINE Optional(U& value) + ALWAYS_INLINE constexpr Optional(U& value) requires(CanBePlacedInOptional) : m_pointer(&value) { } - ALWAYS_INLINE Optional(RemoveReference& value) + ALWAYS_INLINE constexpr Optional(RemoveReference& value) : m_pointer(&value) { } template - ALWAYS_INLINE Optional(Optional& other) + ALWAYS_INLINE constexpr Optional(Optional& other) requires(CanBePlacedInOptional) : m_pointer(other.ptr()) { } template - ALWAYS_INLINE Optional(Optional const& other) + ALWAYS_INLINE constexpr Optional(Optional const& other) requires(CanBePlacedInOptional) : m_pointer(other.ptr()) { } template - ALWAYS_INLINE Optional(Optional&& other) + ALWAYS_INLINE constexpr Optional(Optional&& other) requires(CanBePlacedInOptional) : m_pointer(other.ptr()) { @@ -372,7 +450,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template - ALWAYS_INLINE Optional& operator=(Optional& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional& other) requires(CanBePlacedInOptional) { m_pointer = other.ptr(); @@ -380,7 +458,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template - ALWAYS_INLINE Optional& operator=(Optional const& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional const& other) requires(CanBePlacedInOptional) { m_pointer = other.ptr(); @@ -388,7 +466,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template - ALWAYS_INLINE Optional& operator=(Optional&& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional&& other) requires(CanBePlacedInOptional && IsLvalueReference) { m_pointer = other.m_pointer; @@ -398,7 +476,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { template requires(!IsSame>) - ALWAYS_INLINE Optional& operator=(U& value) + ALWAYS_INLINE constexpr Optional& operator=(U& value) requires(CanBePlacedInOptional) { m_pointer = &value; @@ -412,12 +490,12 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { requires(CanBePlacedInOptional) = delete; - ALWAYS_INLINE void clear() + ALWAYS_INLINE constexpr void clear() { m_pointer = nullptr; } - [[nodiscard]] ALWAYS_INLINE bool has_value() const { return m_pointer != nullptr; } + [[nodiscard]] ALWAYS_INLINE constexpr bool has_value() const { return m_pointer != nullptr; } [[nodiscard]] ALWAYS_INLINE RemoveReference* ptr() { @@ -429,20 +507,20 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { return m_pointer; } - [[nodiscard]] ALWAYS_INLINE T value() + [[nodiscard]] ALWAYS_INLINE constexpr T value() { VERIFY(m_pointer); return *m_pointer; } - [[nodiscard]] ALWAYS_INLINE AddConstToReferencedType value() const + [[nodiscard]] ALWAYS_INLINE constexpr AddConstToReferencedType value() const { VERIFY(m_pointer); return *m_pointer; } template - requires(IsBaseOf, U>) [[nodiscard]] ALWAYS_INLINE AddConstToReferencedType value_or(U& fallback) const + requires(IsBaseOf, U>) [[nodiscard]] ALWAYS_INLINE constexpr AddConstToReferencedType value_or(U& fallback) const { if (m_pointer) return value(); @@ -450,26 +528,26 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } // Note that this ends up copying the value. - [[nodiscard]] ALWAYS_INLINE RemoveCVReference value_or(RemoveCVReference fallback) const + [[nodiscard]] ALWAYS_INLINE constexpr RemoveCVReference value_or(RemoveCVReference fallback) const { if (m_pointer) return value(); return fallback; } - [[nodiscard]] ALWAYS_INLINE T release_value() + [[nodiscard]] ALWAYS_INLINE constexpr T release_value() { return *exchange(m_pointer, nullptr); } - ALWAYS_INLINE AddConstToReferencedType operator*() const { return value(); } - ALWAYS_INLINE T operator*() { return value(); } + 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 operator Optional>() const + ALWAYS_INLINE constexpr operator Optional>() const requires(IsTriviallyCopyable>) { if (has_value()) @@ -478,7 +556,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } // Conversion operators from Optional -> Optional, explicit when T is not trivially copyable, since this is usually a mistake. - ALWAYS_INLINE explicit operator Optional>() const + ALWAYS_INLINE explicit constexpr operator Optional>() const requires(!IsTriviallyCopyable>) { if (has_value()) @@ -492,7 +570,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template - [[nodiscard]] ALWAYS_INLINE T value_or_lazy_evaluated(Callback callback) const + [[nodiscard]] ALWAYS_INLINE constexpr T value_or_lazy_evaluated(Callback callback) const { if (m_pointer != nullptr) return value(); @@ -500,7 +578,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template - [[nodiscard]] ALWAYS_INLINE Optional value_or_lazy_evaluated_optional(Callback callback) const + [[nodiscard]] ALWAYS_INLINE constexpr Optional value_or_lazy_evaluated_optional(Callback callback) const { if (m_pointer != nullptr) return value(); @@ -508,7 +586,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template - [[nodiscard]] ALWAYS_INLINE ErrorOr try_value_or_lazy_evaluated(Callback callback) const + [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr try_value_or_lazy_evaluated(Callback callback) const { if (m_pointer != nullptr) return value(); @@ -516,7 +594,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template - [[nodiscard]] ALWAYS_INLINE ErrorOr> try_value_or_lazy_evaluated_optional(Callback callback) const + [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr> try_value_or_lazy_evaluated_optional(Callback callback) const { if (m_pointer != nullptr) return value(); @@ -524,7 +602,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> - ALWAYS_INLINE Conditional, OptionalType> map(F&& mapper) + ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) { if constexpr (IsErrorOr) { if (m_pointer != nullptr) @@ -539,7 +617,7 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { } template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> - ALWAYS_INLINE Conditional, OptionalType> map(F&& mapper) const + ALWAYS_INLINE constexpr Conditional, OptionalType> map(F&& mapper) const { if constexpr (IsErrorOr) { if (m_pointer != nullptr) @@ -558,20 +636,20 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { }; template -ALWAYS_INLINE bool operator==(Optional const& first, Optional const& second) +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 bool operator==(Optional const& first, T2 const& second) +ALWAYS_INLINE constexpr bool operator==(Optional const& first, T2 const& second) { return first.has_value() && first.value() == second; } template -ALWAYS_INLINE bool operator==(Optional const& first, OptionalNone) +ALWAYS_INLINE constexpr bool operator==(Optional const& first, OptionalNone) { return !first.has_value(); } diff --git a/AK/StdLibExtraDetails.h b/AK/StdLibExtraDetails.h index d3b5be83bec3..03e1adc4bc00 100644 --- a/AK/StdLibExtraDetails.h +++ b/AK/StdLibExtraDetails.h @@ -91,6 +91,15 @@ inline constexpr bool __IsPointerHelper = true; template inline constexpr bool IsPointer = __IsPointerHelper>; +template +inline constexpr bool __IsMemberPointer = false; + +template +inline constexpr bool __IsMemberPointer = true; + +template +inline constexpr bool IsMemberPointer = __IsMemberPointer>; + template inline constexpr bool IsFunction = false; template @@ -398,6 +407,9 @@ inline constexpr bool IsArithmetic = IsIntegral || IsFloatingPoint; template inline constexpr bool IsFundamental = IsArithmetic || IsVoid || IsNullPointer; +template +inline constexpr bool IsScalar = IsArithmetic || IsEnum || IsPointer || IsNullPointer || IsMemberPointer; + template struct IntegerSequence { using Type = T; @@ -584,6 +596,12 @@ inline constexpr bool IsSameIgnoringCV = IsSame, RemoveCV>; template inline constexpr bool IsOneOfIgnoringCV = (IsSameIgnoringCV || ...); +template +inline constexpr bool IsSameIgnoringCVReference = IsSame, RemoveCVReference>; + +template +inline constexpr bool IsOneOfIgnoringCVReference = (IsSameIgnoringCVReference || ...); + template struct __InvokeResult { }; @@ -677,16 +695,20 @@ using AK::Detail::IsFundamental; using AK::Detail::IsHashCompatible; using AK::Detail::IsIntegral; using AK::Detail::IsLvalueReference; +using AK::Detail::IsMemberPointer; using AK::Detail::IsMoveAssignable; using AK::Detail::IsMoveConstructible; using AK::Detail::IsNullPointer; using AK::Detail::IsOneOf; using AK::Detail::IsOneOfIgnoringCV; +using AK::Detail::IsOneOfIgnoringCVReference; using AK::Detail::IsPOD; using AK::Detail::IsPointer; using AK::Detail::IsRvalueReference; using AK::Detail::IsSame; using AK::Detail::IsSameIgnoringCV; +using AK::Detail::IsSameIgnoringCVReference; +using AK::Detail::IsScalar; using AK::Detail::IsSigned; using AK::Detail::IsSpecializationOf; using AK::Detail::IsTemplateBaseOf; diff --git a/AK/StdLibExtras.h b/AK/StdLibExtras.h index 051e82dff370..7feb7a5a0d23 100644 --- a/AK/StdLibExtras.h +++ b/AK/StdLibExtras.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace AK { @@ -31,6 +32,7 @@ requires(AK::Detail::IsIntegral) template void compiletime_fail(Args...); +using std::construct_at; using std::forward; using std::move; } diff --git a/AK/String.h b/AK/String.h index a1fb182219c2..3fa3b46ce2a8 100644 --- a/AK/String.h +++ b/AK/String.h @@ -225,7 +225,7 @@ class String : public Detail::StringBase { using ShortString = Detail::ShortString; - bool is_invalid() const + constexpr bool is_invalid() const { return raw(Badge {}) == 0; } @@ -249,38 +249,38 @@ class Optional : public OptionalBase { public: using ValueType = String; - Optional() = default; + constexpr Optional() = default; template V> - Optional(V) { } + constexpr Optional(V) { } - Optional(Optional const& other) + constexpr Optional(Optional const& other) { if (other.has_value()) m_value = other.m_value; } - Optional(Optional&& other) + constexpr Optional(Optional&& other) : m_value(move(other.m_value)) { } template requires(!IsSame>) - explicit(!IsConvertible) Optional(U&& value) + explicit(!IsConvertible) constexpr Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) : m_value(forward(value)) { } template V> - Optional& operator=(V) + constexpr Optional& operator=(V) { clear(); return *this; } - Optional& operator=(Optional const& other) + constexpr Optional& operator=(Optional const& other) { if (this != &other) { m_value = other.m_value; @@ -288,7 +288,7 @@ class Optional : public OptionalBase { return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { m_value = move(other.m_value); @@ -296,37 +296,37 @@ class Optional : public OptionalBase { return *this; } - void clear() + constexpr void clear() { m_value = String(nullptr); } - [[nodiscard]] bool has_value() const + [[nodiscard]] constexpr bool has_value() const { return !m_value.is_invalid(); } - [[nodiscard]] String& value() & + [[nodiscard]] constexpr String& value() & { VERIFY(has_value()); return m_value; } - [[nodiscard]] String const& value() const& + [[nodiscard]] constexpr String const& value() const& { VERIFY(has_value()); return m_value; } - [[nodiscard]] String value() && + [[nodiscard]] constexpr String value() && { return release_value(); } - [[nodiscard]] String release_value() + [[nodiscard]] constexpr String release_value() { VERIFY(has_value()); - String released_value = m_value; + String released_value = move(m_value); clear(); return released_value; } diff --git a/AK/StringBase.h b/AK/StringBase.h index df468afd4c5e..ab74b1d57278 100644 --- a/AK/StringBase.h +++ b/AK/StringBase.h @@ -70,8 +70,8 @@ class StringBase { [[nodiscard]] bool operator==(StringBase const&) const; - [[nodiscard]] ALWAYS_INLINE FlatPtr raw(Badge) const { return bit_cast(m_impl); } - [[nodiscard]] ALWAYS_INLINE FlatPtr raw(Badge) const { return bit_cast(m_impl); } + [[nodiscard]] ALWAYS_INLINE constexpr FlatPtr raw(Badge) const { return bit_cast(m_impl); } + [[nodiscard]] ALWAYS_INLINE constexpr FlatPtr raw(Badge) const { return bit_cast(m_impl); } template ALWAYS_INLINE ErrorOr replace_with_new_string(Badge, size_t byte_count, Func&& callback) diff --git a/Libraries/LibJS/Runtime/Completion.h b/Libraries/LibJS/Runtime/Completion.h index 7d8ca4f739b0..c1a45a069560 100644 --- a/Libraries/LibJS/Runtime/Completion.h +++ b/Libraries/LibJS/Runtime/Completion.h @@ -60,7 +60,7 @@ class [[nodiscard]] Completion { Throw, }; - ALWAYS_INLINE Completion(Type type, Value value) + ALWAYS_INLINE constexpr Completion(Type type, Value value) : m_type(type) , m_value(value) { @@ -72,12 +72,12 @@ class [[nodiscard]] Completion { // 5.2.3.1 Implicit Completion Values, https://tc39.es/ecma262/#sec-implicit-completion-values // Not `explicit` on purpose. - ALWAYS_INLINE Completion(Value value) + ALWAYS_INLINE constexpr Completion(Value value) : Completion(Type::Normal, value) { } - ALWAYS_INLINE Completion() + ALWAYS_INLINE constexpr Completion() : Completion(js_undefined()) { } @@ -87,21 +87,21 @@ class [[nodiscard]] Completion { Completion(Completion&&) = default; Completion& operator=(Completion&&) = default; - [[nodiscard]] Type type() const + [[nodiscard]] constexpr Type type() const { VERIFY(m_type != Type::Empty); return m_type; } - [[nodiscard]] Value& value() { return m_value; } - [[nodiscard]] Value const& value() const { return m_value; } + [[nodiscard]] constexpr Value& value() { return m_value; } + [[nodiscard]] constexpr Value const& value() const { return m_value; } // "abrupt completion refers to any completion with a [[Type]] value other than normal" - [[nodiscard]] bool is_abrupt() const { return m_type != Type::Normal; } + [[nodiscard]] constexpr bool is_abrupt() const { return m_type != Type::Normal; } // These are for compatibility with the TRY() macro in AK. - [[nodiscard]] bool is_error() const { return m_type == Type::Throw; } - [[nodiscard]] Value release_value() { return m_value; } - Completion release_error() + [[nodiscard]] constexpr bool is_error() const { return m_type == Type::Throw; } + [[nodiscard]] constexpr Value release_value() { return m_value; } + constexpr Completion release_error() { VERIFY(is_error()); return { m_type, release_value() }; @@ -112,12 +112,12 @@ class [[nodiscard]] Completion { }; friend AK::Optional; - Completion(EmptyTag) + constexpr Completion(EmptyTag) : m_type(Type::Empty) { } - bool is_empty() const + constexpr bool is_empty() const { return m_type == Type::Empty; } @@ -139,27 +139,27 @@ class Optional : public OptionalBase { public: using ValueType = JS::Completion; - Optional() = default; + constexpr Optional() = default; - Optional(Optional const& other) + constexpr Optional(Optional const& other) { if (other.has_value()) m_value = other.m_value; } - Optional(Optional&& other) + constexpr Optional(Optional&& other) : m_value(move(other.m_value)) { } template - explicit(!IsConvertible) Optional(U&& value) + explicit(!IsConvertible) constexpr Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) : m_value(forward(value)) { } - Optional& operator=(Optional const& other) + constexpr Optional& operator=(Optional const& other) { if (this != &other) { clear(); @@ -168,7 +168,7 @@ class Optional : public OptionalBase { return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { clear(); @@ -177,34 +177,34 @@ class Optional : public OptionalBase { return *this; } - void clear() + constexpr void clear() { m_value = JS::Completion(JS::Completion::EmptyTag {}); } - [[nodiscard]] bool has_value() const + [[nodiscard]] constexpr bool has_value() const { return !m_value.is_empty(); } - [[nodiscard]] JS::Completion& value() & + [[nodiscard]] constexpr JS::Completion& value() & { VERIFY(has_value()); return m_value; } - [[nodiscard]] JS::Completion const& value() const& + [[nodiscard]] constexpr JS::Completion const& value() const& { VERIFY(has_value()); return m_value; } - [[nodiscard]] JS::Completion value() && + [[nodiscard]] constexpr JS::Completion value() && { return release_value(); } - [[nodiscard]] JS::Completion release_value() + [[nodiscard]] constexpr JS::Completion release_value() { VERIFY(has_value()); JS::Completion released_value = m_value; diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index 5766b798fc31..687966a97916 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -154,7 +154,7 @@ class Value : public GC::NanBoxedValue { return !is_nan() && !is_infinity(); } - Value() + constexpr Value() : Value(UNDEFINED_TAG << GC::TAG_SHIFT, (u64)0) { } @@ -428,12 +428,12 @@ class Value : public GC::NanBoxedValue { enum class EmptyTag { Empty }; - Value(EmptyTag) + constexpr Value(EmptyTag) : Value(EMPTY_TAG << GC::TAG_SHIFT, (u64)0) { } - Value(u64 tag, u64 val) + constexpr Value(u64 tag, u64 val) { ASSERT(!(tag & val)); m_value.encoded = tag | val; @@ -467,9 +467,9 @@ class Value : public GC::NanBoxedValue { ThrowCompletionOr to_i32_slow_case(VM&) const; - friend Value js_undefined(); - friend Value js_null(); - friend Value js_special_empty_value(); + friend constexpr Value js_undefined(); + friend constexpr Value js_null(); + friend constexpr Value js_special_empty_value(); friend ThrowCompletionOr greater_than(VM&, Value lhs, Value rhs); friend ThrowCompletionOr greater_than_equals(VM&, Value lhs, Value rhs); friend ThrowCompletionOr less_than(VM&, Value lhs, Value rhs); @@ -478,17 +478,17 @@ class Value : public GC::NanBoxedValue { friend bool same_value_non_number(Value lhs, Value rhs); }; -inline Value js_undefined() +inline constexpr Value js_undefined() { return Value(UNDEFINED_TAG << GC::TAG_SHIFT, (u64)0); } -inline Value js_null() +inline constexpr Value js_null() { return Value(NULL_TAG << GC::TAG_SHIFT, (u64)0); } -inline Value js_special_empty_value() +inline constexpr Value js_special_empty_value() { return Value(Value::EmptyTag::Empty); } @@ -564,38 +564,38 @@ class Optional : public OptionalBase { public: using ValueType = JS::Value; - Optional() = default; + constexpr Optional() = default; template V> - Optional(V) { } + constexpr Optional(V) { } - Optional(Optional const& other) + constexpr Optional(Optional const& other) { if (other.has_value()) m_value = other.m_value; } - Optional(Optional&& other) + constexpr Optional(Optional&& other) : m_value(other.m_value) { } template requires(!IsSame>) - explicit(!IsConvertible) Optional(U&& value) + explicit(!IsConvertible) constexpr Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) : m_value(forward(value)) { } template V> - Optional& operator=(V) + constexpr Optional& operator=(V) { clear(); return *this; } - Optional& operator=(Optional const& other) + constexpr Optional& operator=(Optional const& other) { if (this != &other) { clear(); @@ -604,7 +604,7 @@ class Optional : public OptionalBase { return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { clear(); @@ -613,34 +613,34 @@ class Optional : public OptionalBase { return *this; } - void clear() + constexpr void clear() { m_value = JS::js_special_empty_value(); } - [[nodiscard]] bool has_value() const + [[nodiscard]] constexpr bool has_value() const { return !m_value.is_special_empty_value(); } - [[nodiscard]] JS::Value& value() & + [[nodiscard]] constexpr JS::Value& value() & { VERIFY(has_value()); return m_value; } - [[nodiscard]] JS::Value const& value() const& + [[nodiscard]] constexpr JS::Value const& value() const& { VERIFY(has_value()); return m_value; } - [[nodiscard]] JS::Value value() && + [[nodiscard]] constexpr JS::Value value() && { return release_value(); } - [[nodiscard]] JS::Value release_value() + [[nodiscard]] constexpr JS::Value release_value() { VERIFY(has_value()); JS::Value released_value = m_value; diff --git a/Libraries/LibTest/Macros.h b/Libraries/LibTest/Macros.h index a79dd65bbb9b..d656bbb0312a 100644 --- a/Libraries/LibTest/Macros.h +++ b/Libraries/LibTest/Macros.h @@ -130,6 +130,9 @@ bool assume(T const& expression, StringView expression_string, SourceLocation lo return true; } +template +consteval void expect_consteval(T) { } + } #define EXPECT(...) \ @@ -179,6 +182,8 @@ bool assume(T const& expression, StringView expression_string, SourceLocation lo ::Test::set_current_test_result(::Test::TestResult::Failed); \ } while (false) +#define EXPECT_CONSTEVAL(...) ::Test::expect_consteval(__VA_ARGS__) + // To use, specify the lambda to execute in a sub process and verify it exits: // EXPECT_CRASH("This should fail", []{ // return Test::Crash::Failure::DidNotCrash; diff --git a/Libraries/LibWeb/Painting/PaintableBox.cpp b/Libraries/LibWeb/Painting/PaintableBox.cpp index 9f1f166ed981..20683e2ffd00 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -841,7 +841,6 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const return; bool should_clip_overflow = computed_values().overflow_x() != CSS::Overflow::Visible && computed_values().overflow_y() != CSS::Overflow::Visible; - Optional corner_clip_id; auto clip_box = absolute_padding_box_rect(); if (get_clip_rect().has_value()) { diff --git a/Tests/AK/TestOptional.cpp b/Tests/AK/TestOptional.cpp index 92a6d855740c..3773eec02824 100644 --- a/Tests/AK/TestOptional.cpp +++ b/Tests/AK/TestOptional.cpp @@ -18,7 +18,7 @@ class NonCopyable { AK_MAKE_DEFAULT_MOVABLE(NonCopyable); public: - NonCopyable() { } + constexpr NonCopyable() { } ~NonCopyable() = default; int x { 13 }; @@ -29,7 +29,7 @@ class NonTriviallyCopyable { AK_MAKE_DEFAULT_MOVABLE(NonTriviallyCopyable); public: - NonTriviallyCopyable() = default; + constexpr NonTriviallyCopyable() = default; ~NonTriviallyCopyable() = default; ByteString x { "13" }; @@ -40,7 +40,7 @@ class TriviallyCopyable { AK_MAKE_DEFAULT_MOVABLE(TriviallyCopyable); public: - TriviallyCopyable() = default; + constexpr TriviallyCopyable() = default; ~TriviallyCopyable() = default; int x { 13 }; @@ -67,7 +67,6 @@ TEST_CASE(move_optional) y = move(x); EXPECT_EQ(y.has_value(), true); EXPECT_EQ(y.value(), 3); - EXPECT_EQ(x.has_value(), false); } TEST_CASE(optional_rvalue_ref_qualified_getters) @@ -144,17 +143,94 @@ TEST_CASE(comparison_with_numeric_types) EXPECT_NE(opt1, -2); } +TEST_CASE(test_constexpr) +{ + int i = 13; + NonCopyable dcm {}; + + EXPECT_CONSTEVAL(Optional {}); + EXPECT_CONSTEVAL(Optional {}); + EXPECT_CONSTEVAL(Optional {}); + EXPECT_CONSTEVAL(Optional {}); + EXPECT_CONSTEVAL(Optional {}); + EXPECT_CONSTEVAL(Optional {}); + EXPECT_CONSTEVAL(Optional {}); + EXPECT_CONSTEVAL(Optional {}); + + EXPECT_CONSTEVAL(Optional { 13 }); + EXPECT_CONSTEVAL(Optional { NonCopyable {} }); + EXPECT_CONSTEVAL(Optional { 13 }); + EXPECT_CONSTEVAL(Optional { NonCopyable {} }); + EXPECT_CONSTEVAL(Optional { i }); + EXPECT_CONSTEVAL(Optional { dcm }); + EXPECT_CONSTEVAL(Optional { 13 }); + EXPECT_CONSTEVAL(Optional { NonCopyable {} }); + + static_assert(!Optional {}.has_value()); + static_assert(!Optional {}.has_value()); + static_assert(!Optional {}.has_value()); + static_assert(!Optional {}.has_value()); + static_assert(!Optional {}.has_value()); + static_assert(!Optional {}.has_value()); + static_assert(!Optional {}.has_value()); + static_assert(!Optional {}.has_value()); + + static_assert(Optional { 13 }.has_value()); + static_assert(Optional { NonCopyable {} }.has_value()); + static_assert(Optional { 13 }.has_value()); + static_assert(Optional { NonCopyable {} }.has_value()); + static_assert(Optional { i }.has_value()); + static_assert(Optional { dcm }.has_value()); + static_assert(Optional { 13 }.has_value()); + static_assert(Optional { NonCopyable {} }.has_value()); + + static_assert(Optional { 13 }.value() == 13); + static_assert(Optional { NonCopyable {} }.value().x == 13); + static_assert(Optional { 13 }.value() == 13); + static_assert(Optional { 13 }.value() == 13); + static_assert(Optional { NonCopyable {} }.value().x == 13); + + static_assert(!(Optional { 1 } = {}).has_value(), "Assigning a `{}` should clear the Optional, even for scalar types^^"); +} + +TEST_CASE(non_trivial_destructor_is_called_on_move_assignment) +{ + static int foo_destruction_count = 0; + + struct Foo { + Foo() { } + Foo(Foo&&) = default; + ~Foo() + { + ++foo_destruction_count; + } + + Foo& operator=(Foo&&) = default; + }; + static_assert(!IsTriviallyMoveAssignable>); + + Optional foo = Foo {}; // 1. The immediate value needs to be destroyed + Optional foo2; + foo = AK::move(foo2); // 2. The move releases the value, which destroys the moved-from stored value + + EXPECT_EQ(foo_destruction_count, 2); + + // As Optional does not trivially move, moved-from values are empty + // Ignoring the fact that we are touching a moved from value here + EXPECT_EQ(foo.has_value(), false); +} + TEST_CASE(test_copy_ctor_and_dtor_called) { -#ifdef AK_HAVE_CONDITIONALLY_TRIVIAL static_assert(IsTriviallyDestructible>); static_assert(IsTriviallyCopyable>); static_assert(IsTriviallyCopyConstructible>); static_assert(IsTriviallyCopyAssignable>); - // These can't be trivial as we have to clear the original object. - static_assert(!IsTriviallyMoveConstructible>); - static_assert(!IsTriviallyMoveAssignable>); -#endif + static_assert(IsTriviallyMoveConstructible>); + static_assert(IsTriviallyMoveAssignable>); + static_assert(IsTriviallyCopyConstructible>); + static_assert(IsTriviallyCopyAssignable>); + static_assert(IsTriviallyDestructible>); struct DestructionChecker { explicit DestructionChecker(bool& was_destroyed) @@ -228,12 +304,10 @@ TEST_CASE(test_copy_ctor_and_dtor_called) Optional move2 = move(move1); EXPECT(was_moved); -#ifdef AK_HAVE_CONDITIONALLY_TRIVIAL struct NonDestructible { ~NonDestructible() = delete; }; static_assert(!IsDestructible>); -#endif } TEST_CASE(basic_optional_reference) @@ -293,6 +367,63 @@ TEST_CASE(comparison_reference) EXPECT_NE(opt1, opt3); } +TEST_CASE(uninitialized_constructor) +{ + static bool was_constructed = false; + struct Internal { + Internal() { was_constructed = true; } + }; + + struct ShouldNotBeDefaultConstructed { + bool m_default_constructed { true }; + Internal m_internal; + ShouldNotBeDefaultConstructed() = default; + ShouldNotBeDefaultConstructed(bool) + : m_default_constructed(false) + { + } + }; + static_assert(IsConstructible); + + Optional opt; + EXPECT(!was_constructed); + EXPECT(!opt.has_value()); + + opt = ShouldNotBeDefaultConstructed { true }; + EXPECT(was_constructed); + EXPECT(opt.has_value()); + EXPECT(!opt.value().m_default_constructed); +} + +consteval bool test_constexpr() +{ + Optional none; + if (none.has_value()) + return false; + + Optional x; + x = 3; + if (!x.has_value()) + return false; + + if (x.value() != 3) + return false; + + Optional y; + y = x.release_value(); + if (!y.has_value()) + return false; + + if (y.value() != 3) + return false; + + if (x.has_value()) + return false; + + return true; +} +static_assert(test_constexpr()); + template struct CheckAssignments; diff --git a/Tests/ClangPlugins/CMakeLists.txt b/Tests/ClangPlugins/CMakeLists.txt index e6a3c3c309d2..45f0ea40a2d5 100644 --- a/Tests/ClangPlugins/CMakeLists.txt +++ b/Tests/ClangPlugins/CMakeLists.txt @@ -11,6 +11,7 @@ list(APPEND CLANG_PLUGINS_COMPILE_OPTIONS_FOR_TESTS -Wno-literal-range -Wno-unknown-warning-option -Wno-unqualified-std-cast-call + -fgnuc-version=4.2.1 # NOTE: Clang default as of 10.0.0 ) # Ensure we always check for invalid function field types regardless of the value of ENABLE_CLANG_PLUGINS_INVALID_FUNCTION_MEMBERS