diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1619,7 +1619,33 @@ if (LHS.getAPSIntType() == RHS.getAPSIntType()) { if (intersect(RangeFactory, LHS, RHS).isEmpty()) return getTrueRange(T); + } else { + // We can only lose information if we are casting smaller signed type to + // bigger unsigned type. For e.g., + // LHS (unsigned short): [2, USHRT_MAX] + // RHS (signed short): [SHRT_MIN, 0] + // + // Casting RHS to LHS type will leave us with overlapping values + // CastedRHS : [0, 0] U [SHRT_MAX + 1, USHRT_MAX] + // + // We can avoid this by checking if signed type's maximum value is lesser + // than unsigned type's minimum value. + + // If both have different signs then only we can get more information. + if (LHS.isUnsigned() != RHS.isUnsigned()) { + if (LHS.isUnsigned() && (LHS.getBitWidth() >= RHS.getBitWidth())) { + if (RHS.getMaxValue().isNegative() || + LHS.getAPSIntType().convert(RHS.getMaxValue()) < LHS.getMinValue()) + return getTrueRange(T); + + } else if (RHS.isUnsigned() && (LHS.getBitWidth() <= RHS.getBitWidth())) { + if (LHS.getMaxValue().isNegative() || + RHS.getAPSIntType().convert(LHS.getMaxValue()) < RHS.getMinValue()) + return getTrueRange(T); + } + } + // Both RangeSets should be casted to bigger unsigned type. APSIntType CastingType(std::max(LHS.getBitWidth(), RHS.getBitWidth()), LHS.isUnsigned() || RHS.isUnsigned()); @@ -2148,7 +2174,6 @@ RangeSet::Factory &RangeFactory; }; - bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym, const llvm::APSInt &Constraint) { llvm::SmallSet SimplifiedClasses; diff --git a/clang/test/Analysis/constant-folding.c b/clang/test/Analysis/constant-folding.c --- a/clang/test/Analysis/constant-folding.c +++ b/clang/test/Analysis/constant-folding.c @@ -303,7 +303,8 @@ if (s1 < 0 && s1 > -4 && u1 > UINT_MAX - 4 && u1 < UINT_MAX - 1) { // s1: [-3, -1], u1: [UINT_MAX - 3, UINT_MAX - 2] - clang_analyzer_eval(u1 != s1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(u1 != s1); // expected-warning{{TRUE}} + clang_analyzer_eval(s1 != u1); // expected-warning{{TRUE}} } if (s1 < 1 && s1 > -6 && s1 != -4 && s1 != -3 && @@ -400,12 +401,10 @@ clang_analyzer_eval(uch != sch); // expected-warning{{UNKNOWN}} } - // FIXME: Casting smaller signed types to unsigned one may leave us with - // overlapping values, falsely indicating UNKNOWN, where it is possible to - // assert TRUE. if (uch > 1 && sch < 1) { // uch: [2, UCHAR_MAX], sch: [SCHAR_MIN, 0] - clang_analyzer_eval(uch != sch); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(uch != sch); // expected-warning{{TRUE}} + clang_analyzer_eval(sch != uch); // expected-warning{{TRUE}} } if (uch <= 1 && uch >= 1 && sch <= 1 && sch >= 1) { @@ -419,10 +418,9 @@ clang_analyzer_eval(ush != ssh); // expected-warning{{UNKNOWN}} } - // FIXME: Casting leave us with overlapping values. Should be TRUE. if (ush > 1 && ssh < 1) { // ush: [2, USHRT_MAX], ssh: [SHRT_MIN, 0] - clang_analyzer_eval(ush != ssh); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(ush != ssh); // expected-warning{{TRUE}} } if (ush <= 1 && ush >= 1 && ssh <= 1 && ssh >= 1) {