https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105255
Bug ID: 105255 Summary: Narrowing conversion from enumerator to integer not detected Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: cerrigno at gmail dot com Target Milestone: --- This code should not be valid (at least since C++14), because the direct-list-initialization is a narrowing conversion, being enumerator underlying type either int or unsigned int. using signed_t = signed char; using unsigned_t = unsigned char; enum my_enum {}; enum my_enum_auto_signed { _vsm = -1 }; void foo(my_enum s) { signed_t{s}; // narrowing } void foo(my_enum_auto_signed s) { signed_t{s}; // narrowing } Anyway, it compiles on GCC, as of GCC 11, but fails on clang 14 and MSVC 19.31. On the other hand, void foo(my_enum_auto_signed s) { signed_t{my_enum{}}; // not narrowing, constant expression known to be zero signed_t{my_enum_auto_signed{}}; // not narrowing, constant expression known to be zero } compiles fine on every compiler, and it is correct because source is a constant expression whose value can be stored exactly in the target type. Here I report also a more exaustive test with any enumeration type conversion to integer (also at https://godbolt.org/z/jYK7Gzn7s): #include <type_traits> #include <limits> // void_t for C++14 template <typename... Ts> struct make_void { using type = void; }; template <typename... Ts> using void_t = typename make_void<Ts...>::type; template <typename TOut, typename TIn, typename = void> struct is_narrowing : std::true_type {}; template <typename TOut, typename TIn> struct is_narrowing < TOut, TIn, void_t<decltype(TOut{ std::declval<TIn>() }) >> : std::false_type {}; using signed_t = signed char; using unsigned_t = unsigned char; enum my_enum_opaque_signed : signed_t; enum my_enum_opaque_unsigned : unsigned_t; enum my_enum {}; enum my_enum_auto_signed { _vsm = -1 }; enum my_enum_auto_unsigned { _vum = std::numeric_limits<unsigned int>::max() }; enum my_enum_signed : signed_t {}; enum my_enum_unsigned : unsigned_t {}; enum struct my_enum_opaque_scoped; enum struct my_enum_opaque_scoped_signed : signed_t; enum struct my_enum_opaque_scoped_unsigned : unsigned_t; enum struct my_enum_scoped {}; enum struct my_enum_scoped_signed : signed_t {}; enum struct my_enum_scoped_unsigned : unsigned_t {}; // integer static_assert(!is_narrowing<signed_t, signed_t>::value, ""); // unscoped static_assert(!is_narrowing<signed_t, my_enum_opaque_signed>::value, ""); static_assert( is_narrowing<signed_t, my_enum_opaque_unsigned>::value, ""); static_assert( is_narrowing<signed_t, my_enum>::value, ""); // GCC fails static_assert( is_narrowing<signed_t, my_enum_auto_signed>::value, ""); // GCC fails static_assert( is_narrowing<signed_t, my_enum_auto_unsigned>::value, ""); static_assert(!is_narrowing<signed_t, my_enum_signed>::value, ""); static_assert( is_narrowing<signed_t, my_enum_unsigned>::value, ""); // scoped static_assert( is_narrowing<signed_t, my_enum_opaque_scoped>::value, ""); static_assert( is_narrowing<signed_t, my_enum_opaque_scoped_signed>::value, ""); // MSVC fails (fixed on MSVC2022) static_assert( is_narrowing<signed_t, my_enum_opaque_scoped_unsigned>::value, ""); static_assert( is_narrowing<signed_t, my_enum_scoped>::value, ""); static_assert( is_narrowing<signed_t, my_enum_scoped_signed>::value, ""); // MSVC fails (fixed on MSVC2022) static_assert( is_narrowing<signed_t, my_enum_scoped_unsigned>::value, "");