commit 9c6d843b1dc094b677f052ddd9e6c4e9f39c87cc Author: Yijia Huang Date: 2025-02-13T10:08:47-08:00 [WTF] Refactor EnumTraits to improve constexpr evaluation and handling of signed enums https://bugs.webkit.org/show_bug.cgi?id=287370 rdar://144487521 Reviewed by Keith Miller. This patch: 1. Removed unnecessary forConstexpr, replacing it with std::index_sequence for better compile-time performance. 2. Improved handling of signed enums by introducing enumNamesMin and enumNamesMax, ensuring min and max values are within the valid range of the underlying type. 3. Optimized the computation of enumNamesSize to prevent overflow and ensure correctness for signed and unsigned enums. 4. Updated enumName lookup logic to correctly compute indices for signed enums using unsigned arithmetic. 5. Added new test cases to cover signed and unsigned enums, including cases with negative values, large gaps, and out-of-range lookups. This change ensures robust handling of enums with various underlying types, prevents unexpected overflows, and enhances overall constexpr evaluation efficiency. * Source/WTF/wtf/EnumTraits.h: (WTF::enumName): (WTF::enumNames): Deleted. * Tools/TestWebKitAPI/Tests/WTF/EnumTraits.cpp: (TestWebKitAPI::TEST(WTF_EnumTraits, EnumNameArgument)): Canonical link: https://commits.webkit.org/290345@main diff --git Source/WTF/wtf/EnumTraits.h Source/WTF/wtf/EnumTraits.h index 9a9b53eb9ff9..c8d1c9a5b0be 100644 --- Source/WTF/wtf/EnumTraits.h +++ Source/WTF/wtf/EnumTraits.h @@ -215,38 +215,78 @@ constexpr std::span enumName() return result; } -namespace detail { - -template -constexpr void forConstexpr(const auto& func) +template +constexpr std::underlying_type_t enumNamesMin() { - if constexpr (i < end) { - func(std::integral_constant()); - forConstexpr(func); - } + using Underlying = std::underlying_type_t; + + if constexpr (requires { { EnumTraits::min } -> std::same_as; }) + return EnumTraits::min; + + // Default for both signed and unsigned enums. + return 0; } +template +constexpr std::underlying_type_t enumNamesMax() +{ + using Underlying = std::underlying_type_t; + + if constexpr (requires { { EnumTraits::max } -> std::same_as; }) + return EnumTraits::max; + + constexpr Underlying defaultMax = std::is_signed_v ? INT8_MAX : UINT8_MAX; + constexpr Underlying computedMax = (sizeof(E) > 1) ? static_cast(defaultMax << 1) : defaultMax; + return computedMax; } -template -constexpr std::array, limit> enumNames() +template +constexpr size_t enumNamesSize() { - std::array, limit> names; + using Underlying = std::underlying_type_t; + using Unsigned = std::make_unsigned_t; - detail::forConstexpr<0, limit>([&] (auto i) { - names[i] = enumName(static_cast(i))>(); - }); - return names; + constexpr Underlying min = enumNamesMin(); + constexpr Underlying max = enumNamesMax(); + static_assert(min <= max, "Invalid enum range: min must be <= max."); + return static_cast(static_cast(max - min)) + 1; } - -template -constexpr std::span enumName(T v) +template +constexpr auto makeEnumNames(std::index_sequence) { - constexpr auto names = enumNames>(); - if (static_cast(v) >= names.size()) + constexpr auto min = enumNamesMin(); + return std::array, sizeof...(Is)> { + enumName(static_cast>(Is) + min)>()... + }; +} + +template +constexpr auto enumNames() +{ + constexpr size_t size = enumNamesSize(); + return makeEnumNames(std::make_index_sequence { }); +} + +template +constexpr std::span enumName(E v) +{ + static_assert(std::is_enum_v, "enumName can only be used with enum types."); + + using Underlying = std::underlying_type_t; + using Unsigned = std::make_unsigned_t; + + constexpr auto names = enumNames(); + constexpr Underlying min = enumNamesMin(); + constexpr Underlying max = enumNamesMax(); + + Underlying value = static_cast(v); + if (value < min || value > max) return { "enum out of range" }; - return names[static_cast(v)]; + + // Compute index safely using unsigned extension. + size_t index = static_cast(static_cast(value - min)); + return names[index]; } #if COMPILER(CLANG)