magic_enum: Breakage in Clang trunk

Clang has recently addressed an https://github.com/llvm/llvm-project/issues/50055 where it failed to diagnose the UB of casting an out-of-range integer to an enum in a constant expression.

Since undefined behavior is not allowed in a constant expression, Clang now prohibits enum test { zero }; constexpr int g = static_cast<test>(-1); with error: constexpr variable 'g' must be initialized by a constant expression; note: integer value -1 is outside the valid range of values [0, 1] for this enumeration type

This change breaks magic_enum library, since the library internally uses the [-128, 128] default enum value range. Demonstrated with example, below

#include "magic_enum.hpp"                                
                                                                                
enum Color { Red, Black };                                                      
int main(int argc, char* argv[]) {
   Color color = Red;                                                                                                   
   magic_enum::enum_name(color);                                                                                                            
   return 0;                                                                     
}                                                                                                                      

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 19 (15 by maintainers)

Most upvoted comments

We plan on making this a hard error in the next release. If you are using some internal enum type then giving a fixed underlying type should fix things e.g.

enum E : int {};

If you are using the enum type from the user internally, perhaps you can switch to using some new internal enum type that has a fixed underlying type?

Nice workaround!

do not tell anyone

This issue is linked already to the llvm-project bug.

Below is clang diagnostics:

./magic_enum.hpp:408:51: error: no matching function for call to 'is_valid'
  constexpr std::array<bool, sizeof...(I)> valid{{is_valid<E, value<E, Min, IsFlags>(I)>()...}};
                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./magic_enum.hpp:436:10: note: in instantiation of function template specialization 'magic_enum::detail::values<Color, false, 0, 0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL, 8UL, 9UL, 10UL, 11UL, 12UL, 13UL, 14UL, 15UL, 16UL, 17UL, 18UL, 19UL, 20UL, 21UL, 22UL, 23UL, 24UL, 25UL, 26UL, 27UL, 28UL, 29UL, 30UL, 31UL, 32UL, 33UL, 34UL, 35UL, 36UL, 37UL, 38UL, 39UL, 40UL, 41UL, 42UL, 43UL, 44UL, 45UL, 46UL, 47UL, 48UL, 49UL, 50UL, 51UL, 52UL, 53UL, 54UL, 55UL, 56UL, 57UL, 58UL, 59UL, 60UL, 61UL, 62UL, 63UL, 64UL, 65UL, 66UL, 67UL, 68UL, 69UL, 70UL, 71UL, 72UL, 73UL, 74UL, 75UL, 76UL, 77UL, 78UL, 79UL, 80UL, 81UL, 82UL, 83UL, 84UL, 85UL, 86UL, 87UL, 88UL, 89UL, 90UL, 91UL, 92UL, 93UL, 94UL, 95UL, 96UL, 97UL, 98UL, 99UL, 100UL, 101UL, 102UL, 103UL, 104UL, 105UL, 106UL, 107UL, 108UL, 109UL, 110UL, 111UL, 112UL, 113UL, 114UL, 115UL, 116UL, 117UL, 118UL, 119UL, 120UL, 121UL, 122UL, 123UL, 124UL, 125UL, 126UL, 127UL, 128UL>' requested here
  return values<E, IsFlags, reflected_min_v<E, IsFlags>>(std::make_index_sequence<range_size>{});
         ^
./magic_enum.hpp:440:34: note: in instantiation of function template specialization 'magic_enum::detail::values<Color, false, unsigned int>' requested here
inline constexpr auto values_v = values<E, IsFlags>();
                                 ^
./magic_enum.hpp:446:33: note: in instantiation of variable template specialization 'magic_enum::detail::values_v' requested here
inline constexpr auto count_v = values_v<E, IsFlags>.size();
                                ^
./magic_enum.hpp:567:17: note: in instantiation of variable template specialization 'magic_enum::detail::count_v' requested here
  static_assert(count_v<D, false> > 0, "magic_enum requires enum implementation and valid max and min.");
                ^
./magic_enum.hpp:579:1: note: in instantiation of template class 'magic_enum::detail::enable_if_enum<true, false, Color, std::string_view>' requested here
using enable_if_enum_t = typename enable_if_enum<std::is_enum_v<std::decay_t<T>>, false, T, R>::type;
^
./magic_enum.hpp:690:69: note: in instantiation of template type alias 'enable_if_enum_t' requested here
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t<E, string_view> {
                                                                    ^
main.cc:9:9: note: while substituting deduced template arguments into function template 'enum_name' [with E = Color]
  magic_enum::enum_name(color);
  ^
./magic_enum.hpp:344:16: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'V'
constexpr bool is_valid() noexcept {
               ^
./magic_enum.hpp:408:51: error: no matching function for call to 'is_valid'
  constexpr std::array<bool, sizeof...(I)> valid{{is_valid<E, value<E, Min, IsFlags>(I)>()...}};
                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./magic_enum.hpp:344:16: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'V'
constexpr bool is_valid() noexcept {
               ^
./magic_enum.hpp:408:51: error: no matching function for call to 'is_valid'
  constexpr std::array<bool, sizeof...(I)> valid{{is_valid<E, value<E, Min, IsFlags>(I)>()...}};
                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./magic_enum.hpp:344:16: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'V'
constexpr bool is_valid() noexcept {

......