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 @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" #include "llvm/ADT/STLExtras.h" @@ -1624,7 +1625,52 @@ 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 signed range is =Zero, then maximum value of signed + // type should be lesser than minimum value of unsigned type. + const llvm::APSInt CastedRHSMax = + LHS.getAPSIntType().convert(RHS.getMaxValue()); + + if (CastedRHSMax < LHS.getMinValue()) + return getTrueRange(T); + + } else if (RHS.isUnsigned() && (LHS.getBitWidth() <= RHS.getBitWidth())) { + + llvm::APSInt Zero = LHS.getAPSIntType().getZeroValue(); + bool IsLHSNegative = LHS.getMaxValue() < Zero; + if (IsLHSNegative) + return getTrueRange(T); + + const llvm::APSInt CastedLHSMax = + RHS.getAPSIntType().convert(LHS.getMaxValue()); + if (CastedLHSMax < 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()); @@ -2147,7 +2193,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) {