r347006 - [analyzer] ConversionChecker: handle floating point

Kristof Umann via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 15 17:00:55 PST 2018


Author: szelethus
Date: Thu Nov 15 17:00:55 2018
New Revision: 347006

URL: http://llvm.org/viewvc/llvm-project?rev=347006&view=rev
Log:
[analyzer] ConversionChecker: handle floating point

Extend the alpha.core.Conversion checker to handle implicit converions
where a too large integer value is converted to a floating point type. Each
floating point type has a range where it can exactly represent all integers; we
emit a warning when the integer value is above this range. Although it is
possible to exactly represent some integers which are outside of this range
(those that are divisible by a large enough power of 2); we still report cast
involving those, because their usage may lead to bugs. (For example, if 1<<24
is stored in a float variable x, then x==x+1 holds.)

Patch by: DonĂ¡t Nagy!

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

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
    cfe/trunk/test/Analysis/conversion.c

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp?rev=347006&r1=347005&r2=347006&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp Thu Nov 15 17:00:55 2018
@@ -14,8 +14,10 @@
 // of expressions. A warning is reported when:
 // * a negative value is implicitly converted to an unsigned value in an
 //   assignment, comparison or multiplication.
-// * assignment / initialization when source value is greater than the max
-//   value of target
+// * assignment / initialization when the source value is greater than the max
+//   value of the target integer type
+// * assignment / initialization when the source integer is above the range
+//   where the target floating point type can represent all integers
 //
 // Many compilers and tools have similar checks that are based on semantic
 // analysis. Those checks are sound but have poor precision. ConversionChecker
@@ -28,6 +30,9 @@
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/APFloat.h"
+
+#include <climits>
 
 using namespace clang;
 using namespace ento;
@@ -40,11 +45,9 @@ public:
 private:
   mutable std::unique_ptr<BuiltinBug> BT;
 
-  // Is there loss of precision
   bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
                          CheckerContext &C) const;
 
-  // Is there loss of sign
   bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
 
   void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
@@ -132,19 +135,51 @@ bool ConversionChecker::isLossOfPrecisio
 
   QualType SubType = Cast->IgnoreParenImpCasts()->getType();
 
-  if (!DestType->isIntegerType() || !SubType->isIntegerType())
+  if (!DestType->isRealType() || !SubType->isIntegerType())
     return false;
 
-  if (C.getASTContext().getIntWidth(DestType) >=
-      C.getASTContext().getIntWidth(SubType))
+  const bool isFloat = DestType->isFloatingType();
+
+  const auto &AC = C.getASTContext();
+
+  // We will find the largest RepresentsUntilExp value such that the DestType
+  // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
+  unsigned RepresentsUntilExp;
+
+  if (isFloat) {
+    const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
+    RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
+  } else {
+    RepresentsUntilExp = AC.getIntWidth(DestType);
+    if (RepresentsUntilExp == 1) {
+      // This is just casting a number to bool, probably not a bug.
+      return false;
+    }
+    if (DestType->isSignedIntegerType())
+      RepresentsUntilExp--;
+  }
+
+  if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
+    // Avoid overflow in our later calculations.
     return false;
+  }
+
+  unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
+  if (SubType->isSignedIntegerType())
+    CorrectedSrcWidth--;
 
-  unsigned W = C.getASTContext().getIntWidth(DestType);
-  if (W == 1 || W >= 64U)
+  if (RepresentsUntilExp >= CorrectedSrcWidth) {
+    // Simple case: the destination can store all values of the source type.
     return false;
+  }
 
-  unsigned long long MaxVal = 1ULL << W;
+  unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
+  if (isFloat) {
+    // If this is a floating point type, it can also represent MaxVal exactly.
+    MaxVal++;
+  }
   return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
+  // TODO: maybe also check negative values with too large magnitude.
 }
 
 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,

Modified: cfe/trunk/test/Analysis/conversion.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/conversion.c?rev=347006&r1=347005&r2=347006&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/conversion.c (original)
+++ cfe/trunk/test/Analysis/conversion.c Thu Nov 15 17:00:55 2018
@@ -137,6 +137,12 @@ void dontwarn5() {
   U8 = S + 10;
 }
 
+char dontwarn6(long long x) {
+  long long y = 42;
+  y += x;
+  return y == 42;
+}
+
 
 // C library functions, handled via apiModeling.StdCLibraryFunctions
 
@@ -154,7 +160,7 @@ typedef struct FILE {} FILE; int getc(FI
 # define EOF (-1)
 char reply_string[8192];
 FILE *cin;
-extern int dostuff (void);
+extern int dostuff(void);
 int libraryFunction2() {
   int c, n;
   int dig;
@@ -179,3 +185,26 @@ int libraryFunction2() {
   }
 }
 
+double floating_point(long long a, int b) {
+  if (a > 1LL << 55) {
+    double r = a; // expected-warning {{Loss of precision}}
+    return r;
+  } else if (b > 1 << 25) {
+    float f = b; // expected-warning {{Loss of precision}}
+    return f;
+  }
+  return 137;
+}
+
+double floating_point2() {
+  int a = 1 << 24;
+  long long b = 1LL << 53;
+  float f = a; // no-warning
+  double d = b; // no-warning
+  return d - f;
+}
+
+int floating_point_3(unsigned long long a) {
+  double b = a; // no-warning
+  return 42;
+}




More information about the cfe-commits mailing list