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,14 +621,22 @@ 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 = __builtin_bit_cast(E, V);
#else
constexpr E v = static_cast<E>(V); constexpr E v = static_cast<E>(V);
#endif
[[maybe_unused]] constexpr auto custom = customize::enum_name<E>(v); [[maybe_unused]] constexpr auto custom = customize::enum_name<E>(v);
static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type."); static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type.");
if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { if constexpr (custom.first == customize::detail::customize_tag::custom_tag) {
@ -644,6 +652,9 @@ constexpr bool is_valid() noexcept {
} else { } else {
return false; return false;
} }
} else {
return false;
}
} }
enum class enum_subtype { enum class enum_subtype {