[Bug c++/58559] Underlying type of enum is hybrid of int and unsigned
redi at gcc dot gnu.org
gcc-bugzilla@gcc.gnu.org
Wed Mar 4 11:24:00 GMT 2020
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58559
--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> ---
I don't think there's a bug here. Your program actually has undefined behaviour
due to the (E)-1 conversion. Using -fno-strict-enums only tells GCC not to
optimize on the assumption you never do (E)-1 but it doesn't actually make it
valid.
The underlying type of the enum is implementation-defined, and GCC's choice of
unsigned for your type is documented at
https://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit-fields-implementation.html
The value range of values of the enum are only 0 and 1, so (E)-1 is undefined.
[conv.prom] p3 in the standard is clear that the enumeration type promotes to
int, because that is the first type in the list that can represent all the
values of the type (i.e. 0 and 1). Using -fno-strict-enums doesn't change those
promotion rules (doing so would be an ABI change).
When you convert it explicitly as (long long)e the warning is correct, because
the underlying type is unsigned and so it can't have any values that will
produce a negative long long.
When you do (e < 0) there is an implicit conversion to int (as per the rules of
[conv.prom] p3) and (int)(E)-1 _does_ produce a negative value.
So there is no hybrid type, it's just that the promotion rules of C++ do not
require promotion to the underlying type, but to the first type that can
represent all the valid values.
- If you don't use invalid values, there are no surprises. i.e. don't use (E)-1
- If you explicitly convert to the underlying type instead of allowing implicit
promotion to int, there are no surprises e.g. this won't abort
if (((long long)e < 0) != ((std::underlying_type_t<E>)e < 0))
abort();
- If you give the enumeration a fixed underlying type, there are no surprises
e.g.
enum E : int { A, B }; // ((long long)e < 0) == (e < 0) and no warnings
- If you add a large enumerator so that the range of valid values is increased,
there are no surprises:
enum E { A, B, _max = -1u }; // (e < 0) and f(e) promote to unsigned now
So I think this is only a problem for bad code, and the C++ language provides
several ways to avoid the problem. Relying on -fno-strict-enums and using
invalid values of the enum type is a bad idea.
Arguably, we *could* make -fno-strict-enums act as though all enums have a
fixed underlying type, so that all values of the underlying type (in your case
that's unsigned) are valid values. That would mean the implicit conversion
required by [conv.prom] p3 would convert to the underlying type. But as I said
above, that would be an ABI change.
More information about the Gcc-bugs
mailing list