Compare commits

...

3 commits

Author SHA1 Message Date
Jonne Ransijn
d538490d80
Merge 2c9860cd60 into 63a5717bc7 2024-11-20 16:09:04 -05:00
Jonne Ransijn
2c9860cd60
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.
2024-11-20 21:08:03 +01:00
Jonne Ransijn
89d789ac4e
AK: Allow Optional<T> to be used in constant expressions
Including its specializations, wherever possible.
2024-11-20 12:55:43 +01:00
10 changed files with 226 additions and 162 deletions

View file

@ -21,7 +21,7 @@ class FlyString {
AK_MAKE_DEFAULT_COPYABLE(FlyString); AK_MAKE_DEFAULT_COPYABLE(FlyString);
public: public:
FlyString() = default; constexpr FlyString() = default;
static ErrorOr<FlyString> from_utf8(StringView); static ErrorOr<FlyString> from_utf8(StringView);
static FlyString from_utf8_without_validation(ReadonlyBytes); static FlyString from_utf8_without_validation(ReadonlyBytes);
@ -83,7 +83,7 @@ public:
private: private:
friend class Optional<FlyString>; friend class Optional<FlyString>;
explicit FlyString(nullptr_t) explicit constexpr FlyString(nullptr_t)
: m_data(Detail::StringBase(nullptr)) : m_data(Detail::StringBase(nullptr))
{ {
} }
@ -106,38 +106,38 @@ class Optional<FlyString> : public OptionalBase<FlyString> {
public: public:
using ValueType = FlyString; using ValueType = FlyString;
Optional() = default; constexpr Optional() = default;
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional(V) { } constexpr Optional(V) { }
Optional(Optional<FlyString> const& other) constexpr Optional(Optional<FlyString> const& other)
{ {
if (other.has_value()) if (other.has_value())
m_value = other.m_value; m_value = other.m_value;
} }
Optional(Optional&& other) Optional(Optional&& other)
: m_value(other.m_value) : m_value(move(other.m_value))
{ {
} }
template<typename U = FlyString> template<typename U = FlyString>
requires(!IsSame<OptionalNone, RemoveCVReference<U>>) requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
explicit(!IsConvertible<U&&, FlyString>) Optional(U&& value) explicit(!IsConvertible<U&&, FlyString>) constexpr Optional(U&& value)
requires(!IsSame<RemoveCVReference<U>, Optional<FlyString>> && IsConstructible<FlyString, U &&>) requires(!IsSame<RemoveCVReference<U>, Optional<FlyString>> && IsConstructible<FlyString, U &&>)
: m_value(forward<U>(value)) : m_value(forward<U>(value))
{ {
} }
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional& operator=(V) constexpr Optional& operator=(V)
{ {
clear(); clear();
return *this; return *this;
} }
Optional& operator=(Optional const& other) constexpr Optional& operator=(Optional const& other)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
@ -146,7 +146,7 @@ public:
return *this; return *this;
} }
Optional& operator=(Optional&& other) constexpr Optional& operator=(Optional&& other)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
@ -156,40 +156,40 @@ public:
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(Optional<O> const& other) const ALWAYS_INLINE constexpr bool operator==(Optional<O> const& other) const
{ {
return has_value() == other.has_value() && (!has_value() || value() == other.value()); return has_value() == other.has_value() && (!has_value() || value() == other.value());
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(O const& other) const ALWAYS_INLINE constexpr bool operator==(O const& other) const
{ {
return has_value() && value() == other; return has_value() && value() == other;
} }
void clear() constexpr void clear()
{ {
m_value = FlyString(nullptr); m_value = FlyString(nullptr);
} }
[[nodiscard]] bool has_value() const [[nodiscard]] constexpr bool has_value() const
{ {
return !m_value.is_invalid(); return !m_value.is_invalid();
} }
[[nodiscard]] FlyString& value() & [[nodiscard]] constexpr FlyString& value() &
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;
} }
[[nodiscard]] FlyString const& value() const& [[nodiscard]] constexpr FlyString const& value() const&
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;
} }
[[nodiscard]] FlyString value() && [[nodiscard]] constexpr FlyString value() &&
{ {
return release_value(); return release_value();
} }

View file

@ -46,7 +46,7 @@ template<typename>
class Optional; class Optional;
struct OptionalNone { struct OptionalNone {
explicit OptionalNone() = default; explicit constexpr OptionalNone() = default;
}; };
template<typename T, typename Self = Optional<T>> template<typename T, typename Self = Optional<T>>
@ -55,30 +55,30 @@ public:
using ValueType = T; using ValueType = T;
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Self& operator=(V) constexpr Self& operator=(V)
{ {
static_cast<Self&>(*this).clear(); static_cast<Self&>(*this).clear();
return static_cast<Self&>(*this); return static_cast<Self&>(*this);
} }
[[nodiscard]] ALWAYS_INLINE T* ptr() & [[nodiscard]] ALWAYS_INLINE constexpr T* ptr() &
{ {
return static_cast<Self&>(*this).has_value() ? __builtin_launder(reinterpret_cast<T*>(&static_cast<Self&>(*this).value())) : nullptr; return static_cast<Self&>(*this).has_value() ? __builtin_launder(reinterpret_cast<T*>(&static_cast<Self&>(*this).value())) : nullptr;
} }
[[nodiscard]] ALWAYS_INLINE T const* ptr() const& [[nodiscard]] ALWAYS_INLINE constexpr T const* ptr() const&
{ {
return static_cast<Self const&>(*this).has_value() ? __builtin_launder(reinterpret_cast<T const*>(&static_cast<Self const&>(*this).value())) : nullptr; return static_cast<Self const&>(*this).has_value() ? __builtin_launder(reinterpret_cast<T const*>(&static_cast<Self const&>(*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<Self const&>(*this).has_value()) if (static_cast<Self const&>(*this).has_value())
return static_cast<Self const&>(*this).value(); return static_cast<Self const&>(*this).value();
return fallback; return fallback;
} }
[[nodiscard]] ALWAYS_INLINE T value_or(T&& fallback) && [[nodiscard]] ALWAYS_INLINE constexpr T value_or(T&& fallback) &&
{ {
if (static_cast<Self&>(*this).has_value()) if (static_cast<Self&>(*this).has_value())
return move(static_cast<Self&>(*this).value()); return move(static_cast<Self&>(*this).value());
@ -86,7 +86,7 @@ public:
} }
template<typename Callback, typename O = T> template<typename Callback, typename O = T>
[[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<Self const&>(*this).has_value()) if (static_cast<Self const&>(*this).has_value())
return static_cast<Self const&>(*this).value(); return static_cast<Self const&>(*this).value();
@ -94,7 +94,7 @@ public:
} }
template<typename Callback, typename O = T> template<typename Callback, typename O = T>
[[nodiscard]] ALWAYS_INLINE Optional<O> value_or_lazy_evaluated_optional(Callback callback) const [[nodiscard]] ALWAYS_INLINE constexpr Optional<O> value_or_lazy_evaluated_optional(Callback callback) const
{ {
if (static_cast<Self const&>(*this).has_value()) if (static_cast<Self const&>(*this).has_value())
return static_cast<Self const&>(*this).value(); return static_cast<Self const&>(*this).value();
@ -102,7 +102,7 @@ public:
} }
template<typename Callback, typename O = T> template<typename Callback, typename O = T>
[[nodiscard]] ALWAYS_INLINE ErrorOr<O> try_value_or_lazy_evaluated(Callback callback) const [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr<O> try_value_or_lazy_evaluated(Callback callback) const
{ {
if (static_cast<Self const&>(*this).has_value()) if (static_cast<Self const&>(*this).has_value())
return static_cast<Self const&>(*this).value(); return static_cast<Self const&>(*this).value();
@ -110,7 +110,7 @@ public:
} }
template<typename Callback, typename O = T> template<typename Callback, typename O = T>
[[nodiscard]] ALWAYS_INLINE ErrorOr<Optional<O>> try_value_or_lazy_evaluated_optional(Callback callback) const [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr<Optional<O>> try_value_or_lazy_evaluated_optional(Callback callback) const
{ {
if (static_cast<Self const&>(*this).has_value()) if (static_cast<Self const&>(*this).has_value())
return static_cast<Self const&>(*this).value(); return static_cast<Self const&>(*this).value();
@ -118,7 +118,7 @@ public:
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(Optional<O> const& other) const ALWAYS_INLINE constexpr bool operator==(Optional<O> const& other) const
{ {
return static_cast<Self const&>(*this).has_value() == (other).has_value() return static_cast<Self const&>(*this).has_value() == (other).has_value()
&& (!static_cast<Self const&>(*this).has_value() || static_cast<Self const&>(*this).value() == (other).value()); && (!static_cast<Self const&>(*this).has_value() || static_cast<Self const&>(*this).value() == (other).value());
@ -126,19 +126,19 @@ public:
template<typename O> template<typename O>
requires(!Detail::IsBaseOf<OptionalBase<T, Self>, O>) requires(!Detail::IsBaseOf<OptionalBase<T, Self>, O>)
ALWAYS_INLINE bool operator==(O const& other) const ALWAYS_INLINE constexpr bool operator==(O const& other) const
{ {
return static_cast<Self const&>(*this).has_value() && static_cast<Self const&>(*this).value() == other; return static_cast<Self const&>(*this).has_value() && static_cast<Self const&>(*this).value() == other;
} }
[[nodiscard]] ALWAYS_INLINE T const& operator*() const { return static_cast<Self const&>(*this).value(); } [[nodiscard]] ALWAYS_INLINE constexpr T const& operator*() const { return static_cast<Self const&>(*this).value(); }
[[nodiscard]] ALWAYS_INLINE T& operator*() { return static_cast<Self&>(*this).value(); } [[nodiscard]] ALWAYS_INLINE constexpr T& operator*() { return static_cast<Self&>(*this).value(); }
ALWAYS_INLINE T const* operator->() const { return &static_cast<Self const&>(*this).value(); } ALWAYS_INLINE constexpr T const* operator->() const { return &static_cast<Self const&>(*this).value(); }
ALWAYS_INLINE T* operator->() { return &static_cast<Self&>(*this).value(); } ALWAYS_INLINE constexpr T* operator->() { return &static_cast<Self&>(*this).value(); }
template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>> template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper) ALWAYS_INLINE constexpr Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper)
{ {
if constexpr (IsErrorOr) { if constexpr (IsErrorOr) {
if (static_cast<Self&>(*this).has_value()) if (static_cast<Self&>(*this).has_value())
@ -153,7 +153,7 @@ public:
} }
template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>> template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper) const ALWAYS_INLINE constexpr Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper) const
{ {
if constexpr (IsErrorOr) { if constexpr (IsErrorOr) {
if (static_cast<Self const&>(*this).has_value()) if (static_cast<Self const&>(*this).has_value())
@ -178,13 +178,19 @@ requires(!IsLvalueReference<T>) class [[nodiscard]] Optional<T> : public Optiona
public: public:
using ValueType = T; using ValueType = T;
ALWAYS_INLINE Optional() = default; ALWAYS_INLINE constexpr Optional()
: m_null()
{
}
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional(V) { } constexpr Optional(V)
: m_null()
{
}
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional& operator=(V) constexpr Optional& operator=(V)
{ {
clear(); clear();
return *this; return *this;
@ -194,143 +200,146 @@ public:
AK_MAKE_CONDITIONALLY_NONMOVABLE(Optional, <T>); AK_MAKE_CONDITIONALLY_NONMOVABLE(Optional, <T>);
AK_MAKE_CONDITIONALLY_DESTRUCTIBLE(Optional, <T>); AK_MAKE_CONDITIONALLY_DESTRUCTIBLE(Optional, <T>);
ALWAYS_INLINE Optional(Optional const& other) ALWAYS_INLINE constexpr Optional(Optional const& other)
requires(!IsTriviallyCopyConstructible<T>) requires(!IsTriviallyCopyConstructible<T>)
: m_has_value(other.m_has_value) : m_has_value(other.m_has_value)
{ {
if (other.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) : m_has_value(other.m_has_value)
{ {
if (other.has_value()) if (other.has_value())
new (&m_storage) T(other.release_value()); construct_at(&m_value, other.release_value());
} }
template<typename U> template<typename U>
requires(IsConstructible<T, U const&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit Optional(Optional<U> const& other) requires(IsConstructible<T, U const&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit constexpr Optional(Optional<U> const& other)
: m_has_value(other.has_value()) : m_has_value(other.has_value())
{ {
if (other.has_value()) if (other.has_value())
new (&m_storage) T(other.value()); construct_at(&m_value, other.value());
} }
template<typename U> template<typename U>
requires(IsConstructible<T, U &&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit Optional(Optional<U>&& other) requires(IsConstructible<T, U &&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit constexpr Optional(Optional<U>&& other)
: m_has_value(other.has_value()) : m_has_value(other.has_value())
{ {
if (other.has_value()) if (other.has_value())
new (&m_storage) T(other.release_value()); construct_at(&m_value, other.value());
} }
template<typename U = T> template<typename U = T>
requires(!IsSame<OptionalNone, RemoveCVReference<U>>) requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
ALWAYS_INLINE explicit(!IsConvertible<U&&, T>) Optional(U&& value) ALWAYS_INLINE explicit(!IsConvertible<U&&, T>) constexpr Optional(U&& value)
requires(!IsSame<RemoveCVReference<U>, Optional<T>> && IsConstructible<T, U &&>) requires(!IsSame<RemoveCVReference<U>, Optional<T>> && IsConstructible<T, U &&>)
: m_has_value(true) : m_has_value(true)
{ {
new (&m_storage) T(forward<U>(value)); construct_at(&m_value, forward<U>(value));
} }
ALWAYS_INLINE Optional& operator=(Optional const& other) ALWAYS_INLINE constexpr Optional& operator=(Optional const& other)
requires(!IsTriviallyCopyConstructible<T> || !IsTriviallyDestructible<T>) requires(!IsTriviallyCopyConstructible<T> || !IsTriviallyDestructible<T>)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
m_has_value = other.m_has_value; m_has_value = other.m_has_value;
if (other.has_value()) { if (other.has_value()) {
new (&m_storage) T(other.value()); construct_at(&m_value, other.value());
} }
} }
return *this; return *this;
} }
ALWAYS_INLINE Optional& operator=(Optional&& other) ALWAYS_INLINE constexpr Optional& operator=(Optional&& other)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
m_has_value = other.m_has_value; m_has_value = other.m_has_value;
if (other.has_value()) { if (other.has_value()) {
new (&m_storage) T(other.release_value()); construct_at(&m_value, other.release_value());
} }
} }
return *this; return *this;
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(Optional<O> const& other) const ALWAYS_INLINE constexpr bool operator==(Optional<O> const& other) const
{ {
return has_value() == other.has_value() && (!has_value() || value() == other.value()); return has_value() == other.has_value() && (!has_value() || value() == other.value());
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(O const& other) const ALWAYS_INLINE constexpr bool operator==(O const& other) const
{ {
return has_value() && value() == other; return has_value() && value() == other;
} }
ALWAYS_INLINE ~Optional() ALWAYS_INLINE constexpr ~Optional()
requires(!IsTriviallyDestructible<T> && IsDestructible<T>) requires(!IsTriviallyDestructible<T> && IsDestructible<T>)
{ {
clear(); clear();
} }
ALWAYS_INLINE void clear() ALWAYS_INLINE constexpr void clear()
{ {
if (m_has_value) { if (m_has_value) {
value().~T(); m_value.~T();
m_has_value = false; m_has_value = false;
} }
} }
template<typename... Parameters> template<typename... Parameters>
ALWAYS_INLINE void emplace(Parameters&&... parameters) ALWAYS_INLINE constexpr void emplace(Parameters&&... parameters)
{ {
clear(); clear();
m_has_value = true; m_has_value = true;
new (&m_storage) T(forward<Parameters>(parameters)...); construct_at(&m_value, forward<Parameters>(parameters)...);
} }
template<typename Callable> template<typename Callable>
ALWAYS_INLINE void lazy_emplace(Callable callable) ALWAYS_INLINE constexpr void lazy_emplace(Callable callable)
{ {
clear(); clear();
m_has_value = true; 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); VERIFY(m_has_value);
return *__builtin_launder(reinterpret_cast<T*>(&m_storage)); return m_value;
} }
[[nodiscard]] ALWAYS_INLINE T const& value() const& [[nodiscard]] ALWAYS_INLINE constexpr T const& value() const&
{ {
VERIFY(m_has_value); VERIFY(m_has_value);
return *__builtin_launder(reinterpret_cast<T const*>(&m_storage)); return m_value;
} }
[[nodiscard]] ALWAYS_INLINE T value() && [[nodiscard]] ALWAYS_INLINE constexpr T value() &&
{ {
return release_value(); return release_value();
} }
[[nodiscard]] ALWAYS_INLINE T release_value() [[nodiscard]] ALWAYS_INLINE constexpr T release_value()
{ {
VERIFY(m_has_value); VERIFY(m_has_value);
T released_value = move(value()); T released_value = move(m_value);
value().~T(); m_value.~T();
m_has_value = false; m_has_value = false;
return released_value; return released_value;
} }
private: private:
alignas(T) u8 m_storage[sizeof(T)]; union {
char m_null;
T m_value;
};
bool m_has_value { false }; bool m_has_value { false };
}; };
@ -345,76 +354,76 @@ requires(IsLvalueReference<T>) class [[nodiscard]] Optional<T> {
public: public:
using ValueType = T; using ValueType = T;
ALWAYS_INLINE Optional() = default; ALWAYS_INLINE constexpr Optional() = default;
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional(V) { } constexpr Optional(V) { }
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional& operator=(V) constexpr Optional& operator=(V)
{ {
clear(); clear();
return *this; return *this;
} }
template<typename U = T> template<typename U = T>
ALWAYS_INLINE Optional(U& value) ALWAYS_INLINE constexpr Optional(U& value)
requires(CanBePlacedInOptional<U&>) requires(CanBePlacedInOptional<U&>)
: m_pointer(&value) : m_pointer(&value)
{ {
} }
ALWAYS_INLINE Optional(RemoveReference<T>& value) ALWAYS_INLINE constexpr Optional(RemoveReference<T>& value)
: m_pointer(&value) : m_pointer(&value)
{ {
} }
ALWAYS_INLINE Optional(Optional const& other) ALWAYS_INLINE constexpr Optional(Optional const& other)
: m_pointer(other.m_pointer) : m_pointer(other.m_pointer)
{ {
} }
ALWAYS_INLINE Optional(Optional&& other) ALWAYS_INLINE constexpr Optional(Optional&& other)
: m_pointer(other.m_pointer) : m_pointer(other.m_pointer)
{ {
other.m_pointer = nullptr; other.m_pointer = nullptr;
} }
template<typename U> template<typename U>
ALWAYS_INLINE Optional(Optional<U>& other) ALWAYS_INLINE constexpr Optional(Optional<U>& other)
requires(CanBePlacedInOptional<U>) requires(CanBePlacedInOptional<U>)
: m_pointer(other.ptr()) : m_pointer(other.ptr())
{ {
} }
template<typename U> template<typename U>
ALWAYS_INLINE Optional(Optional<U> const& other) ALWAYS_INLINE constexpr Optional(Optional<U> const& other)
requires(CanBePlacedInOptional<U const>) requires(CanBePlacedInOptional<U const>)
: m_pointer(other.ptr()) : m_pointer(other.ptr())
{ {
} }
template<typename U> template<typename U>
ALWAYS_INLINE Optional(Optional<U>&& other) ALWAYS_INLINE constexpr Optional(Optional<U>&& other)
requires(CanBePlacedInOptional<U>) requires(CanBePlacedInOptional<U>)
: m_pointer(other.ptr()) : m_pointer(other.ptr())
{ {
other.m_pointer = nullptr; other.m_pointer = nullptr;
} }
ALWAYS_INLINE Optional& operator=(Optional& other) ALWAYS_INLINE constexpr Optional& operator=(Optional& other)
{ {
m_pointer = other.m_pointer; m_pointer = other.m_pointer;
return *this; return *this;
} }
ALWAYS_INLINE Optional& operator=(Optional const& other) ALWAYS_INLINE constexpr Optional& operator=(Optional const& other)
{ {
m_pointer = other.m_pointer; m_pointer = other.m_pointer;
return *this; return *this;
} }
ALWAYS_INLINE Optional& operator=(Optional&& other) ALWAYS_INLINE constexpr Optional& operator=(Optional&& other)
{ {
m_pointer = other.m_pointer; m_pointer = other.m_pointer;
other.m_pointer = nullptr; other.m_pointer = nullptr;
@ -422,7 +431,7 @@ public:
} }
template<typename U> template<typename U>
ALWAYS_INLINE Optional& operator=(Optional<U>& other) ALWAYS_INLINE constexpr Optional& operator=(Optional<U>& other)
requires(CanBePlacedInOptional<U>) requires(CanBePlacedInOptional<U>)
{ {
m_pointer = other.ptr(); m_pointer = other.ptr();
@ -430,7 +439,7 @@ public:
} }
template<typename U> template<typename U>
ALWAYS_INLINE Optional& operator=(Optional<U> const& other) ALWAYS_INLINE constexpr Optional& operator=(Optional<U> const& other)
requires(CanBePlacedInOptional<U const>) requires(CanBePlacedInOptional<U const>)
{ {
m_pointer = other.ptr(); m_pointer = other.ptr();
@ -438,7 +447,7 @@ public:
} }
template<typename U> template<typename U>
ALWAYS_INLINE Optional& operator=(Optional<U>&& other) ALWAYS_INLINE constexpr Optional& operator=(Optional<U>&& other)
requires(CanBePlacedInOptional<U> && IsLvalueReference<U>) requires(CanBePlacedInOptional<U> && IsLvalueReference<U>)
{ {
m_pointer = other.m_pointer; m_pointer = other.m_pointer;
@ -449,44 +458,44 @@ public:
// Note: Disallows assignment from a temporary as this does not do any lifetime extension. // Note: Disallows assignment from a temporary as this does not do any lifetime extension.
template<typename U> template<typename U>
requires(!IsSame<OptionalNone, RemoveCVReference<U>>) requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
ALWAYS_INLINE Optional& operator=(U&& value) ALWAYS_INLINE constexpr Optional& operator=(U&& value)
requires(CanBePlacedInOptional<U> && IsLvalueReference<U>) requires(CanBePlacedInOptional<U> && IsLvalueReference<U>)
{ {
m_pointer = &value; m_pointer = &value;
return *this; return *this;
} }
ALWAYS_INLINE void clear() ALWAYS_INLINE constexpr void clear()
{ {
m_pointer = nullptr; 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<T>* ptr() [[nodiscard]] ALWAYS_INLINE constexpr RemoveReference<T>* ptr()
{ {
return m_pointer; return m_pointer;
} }
[[nodiscard]] ALWAYS_INLINE RemoveReference<T> const* ptr() const [[nodiscard]] ALWAYS_INLINE constexpr RemoveReference<T> const* ptr() const
{ {
return m_pointer; return m_pointer;
} }
[[nodiscard]] ALWAYS_INLINE T value() [[nodiscard]] ALWAYS_INLINE constexpr T value()
{ {
VERIFY(m_pointer); VERIFY(m_pointer);
return *m_pointer; return *m_pointer;
} }
[[nodiscard]] ALWAYS_INLINE AddConstToReferencedType<T> value() const [[nodiscard]] ALWAYS_INLINE constexpr AddConstToReferencedType<T> value() const
{ {
VERIFY(m_pointer); VERIFY(m_pointer);
return *m_pointer; return *m_pointer;
} }
template<typename U> template<typename U>
requires(IsBaseOf<RemoveCVReference<T>, U>) [[nodiscard]] ALWAYS_INLINE AddConstToReferencedType<T> value_or(U& fallback) const requires(IsBaseOf<RemoveCVReference<T>, U>) [[nodiscard]] ALWAYS_INLINE constexpr AddConstToReferencedType<T> value_or(U& fallback) const
{ {
if (m_pointer) if (m_pointer)
return value(); return value();
@ -494,38 +503,38 @@ public:
} }
// Note that this ends up copying the value. // Note that this ends up copying the value.
[[nodiscard]] ALWAYS_INLINE RemoveCVReference<T> value_or(RemoveCVReference<T> fallback) const [[nodiscard]] ALWAYS_INLINE constexpr RemoveCVReference<T> value_or(RemoveCVReference<T> fallback) const
{ {
if (m_pointer) if (m_pointer)
return value(); return value();
return fallback; return fallback;
} }
[[nodiscard]] ALWAYS_INLINE T release_value() [[nodiscard]] ALWAYS_INLINE constexpr T release_value()
{ {
return *exchange(m_pointer, nullptr); return *exchange(m_pointer, nullptr);
} }
template<typename U> template<typename U>
ALWAYS_INLINE bool operator==(Optional<U> const& other) const ALWAYS_INLINE constexpr bool operator==(Optional<U> const& other) const
{ {
return has_value() == other.has_value() && (!has_value() || value() == other.value()); return has_value() == other.has_value() && (!has_value() || value() == other.value());
} }
template<typename U> template<typename U>
ALWAYS_INLINE bool operator==(U const& other) const ALWAYS_INLINE constexpr bool operator==(U const& other) const
{ {
return has_value() && value() == other; return has_value() && value() == other;
} }
ALWAYS_INLINE AddConstToReferencedType<T> operator*() const { return value(); } ALWAYS_INLINE constexpr AddConstToReferencedType<T> operator*() const { return value(); }
ALWAYS_INLINE T operator*() { return value(); } ALWAYS_INLINE constexpr T operator*() { return value(); }
ALWAYS_INLINE RawPtr<AddConst<RemoveReference<T>>> operator->() const { return &value(); } ALWAYS_INLINE constexpr RawPtr<AddConst<RemoveReference<T>>> operator->() const { return &value(); }
ALWAYS_INLINE RawPtr<RemoveReference<T>> operator->() { return &value(); } ALWAYS_INLINE constexpr RawPtr<RemoveReference<T>> operator->() { return &value(); }
// Conversion operators from Optional<T&> -> Optional<T> // Conversion operators from Optional<T&> -> Optional<T>
ALWAYS_INLINE operator Optional<RemoveCVReference<T>>() const ALWAYS_INLINE constexpr operator Optional<RemoveCVReference<T>>() const
{ {
if (has_value()) if (has_value())
return Optional<RemoveCVReference<T>>(value()); return Optional<RemoveCVReference<T>>(value());
@ -533,7 +542,7 @@ public:
} }
template<typename Callback> template<typename Callback>
[[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) if (m_pointer != nullptr)
return value(); return value();
@ -541,7 +550,7 @@ public:
} }
template<typename Callback> template<typename Callback>
[[nodiscard]] ALWAYS_INLINE Optional<T> value_or_lazy_evaluated_optional(Callback callback) const [[nodiscard]] ALWAYS_INLINE constexpr Optional<T> value_or_lazy_evaluated_optional(Callback callback) const
{ {
if (m_pointer != nullptr) if (m_pointer != nullptr)
return value(); return value();
@ -549,7 +558,7 @@ public:
} }
template<typename Callback> template<typename Callback>
[[nodiscard]] ALWAYS_INLINE ErrorOr<T> try_value_or_lazy_evaluated(Callback callback) const [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr<T> try_value_or_lazy_evaluated(Callback callback) const
{ {
if (m_pointer != nullptr) if (m_pointer != nullptr)
return value(); return value();
@ -557,7 +566,7 @@ public:
} }
template<typename Callback> template<typename Callback>
[[nodiscard]] ALWAYS_INLINE ErrorOr<Optional<T>> try_value_or_lazy_evaluated_optional(Callback callback) const [[nodiscard]] ALWAYS_INLINE constexpr ErrorOr<Optional<T>> try_value_or_lazy_evaluated_optional(Callback callback) const
{ {
if (m_pointer != nullptr) if (m_pointer != nullptr)
return value(); return value();
@ -565,7 +574,7 @@ public:
} }
template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>> template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper) ALWAYS_INLINE constexpr Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper)
{ {
if constexpr (IsErrorOr) { if constexpr (IsErrorOr) {
if (m_pointer != nullptr) if (m_pointer != nullptr)
@ -580,7 +589,7 @@ public:
} }
template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>> template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper) const ALWAYS_INLINE constexpr Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper) const
{ {
if constexpr (IsErrorOr) { if constexpr (IsErrorOr) {
if (m_pointer != nullptr) if (m_pointer != nullptr)

View file

@ -10,6 +10,7 @@
#include <AK/Platform.h> #include <AK/Platform.h>
#include <AK/StdLibExtraDetails.h> #include <AK/StdLibExtraDetails.h>
#include <memory>
#include <utility> #include <utility>
namespace AK { namespace AK {
@ -31,6 +32,7 @@ requires(AK::Detail::IsIntegral<T>)
template<typename... Args> template<typename... Args>
void compiletime_fail(Args...); void compiletime_fail(Args...);
using std::construct_at;
using std::forward; using std::forward;
using std::move; using std::move;
} }

View file

@ -236,38 +236,38 @@ class Optional<String> : public OptionalBase<String> {
public: public:
using ValueType = String; using ValueType = String;
Optional() = default; constexpr Optional() = default;
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional(V) { } constexpr Optional(V) { }
Optional(Optional<String> const& other) constexpr Optional(Optional<String> const& other)
{ {
if (other.has_value()) if (other.has_value())
m_value = other.m_value; m_value = other.m_value;
} }
Optional(Optional&& other) constexpr Optional(Optional&& other)
: m_value(move(other.m_value)) : m_value(move(other.m_value))
{ {
} }
template<typename U = String> template<typename U = String>
requires(!IsSame<OptionalNone, RemoveCVReference<U>>) requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
explicit(!IsConvertible<U&&, String>) Optional(U&& value) explicit(!IsConvertible<U&&, String>) constexpr Optional(U&& value)
requires(!IsSame<RemoveCVReference<U>, Optional<String>> && IsConstructible<String, U &&>) requires(!IsSame<RemoveCVReference<U>, Optional<String>> && IsConstructible<String, U &&>)
: m_value(forward<U>(value)) : m_value(forward<U>(value))
{ {
} }
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional& operator=(V) constexpr Optional& operator=(V)
{ {
clear(); clear();
return *this; return *this;
} }
Optional& operator=(Optional const& other) constexpr Optional& operator=(Optional const& other)
{ {
if (this != &other) { if (this != &other) {
m_value = other.m_value; m_value = other.m_value;
@ -275,7 +275,7 @@ public:
return *this; return *this;
} }
Optional& operator=(Optional&& other) constexpr Optional& operator=(Optional&& other)
{ {
if (this != &other) { if (this != &other) {
m_value = move(other.m_value); m_value = move(other.m_value);
@ -284,48 +284,48 @@ public:
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(Optional<O> const& other) const ALWAYS_INLINE constexpr bool operator==(Optional<O> const& other) const
{ {
return has_value() == other.has_value() && (!has_value() || value() == other.value()); return has_value() == other.has_value() && (!has_value() || value() == other.value());
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(O const& other) const ALWAYS_INLINE constexpr bool operator==(O const& other) const
{ {
return has_value() && value() == other; return has_value() && value() == other;
} }
void clear() constexpr void clear()
{ {
m_value = String(nullptr); m_value = String(nullptr);
} }
[[nodiscard]] bool has_value() const [[nodiscard]] constexpr bool has_value() const
{ {
return !m_value.is_invalid(); return !m_value.is_invalid();
} }
[[nodiscard]] String& value() & [[nodiscard]] constexpr String& value() &
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;
} }
[[nodiscard]] String const& value() const& [[nodiscard]] constexpr String const& value() const&
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;
} }
[[nodiscard]] String value() && [[nodiscard]] constexpr String value() &&
{ {
return release_value(); return release_value();
} }
[[nodiscard]] String release_value() [[nodiscard]] constexpr String release_value()
{ {
VERIFY(has_value()); VERIFY(has_value());
String released_value = m_value; String released_value = move(m_value);
clear(); clear();
return released_value; return released_value;
} }

View file

@ -164,7 +164,7 @@ class Optional<JS::Completion> : public OptionalBase<JS::Completion> {
public: public:
using ValueType = JS::Completion; using ValueType = JS::Completion;
Optional() = default; constexpr Optional() = default;
Optional(Optional<JS::Completion> const& other) Optional(Optional<JS::Completion> const& other)
{ {
@ -172,19 +172,19 @@ public:
m_value = other.m_value; m_value = other.m_value;
} }
Optional(Optional&& other) constexpr Optional(Optional&& other)
: m_value(move(other.m_value)) : m_value(move(other.m_value))
{ {
} }
template<typename U = JS::Completion> template<typename U = JS::Completion>
explicit(!IsConvertible<U&&, JS::Completion>) Optional(U&& value) explicit(!IsConvertible<U&&, JS::Completion>) constexpr Optional(U&& value)
requires(!IsSame<RemoveCVReference<U>, Optional<JS::Completion>> && IsConstructible<JS::Completion, U &&>) requires(!IsSame<RemoveCVReference<U>, Optional<JS::Completion>> && IsConstructible<JS::Completion, U &&>)
: m_value(forward<U>(value)) : m_value(forward<U>(value))
{ {
} }
Optional& operator=(Optional const& other) constexpr Optional& operator=(Optional const& other)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
@ -193,7 +193,7 @@ public:
return *this; return *this;
} }
Optional& operator=(Optional&& other) constexpr Optional& operator=(Optional&& other)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
@ -207,18 +207,18 @@ public:
m_value = JS::Completion(JS::Completion::EmptyTag {}); m_value = JS::Completion(JS::Completion::EmptyTag {});
} }
[[nodiscard]] bool has_value() const [[nodiscard]] constexpr bool has_value() const
{ {
return !m_value.is_empty(); return !m_value.is_empty();
} }
[[nodiscard]] JS::Completion& value() & [[nodiscard]] constexpr JS::Completion& value() &
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;
} }
[[nodiscard]] JS::Completion const& value() const& [[nodiscard]] constexpr JS::Completion const& value() const&
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;

View file

@ -154,7 +154,7 @@ public:
return !is_nan() && !is_infinity(); return !is_nan() && !is_infinity();
} }
Value() constexpr Value()
: Value(EMPTY_TAG << GC::TAG_SHIFT, (u64)0) : Value(EMPTY_TAG << GC::TAG_SHIFT, (u64)0)
{ {
} }
@ -419,7 +419,7 @@ private:
ThrowCompletionOr<Value> to_numeric_slow_case(VM&) const; ThrowCompletionOr<Value> to_numeric_slow_case(VM&) const;
ThrowCompletionOr<Value> to_primitive_slow_case(VM&, PreferredType) const; ThrowCompletionOr<Value> to_primitive_slow_case(VM&, PreferredType) const;
Value(u64 tag, u64 val) constexpr Value(u64 tag, u64 val)
{ {
ASSERT(!(tag & val)); ASSERT(!(tag & val));
m_value.encoded = tag | val; m_value.encoded = tag | val;
@ -544,10 +544,10 @@ class Optional<JS::Value> : public OptionalBase<JS::Value> {
public: public:
using ValueType = JS::Value; using ValueType = JS::Value;
Optional() = default; constexpr Optional() = default;
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional(V) { } constexpr Optional(V) { }
Optional(Optional<JS::Value> const& other) Optional(Optional<JS::Value> const& other)
{ {
@ -555,27 +555,27 @@ public:
m_value = other.m_value; m_value = other.m_value;
} }
Optional(Optional&& other) constexpr Optional(Optional&& other)
: m_value(other.m_value) : m_value(other.m_value)
{ {
} }
template<typename U = JS::Value> template<typename U = JS::Value>
requires(!IsSame<OptionalNone, RemoveCVReference<U>>) requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
explicit(!IsConvertible<U&&, JS::Value>) Optional(U&& value) explicit(!IsConvertible<U&&, JS::Value>) constexpr Optional(U&& value)
requires(!IsSame<RemoveCVReference<U>, Optional<JS::Value>> && IsConstructible<JS::Value, U &&>) requires(!IsSame<RemoveCVReference<U>, Optional<JS::Value>> && IsConstructible<JS::Value, U &&>)
: m_value(forward<U>(value)) : m_value(forward<U>(value))
{ {
} }
template<SameAs<OptionalNone> V> template<SameAs<OptionalNone> V>
Optional& operator=(V) constexpr Optional& operator=(V)
{ {
clear(); clear();
return *this; return *this;
} }
Optional& operator=(Optional const& other) constexpr Optional& operator=(Optional const& other)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
@ -584,7 +584,7 @@ public:
return *this; return *this;
} }
Optional& operator=(Optional&& other) constexpr Optional& operator=(Optional&& other)
{ {
if (this != &other) { if (this != &other) {
clear(); clear();
@ -594,34 +594,34 @@ public:
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(Optional<O> const& other) const ALWAYS_INLINE constexpr bool operator==(Optional<O> const& other) const
{ {
return has_value() == other.has_value() && (!has_value() || value() == other.value()); return has_value() == other.has_value() && (!has_value() || value() == other.value());
} }
template<typename O> template<typename O>
ALWAYS_INLINE bool operator==(O const& other) const ALWAYS_INLINE constexpr bool operator==(O const& other) const
{ {
return has_value() && value() == other; return has_value() && value() == other;
} }
void clear() constexpr void clear()
{ {
m_value = {}; m_value = {};
} }
[[nodiscard]] bool has_value() const [[nodiscard]] constexpr bool has_value() const
{ {
return !m_value.is_empty(); return !m_value.is_empty();
} }
[[nodiscard]] JS::Value& value() & [[nodiscard]] constexpr JS::Value& value() &
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;
} }
[[nodiscard]] JS::Value const& value() const& [[nodiscard]] constexpr JS::Value const& value() const&
{ {
VERIFY(has_value()); VERIFY(has_value());
return m_value; return m_value;

View file

@ -130,6 +130,9 @@ bool assume(T const& expression, StringView expression_string, SourceLocation lo
return true; return true;
} }
template<typename T>
consteval void expect_consteval(T) { }
} }
#define EXPECT(x) \ #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); \ ::Test::set_current_test_result(::Test::TestResult::Failed); \
} while (false) } 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: // To use, specify the lambda to execute in a sub process and verify it exits:
// EXPECT_CRASH("This should fail", []{ // EXPECT_CRASH("This should fail", []{
// return Test::Crash::Failure::DidNotCrash; // return Test::Crash::Failure::DidNotCrash;

View file

@ -706,7 +706,6 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
return; return;
bool should_clip_overflow = computed_values().overflow_x() != CSS::Overflow::Visible && computed_values().overflow_y() != CSS::Overflow::Visible; bool should_clip_overflow = computed_values().overflow_x() != CSS::Overflow::Visible && computed_values().overflow_y() != CSS::Overflow::Visible;
Optional<u32> corner_clip_id;
auto clip_box = absolute_padding_box_rect(); auto clip_box = absolute_padding_box_rect();
if (get_clip_rect().has_value()) { if (get_clip_rect().has_value()) {

View file

@ -13,6 +13,17 @@
#include <AK/String.h> #include <AK/String.h>
#include <AK/Vector.h> #include <AK/Vector.h>
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) TEST_CASE(basic_optional)
{ {
Optional<int> x; Optional<int> x;
@ -39,17 +50,6 @@ TEST_CASE(move_optional)
TEST_CASE(optional_rvalue_ref_qualified_getters) 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<DontCopyMe> { auto make_an_optional = []() -> Optional<DontCopyMe> {
return DontCopyMe {}; return DontCopyMe {};
}; };
@ -122,6 +122,54 @@ TEST_CASE(comparison_with_numeric_types)
EXPECT_NE(opt1, -2); EXPECT_NE(opt1, -2);
} }
TEST_CASE(test_constexpr)
{
int i = 13;
DontCopyMe dcm {};
EXPECT_CONSTEVAL(Optional<int> {});
EXPECT_CONSTEVAL(Optional<DontCopyMe> {});
EXPECT_CONSTEVAL(Optional<int const> {});
EXPECT_CONSTEVAL(Optional<DontCopyMe const> {});
EXPECT_CONSTEVAL(Optional<int&> {});
EXPECT_CONSTEVAL(Optional<DontCopyMe&> {});
EXPECT_CONSTEVAL(Optional<int const&> {});
EXPECT_CONSTEVAL(Optional<DontCopyMe const&> {});
EXPECT_CONSTEVAL(Optional<int> { 13 });
EXPECT_CONSTEVAL(Optional<DontCopyMe> { DontCopyMe {} });
EXPECT_CONSTEVAL(Optional<int const> { 13 });
EXPECT_CONSTEVAL(Optional<DontCopyMe const> { DontCopyMe {} });
EXPECT_CONSTEVAL(Optional<int&> { i });
EXPECT_CONSTEVAL(Optional<DontCopyMe&> { dcm });
EXPECT_CONSTEVAL(Optional<int const&> { 13 });
EXPECT_CONSTEVAL(Optional<DontCopyMe const&> { DontCopyMe {} });
static_assert(!Optional<int> {}.has_value());
static_assert(!Optional<DontCopyMe> {}.has_value());
static_assert(!Optional<int const> {}.has_value());
static_assert(!Optional<DontCopyMe const> {}.has_value());
static_assert(!Optional<int&> {}.has_value());
static_assert(!Optional<DontCopyMe&> {}.has_value());
static_assert(!Optional<int const&> {}.has_value());
static_assert(!Optional<DontCopyMe const&> {}.has_value());
static_assert(Optional<int> { 13 }.has_value());
static_assert(Optional<DontCopyMe> { DontCopyMe {} }.has_value());
static_assert(Optional<int const> { 13 }.has_value());
static_assert(Optional<DontCopyMe const> { DontCopyMe {} }.has_value());
static_assert(Optional<int&> { i }.has_value());
static_assert(Optional<DontCopyMe&> { dcm }.has_value());
static_assert(Optional<int const&> { 13 }.has_value());
static_assert(Optional<DontCopyMe const&> { DontCopyMe {} }.has_value());
static_assert(Optional<int> { 13 }.value() == 13);
static_assert(Optional<DontCopyMe> { DontCopyMe {} }.value().x == 13);
static_assert(Optional<int const> { 13 }.value() == 13);
static_assert(Optional<int const&> { 13 }.value() == 13);
static_assert(Optional<DontCopyMe const&> { DontCopyMe {} }.value().x == 13);
}
TEST_CASE(test_copy_ctor_and_dtor_called) TEST_CASE(test_copy_ctor_and_dtor_called)
{ {
#ifdef AK_HAVE_CONDITIONALLY_TRIVIAL #ifdef AK_HAVE_CONDITIONALLY_TRIVIAL

View file

@ -11,6 +11,7 @@ list(APPEND CLANG_PLUGINS_COMPILE_OPTIONS_FOR_TESTS
-Wno-literal-range -Wno-literal-range
-Wno-unknown-warning-option -Wno-unknown-warning-option
-Wno-unqualified-std-cast-call -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 # Ensure we always check for invalid function field types regardless of the value of ENABLE_CLANG_PLUGINS_INVALID_FUNCTION_MEMBERS