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.

Reply via email to