[Bug c++/126353] New: Out-of-range static_cast to unscoped enum without fixed underlying type not rejected in constant expression

gongke at ios dot ac.cn gcc-bugzilla@gcc.gnu.org
Wed Jul 22 14:05:44 GMT 2026


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.


More information about the Gcc-bugs mailing list