From 6017cb31bb3548641465ea66219e11abc3106d38 Mon Sep 17 00:00:00 2001 From: Valeriy Savchenko Date: Thu, 1 Jul 2021 12:51:52 +0300 Subject: [PATCH] [analyzer][solver] Use all sources of constraints Prior to this patch, we always gave priority to constraints that we actually know about symbols in question. However, these can get outdated and we can get better results if we look at all possible sources of knowledge, including sub-expressions. Differential Revision: https://reviews.llvm.org/D105436 --- .../StaticAnalyzer/Core/RangeConstraintManager.cpp | 42 +++++++++++----------- clang/test/Analysis/constant-folding.c | 30 ++++++++++++++++ 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 0e57a1a..bc8c831 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -884,26 +884,28 @@ private: } RangeSet infer(SymbolRef Sym) { - if (Optional ConstraintBasedRange = intersect( - RangeFactory, getConstraint(State, Sym), - // If Sym is a difference of symbols A - B, then maybe we have range - // set stored for B - A. - // - // If we have range set stored for both A - B and B - A then - // calculate the effective range set by intersecting the range set - // for A - B and the negated range set of B - A. - getRangeForNegatedSub(Sym), getRangeForEqualities(Sym))) { - return *ConstraintBasedRange; - } - - // If Sym is a comparison expression (except <=>), - // find any other comparisons with the same operands. - // See function description. - if (Optional CmpRangeSet = getRangeForComparisonSymbol(Sym)) { - return *CmpRangeSet; - } - - return Visit(Sym); + return intersect( + RangeFactory, + // Of course, we should take the constraint directly associated with + // this symbol into consideration. + getConstraint(State, Sym), + // If Sym is a difference of symbols A - B, then maybe we have range + // set stored for B - A. + // + // If we have range set stored for both A - B and B - A then + // calculate the effective range set by intersecting the range set + // for A - B and the negated range set of B - A. + getRangeForNegatedSub(Sym), + // If Sym is (dis)equality, we might have some information on that + // in our equality classes data structure. + getRangeForEqualities(Sym), + // If Sym is a comparison expression (except <=>), + // find any other comparisons with the same operands. + // See function description. + getRangeForComparisonSymbol(Sym), + // Apart from the Sym itself, we can infer quite a lot if we look + // into subexpressions of Sym. + Visit(Sym)); } RangeSet infer(EquivalenceClass Class) { diff --git a/clang/test/Analysis/constant-folding.c b/clang/test/Analysis/constant-folding.c index 08a7acc..116e74b 100644 --- a/clang/test/Analysis/constant-folding.c +++ b/clang/test/Analysis/constant-folding.c @@ -179,6 +179,36 @@ void testBitwiseRules(unsigned int a, int b, int c) { } } +unsigned reset(); + +void testCombinedSources(unsigned a, unsigned b) { + if (b >= 10 && (a | b) <= 30) { + // Check that we can merge constraints from (a | b), a, and b. + // Because of the order of assumptions, we already know that (a | b) is [10, 30]. + clang_analyzer_eval((a | b) >= 10 && (a | b) <= 30); // expected-warning{{TRUE}} + } + + a = reset(); + b = reset(); + + if ((a | b) <= 30 && b >= 10) { + // Check that we can merge constraints from (a | b), a, and b. + // At this point, we know that (a | b) is [0, 30], but the knowledge + // of b >= 10 added later can help us to refine it and change it to [10, 30]. + clang_analyzer_eval(10 <= (a | b) && (a | b) <= 30); // expected-warning{{TRUE}} + } + + a = reset(); + b = reset(); + + unsigned c = (a | b) & (a != b); + if (c <= 40 && a == b) { + // Even though we have a directo constraint for c [0, 40], + // we can get a more precise range by looking at the expression itself. + clang_analyzer_eval(c == 0); // expected-warning{{TRUE}} + } +} + void testRemainderRules(unsigned int a, unsigned int b, int c, int d) { // Check that we know that remainder of zero divided by any number is still 0. clang_analyzer_eval((0 % c) == 0); // expected-warning{{TRUE}} -- 2.7.4