]> granicus.if.org Git - clang/commitdiff
Improve -Wconstant-conversion
authorRichard Trieu <rtrieu@google.com>
Fri, 29 Jan 2016 23:51:16 +0000 (23:51 +0000)
committerRichard Trieu <rtrieu@google.com>
Fri, 29 Jan 2016 23:51:16 +0000 (23:51 +0000)
Switch the evaluation from isIntegerConstantExpr to EvaluateAsInt.
EvaluateAsInt will evaluate more types of expressions than
isIntegerConstantExpr.

Move one case from -Wsign-conversion to -Wconstant-conversion.  The case is:
1) Source and target types are signed
2) Source type is wider than the target type
3) The source constant value is positive
4) The conversion will store the value as negative in the target.

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

lib/Sema/SemaChecking.cpp
test/CXX/expr/expr.const/p2-0x.cpp
test/Sema/constant-conversion.c

index 60a2e505d884afc79c15ed24258566a96188ee48..295ff71eff7884eb13fdda92edcb08459b13c164 100644 (file)
@@ -7578,7 +7578,7 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
     // If the source is a constant, use a default-on diagnostic.
     // TODO: this should happen for bitfield stores, too.
     llvm::APSInt Value(32);
-    if (E->isIntegerConstantExpr(Value, S.Context)) {
+    if (E->EvaluateAsInt(Value, S.Context, Expr::SE_AllowSideEffects)) {
       if (S.SourceMgr.isInSystemMacro(CC))
         return;
 
@@ -7603,6 +7603,42 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
     return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_integer_precision);
   }
 
+  if (TargetRange.Width == SourceRange.Width && !TargetRange.NonNegative &&
+      SourceRange.NonNegative && Source->isSignedIntegerType()) {
+    // Warn when doing a signed to signed conversion, warn if the positive
+    // source value is exactly the width of the target type, which will
+    // cause a negative value to be stored.
+
+    llvm::APSInt Value;
+    if (E->EvaluateAsInt(Value, S.Context, Expr::SE_AllowSideEffects)) {
+      if (!S.SourceMgr.isInSystemMacro(CC)) {
+
+        IntegerLiteral *IntLit =
+            dyn_cast<IntegerLiteral>(E->IgnoreParenImpCasts());
+
+        // If initializing from a constant, and the constant starts with '0',
+        // then it is a binary, octal, or hexadecimal.  Allow these constants
+        // to fill all the bits, even if there is a sign change.
+        if (!IntLit ||
+            *(S.getSourceManager().getCharacterData(IntLit->getLocStart())) !=
+                '0') {
+
+          std::string PrettySourceValue = Value.toString(10);
+          std::string PrettyTargetValue =
+              PrettyPrintInRange(Value, TargetRange);
+
+          S.DiagRuntimeBehavior(
+              E->getExprLoc(), E,
+              S.PDiag(diag::warn_impcast_integer_precision_constant)
+                  << PrettySourceValue << PrettyTargetValue << E->getType() << T
+                  << E->getSourceRange() << clang::SourceRange(CC));
+          return;
+        }
+      }
+    }
+    // Fall through for non-constants to give a sign conversion warning.
+  }
+
   if ((TargetRange.NonNegative && !SourceRange.NonNegative) ||
       (!TargetRange.NonNegative && SourceRange.NonNegative &&
        SourceRange.Width == TargetRange.Width)) {
index c519ecbda2280f38a1f9920ae1a5f48bc59d2271..fd15960647ca0c22b16ac5310ba8a28e8af696cf 100644 (file)
@@ -242,8 +242,8 @@ namespace UndefinedBehavior {
     constexpr int n13 = n5 + n5; // expected-error {{constant expression}} expected-note {{value -4294967296 is outside the range of }}
     constexpr int n14 = n3 - n5; // expected-error {{constant expression}} expected-note {{value 4294967295 is outside the range of }}
     constexpr int n15 = n5 * n5; // expected-error {{constant expression}} expected-note {{value 4611686018427387904 is outside the range of }}
-    constexpr signed char c1 = 100 * 2; // ok
-    constexpr signed char c2 = '\x64' * '\2'; // also ok
+    constexpr signed char c1 = 100 * 2; // ok expected-warning{{changes value}}
+    constexpr signed char c2 = '\x64' * '\2'; // also ok  expected-warning{{changes value}}
     constexpr long long ll1 = 0x7fffffffffffffff; // ok
     constexpr long long ll2 = ll1 + 1; // expected-error {{constant}} expected-note {{ 9223372036854775808 }}
     constexpr long long ll3 = -ll1 - 1; // ok
index 137633396712763973aedcdb60b5fb63a9b95225..74a469d08a9018e9dc1839babedac6127a687b79 100644 (file)
@@ -80,3 +80,34 @@ void test8() {
   struct { enum E x : 1; } f;
   f.x = C; // expected-warning {{implicit truncation from 'int' to bitfield changes value from 2 to 0}}
 }
+
+void test9() {
+  const char max_char = 0x7F;
+  const short max_short = 0x7FFF;
+  const int max_int = 0x7FFFFFFF;
+
+  const short max_char_plus_one = (short)max_char + 1;
+  const int max_short_plus_one = (int)max_short + 1;
+  const long max_int_plus_one = (long)max_int + 1;
+
+  char new_char = max_char_plus_one;  // expected-warning {{implicit conversion from 'const short' to 'char' changes value from 128 to -128}}
+  short new_short = max_short_plus_one;  // expected-warning {{implicit conversion from 'const int' to 'short' changes value from 32768 to -32768}}
+  int new_int = max_int_plus_one;  // expected-warning {{implicit conversion from 'const long' to 'int' changes value from 2147483648 to -2147483648}}
+
+  char hex_char = 0x80;
+  short hex_short = 0x8000;
+  int hex_int = 0x80000000;
+
+  char oct_char = 0200;
+  short oct_short = 0100000;
+  int oct_int = 020000000000;
+
+  char bin_char = 0b10000000;
+  short bin_short = 0b1000000000000000;
+  int bin_int = 0b10000000000000000000000000000000;
+
+#define CHAR_MACRO_HEX 0xff
+  char macro_char_hex = CHAR_MACRO_HEX;
+#define CHAR_MACRO_DEC 255
+  char macro_char_dec = CHAR_MACRO_DEC;  // expected-warning {{implicit conversion from 'int' to 'char' changes value from 255 to -1}}
+}