V1016. The value is out of range of enum values. This causes unspecified or undefined behavior.
The analyzer detected a dangerous cast from a numeric type to an enumeration. The specified number may not be in the range of 'enum' values.
Note 1: This rule is only relevant for the C++ language. The underlying 'enum' type is always 'int' in the C language.
Note 2: This rule is only relevant for C++ compilers that calculate the actual size of 'enum' according to the standard. For example, such compilers are GCC and Clang. MSVC compiler doen't fall into this category, since it calculates the 'enum' size for backward compatibility purposes according to the rules of the C language. It always uses the 'int' type as the underlying type, unless a different type is specified.
The result of casting a number whose value is not in the range of 'enum' elements is unspecified behavior up to C++17 and undefined behavior starting from C++17.
If an underlying type is specified for 'enum', then all values that can fit into this type can be cast to this 'enum' type.
Example 1:
enum class byte : unsigned char {}; // Range: [0; 255]
byte b1 = static_cast<byte>(255); // ok
The number 256 no longer fits in the 'char' type, so this code is incorrect:
byte b2 = static_cast<byte>(256); // UB
If an underlying type is not specified, then, according to the standard, the compiler tries to fit the values depending on the initializer into the following types:
int -> unsigned int -> long -> unsigned long ->
long long -> unsigned long long
Within the selected type, the compiler uses the minimum required number of bits (n) that can fit the maximum number in the enumeration. In such an 'enum', you can fit the range of values [- (2^n) / 2; 2^n / 2 - 1] for 'enum' with a signed underlying type and [0; 2^n - 1] for 'enum' with an unsigned underlying type. Bounds violation of this range is unspecified behavior (before C++17) or undefined behavior (since C++17).
Example 2:
enum foo { a = 0, b = UINT_MAX }; // Range: [0; UINT_MAX]
foo x = foo(-1); // UB
At first glance, this code is correct, but in fact it can result in troubles. The underlying 'enum' type is set to 'unsigned int'. The number '-1' does not fall within the range of this type, so such an assignment may lead to unspecified or undefined behavior.
Example 3.
enum EN { low = 2, high = 4 }; // Uses 3 bits, range: [0; 7]
EN a1 = static_cast<EN>(7); // ok
According to the standard, the underlying type for this enum is 'int'. Inside this type, the compiler uses the minimum width of the bit field that can fit all the values of enum constants.
In this case, you will need at least 3 bits to fit all the values (2 = 0b010 and 4 = 0b100), so an EN variable can fit numbers from 0 (0b000) to 7 (0b111) inclusively. The number 8 already occupies four bits (0b1000), so it no longer fits in the EN type:
EN a2 = static_cast<EN>(8); // UB
UndefinedBehaviorSanitizer also finds an error in this example: https://godbolt.org/z/GGYo7z.
At the same time, if you specify the underlying type for EN, for example, 'unsigned char', then this will be the correct code version:
enum EN : unsigned char { low = 2, high = 4 }; // Range: [0; 255]
EN a2 = static_cast<EN>(8); // ok
This diagnostic is classified as:
|