ASDenysPetrov updated this revision to Diff 477233.
ASDenysPetrov added a comment.

Completely reworked solution.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D103096/new/

https://reviews.llvm.org/D103096

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/svalbuilder-casts.cpp
  clang/test/Analysis/symbol-integral-cast.cpp

Index: clang/test/Analysis/symbol-integral-cast.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/symbol-integral-cast.cpp
@@ -0,0 +1,395 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -analyzer-config eagerly-assume=false -analyzer-config support-symbolic-integer-casts=true -verify %s
+
+template <typename T>
+void clang_analyzer_eval(T);
+void clang_analyzer_warnIfReached();
+template<typename T>
+void clang_analyzer_value(T x);
+
+typedef short int16_t;
+typedef int int32_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+
+void test1(int x) {
+  // Even if two lower bytes of `x` equal to zero, it doesn't mean that
+  // the entire `x` is zero. We are not able to know the exact value of x.
+  // It can be one of  65536 possible values like [0, 65536, 131072, ...]
+  // and so on. To avoid huge range sets we still assume `x` in the range
+  // [INT_MIN, INT_MAX].
+  if (!(short)x) {
+    if (!x) {
+      clang_analyzer_value((short)x); // expected-warning {{16s:0}}
+      clang_analyzer_value(x); // expected-warning {{32s:0}}
+    } else {
+      // FIXME: Shall be simplified to ConcreteInt 16s:0.
+      clang_analyzer_value((short)x); // expected-warning {{16s:{ [0, 0] }}}
+      clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, -1], [1, 2147483647] }}}
+    }
+  }
+}
+
+void test2(int x) {
+  // If two lower bytes of `x` equal to zero, and we know x to be 65537,
+  // which is not truncated to short as zero. Thus the branch is infisible.
+  short s = x;
+  if (!s) {
+    if (x == 65537) {
+      clang_analyzer_warnIfReached(); // no-warning
+    } else {
+      clang_analyzer_value(s); // expected-warning {{16s:0}}
+      clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, 65536], [65538, 2147483647] }}}
+    }
+  }
+}
+
+void test3(int x, short s) {
+  s = x;
+  if ((short)x > -10 && s < 10) {
+    if (x > 0 && x < 10) {
+      // FIXME: If the range of the whole variable was constrained then reason
+      // again about truncated bytes to make the ranges more precise.
+      // Shall be 16s:{ [1, 9] }
+      clang_analyzer_value((short)x); // expected-warning {{16s:{ [-9, 9] }}}
+      clang_analyzer_value(x); // expected-warning {{32s:{ [1, 9] }}}
+    }
+  }
+}
+
+void test4(unsigned x) {
+  if ((char)x > 8) {
+    clang_analyzer_value((char)x); // expected-warning {{8s:{ [9, 127] }}}
+    if (x < 42) {
+      // FIXME: Update lower(less) bytes if higher(more) bytes are updated.
+      // Should be 8s:{ [9, 41] }.
+      clang_analyzer_value((char)x); // expected-warning {{8s:{ [9, 127] }}}
+    }
+  }
+}
+
+void test5(unsigned x) {
+  if ((char)x > -10 && (char)x < 10) {
+    if ((short)x == 8) {
+      clang_analyzer_value(x); // expected-warning {{32u:{ [0, 4294967295] }}}
+      clang_analyzer_value((short)x); // expected-warning {{16s:{ [8, 8] }}}
+      // FIXME: Update lower(less) bytes if higher(more) bytes are updated.
+      // Should be 8s:{ [8, 8] }.
+      clang_analyzer_value((char)x); // expected-warning {{8s:{ [-9, 9] }}}
+    }
+  }
+}
+
+void test6(int x) {
+  // Even if two lower bytes of `x` less than zero, it doesn't mean that `x`
+  // can't be greater than zero. Thence we don't change the native range of
+  // `x` and this branch is feasible.
+  if (x > 0)
+    if ((short)x < 0)
+      clang_analyzer_value(x); // expected-warning {{32s:{ [1, 2147483647] }}}
+}
+
+void test7(int x) {
+  // The range of two lower bytes of `x` [1, SHORT_MAX] is enough to cover
+  // all possible values of char [CHAR_MIN, CHAR_MAX]. So the lowest byte
+  // can be lower than zero.
+  if ((short)x > 0) {
+    if ((char)x < 0)
+      clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+    else
+      clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  }
+}
+
+void test8(int x) {
+  // Promotion from `signed int` to `signed long long` also reasoning about the
+  // original range, because we know the fact that even after promotion it
+  // remains in the range [INT_MIN, INT_MAX].
+  if ((long long)x < 0)
+    clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, -1] }}}
+}
+
+void test9(signed int x) {
+  // Any cast `signed` to `unsigned` produces an unsigned range, which is
+  // [0, UNSIGNED_MAX] and can not be lower than zero.
+  if ((unsigned long long)x < 0)
+    clang_analyzer_warnIfReached(); // no-warning
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+
+  if ((unsigned int)x < 0)
+    clang_analyzer_warnIfReached(); // no-warning
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+
+  if ((unsigned short)x < 0)
+    clang_analyzer_warnIfReached(); // no-warning
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+
+  if ((unsigned char)x < 0)
+    clang_analyzer_warnIfReached(); // no-warning
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test10(unsigned int x, signed char sc) {
+  // Promotion from `unsigned` to `signed` produces a signed range,
+  // which is able to cover all the values of the original,
+  // so that such cast is not lower than zero.
+  if ((signed long long)x < 0)
+    clang_analyzer_warnIfReached(); // no-warning
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+
+  // Any other cast(conversion or truncation) from `unsigned` to `signed`
+  // produces a signed range, which is [SIGNED_MIN, SIGNED_MAX]
+  // and can be lower than zero.
+  if ((signed int)x < 0)            // explicit cast
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+
+  signed short ss = x; // initialization
+  if (ss < 0)
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+
+  sc = x; // assignment
+  if (sc < 0)
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  else
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test11(unsigned int x) {
+  // Promotion from 'unsigned' to 'signed' entirely covers the original range.
+  // Thence such cast is not lower than zero and the `true` branch is
+  // infiseable. But it doesn't affect the original range, which still remains
+  // as [0, UNSIGNED_MAX].
+  if ((signed long long)x < 0)
+    clang_analyzer_warnIfReached(); // no-warning
+  else
+    clang_analyzer_eval(x < 0); // expected-warning {{FALSE}}
+
+  // Any other cast(conversion or truncation) from `unsigned` to `signed`
+  // produces a signed range, which is [SIGNED_MIN, SIGNED_MAX]. But it doesn't
+  // affect the original range, which still remains as [0, UNSIGNED_MAX].
+  if ((signed int)x < 0)
+    clang_analyzer_eval(x < 0); // expected-warning {{FALSE}}
+
+  if ((signed short)x < 0)
+    clang_analyzer_eval(x < 0); // expected-warning {{FALSE}}
+
+  if ((signed char)x < 0)
+    clang_analyzer_eval(x < 0); // expected-warning {{FALSE}}
+}
+
+void test12(int x, char c) {
+  if (x >= 5308) {
+    if (x <= 5419) {
+      // Truncation on assignment: int[5308, 5419] -> char[-68, 43]
+      c = x;
+      clang_analyzer_value(c); // expected-warning {{8s:{ [-68, 43] }}}
+      // Truncation on initializaion: int[5308, 5419] -> char[-68, 43]
+      char c1 = x;
+      clang_analyzer_value(c1); // expected-warning {{8s:{ [-68, 43] }}}
+    }
+  }
+}
+
+void test13(int x) {
+  if (x > 913440767 && x < 913440769) { // 0x36720000
+    // Truncation: int[913440768] -> short[0]
+    clang_analyzer_value((short)x); // expected-warning {{16s:0}}
+    short s = x;
+    clang_analyzer_value(s); // expected-warning {{16s:0}}
+  }
+}
+
+void test14(int x) {
+  if (x >= -1569193983 && x <= 578290016) {
+    // The big range of `x` covers all possible values of short.
+    // Truncation: int[-1569193983, 578290016] -> short[-32768, 32767]
+    if ((short)x > 0) {
+      clang_analyzer_value(x); // expected-warning {{32s:{ [-1569193983, 578290016] }}}
+      short s = x;
+      clang_analyzer_value(s); // expected-warning {{16s:{ [1, 32767] }}}
+    }
+  }
+}
+
+void test15(int x) {
+  if (x >= -1569193983 && x <= -1569193871) { // [0xA2780001, 0xA2780071]
+    // The small range of `x` covers only several values of short.
+    // Truncation: int[-1569193983, -1569193871] -> short[1, 113]
+    clang_analyzer_value(x); // expected-warning {{32s:{ [-1569193983, -1569193871] }}}
+    clang_analyzer_value((short)x); // expected-warning {{16s:{ [1, 113] }}}
+  }
+}
+
+void test16(char x) {
+  if (x < 0)
+    clang_analyzer_value(x); // expected-warning {{8s:{ [-128, -1] }}}
+  else
+    clang_analyzer_value(x); // expected-warning {{8s:{ [0, 127] }}}
+}
+
+void test17(char x) {
+  if (-11 <= x && x <= -10) {
+    unsigned u = x;
+    // Conversion: char[-11, -10] -> unsigned int[4294967285, 4294967286]
+    clang_analyzer_value(u); // expected-warning {{32u:{ [4294967285, 4294967286] }}}
+    unsigned short us = x;
+    // Conversion: char[-11, -10] -> unsigned short[65525, 65526]
+    clang_analyzer_value(us); // expected-warning {{16u:{ [65525, 65526] }}}
+    unsigned char uc = x;
+    // Conversion: char[-11, -10] -> unsigned char[245, 246]
+    clang_analyzer_value(uc); // expected-warning {{8u:{ [245, 246] }}}
+  }
+}
+
+void test18(char c, short s, int i) {
+  // Any char value always is less then 1000.
+  int OneThousand = 1000;
+  c = i;
+  clang_analyzer_eval(c < OneThousand); // expected-warning {{TRUE}}
+  int MinusFourtyThousands = -40000;
+  s = i;
+  clang_analyzer_eval(s > MinusFourtyThousands); // expected-warning {{TRUE}}
+}
+
+void test19(char x, short y) {
+  if (-43 <= x && x <= -42) { // x[-42, -43]
+    y = 42;
+    clang_analyzer_eval(int16_t(x) < int16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int16_t(x) < int32_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int32_t(x) < int16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int32_t(x) < int32_t(y)); // expected-warning {{TRUE}}
+
+    clang_analyzer_eval(int16_t(x) < uint16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int16_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(int32_t(x) < uint16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int32_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+
+    clang_analyzer_eval(uint16_t(x) < int16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint16_t(x) < int32_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < int16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < int32_t(y)); // expected-warning {{FALSE}}
+
+    clang_analyzer_eval(uint16_t(x) < uint16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint16_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < uint16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+  }
+}
+
+void test20(char x, short y) {
+  if (42 <= y && y <= 43) { // y[42, 43]
+    x = -42;
+    clang_analyzer_eval(int16_t(x) < int16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int16_t(x) < int32_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int32_t(x) < int16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int32_t(x) < int32_t(y)); // expected-warning {{TRUE}}
+
+    clang_analyzer_eval(int16_t(x) < uint16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int16_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(int32_t(x) < uint16_t(y)); // expected-warning {{TRUE}}
+    clang_analyzer_eval(int32_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+
+    clang_analyzer_eval(uint16_t(x) < int16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint16_t(x) < int32_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < int16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < int32_t(y)); // expected-warning {{FALSE}}
+
+    clang_analyzer_eval(uint16_t(x) < uint16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint16_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < uint16_t(y)); // expected-warning {{FALSE}}
+    clang_analyzer_eval(uint32_t(x) < uint32_t(y)); // expected-warning {{FALSE}}
+  }
+}
+
+void test21(unsigned x) {
+  if (x > 42) {
+    // Unsigned range can generate two signed ranges.
+    // Conversion: unsigned[43, 4294967295] -> int[-2147483648, -1]U[43, 2147483647]
+    int i = x;               // initialization
+    clang_analyzer_value(i); // expected-warning {{32s:{ [-2147483648, -1], [43, 2147483647] }}}
+  }
+}
+
+void test22(int x, unsigned u) {
+  if (x > -42) {
+    // Signed range can generate two unsigned ranges.
+    // Conversion: int[-41, 2147483647] -> unsigned[0, 2147483647]U[4294967255, 4294967295]
+    u = x;                   // assignment
+    clang_analyzer_value(u); // expected-warning {{32u:{ [0, 2147483647], [4294967255, 4294967295] }}}
+  }
+}
+
+// PR51036
+void test23(signed char c) {
+  // Conversion: char[-128, 127] -> unsigned int[0, 127]U[4294967168, 4294967295]
+  if ((unsigned int)c <= 200) {
+    // [0, 127]U[4294967168, 4294967295] x [0, 200] = [0, 127]
+    clang_analyzer_value(c); // expected-warning {{8s:{ [0, 127] }}}
+  }
+}
+
+void test24(int x, int y) {
+  if (x == y) {
+    short s = x;
+    if (!s) {
+      if (y == 65537)
+        // FIXME: `(short)y == 0` and `y == 65537` is infeasible.
+        // This should not warn.
+        clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+      else
+        clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+    }
+  }
+}
+
+void test25(int x, int y) {
+  if ((unsigned char)x == y) {
+    // FIXME: Should be 32s:{ [0, 255] }
+    clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}}
+  }
+}
+
+void test26(int x, long long y, long z) {
+  // FIXME: Assign range at comparison expression.
+
+  if (x == y) {
+    clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, 2147483647] }}}
+    // FIXME: Should be 64s:{ [-2147483648, 2147483647] }
+    clang_analyzer_value(y); // expected-warning {{64s:{ [-9223372036854775808, 9223372036854775807] }}}
+    if (y == (short)z) {
+      clang_analyzer_value((short)z); // expected-warning {{16s:{ [-32768, 32767] }}}
+      // FIXME: Should be 32s:{ [-32768, 32767] }
+      clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, 2147483647] }}}
+      // FIXME: Should be 64s:{ [-32768, 32767] }
+      clang_analyzer_value(y); // expected-warning {{64s:{ [-9223372036854775808, 9223372036854775807] }}}
+      if (x > 0) {
+        // FIXME: Should be 16s:{ [1, 32767] }
+        clang_analyzer_value((short)z); // expected-warning {{16s:{ [-32768, 32767] }}}
+        // FIXME: Should be 32s:{ [1, 32767] }
+        clang_analyzer_value(x); // expected-warning {{32s:{ [1, 2147483647] }}}
+        // FIXME: Should be 64s:{ [1, 32767] }
+        clang_analyzer_value(y); // expected-warning {{64s:{ [1, 2147483647] }}}
+        if (y < 42) {
+          clang_analyzer_value((short)z); // expected-warning {{16s:{ [1, 41] }}}
+          clang_analyzer_value(x); // expected-warning {{32s:{ [1, 41] }}}
+          clang_analyzer_value(y); // expected-warning {{64s:{ [1, 41] }}}
+        } else if ((short)z < 42) {
+          // FIXME: Should be 16s:{ [1, 41] }
+          clang_analyzer_value((short)z); // expected-warning {{16s:{ [-32768, 41] }}}
+          // FIXME: Should be 32s:{ [1, 41] }
+          clang_analyzer_value(x); // expected-warning {{32s:{ [-32768, 41] }}}
+          // FIXME: Should be 64s:{ [1, 41] }
+          clang_analyzer_value(y); // expected-warning {{64s:{ [-32768, 41] }}}
+        }
+      }
+    }
+  }
+}
Index: clang/test/Analysis/svalbuilder-casts.cpp
===================================================================
--- clang/test/Analysis/svalbuilder-casts.cpp
+++ clang/test/Analysis/svalbuilder-casts.cpp
@@ -39,17 +39,17 @@
 
   // These are not truncated to short as zero.
   static_assert((short)1 != 0, "");
-  clang_analyzer_eval(x == 1);       // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == 1);       // expected-warning{{FALSE}}
   static_assert((short)-1 != 0, "");
-  clang_analyzer_eval(x == -1);      // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == -1);      // expected-warning{{FALSE}}
   static_assert((short)65537 != 0, "");
-  clang_analyzer_eval(x == 65537);   // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == 65537);   // expected-warning{{FALSE}}
   static_assert((short)-65537 != 0, "");
-  clang_analyzer_eval(x == -65537);  // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == -65537);  // expected-warning{{FALSE}}
   static_assert((short)131073 != 0, "");
-  clang_analyzer_eval(x == 131073);  // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == 131073);  // expected-warning{{FALSE}}
   static_assert((short)-131073 != 0, "");
-  clang_analyzer_eval(x == -131073); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == -131073); // expected-warning{{FALSE}}
 
   // Check for implicit cast.
   short s = y;
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -27,6 +27,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <iterator>
+#include <set>
 
 using namespace clang;
 using namespace ento;
@@ -1228,6 +1229,8 @@
   RangeSet VisitSymExpr(SymbolRef Sym) {
     if (Optional<RangeSet> RS = getRangeForNegatedSym(Sym))
       return *RS;
+    if (Optional<RangeSet> RS = inferRangeForIntegralCast(State, Sym))
+      return *RS;
     // If we've reached this line, the actual type of the symbolic
     // expression is not supported for advanced inference.
     // In this case, we simply backoff to the default "let's simply
@@ -1296,6 +1299,10 @@
   }
 
   RangeSet infer(SymbolRef Sym) {
+    AnalyzerOptions &Opts = State->getAnalysisManager().getAnalyzerOptions();
+    if (Opts.ShouldSupportSymbolicIntegerCasts)
+      return Visit(Sym);
+
     return intersect(RangeFactory,
                      // Of course, we should take the constraint directly
                      // associated with this symbol into consideration.
@@ -1629,6 +1636,100 @@
     return RangeSet(RangeFactory, Zero);
   }
 
+  Optional<RangeSet> inferRangeForIntegralCast(ProgramStateRef State,
+                                               SymbolRef Sym) {
+    AnalyzerOptions &Opts = State->getAnalysisManager().getAnalyzerOptions();
+    if (!Opts.ShouldSupportSymbolicIntegerCasts)
+      return None;
+
+    using NestedCastTypes = SmallVector<QualType, 3>;
+    using ParsedSym = std::tuple<
+        NestedCastTypes, // Nested types of cast. E.g. (int)(char)(short x).
+        BitWidthType,    // Minimal bitwidth. E.g. (int)(char)(short x) ->
+                         // sizeof(char).
+        SymbolRef,       // Root symbol. E.g. (int)(char)(short x) -> short x.
+        bool             // Success flag.
+        >;
+
+    auto ParseSym = [&](SymbolRef Sym) -> ParsedSym {
+      ASTContext &C = State->getStateManager().getContext();
+      BitWidthType MinBitWidth = std::numeric_limits<BitWidthType>::max();
+      NestedCastTypes Types;
+      do {
+        const QualType T = Sym->getType();
+        // Check if we can handle the symbol.
+        if (!T->isIntegralOrEnumerationType())
+          return {{}, {}, {}, false};
+        // Find a minimal bitwidth.
+        const BitWidthType BitWidth = C.getIntWidth(T);
+        if (MinBitWidth > BitWidth)
+          MinBitWidth = BitWidth;
+        // Collect nested cast types.
+        Types.push_back(T);
+        // Traverse through cast symbols.
+        auto *SC = dyn_cast<SymbolCast>(Sym);
+        if (!SC)
+          break;
+        // Go to the root symbol.
+        Sym = SC->getOperand();
+      } while (true);
+      return {std::move(Types), MinBitWidth, Sym, true};
+    };
+
+    ParsedSym PS = ParseSym(Sym);
+
+    if (!std::get<3>(PS)) // Success flag.
+      return None;
+
+    auto DoSequenceCast = [&](RangeSet RS,
+                              const NestedCastTypes &Types) -> RangeSet {
+      if (Types.size() == 1)
+        return RangeFactory.castTo(RS, Types.front());
+
+      auto TypesReversed = llvm::make_range(Types.rbegin(), Types.rend());
+      for (const QualType T : TypesReversed)
+        RS = RangeFactory.castTo(RS, T);
+
+      return RS;
+    };
+
+    auto findClosestRange = [&](SymbolRef Sym,
+                                BitWidthType BW) -> const RangeSet * {
+      const ClassMap *CM = State->get<SymClassMap>(Sym);
+      if (!CM)
+        return nullptr;
+      EquivalenceClass EC;
+      bool Found = false;
+      if (const EquivalenceClass *ECPtr = CM->lookup(BW)) {
+        Found = true;
+        EC = *ECPtr;
+      } else {
+        BitWidthType ClosestBiggerBW = std::numeric_limits<BitWidthType>::max();
+        for (ClassMap::value_type &V : *CM) {
+          if (V.first >= BW) {
+            if (V.first < ClosestBiggerBW) {
+              ClosestBiggerBW = V.first;
+              EC = V.second;
+            }
+          }
+        }
+        Found = (ClosestBiggerBW != std::numeric_limits<BitWidthType>::max());
+      }
+      if (!Found)
+        return nullptr;
+      if (const RangeSet *RS = State->get<ConstraintRange>(EC))
+        return RS;
+      return nullptr;
+    };
+
+    if (const RangeSet *RS = findClosestRange(std::get<2>(PS), std::get<1>(PS)))
+      return DoSequenceCast(*RS, std::get<0>(PS));
+
+    RangeSet RS = infer(std::get<2>(PS)->getType());
+    RS = DoSequenceCast(RS, std::get<0>(PS));
+    return RS;
+  }
+
   BasicValueFactory &ValueFactory;
   RangeSet::Factory &RangeFactory;
   ProgramStateRef State;
@@ -2081,6 +2182,10 @@
   /// Base method for handling new constraints for classes.
   [[nodiscard]] ProgramStateRef assign(EquivalenceClass Class,
                                        RangeSet NewConstraint) {
+    AnalyzerOptions &Opts = State->getAnalysisManager().getAnalyzerOptions();
+    if (Opts.ShouldSupportSymbolicIntegerCasts)
+      if (!intersectWithExistingIntegralCastSymbol(State, Class, NewConstraint))
+        return nullptr;
     // There is a chance that we might need to update constraints for the
     // classes that are known to be disequal to Class.
     //
@@ -2116,6 +2221,37 @@
     return setConstraint(State, Class, NewConstraint);
   }
 
+  bool intersectWithExistingIntegralCastSymbol(ProgramStateRef State,
+                                               EquivalenceClass Class,
+                                               RangeSet NewConstraint) {
+    std::map<EquivalenceClass, BitWidthType> M;
+    SymbolSet SS = Class.getClassMembers(State);
+    BitWidthType BW = std::numeric_limits<BitWidthType>::max();
+    for (SymbolRef S : SS) {
+      MinBitWidthAndSymbolRoot MBWSR = parseSymbolCast(State, S);
+      BW = std::min(BW, MBWSR.first);
+      if (const ClassMap *CM = State->get<SymClassMap>(MBWSR.second))
+        for (const ClassMap::value_type &BWEC : *CM) {
+          auto it = M.find(BWEC.second);
+          if (it != M.end())
+            it->second = std::min(BWEC.first, it->second);
+          else
+            M.emplace_hint(it, BWEC.second, BWEC.first);
+        }
+    }
+
+    for (const auto &P : M)
+      if (const RangeSet *RSPtr = getConstraint(State, P.first)) {
+        const APSIntType Ty{std::min(BW, P.second), true};
+        RangeSet RS1 = RangeFactory.castTo(*RSPtr, Ty);
+        RangeSet RS2 = RangeFactory.castTo(NewConstraint, Ty);
+        RS1 = RangeFactory.intersect(RS1, RS2);
+        if (RS1.isEmpty())
+          return false;
+      }
+    return true;
+  }
+
   ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS,
                                    SymbolRef RHS) {
     return EquivalenceClass::markDisequal(RangeFactory, State, LHS, RHS);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to