https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126353
Bug ID: 126353
Summary: Out-of-range static_cast to unscoped enum without
fixed underlying type not rejected in constant
expression
Product: gcc
Version: 16.1.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: gongke at ios dot ac.cn
Target Milestone: ---
Consider the following code:
```cpp
#include <iostream>
// underlying type not-fixed --> M = 2 --> values of the enumeration type are
0, 1, 2, and 3
enum E { a = 1, b = 2 };
constexpr int test() {
E e = static_cast<E>(4); // UB: 4 is outside the range of E
return static_cast<int>(e);
}
int main() {
constexpr int x = test(); // forces compile-time evaluation
std::cout << x << '\n';
return 0;
}
```
This code with `-std=c++20`, is rejected by Clang but accepted by GCC.
[godbolt](https://godbolt.org/z/35Kbo73cd)
Taking the draft N4861 as C++20's reference. [expr.static.cast]/10 says that
> A value of integral or enumeration type can be explicitly converted to a
> complete enumeration type. If the enumeration type has a fixed underlying
> type, the value is first converted to that type by integral conversion, if
> necessary, and then to the enumeration type. If the enumeration type does not
> have a fixed underlying type, the value is unchanged if the original value is
> within the range of the enumeration values (9.7.1), and otherwise, the
> behavior is undefined.
[dcl.enum]/5:
> Each enumeration defines a type that is different from all other types. Each
> enumeration also has an underlying type. The underlying type can be
> explicitly specified using an enum-base. For a scoped enumeration type, the
> underlying type is int if it is not explicitly specified. In both of these
> cases, the underlying type is said to be fixed.
This means that this `E` is an enumeration type without a fixed underlying
type.
[dcl.enum]/8:
> For an enumeration whose underlying type is fixed, the values of the
> enumeration are the values of the underlying type. Otherwise, the values of
> the enumeration are the values representable by a hypothetical integer type
> with minimal width M such that all enumerators can be represented. The width
> of the smallest bit-field large enough to hold all the values of the
> enumeration type is M . It is possible to define an enumeration that has
> values not defined by any of its enumerators. If the enumerator-list is
> empty, the values of the enumeration are as if the enumeration had a single
> enumerator with value 0.
So the enumeration values of `E` are {0, 1, 2, 3} and here M=2. Hence this
static_cast is undefined behavior and should be rejected when
constant-evaluated. Clang correctly rejects this and states this reason in the
diagnostic message.