]> granicus.if.org Git - clang/commitdiff
Fix constexpr __builtin_*_overflow issue when unsigned->signed operand.
authorErich Keane <erich.keane@intel.com>
Thu, 30 May 2019 21:35:32 +0000 (21:35 +0000)
committerErich Keane <erich.keane@intel.com>
Thu, 30 May 2019 21:35:32 +0000 (21:35 +0000)
As reported here https://bugs.llvm.org/show_bug.cgi?id=42000, it was
possible to get the constexpr version of __builtin_*_overflow to give
the wrong answer.

This was because when extending the operands to fit the largest type (so
that the math could be done), the decision on whether to sign/zero
extend the operands was based on the result signedness, not on the
operands signedness.

In the reported case, (unsigned char)255 - (int)100 needed
to have each extended to the int in order to do the math.  However, when
extending the first operand to 'int', we incorrectly sign extended it
instead of zero extending.  Thus, the result didnt fit back into the
unsigned char.

The fix for this was simply to choose zero/sign extension based on the
sign of the operand itself.

Differential Revision: https://reviews.llvm.org/D62665

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@362157 91177308-0d34-0410-b5e6-96231b3b80d8

lib/AST/ExprConstant.cpp
test/SemaCXX/builtins-overflow.cpp

index df9b3067b8d43fad2e24035e113a03d647397d21..57d428282cb86ac07dc9f0d68e91ce2429bb50c5 100644 (file)
@@ -9454,10 +9454,8 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
       if (IsSigned && !AllSigned)
         ++MaxBits;
 
-      LHS = APSInt(IsSigned ? LHS.sextOrSelf(MaxBits) : LHS.zextOrSelf(MaxBits),
-                   !IsSigned);
-      RHS = APSInt(IsSigned ? RHS.sextOrSelf(MaxBits) : RHS.zextOrSelf(MaxBits),
-                   !IsSigned);
+      LHS = APSInt(LHS.extOrTrunc(MaxBits), !IsSigned);
+      RHS = APSInt(RHS.extOrTrunc(MaxBits), !IsSigned);
       Result = APSInt(MaxBits, !IsSigned);
     }
 
index 65733c0c154f518f664e7f1a692799ad7a6cc3c8..c84b7da00b543ccd35233fcec5e767b2e26fb05a 100644 (file)
@@ -2,6 +2,7 @@
 // expected-no-diagnostics
 
 #include <limits.h>
+#include <stdint.h>
 
 int a() {
   const int x = 3;
@@ -50,6 +51,7 @@ constexpr Result<RET> sub(LHS &&lhs, RHS &&rhs) {
 static_assert(sub<unsigned char>(static_cast<char>(0),static_cast<char>(1)) == Result<unsigned char>{true, UCHAR_MAX});
 static_assert(sub<char>(static_cast<unsigned char>(0),static_cast<unsigned char>(1)) == Result<char>{false, -1});
 static_assert(sub<unsigned short>(static_cast<short>(0),static_cast<short>(1)) == Result<unsigned short>{true, USHRT_MAX});
+static_assert(sub<uint8_t>(static_cast<uint8_t>(255),static_cast<int>(100)) == Result<uint8_t>{false, 155});
 
 static_assert(sub<int>(17,22) == Result<int>{false, -5});
 static_assert(sub<int>(INT_MAX - 22, -23) == Result<int>{true, INT_MIN});
@@ -91,3 +93,4 @@ constexpr Result<int> smul(int lhs, int rhs) {
 static_assert(smul(17,22) == Result<int>{false, 374});
 static_assert(smul(INT_MAX / 22, 23) == Result<int>{true, -2049870757});
 static_assert(smul(INT_MIN / 22, -23) == Result<int>{true, -2049870757});
+