V1112. Comparing expressions with different signedness can lead to unexpected results.
The analyzer has detected a suspicious comparison where expression types have the same ranks but different signs. So, the type ranks are smaller than the 'int' rank. Since such expressions are implicitly converted to the 'int' or 'unsigned int' type, the comparison may lead to unexpected results.
Consider the synthetic example:
bool foo(char lhs, unsigned char rhs)
{
return lhs == rhs; // <=
}
The example contains the variable comparison with types of different signedness: 'lhs' of the 'char' type and 'rhs' of the 'unsigned char' type. Let's consider that the following 'char' type is the 'signed char' (for example, on the x86_64 architecture). The 'unsigned char' type can cover a range of [0 .. 255], while the 'char' type covers [-128 .. 127]. According to C and C++ standards, an implicit type conversion (integral promotion) occurs before variable values are compared, which may cause the issue.
The compiler transforms the code with the comparison into the following code:
return (int) lhs == (int) rhs;
The compiler converts it in such a way if the 'int' type can cover the 'char' and 'unsigned char' range. Otherwise, the compiler selects 'unsigned int' instead of 'int'. On most modern platforms, the 'int' type has a size of 4 bytes and can easily cover these ranges.
If 'lhs' had a negative value, the same value is saved in the left operand as a conversion result. The value of the right operand after the 'rhs' conversion is always non-negative, because the original type was unsigned. So, the comparison result is 'false'. A user can have the opposite case. If the 'rhs' variable contains a value in the range of [128 .. 255], then the comparison result is also 'false'.
If the user changes the compiler or configuration, this error may occur. It can happen when the 'char' type was unsigned before and became signed, and vice versa. For example, when the user calls the 'foo' function with the '\xEE' and '\xEE' arguments, unequal values are passed. According to the standard, this behavior is logical, but it may also be unexpected by the developer.
The user can avoid this in two approaches:
Approach N1. Convert expressions to a common type by sign:
if ((unsigned char) lhs == rhs)
Approach N2. Use the 'std::cmp_*' functions (C++20) or their analogs to compare expressions which types have different signedness:
if (std::cmp_equal(lhs, rhs))
Note: the diagnostic rule implements several exceptions that are added to reduce the number of false positives. The analyzer issues a warning only if it has been able to prove that the value range of one operand cannot be represented by the type of the other operand. If you need to detect all the fragments in the code where such an operand comparison of different signedness occurs, you may use the following comment:
//+V1112, ENABLE_ON_UNKNOWN_VALUES
For this reason, the analyzer does not issue a warning for the synthetic example given earlier without this setting.
This diagnostic is classified as: