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:
parent
1a1824df7a
commit
2ec43969d8
1 changed files with 26 additions and 15 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue