1

the following simplest code example causes one warning:

#include <iostream>
#include <climits>

int b = INT_MAX + 1; //warning: overflow in expression; result is -2147483648 
                     //with type 'int' [-Winteger-overflow]

unsigned int a = INT_MAX + 1; //OK
int main()
{
    std::cout << b << std::endl << a;
}

DEMO

Why does the second line with overflow not cause any warning? It is a bug or I don't understand something?

Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • 1
    I agree with T.C., it is a bug (or possibly a feature). I shall see if I can figure out how it works. However, I have a few other things like eating breakfast that I need to do right now, so it will have to wait a while - probably a couple of hours until I get back on it. – Mats Petersson Oct 11 '14 at 08:11
  • http://stackoverflow.com/questions/16188263/is-signed-integer-overflow-still-undefined-behavior-in-c UB...? – deviantfan Oct 11 '14 at 08:15
  • In looking around I found many places which say..."A computation involving unsigned operands can never overflow..." – ChiefTwoPencils Oct 11 '14 at 08:18
  • 3
    @ChiefTwoPencils the rhs of that assignment is signed until the assignment, when it is summarily promoted to unsigned. The actual addition is of two `int`, thus the bug. `g++` does the right thing, btw. [**See it live**](http://coliru.stacked-crooked.com/a/ea342de523757ab1). – WhozCraig Oct 11 '14 at 08:19
  • 1
    @ChiefTwoPencils Neither `INT_MAX` nor `1` is unsigned. – T.C. Oct 11 '14 at 08:19
  • @WhozCraig, T.C. - got it. – ChiefTwoPencils Oct 11 '14 at 08:21

1 Answers1

2

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...

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • It helps that I was hacking clang to "not make an error for X" just the other day, so I know how to search for errors and find their usage... This one was pretty easy, really, as it only happens in one place. Some errors happen in 11 different places, and that makes it much less fun! ;) – Mats Petersson Oct 11 '14 at 08:38