V1084. The expression is always true/false. The value is out of range of enum values.
The analyzer detected a suspicious comparison. The enumeration variable is compared with a number. This number is out of the enumeration values range, so the comparison does not make any sense.
If the enumeration has an underlying type, values that fit into this type should be compared with a variable of this enumeration.
Look at the following example:
enum byte : unsigned char {}; // Range: [0; 255]
void foo(byte b1)
{
if (b1 == 256) // logic error : always false
{
//....
}
}
The 'byte' enumeration has the underlying 'unsigned char' type. Number 256 is out of the 'unsigned char' type range, so the 'b1 == 256' comparison is always false.
Here's an example of a correct comparison:
enum byte : unsigned char {}; // Range: [0; 255]
void foo(byte b1)
{
if (b1 == 255) // ok
{
//....
}
}
A more complicated case is when an enumeration doesn't have explicitly specified underlying type.
In the C language, a compiler always uses the 'int' type as an underlying type. The whole 'int' range will be the enumeration values range.
In the C++ language, a compiler uses 'int' as an underlying type for scoped enumerations. The whole 'int' range will also be the values range.
For unscoped enumerations whose underlying type isn't fixed, the values range and the underlying type are evaluated in a special way. According to the C++ standard, the compiler outputs the underlying type basing on the enumerators' values. The compiler tries to fit them into the following types:
int -> unsigned int -> long -> unsigned long ->
long long -> unsigned long long
In the selected type, the compiler uses the smallest bit field (n) large enough to hold all enumerators. Such enumerations will be able to handle the [- (2 ^ n) / 2; (2 ^ n) / 2 - 1] range of values for the signed and [0; (2 ^ n) - 1] for the unsigned underlying type, respectively.
That's why the following fragment in the C++ language will have an error if a compiler other than MSVC is used (for example, GCC or Clang):
enum EN { low = 2, high = 4 }; // Uses 3 bits, range: [0; 7]
void foo(EN en1)
{
if (en1 != 8) // logic error : always true
{
//....
}
}
According to the C++ standard, the underlying type for this enumeration is 'int'. In this type, the compiler uses the smallest bit field that can fit all the values of enumerators.
In this case, at least 3 bits are needed to fit all values (2 = 0b010 and 4 = 0b100), so a variable of the 'EN' type can fit numbers from 0 (0b000) to 7 (0b111). 8 occupies four bits (0b1000), so it no longer fits into the 'EN' type. To fix the error, you can explicitly specify the underlying type:
enum EN : int32_t { low = 2, high = 4 };
// Now range is: [−2 147 483 648, 2 147 483 647]
void foo(EN en1)
{
if (en1 != 8) // ok
{
//....
}
}
Not all C++ compilers evaluate the actual size of the enumeration according to the standard. For example, MSVC when compiling code, doesn't follow the standard. It evaluates the enumeration size for backward compatibility according to the C language. Therefore, MSVC always uses the 'int' type as the underlying type, unless a different type is specified. In this case, the range of enumeration values is the 'int' range. That's why there's no error in the example above, if you use MSVC:
enum EN { low = 2, high = 4 };
// MSVC will use int as underlying type
// range is: [−2 147 483 648, 2 147 483 647]
void foo(EN en1)
{
if (en1 != 8) // no logic error
{
//....
}
}
However, don't write such code because it's non-portable to other compilers. You should explicitly specify 'int' as the underlying type.
If you're using the MSVC compiler and you are not interested in portability to other compilers, you can write the following comment. It disables diagnostics that warn about the non-portable code:
//-V1084_TURN_OFF_ON_MSVC
The V1084 warnings relevant to MSVC will remain.
This diagnostic is classified as:
|