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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits