This is a bug, although the result is "correct". And it seems like the operation is actually turned into an unsigned before the add operation, as far as I can tell.
The warning is generated by this code, which clearly does check for unsigned and skips the overflow check (clearly intended and covered in the comment):
/// Perform the given integer operation, which is known to need at most BitWidth
/// bits, and check for overflow in the original type (if that type was not an
/// unsigned type).
template<typename Operation>
static APSInt CheckedIntArithmetic(EvalInfo &Info, const Expr *E,
const APSInt &LHS, const APSInt &RHS,
unsigned BitWidth, Operation Op) {
if (LHS.isUnsigned())
return Op(LHS, RHS);
APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)), false);
APSInt Result = Value.trunc(LHS.getBitWidth());
if (Result.extend(BitWidth) != Value) {
if (Info.checkingForOverflow())
Info.Ctx.getDiagnostics().Report(E->getExprLoc(),
diag::warn_integer_constant_overflow)
<< Result.toString(10) << E->getType();
else
HandleOverflow(Info, E, Value, E->getType());
}
return Result;
}
As we can see, unsigned is not giving the warning:
if (LHS.isUnsigned())
return Op(LHS, RHS);
Cutting down the program (removing the #includes
, copying a #define INT_MAX ...
from the relevant header), and running clang with clang++ -Xclang -ast-dump ...
, gives the following output:
TranslationUnitDecl 0x4ca2830 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x4ca2d70 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
|-TypedefDecl 0x4ca2dd0 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
|-TypedefDecl 0x4ca3190 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
|-VarDecl 0x4ca31f0 <of.cpp:3:1, col:19> col:5 b 'int' cinit
| `-BinaryOperator 0x4ca3288 <line:1:19, line:3:19> 'int' '+'
| |-IntegerLiteral 0x4ca3248 <line:1:19> 'int' 2147483647
| `-IntegerLiteral 0x4ca3268 <line:3:19> 'int' 1
|-VarDecl 0x4ce2280 <line:6:1, col:28> col:14 a 'unsigned int' cinit
| `-ImplicitCastExpr 0x4ce2340 <line:1:19, line:6:28> 'unsigned int' <IntegralCast>
| `-BinaryOperator 0x4ce2318 <line:1:19, line:6:28> 'int' '+'
| |-IntegerLiteral 0x4ce22d8 <line:1:19> 'int' 2147483647
| `-IntegerLiteral 0x4ce22f8 <line:6:28> 'int' 1
`-FunctionDecl 0x4ce23b0 <line:7:1, line:10:1> line:7:5 main 'int (void)'
`-CompoundStmt 0x4ce24a8 <line:8:1, line:10:1>
We can clearly see that it is integer here:
| `-BinaryOperator 0x4ce2318 <line:1:19, line:6:28> 'int' '+'
| |-IntegerLiteral 0x4ce22d8 <line:1:19> 'int' 2147483647
| `-IntegerLiteral 0x4ce22f8 <line:6:28> 'int' 1
So, the compiler must be casting to unsigned first, then applying +
. The casting meaning this:
| `-ImplicitCastExpr 0x4ce2340 <line:1:19, line:6:28> 'unsigned int' <IntegralCast>
I will have another look to see if I can figure out where this is actually going wrong. But that will have to wait a while...