From 89d789ac4e2e08aa2bfa06bf76545a9259a93844 Mon Sep 17 00:00:00 2001 From: Jonne Ransijn Date: Wed, 20 Nov 2024 12:55:39 +0100 Subject: [PATCH 1/2] AK: Allow `Optional` to be used in constant expressions Including its specializations, wherever possible. --- AK/FlyString.h | 34 ++-- AK/Optional.h | 195 +++++++++++---------- AK/StdLibExtras.h | 2 + AK/String.h | 34 ++-- Libraries/LibJS/Runtime/Completion.h | 16 +- Libraries/LibJS/Runtime/Value.h | 30 ++-- Libraries/LibTest/Macros.h | 5 + Libraries/LibWeb/Painting/PaintableBox.cpp | 1 - Tests/AK/TestOptional.cpp | 70 ++++++-- 9 files changed, 225 insertions(+), 162 deletions(-) diff --git a/AK/FlyString.h b/AK/FlyString.h index a0963e6b831..640b7fd18f3 100644 --- a/AK/FlyString.h +++ b/AK/FlyString.h @@ -21,7 +21,7 @@ class FlyString { AK_MAKE_DEFAULT_COPYABLE(FlyString); public: - FlyString() = default; + constexpr FlyString() = default; static ErrorOr from_utf8(StringView); static FlyString from_utf8_without_validation(ReadonlyBytes); @@ -83,7 +83,7 @@ public: private: friend class Optional; - explicit FlyString(nullptr_t) + explicit constexpr FlyString(nullptr_t) : m_data(Detail::StringBase(nullptr)) { } @@ -106,38 +106,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) + : 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(); @@ -146,7 +146,7 @@ public: return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { clear(); @@ -156,40 +156,40 @@ public: } template - ALWAYS_INLINE bool operator==(Optional const& other) const + ALWAYS_INLINE constexpr bool operator==(Optional const& other) const { return has_value() == other.has_value() && (!has_value() || value() == other.value()); } template - ALWAYS_INLINE bool operator==(O const& other) const + ALWAYS_INLINE constexpr bool operator==(O const& other) const { return has_value() && value() == other; } - 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(); } diff --git a/AK/Optional.h b/AK/Optional.h index 1bb6441bcbe..caa6f07780d 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,30 +55,30 @@ public: using ValueType = T; template V> - Self& operator=(V) + 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; } - [[nodiscard]] ALWAYS_INLINE T value_or(T const& fallback) const& + [[nodiscard]] ALWAYS_INLINE constexpr T value_or(T const& fallback) const& { if (static_cast(*this).has_value()) return static_cast(*this).value(); return fallback; } - [[nodiscard]] ALWAYS_INLINE T value_or(T&& fallback) && + [[nodiscard]] ALWAYS_INLINE constexpr T value_or(T&& fallback) && { if (static_cast(*this).has_value()) return move(static_cast(*this).value()); @@ -86,7 +86,7 @@ public: } 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(); @@ -94,7 +94,7 @@ public: } 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(); @@ -102,7 +102,7 @@ public: } 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(); @@ -110,7 +110,7 @@ public: } 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(); @@ -118,7 +118,7 @@ public: } template - ALWAYS_INLINE bool operator==(Optional const& other) const + ALWAYS_INLINE constexpr bool operator==(Optional const& other) const { return static_cast(*this).has_value() == (other).has_value() && (!static_cast(*this).has_value() || static_cast(*this).value() == (other).value()); @@ -126,19 +126,19 @@ public: template requires(!Detail::IsBaseOf, O>) - ALWAYS_INLINE bool operator==(O const& other) const + ALWAYS_INLINE constexpr bool operator==(O const& other) const { return static_cast(*this).has_value() && static_cast(*this).value() == other; } - [[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()) @@ -153,7 +153,7 @@ public: } 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()) @@ -178,13 +178,19 @@ requires(!IsLvalueReference) class [[nodiscard]] Optional : public Optiona public: using ValueType = T; - ALWAYS_INLINE Optional() = default; + ALWAYS_INLINE constexpr Optional() + : m_null() + { + } template V> - Optional(V) { } + constexpr Optional(V) + : m_null() + { + } template V> - Optional& operator=(V) + constexpr Optional& operator=(V) { clear(); return *this; @@ -194,143 +200,146 @@ public: AK_MAKE_CONDITIONALLY_NONMOVABLE(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_value, other.value()); } - ALWAYS_INLINE Optional(Optional&& other) + ALWAYS_INLINE constexpr Optional(Optional&& other) : m_has_value(other.m_has_value) { if (other.has_value()) - new (&m_storage) T(other.release_value()); + construct_at(&m_value, other.release_value()); } template - requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf) ALWAYS_INLINE explicit Optional(Optional const& other) + requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf) 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_value, other.value()); } template - requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf) ALWAYS_INLINE explicit Optional(Optional&& other) + requires(IsConstructible && !IsSpecializationOf && !IsSpecializationOf) 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_value, other.value()); } 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_value, 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()); + construct_at(&m_value, other.value()); } } return *this; } - ALWAYS_INLINE Optional& operator=(Optional&& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional&& other) { if (this != &other) { clear(); m_has_value = other.m_has_value; if (other.has_value()) { - new (&m_storage) T(other.release_value()); + construct_at(&m_value, other.release_value()); } } return *this; } template - ALWAYS_INLINE bool operator==(Optional const& other) const + ALWAYS_INLINE constexpr bool operator==(Optional const& other) const { return has_value() == other.has_value() && (!has_value() || value() == other.value()); } template - ALWAYS_INLINE bool operator==(O const& other) const + ALWAYS_INLINE constexpr bool operator==(O const& other) const { return has_value() && value() == other; } - 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(); + m_value.~T(); m_has_value = false; } } 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_value, 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_value, 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_value; } - [[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_value; } - [[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()); - value().~T(); + T released_value = move(m_value); + m_value.~T(); m_has_value = false; return released_value; } private: - alignas(T) u8 m_storage[sizeof(T)]; + union { + char m_null; + T m_value; + }; bool m_has_value { false }; }; @@ -345,76 +354,76 @@ requires(IsLvalueReference) class [[nodiscard]] Optional { public: using ValueType = T; - ALWAYS_INLINE Optional() = default; + ALWAYS_INLINE constexpr Optional() = default; template V> - Optional(V) { } + constexpr Optional(V) { } template V> - Optional& operator=(V) + 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) { } - ALWAYS_INLINE Optional(Optional const& other) + ALWAYS_INLINE constexpr Optional(Optional const& other) : m_pointer(other.m_pointer) { } - ALWAYS_INLINE Optional(Optional&& other) + ALWAYS_INLINE constexpr Optional(Optional&& other) : m_pointer(other.m_pointer) { other.m_pointer = nullptr; } 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()) { other.m_pointer = nullptr; } - ALWAYS_INLINE Optional& operator=(Optional& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional& other) { m_pointer = other.m_pointer; return *this; } - ALWAYS_INLINE Optional& operator=(Optional const& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional const& other) { m_pointer = other.m_pointer; return *this; } - ALWAYS_INLINE Optional& operator=(Optional&& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional&& other) { m_pointer = other.m_pointer; other.m_pointer = nullptr; @@ -422,7 +431,7 @@ public: } template - ALWAYS_INLINE Optional& operator=(Optional& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional& other) requires(CanBePlacedInOptional) { m_pointer = other.ptr(); @@ -430,7 +439,7 @@ public: } template - ALWAYS_INLINE Optional& operator=(Optional const& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional const& other) requires(CanBePlacedInOptional) { m_pointer = other.ptr(); @@ -438,7 +447,7 @@ public: } template - ALWAYS_INLINE Optional& operator=(Optional&& other) + ALWAYS_INLINE constexpr Optional& operator=(Optional&& other) requires(CanBePlacedInOptional && IsLvalueReference) { m_pointer = other.m_pointer; @@ -449,44 +458,44 @@ public: // Note: Disallows assignment from a temporary as this does not do any lifetime extension. template requires(!IsSame>) - ALWAYS_INLINE Optional& operator=(U&& value) + ALWAYS_INLINE constexpr Optional& operator=(U&& value) requires(CanBePlacedInOptional && IsLvalueReference) { m_pointer = &value; return *this; } - 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() + [[nodiscard]] ALWAYS_INLINE constexpr RemoveReference* ptr() { return m_pointer; } - [[nodiscard]] ALWAYS_INLINE RemoveReference const* ptr() const + [[nodiscard]] ALWAYS_INLINE constexpr RemoveReference const* ptr() const { 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(); @@ -494,38 +503,38 @@ public: } // 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); } template - ALWAYS_INLINE bool operator==(Optional const& other) const + ALWAYS_INLINE constexpr bool operator==(Optional const& other) const { return has_value() == other.has_value() && (!has_value() || value() == other.value()); } template - ALWAYS_INLINE bool operator==(U const& other) const + ALWAYS_INLINE constexpr bool operator==(U const& other) const { return has_value() && value() == other; } - 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(); } + ALWAYS_INLINE constexpr RawPtr>> operator->() const { return &value(); } + ALWAYS_INLINE constexpr RawPtr> operator->() { return &value(); } // Conversion operators from Optional -> Optional - ALWAYS_INLINE operator Optional>() const + ALWAYS_INLINE constexpr operator Optional>() const { if (has_value()) return Optional>(value()); @@ -533,7 +542,7 @@ public: } 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(); @@ -541,7 +550,7 @@ public: } 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(); @@ -549,7 +558,7 @@ public: } 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(); @@ -557,7 +566,7 @@ public: } 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(); @@ -565,7 +574,7 @@ public: } 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) @@ -580,7 +589,7 @@ public: } 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) diff --git a/AK/StdLibExtras.h b/AK/StdLibExtras.h index d0ba945aaf8..523c3812187 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 825677b9408..4c4ba6548a5 100644 --- a/AK/String.h +++ b/AK/String.h @@ -236,38 +236,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; @@ -275,7 +275,7 @@ public: return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { m_value = move(other.m_value); @@ -284,48 +284,48 @@ public: } template - ALWAYS_INLINE bool operator==(Optional const& other) const + ALWAYS_INLINE constexpr bool operator==(Optional const& other) const { return has_value() == other.has_value() && (!has_value() || value() == other.value()); } template - ALWAYS_INLINE bool operator==(O const& other) const + ALWAYS_INLINE constexpr bool operator==(O const& other) const { return has_value() && value() == other; } - 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/Libraries/LibJS/Runtime/Completion.h b/Libraries/LibJS/Runtime/Completion.h index b599a0911d1..70c70a3bce2 100644 --- a/Libraries/LibJS/Runtime/Completion.h +++ b/Libraries/LibJS/Runtime/Completion.h @@ -164,7 +164,7 @@ class Optional : public OptionalBase { public: using ValueType = JS::Completion; - Optional() = default; + constexpr Optional() = default; Optional(Optional const& other) { @@ -172,19 +172,19 @@ public: 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(); @@ -193,7 +193,7 @@ public: return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { clear(); @@ -207,18 +207,18 @@ public: 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; diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index 11cc0cb8908..9b43ea836e8 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -154,7 +154,7 @@ public: return !is_nan() && !is_infinity(); } - Value() + constexpr Value() : Value(EMPTY_TAG << GC::TAG_SHIFT, (u64)0) { } @@ -419,7 +419,7 @@ private: ThrowCompletionOr to_numeric_slow_case(VM&) const; ThrowCompletionOr to_primitive_slow_case(VM&, PreferredType) const; - Value(u64 tag, u64 val) + constexpr Value(u64 tag, u64 val) { ASSERT(!(tag & val)); m_value.encoded = tag | val; @@ -544,10 +544,10 @@ 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) { @@ -555,27 +555,27 @@ public: 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(); @@ -584,7 +584,7 @@ public: return *this; } - Optional& operator=(Optional&& other) + constexpr Optional& operator=(Optional&& other) { if (this != &other) { clear(); @@ -594,34 +594,34 @@ public: } template - ALWAYS_INLINE bool operator==(Optional const& other) const + ALWAYS_INLINE constexpr bool operator==(Optional const& other) const { return has_value() == other.has_value() && (!has_value() || value() == other.value()); } template - ALWAYS_INLINE bool operator==(O const& other) const + ALWAYS_INLINE constexpr bool operator==(O const& other) const { return has_value() && value() == other; } - void clear() + constexpr void clear() { m_value = {}; } - [[nodiscard]] bool has_value() const + [[nodiscard]] constexpr bool has_value() const { return !m_value.is_empty(); } - [[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; diff --git a/Libraries/LibTest/Macros.h b/Libraries/LibTest/Macros.h index 1be6c82075e..8a4a81bc190 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(x) \ @@ -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 aaff8189b74..d218d78351a 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -706,7 +706,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 cb17d62debd..84dc0a9c92f 100644 --- a/Tests/AK/TestOptional.cpp +++ b/Tests/AK/TestOptional.cpp @@ -13,6 +13,17 @@ #include #include +struct DontCopyMe { + constexpr DontCopyMe() { } + ~DontCopyMe() = default; + DontCopyMe(DontCopyMe&&) = default; + DontCopyMe& operator=(DontCopyMe&&) = default; + DontCopyMe(DontCopyMe const&) = delete; + DontCopyMe& operator=(DontCopyMe const&) = delete; + + int x { 13 }; +}; + TEST_CASE(basic_optional) { Optional x; @@ -39,17 +50,6 @@ TEST_CASE(move_optional) TEST_CASE(optional_rvalue_ref_qualified_getters) { - struct DontCopyMe { - DontCopyMe() { } - ~DontCopyMe() = default; - DontCopyMe(DontCopyMe&&) = default; - DontCopyMe& operator=(DontCopyMe&&) = default; - DontCopyMe(DontCopyMe const&) = delete; - DontCopyMe& operator=(DontCopyMe const&) = delete; - - int x { 13 }; - }; - auto make_an_optional = []() -> Optional { return DontCopyMe {}; }; @@ -122,6 +122,54 @@ TEST_CASE(comparison_with_numeric_types) EXPECT_NE(opt1, -2); } +TEST_CASE(test_constexpr) +{ + int i = 13; + DontCopyMe 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 { DontCopyMe {} }); + EXPECT_CONSTEVAL(Optional { 13 }); + EXPECT_CONSTEVAL(Optional { DontCopyMe {} }); + EXPECT_CONSTEVAL(Optional { i }); + EXPECT_CONSTEVAL(Optional { dcm }); + EXPECT_CONSTEVAL(Optional { 13 }); + EXPECT_CONSTEVAL(Optional { DontCopyMe {} }); + + 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 { DontCopyMe {} }.has_value()); + static_assert(Optional { 13 }.has_value()); + static_assert(Optional { DontCopyMe {} }.has_value()); + static_assert(Optional { i }.has_value()); + static_assert(Optional { dcm }.has_value()); + static_assert(Optional { 13 }.has_value()); + static_assert(Optional { DontCopyMe {} }.has_value()); + + static_assert(Optional { 13 }.value() == 13); + static_assert(Optional { DontCopyMe {} }.value().x == 13); + static_assert(Optional { 13 }.value() == 13); + static_assert(Optional { 13 }.value() == 13); + static_assert(Optional { DontCopyMe {} }.value().x == 13); +} + TEST_CASE(test_copy_ctor_and_dtor_called) { #ifdef AK_HAVE_CONDITIONALLY_TRIVIAL From 2c9860cd602007b7fb4cea2769d8aade9c66b0e3 Mon Sep 17 00:00:00 2001 From: Jonne Ransijn Date: Wed, 20 Nov 2024 21:07:58 +0100 Subject: [PATCH 2/2] Tests: Fix missing argument removed by `-cc1` This argument is removed (or rather, never added) by `-cc1`, which breaks any builds using GNU libc++, including CI. --- Tests/ClangPlugins/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/ClangPlugins/CMakeLists.txt b/Tests/ClangPlugins/CMakeLists.txt index e6a3c3c309d..45f0ea40a2d 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