This commit is contained in:
Jonne Ransijn 2024-11-20 16:09:04 -05:00 committed by GitHub
commit d538490d80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 226 additions and 162 deletions

View file

@ -21,7 +21,7 @@ class FlyString {
AK_MAKE_DEFAULT_COPYABLE(FlyString);
public:
FlyString() = default;
constexpr FlyString() = default;
static ErrorOr<FlyString> from_utf8(StringView);
static FlyString from_utf8_without_validation(ReadonlyBytes);
@ -83,7 +83,7 @@ public:
private:
friend class Optional<FlyString>;
explicit FlyString(nullptr_t)
explicit constexpr FlyString(nullptr_t)
: m_data(Detail::StringBase(nullptr))
{
}
@ -106,38 +106,38 @@ class Optional<FlyString> : public OptionalBase<FlyString> {
public:
using ValueType = FlyString;
Optional() = default;
constexpr Optional() = default;
template<SameAs<OptionalNone> V>
Optional(V) { }
constexpr Optional(V) { }
Optional(Optional<FlyString> const& other)
constexpr Optional(Optional<FlyString> 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<typename U = FlyString>
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 &&>)
: m_value(forward<U>(value))
{
}
template<SameAs<OptionalNone> 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<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());
}
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;
}
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();
}

View file

@ -46,7 +46,7 @@ template<typename>
class Optional;
struct OptionalNone {
explicit OptionalNone() = default;
explicit constexpr OptionalNone() = default;
};
template<typename T, typename Self = Optional<T>>
@ -55,30 +55,30 @@ public:
using ValueType = T;
template<SameAs<OptionalNone> V>
Self& operator=(V)
constexpr Self& operator=(V)
{
static_cast<Self&>(*this).clear();
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;
}
[[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;
}
[[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())
return static_cast<Self const&>(*this).value();
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())
return move(static_cast<Self&>(*this).value());
@ -86,7 +86,7 @@ public:
}
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())
return static_cast<Self const&>(*this).value();
@ -94,7 +94,7 @@ public:
}
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())
return static_cast<Self const&>(*this).value();
@ -102,7 +102,7 @@ public:
}
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())
return static_cast<Self const&>(*this).value();
@ -110,7 +110,7 @@ public:
}
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())
return static_cast<Self const&>(*this).value();
@ -118,7 +118,7 @@ public:
}
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()
&& (!static_cast<Self const&>(*this).has_value() || static_cast<Self const&>(*this).value() == (other).value());
@ -126,19 +126,19 @@ public:
template<typename 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;
}
[[nodiscard]] ALWAYS_INLINE 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 const& operator*() const { return static_cast<Self const&>(*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 T* operator->() { return &static_cast<Self&>(*this).value(); }
ALWAYS_INLINE constexpr T const* operator->() const { return &static_cast<Self const&>(*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>>>
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 (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>>>
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 (static_cast<Self const&>(*this).has_value())
@ -178,13 +178,19 @@ requires(!IsLvalueReference<T>) class [[nodiscard]] Optional<T> : public Optiona
public:
using ValueType = T;
ALWAYS_INLINE Optional() = default;
ALWAYS_INLINE constexpr Optional()
: m_null()
{
}
template<SameAs<OptionalNone> V>
Optional(V) { }
constexpr Optional(V)
: m_null()
{
}
template<SameAs<OptionalNone> V>
Optional& operator=(V)
constexpr Optional& operator=(V)
{
clear();
return *this;
@ -194,143 +200,146 @@ public:
AK_MAKE_CONDITIONALLY_NONMOVABLE(Optional, <T>);
AK_MAKE_CONDITIONALLY_DESTRUCTIBLE(Optional, <T>);
ALWAYS_INLINE Optional(Optional const& other)
ALWAYS_INLINE constexpr Optional(Optional const& other)
requires(!IsTriviallyCopyConstructible<T>)
: 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<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())
{
if (other.has_value())
new (&m_storage) T(other.value());
construct_at(&m_value, other.value());
}
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())
{
if (other.has_value())
new (&m_storage) T(other.release_value());
construct_at(&m_value, other.value());
}
template<typename U = T>
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 &&>)
: 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>)
{
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<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());
}
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;
}
ALWAYS_INLINE ~Optional()
ALWAYS_INLINE constexpr ~Optional()
requires(!IsTriviallyDestructible<T> && IsDestructible<T>)
{
clear();
}
ALWAYS_INLINE void clear()
ALWAYS_INLINE constexpr void clear()
{
if (m_has_value) {
value().~T();
m_value.~T();
m_has_value = false;
}
}
template<typename... Parameters>
ALWAYS_INLINE void emplace(Parameters&&... parameters)
ALWAYS_INLINE constexpr void emplace(Parameters&&... parameters)
{
clear();
m_has_value = true;
new (&m_storage) T(forward<Parameters>(parameters)...);
construct_at(&m_value, forward<Parameters>(parameters)...);
}
template<typename Callable>
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<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);
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();
}
[[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<T>) class [[nodiscard]] Optional<T> {
public:
using ValueType = T;
ALWAYS_INLINE Optional() = default;
ALWAYS_INLINE constexpr Optional() = default;
template<SameAs<OptionalNone> V>
Optional(V) { }
constexpr Optional(V) { }
template<SameAs<OptionalNone> V>
Optional& operator=(V)
constexpr Optional& operator=(V)
{
clear();
return *this;
}
template<typename U = T>
ALWAYS_INLINE Optional(U& value)
ALWAYS_INLINE constexpr Optional(U& value)
requires(CanBePlacedInOptional<U&>)
: m_pointer(&value)
{
}
ALWAYS_INLINE Optional(RemoveReference<T>& value)
ALWAYS_INLINE constexpr Optional(RemoveReference<T>& 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<typename U>
ALWAYS_INLINE Optional(Optional<U>& other)
ALWAYS_INLINE constexpr Optional(Optional<U>& other)
requires(CanBePlacedInOptional<U>)
: m_pointer(other.ptr())
{
}
template<typename U>
ALWAYS_INLINE Optional(Optional<U> const& other)
ALWAYS_INLINE constexpr Optional(Optional<U> const& other)
requires(CanBePlacedInOptional<U const>)
: m_pointer(other.ptr())
{
}
template<typename U>
ALWAYS_INLINE Optional(Optional<U>&& other)
ALWAYS_INLINE constexpr Optional(Optional<U>&& other)
requires(CanBePlacedInOptional<U>)
: 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<typename U>
ALWAYS_INLINE Optional& operator=(Optional<U>& other)
ALWAYS_INLINE constexpr Optional& operator=(Optional<U>& other)
requires(CanBePlacedInOptional<U>)
{
m_pointer = other.ptr();
@ -430,7 +439,7 @@ public:
}
template<typename U>
ALWAYS_INLINE Optional& operator=(Optional<U> const& other)
ALWAYS_INLINE constexpr Optional& operator=(Optional<U> const& other)
requires(CanBePlacedInOptional<U const>)
{
m_pointer = other.ptr();
@ -438,7 +447,7 @@ public:
}
template<typename U>
ALWAYS_INLINE Optional& operator=(Optional<U>&& other)
ALWAYS_INLINE constexpr Optional& operator=(Optional<U>&& other)
requires(CanBePlacedInOptional<U> && IsLvalueReference<U>)
{
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<typename U>
requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
ALWAYS_INLINE Optional& operator=(U&& value)
ALWAYS_INLINE constexpr Optional& operator=(U&& value)
requires(CanBePlacedInOptional<U> && IsLvalueReference<U>)
{
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<T>* ptr()
[[nodiscard]] ALWAYS_INLINE constexpr RemoveReference<T>* ptr()
{
return m_pointer;
}
[[nodiscard]] ALWAYS_INLINE RemoveReference<T> const* ptr() const
[[nodiscard]] ALWAYS_INLINE constexpr RemoveReference<T> 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<T> value() const
[[nodiscard]] ALWAYS_INLINE constexpr AddConstToReferencedType<T> value() const
{
VERIFY(m_pointer);
return *m_pointer;
}
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)
return value();
@ -494,38 +503,38 @@ public:
}
// 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)
return value();
return fallback;
}
[[nodiscard]] ALWAYS_INLINE T release_value()
[[nodiscard]] ALWAYS_INLINE constexpr T release_value()
{
return *exchange(m_pointer, nullptr);
}
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());
}
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;
}
ALWAYS_INLINE AddConstToReferencedType<T> operator*() const { return value(); }
ALWAYS_INLINE T operator*() { return value(); }
ALWAYS_INLINE constexpr AddConstToReferencedType<T> operator*() const { return value(); }
ALWAYS_INLINE constexpr T operator*() { return value(); }
ALWAYS_INLINE RawPtr<AddConst<RemoveReference<T>>> operator->() const { return &value(); }
ALWAYS_INLINE RawPtr<RemoveReference<T>> operator->() { return &value(); }
ALWAYS_INLINE constexpr RawPtr<AddConst<RemoveReference<T>>> operator->() const { return &value(); }
ALWAYS_INLINE constexpr RawPtr<RemoveReference<T>> operator->() { return &value(); }
// 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())
return Optional<RemoveCVReference<T>>(value());
@ -533,7 +542,7 @@ public:
}
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)
return value();
@ -541,7 +550,7 @@ public:
}
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)
return value();
@ -549,7 +558,7 @@ public:
}
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)
return value();
@ -557,7 +566,7 @@ public:
}
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)
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>>>
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 (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>>>
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 (m_pointer != nullptr)

View file

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

View file

@ -236,38 +236,38 @@ class Optional<String> : public OptionalBase<String> {
public:
using ValueType = String;
Optional() = default;
constexpr Optional() = default;
template<SameAs<OptionalNone> V>
Optional(V) { }
constexpr Optional(V) { }
Optional(Optional<String> const& other)
constexpr Optional(Optional<String> 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<typename U = String>
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 &&>)
: m_value(forward<U>(value))
{
}
template<SameAs<OptionalNone> 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<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());
}
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;
}
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;
}

View file

@ -164,7 +164,7 @@ class Optional<JS::Completion> : public OptionalBase<JS::Completion> {
public:
using ValueType = JS::Completion;
Optional() = default;
constexpr Optional() = default;
Optional(Optional<JS::Completion> 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<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 &&>)
: m_value(forward<U>(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;

View file

@ -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<Value> to_numeric_slow_case(VM&) const;
ThrowCompletionOr<Value> 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<JS::Value> : public OptionalBase<JS::Value> {
public:
using ValueType = JS::Value;
Optional() = default;
constexpr Optional() = default;
template<SameAs<OptionalNone> V>
Optional(V) { }
constexpr Optional(V) { }
Optional(Optional<JS::Value> 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<typename U = JS::Value>
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 &&>)
: m_value(forward<U>(value))
{
}
template<SameAs<OptionalNone> 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<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());
}
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;
}
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;

View file

@ -130,6 +130,9 @@ bool assume(T const& expression, StringView expression_string, SourceLocation lo
return true;
}
template<typename T>
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;

View file

@ -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<u32> corner_clip_id;
auto clip_box = absolute_padding_box_rect();
if (get_clip_rect().has_value()) {

View file

@ -13,6 +13,17 @@
#include <AK/String.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)
{
Optional<int> 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<DontCopyMe> {
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<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)
{
#ifdef AK_HAVE_CONDITIONALLY_TRIVIAL

View file

@ -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