AK+LibGfx: Prefer if consteval over is_constant_evaluated

This is a new language construct in C++23, meant to generally replace
is_constant_evaluated. See:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1938r3.html#problems-with-status-quo
This commit is contained in:
Timothy Flynn
2026-02-22 11:23:12 -05:00
committed by Tim Flynn
parent d2f10c76fd
commit 6bc66fe786
Notes: github-actions[bot] 2026-02-22 18:57:04 +00:00
9 changed files with 75 additions and 68 deletions

View File

@@ -188,7 +188,18 @@ ALWAYS_INLINE constexpr Word extend_sign(bool sign)
template<typename WordType>
ALWAYS_INLINE constexpr WordType add_words(WordType word1, WordType word2, bool& carry)
{
if (!is_constant_evaluated()) {
if consteval {
// Note: This is usually too confusing for both GCC and Clang.
WordType output;
bool ncarry = __builtin_add_overflow(word1, word2, &output);
if (carry) {
++output;
if (output == 0)
ncarry = true;
}
carry = ncarry;
return output;
} else {
#if __has_builtin(__builtin_addc)
WordType ncarry, output;
if constexpr (SameAs<WordType, unsigned int>)
@@ -215,22 +226,23 @@ ALWAYS_INLINE constexpr WordType add_words(WordType word1, WordType word2, bool&
}
#endif
}
// Note: This is usually too confusing for both GCC and Clang.
WordType output;
bool ncarry = __builtin_add_overflow(word1, word2, &output);
if (carry) {
++output;
if (output == 0)
ncarry = true;
}
carry = ncarry;
return output;
}
template<typename WordType>
ALWAYS_INLINE constexpr WordType sub_words(WordType word1, WordType word2, bool& carry)
{
if (!is_constant_evaluated()) {
if consteval {
// Note: This is usually too confusing for both GCC and Clang.
WordType output;
bool ncarry = __builtin_sub_overflow(word1, word2, &output);
if (carry) {
if (output == 0)
ncarry = true;
--output;
}
carry = ncarry;
return output;
} else {
#if __has_builtin(__builtin_subc)
WordType ncarry, output;
if constexpr (SameAs<WordType, unsigned int>)
@@ -257,16 +269,6 @@ ALWAYS_INLINE constexpr WordType sub_words(WordType word1, WordType word2, bool&
}
#endif
}
// Note: This is usually too confusing for both GCC and Clang.
WordType output;
bool ncarry = __builtin_sub_overflow(word1, word2, &output);
if (carry) {
if (output == 0)
ncarry = true;
--output;
}
carry = ncarry;
return output;
}
template<typename WordType>

View File

@@ -65,12 +65,12 @@ constexpr T to_degrees(T radians)
}
#define CONSTEXPR_STATE(function, args...) \
if (is_constant_evaluated()) { \
if (IsSame<T, long double>) \
if consteval { \
if constexpr (IsSame<T, long double>) \
return __builtin_##function##l(args); \
if (IsSame<T, double>) \
if constexpr (IsSame<T, double>) \
return __builtin_##function(args); \
if (IsSame<T, float>) \
if constexpr (IsSame<T, float>) \
return __builtin_##function##f(args); \
}
@@ -110,7 +110,7 @@ template<FloatingPoint T>
constexpr T ceil(T num)
{
// FIXME: SSE4.1 rounds[sd] num, res, 0b110
if (is_constant_evaluated()) {
if consteval {
if (num < NumericLimits<i64>::min() || num > NumericLimits<i64>::max())
return num;
return (static_cast<T>(static_cast<i64>(num)) == num)
@@ -133,7 +133,7 @@ template<FloatingPoint T>
constexpr T floor(T num)
{
// FIXME: SSE4.1 rounds[sd] num, res, 0b101
if (is_constant_evaluated()) {
if consteval {
if (num < NumericLimits<i64>::min() || num > NumericLimits<i64>::max())
return num;
return (static_cast<T>(static_cast<i64>(num)) == num)
@@ -156,7 +156,7 @@ template<FloatingPoint T>
constexpr T trunc(T num)
{
#if ARCH(AARCH64)
if (is_constant_evaluated()) {
if consteval {
if (num < NumericLimits<i64>::min() || num > NumericLimits<i64>::max())
return num;
return static_cast<T>(static_cast<i64>(num));
@@ -601,7 +601,7 @@ constexpr T cos(T angle)
template<FloatingPoint T>
constexpr void sincos(T angle, T& sin_val, T& cos_val)
{
if (is_constant_evaluated()) {
if consteval {
sin_val = sin(angle);
cos_val = cos(angle);
return;

View File

@@ -376,22 +376,23 @@ public:
}
private:
ALWAYS_INLINE constexpr void construct_null_if_necessary(bool should_construct = is_constant_evaluated())
ALWAYS_INLINE constexpr void construct_null_if_necessary()
{
// OPTIMIZATION: Only construct the `m_null` member when we are constant-evaluating.
// Otherwise, this generates an unnecessary zero-fill.
// OPTIMIZATION: Only construct the `m_null` member when we are constant-evaluating. Otherwise, this generates
// an unnecessary zero-fill.
#if defined(AK_COMPILER_GCC)
// NOTE: GCCs -Wuninitialized warning ends up checking this as well.
should_construct = true;
#endif
if (should_construct)
// GCC's -Wuninitialized warning ends up checking this as well.
construct_at(&m_null);
#else
if consteval {
construct_at(&m_null);
}
#endif
}
union {
// FIXME: GCC seems to have an issue with uninitialized unions and non trivial types,
// which forces us to have an equally sized trivial null member in the union
// to pseudo-initialize the union.
// FIXME: GCC seems to have an issue with uninitialized unions and non trivial types, which forces us to have an
// equally sized trivial null member in the union to pseudo-initialize the union.
struct {
u8 _[sizeof(T)];
} m_null;

View File

@@ -122,20 +122,11 @@ requires(IsEnum<V>)
return static_cast<UnderlyingType<V>>(value);
}
constexpr bool is_constant_evaluated()
{
#if __has_builtin(__builtin_is_constant_evaluated)
return __builtin_is_constant_evaluated();
#else
return false;
#endif
}
template<typename T>
ALWAYS_INLINE constexpr void taint_for_optimizer(T& value)
requires(IsIntegral<T>)
{
if (!is_constant_evaluated()) {
if !consteval {
asm volatile(""
: "+r"(value));
}
@@ -145,7 +136,7 @@ template<typename T>
ALWAYS_INLINE constexpr void taint_for_optimizer(T& value)
requires(!IsIntegral<T>)
{
if (!is_constant_evaluated()) {
if !consteval {
asm volatile(""
:
: "m"(value)
@@ -158,9 +149,11 @@ requires(!IsIntegral<T>)
#define __DEFINE_GENERIC_ABS(type, zero, intrinsic) \
constexpr type abs(type num) \
{ \
if (is_constant_evaluated()) \
if consteval { \
return num < (zero) ? -num : num; \
} else { \
return __builtin_##intrinsic(num); \
} \
}
__DEFINE_GENERIC_ABS(int, 0, abs);
@@ -180,7 +173,6 @@ using AK::ceil_div;
using AK::clamp;
using AK::exchange;
using AK::forward;
using AK::is_constant_evaluated;
using AK::is_power_of_two;
using AK::max;
using AK::min;

View File

@@ -59,17 +59,20 @@ public:
constexpr ~StringBase()
{
if (!is_constant_evaluated())
if !consteval {
destroy_string();
}
}
// NOTE: This is primarily interesting to unit tests.
[[nodiscard]] constexpr bool is_short_string() const
{
if (is_constant_evaluated())
if consteval {
return (m_impl.short_string.byte_count_and_short_string_flag & SHORT_STRING_FLAG) != 0;
} else {
return (short_string_without_union_member_assertion().byte_count_and_short_string_flag & SHORT_STRING_FLAG) != 0;
}
}
// Returns the underlying UTF-8 encoded bytes.
// NOTE: There is no guarantee about null-termination.

View File

@@ -27,9 +27,10 @@ public:
: m_characters(characters)
, m_length(length)
{
if (!is_constant_evaluated())
if !consteval {
VERIFY(!Checked<uintptr_t>::addition_would_overflow(reinterpret_cast<uintptr_t>(characters), length));
}
}
ALWAYS_INLINE StringView(unsigned char const* characters, size_t length)
: m_characters(reinterpret_cast<char const*>(characters))
@@ -67,8 +68,9 @@ public:
constexpr char const& operator[](size_t index) const
{
if (!is_constant_evaluated())
if !consteval {
VERIFY(index < m_length);
}
return m_characters[index];
}
@@ -116,15 +118,17 @@ public:
[[nodiscard]] constexpr StringView substring_view(size_t start, size_t length) const
{
if (!is_constant_evaluated())
if !consteval {
VERIFY(start + length <= m_length);
}
return { m_characters + start, length };
}
[[nodiscard]] constexpr StringView substring_view(size_t start) const
{
if (!is_constant_evaluated())
if !consteval {
VERIFY(start <= length());
}
return substring_view(start, length() - start);
}

View File

@@ -60,10 +60,12 @@ unsigned day_of_week(int year, unsigned month, int day);
// can be negative.
constexpr int day_of_year(int year, unsigned month, int day)
{
if (is_constant_evaluated())
if consteval {
VERIFY(month >= 1 && month <= 12); // Note that this prevents bad constexpr months, but never actually prints anything.
else if (!(month >= 1 && month <= 12))
} else {
if (!(month >= 1 && month <= 12))
return 0;
}
constexpr Array seek_table = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
int day_of_year = seek_table[month - 1] + day - 1;

View File

@@ -49,9 +49,10 @@ public:
constexpr ~Utf16StringBase()
{
if (!is_constant_evaluated())
if !consteval {
destroy_string();
}
}
ALWAYS_INLINE operator Utf16View() const& LIFETIME_BOUND { return utf16_view(); }
explicit operator Utf16View() const&& = delete;
@@ -278,10 +279,12 @@ public:
// This is primarily interesting to unit tests.
[[nodiscard]] constexpr bool has_short_ascii_storage() const
{
if (is_constant_evaluated())
if consteval {
return (m_value.short_ascii_string.byte_count_and_short_string_flag & StringBase::SHORT_STRING_FLAG) != 0;
} else {
return (short_ascii_string_without_union_member_assertion().byte_count_and_short_string_flag & StringBase::SHORT_STRING_FLAG) != 0;
}
}
// This is primarily interesting to unit tests.
[[nodiscard]] ALWAYS_INLINE bool has_long_ascii_storage() const

View File

@@ -46,7 +46,7 @@ public:
constexpr Matrix& operator=(Matrix const& other)
{
#ifndef __clang__
if (is_constant_evaluated()) {
if consteval {
for (size_t i = 0; i < N; i++) {
for (size_t j = 0; j < N; j++) {
(*this)[i, j] = other[i, j];