为什么唯一的带符号整数类型会导致clang中的溢出警告

Why does the only signed integer type causes overflow warning in clang

本文关键字:clang 溢出 警告 唯一 带符号 整数 类型 为什么      更新时间:2023-10-16

以下最简单的代码示例导致一个警告:

#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;
}

演示

为什么溢出的第二行没有引起任何警告?是虫子还是我什么都不懂?

这是一个错误,尽管结果是"正确的"。据我所知,在加法运算之前,这个运算似乎实际上变成了一个无符号运算。

警告是由这段代码生成的,它显然会检查未签名的代码,并跳过溢出检查(注释中明确说明了这一点):

/// 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;
}

正如我们所看到的,unsigned并没有给出警告:

  if (LHS.isUnsigned())
    return Op(LHS, RHS);

减少程序(删除#includes,从相关标头复制#define INT_MAX ...),并使用clang++ -Xclang -ast-dump ...运行clang,会得到以下输出:

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>

我们可以清楚地看到,这里是整数:

|   `-BinaryOperator 0x4ce2318 <line:1:19, line:6:28> 'int' '+'
|     |-IntegerLiteral 0x4ce22d8 <line:1:19> 'int' 2147483647
|     `-IntegerLiteral 0x4ce22f8 <line:6:28> 'int' 1

因此,编译器必须先转换为unsigned,然后应用+。铸造意味着:

| `-ImplicitCastExpr 0x4ce2340 <line:1:19, line:6:28> 'unsigned int' <IntegralCast>

我会再看一眼,看看我是否能弄清楚到底哪里出了问题。但这还需要等待一段时间。。。