1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-09 23:34:23 +00:00

More reliable value validness detection towards CWG1766 (#398)

CWG1766 made out-of-range cast to enumeration without fixed underlying
type raise undefined behavior. Such UB arguably also applies to
`bit_cast`, although it's not required that UB in `bit_cast` makes the
expression non-constant.

Currently, only Clang has implemented CWG1766, while Clang's underlying
`__builtin_bit_cast` happens to be a workaround. However, it's more
reliable to me to rely on the guarantee that core language UB causes
constant evaluation failure.

The approach in this patch effectively detects whether
`std::integral_constant<E, static_cast<E>(V)>` is a valid type, which is
equivalent to whether `static_cast<E>(V)` is a constant expression. When
the answer is `false`, value `V` can't be an enumerator of `E`.
This commit is contained in:
A. Jiang 2025-02-20 16:34:09 +08:00 committed by GitHub
parent 1a1824df7a
commit 2ec43969d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -621,26 +621,37 @@ constexpr auto enum_name() noexcept {
template <typename E, E V> template <typename E, E V>
inline constexpr auto enum_name_v = enum_name<E, V>(); inline constexpr auto enum_name_v = enum_name<E, V>();
// CWG1766: Values outside the range of the values of an enumeration
// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307
#if defined(__clang__) && __clang_major__ >= 16
template <typename E, auto V, typename = void>
inline constexpr bool is_enum_constexpr_static_cast_valid = false;
template <typename E, auto V>
inline constexpr bool is_enum_constexpr_static_cast_valid<E, V, std::void_t<std::integral_constant<E, static_cast<E>(V)>>> = true;
#else
template <typename, auto>
inline constexpr bool is_enum_constexpr_static_cast_valid = true;
#endif
template <typename E, auto V> template <typename E, auto V>
constexpr bool is_valid() noexcept { constexpr bool is_valid() noexcept {
#if defined(__clang__) && __clang_major__ >= 16 if constexpr (is_enum_constexpr_static_cast_valid<E, V>) {
// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 constexpr E v = static_cast<E>(V);
constexpr E v = __builtin_bit_cast(E, V); [[maybe_unused]] constexpr auto custom = customize::enum_name<E>(v);
#else static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type.");
constexpr E v = static_cast<E>(V); if constexpr (custom.first == customize::detail::customize_tag::custom_tag) {
#endif constexpr auto name = custom.second;
[[maybe_unused]] constexpr auto custom = customize::enum_name<E>(v); static_assert(!name.empty(), "magic_enum::customize requires not empty string.");
static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type."); return name.size() != 0;
if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) {
constexpr auto name = custom.second;
static_assert(!name.empty(), "magic_enum::customize requires not empty string.");
return name.size() != 0;
} else if constexpr (custom.first == customize::detail::customize_tag::default_tag) {
#if defined(MAGIC_ENUM_VS_2017_WORKAROUND) #if defined(MAGIC_ENUM_VS_2017_WORKAROUND)
return n<E, v>().size_ != 0; return n<E, v>().size_ != 0;
#else #else
return n<v>().size_ != 0; return n<v>().size_ != 0;
#endif #endif
} else {
return false;
}
} else { } else {
return false; return false;
} }