magic_enum icon indicating copy to clipboard operation
magic_enum copied to clipboard

Breakage in Clang trunk

Open hokein opened this issue 3 years ago • 4 comments

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;                                                                     
}                                                                                                                      

hokein avatar Aug 05 '22 13:08 hokein

do you have an example error?

Neargye avatar Aug 05 '22 14:08 Neargye

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 {

......

hokein avatar Aug 05 '22 14:08 hokein

Thanks, I will think about what can be done.

Neargye avatar Aug 05 '22 14:08 Neargye

@hokein https://reviews.llvm.org/D131307 If this is included as an error(now warning) in future releases, the library may still stop working. I don’t know yet how to make the option work without an enumeration range.

Neargye avatar Aug 08 '22 09:08 Neargye

Look like can use -Wno-error=enum-constexpr-conversion but it's required a change on the user build config, as I know inside the library we can change the error via pragma.

Neargye avatar Aug 08 '22 10:08 Neargye

Thanks for the quick replies!

Look like can use -Wno-error=enum-constexpr-conversion but it's required a change on the user build config, as I know inside the library we can change the error via pragma.

Yeah, the library can live with the downgraded warning, but this is a temporary solution to give users time to cleanup their codebase, this warning will be upgraded to an error in the future (clang16 or later).

Some options to overcome the issue in client sides:

  1. set a underlying type for all enums
  2. use the magic_enum::customize::enum_range to set the min/max range correctly

However, both of them don't scale.

hokein avatar Aug 08 '22 12:08 hokein

@hokein Can you please check the clang trunk where there is a change about enums? I checked on godbolt and it seems to be working.

Neargye avatar Aug 08 '22 16:08 Neargye

The patch should be available in the clang trunk on godbolt, I can see the error on a simple sample.

I checked on godbolt and it seems to be working.

Oh, interesting, I can confirm it works on the main branch, but not in any release revision (I saw the error on v0.8.1 as well), I think it is fixed by your recent change?

hokein avatar Aug 08 '22 20:08 hokein

Yes, I just today find and upload a fix. I checked for godbolt, but the release version(v0.8.1) does not work, the master worked

Neargye avatar Aug 08 '22 20:08 Neargye

If the fix works for you, I will try to prepare a release as soon as possible.

Neargye avatar Aug 08 '22 21:08 Neargye

The new commit(https://reviews.llvm.org/D131307) changed the behavior again, now the workaround doesn't work again

Neargye avatar Aug 09 '22 11:08 Neargye

added clang diagnostic ignored "-Wenum-constexpr-conversion"

Neargye avatar Aug 09 '22 11:08 Neargye

Ok, new fix it's work without clang diagnostic ignored "-Wenum-constexpr-conversion" and work on new clang trunk with https://reviews.llvm.org/D131307

Please, do not tell anyone how to bypass this diagnostic 😃

Neargye avatar Aug 09 '22 12:08 Neargye

Nice workaround!

do not tell anyone

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

schaumb avatar Aug 09 '22 15:08 schaumb

👀 If there are any other related clang commits to this, please send me the links. I have not found anything new yet, I hope the clan will not change anything retaled enum.

For now, it looks to work for the clang trunk.

Neargye avatar Aug 09 '22 15:08 Neargye

New changes https://reviews.llvm.org/D131528 👀 wait to update on godbolt to check.

Neargye avatar Aug 10 '22 08:08 Neargye

Added clang15 and clang16 to CI

Neargye avatar Aug 10 '22 11:08 Neargye

Okay, still work on clang trunk. I close this, if again there are some breaking changes and I don’t notice, open a new issues.

Neargye avatar Aug 11 '22 16:08 Neargye

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?

shafik avatar Sep 06 '22 16:09 shafik