Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -139,6 +139,30 @@ /// Complexity: O(N) /// where N = size(Original) RangeSet add(RangeSet Original, const llvm::APSInt &Point); + /// Create a new set which is a union of two given ranges. + /// Possible intersections are not checked here. + /// + /// Complexity: O(N + M) + /// where N = size(LHS), M = size(RHS) + RangeSet unite(RangeSet Original, RangeSet RHS); + /// Create a new set by uniting given range set with the given range. + /// All intersections and adjacent ranges are handled here. + /// + /// Complexity: O(N) + /// where N = size(Original) + RangeSet unite(RangeSet Original, Range Element); + /// Create a new set by uniting given range set with the given point. + /// All intersections and adjacent ranges are handled here. + /// + /// Complexity: O(N) + /// where N = size(Original) + RangeSet unite(RangeSet Original, llvm::APSInt Point); + /// Create a new set by uniting given range set with the given range + /// between points. All intersections and adjacent ranges are handled here. + /// + /// Complexity: O(N) + /// where N = size(Original) + RangeSet unite(RangeSet Original, llvm::APSInt From, llvm::APSInt To); RangeSet getEmptySet() { return &EmptySet; } @@ -220,6 +244,7 @@ ContainerType *construct(ContainerType &&From); RangeSet intersect(const ContainerType &LHS, const ContainerType &RHS); + ContainerType unite(const ContainerType &LHS, const ContainerType &RHS); // Many operations include producing new APSInt values and that's why // we need this factory. Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -108,6 +108,13 @@ RangeSet::ContainerType RangeSet::Factory::EmptySet{}; +RangeSet RangeSet::Factory::add(RangeSet LHS, RangeSet RHS) { + ContainerType Result; + std::merge(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(), + std::back_inserter(Result)); + return makePersistent(std::move(Result)); +} + RangeSet RangeSet::Factory::add(RangeSet Original, Range Element) { ContainerType Result; Result.reserve(Original.size() + 1); @@ -124,6 +131,90 @@ return add(Original, Range(Point)); } +RangeSet RangeSet::Factory::unite(RangeSet LHS, RangeSet RHS) { + ContainerType Result = unite(*LHS.Impl, *RHS.Impl); + return makePersistent(std::move(Result)); +} + +RangeSet RangeSet::Factory::unite(RangeSet Original, Range R) { + ContainerType Result; + Result.push_back(R); + Result = unite(*Original.Impl, Result); + return makePersistent(std::move(Result)); +} + +RangeSet RangeSet::Factory::unite(RangeSet Original, llvm::APSInt Point) { + return unite(Original, Range(ValueFactory.getValue(Point))); +} + +RangeSet RangeSet::Factory::unite(RangeSet Original, llvm::APSInt From, + llvm::APSInt To) { + return unite(Original, + Range(ValueFactory.getValue(From), ValueFactory.getValue(To))); +} + +RangeSet::ContainerType RangeSet::Factory::unite(const ContainerType &LHS, + const ContainerType &RHS) { + if (LHS.empty()) + return RHS; + if (RHS.empty()) + return LHS; + + using llvm::APSInt; + const_iterator B = LHS.begin(); + const_iterator E = LHS.end(); + const_iterator It; + APSIntType Ty = APSIntType(B->From()); + APSInt One = Ty.getValue(1); + APSInt Max = Ty.getMaxValue(); + APSInt Min = Ty.getMinValue(); + const APSInt *From = nullptr; + const APSInt *To = nullptr; + ContainerType Result; + + for (Range R : RHS) { + // We also use `To` as a flag to show that the Range + // is waiting to be pushed back. + if (To) { + if ((R.From() - One) <= *To) { + R = {*From, R.To()}; + } else { + Result.emplace_back(*From, *To); + To = nullptr; + } + } + if (B == E) { + Result.push_back(R); + To = nullptr; + } else { + // We don't need to handle the left side while the new range is still + // waiting for defining its right bound. + if (!To) { + It = std::lower_bound( + B, E, R, [&One, &Max](const Range &OrigR, const Range &R) { + return OrigR.To() != Max && (OrigR.To() + One) < R.From(); + }); + // Append all disjoint ranges on the left from the range. + Result.append(B, It); + From = (It == E) ? &R.From() : &std::min(R.From(), It->From()); + } + It = std::lower_bound( + It, E, R, [&One, &Min](const Range &OrigR, const Range &R) { + return OrigR.From() == Min || (OrigR.From() - One) <= R.To(); + }); + To = (It == B) ? &R.To() : &std::max(R.To(), (It - 1)->To()); + B = It; + } + } + // Append the range if it is still waiting to be appended. + if (To) + Result.emplace_back(*From, *To); + // Append the rest of the ranges in LHS. + Result.append(B, E); + + return Result; +} + RangeSet RangeSet::Factory::getRangeSet(Range From) { ContainerType Result; Result.push_back(From); @@ -153,13 +244,6 @@ return new (Buffer) ContainerType(std::move(From)); } -RangeSet RangeSet::Factory::add(RangeSet LHS, RangeSet RHS) { - ContainerType Result; - std::merge(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(), - std::back_inserter(Result)); - return makePersistent(std::move(Result)); -} - const llvm::APSInt &RangeSet::getMinValue() const { assert(!isEmpty()); return begin()->From(); Index: clang/unittests/StaticAnalyzer/RangeSetTest.cpp =================================================================== --- clang/unittests/StaticAnalyzer/RangeSetTest.cpp +++ clang/unittests/StaticAnalyzer/RangeSetTest.cpp @@ -61,6 +61,9 @@ static constexpr BaseType getMax() { return std::numeric_limits::max(); } + // MID is a value in the middle of the range + // which unary minus does not affect on, + // e.g. int8/int32(0), uint8(128), uint32(2147483648). static constexpr BaseType getMid() { return isSigned() ? 0 : ~(fromInt(-1) / fromInt(2)); } @@ -160,7 +163,7 @@ void checkAdd(RawRangeSet RawLHS, RawRangeSet RawRHS, RawRangeSet RawExpected) { - wrap(&Self::checkAddImpl, RawRHS, RawLHS, RawExpected); + wrap(&Self::checkAddImpl, RawLHS, RawRHS, RawExpected); } void checkAdd(RawRangeSet RawLHS, BaseType RawRHS, RawRangeSet RawExpected) { @@ -168,6 +171,29 @@ RawExpected); } + template + void checkUniteImpl(RangeSet LHS, RHSType RHS, RangeSet Expected) { + RangeSet Result = F.unite(LHS, RHS); + EXPECT_EQ(Result, Expected) + << "while uniting " << toString(LHS) << " and " << toString(RHS); + } + + void checkUnite(RawRangeSet RawLHS, RawRange RawRHS, + RawRangeSet RawExpected) { + wrap(&Self::checkUniteImpl, RawLHS, RawRHS, RawExpected); + } + + void checkUnite(RawRangeSet RawLHS, RawRangeSet RawRHS, + RawRangeSet RawExpected) { + wrap(&Self::checkUniteImpl, RawLHS, RawRHS, RawExpected); + } + + void checkUnite(RawRangeSet RawLHS, BaseType RawRHS, + RawRangeSet RawExpected) { + wrap(&Self::checkUniteImpl, RawLHS, RawRHS, + RawExpected); + } + void checkDeleteImpl(const llvm::APSInt &Point, RangeSet From, RangeSet Expected) { RangeSet Result = F.deletePoint(From, Point); @@ -195,9 +221,6 @@ constexpr TypeParam MIN = TestFixture::getMin(); constexpr TypeParam MAX = TestFixture::getMax(); - // MID is a value in the middle of the range - // which unary minus does not affect on, - // e.g. int8/int32(0), uint8(128), uint32(2147483648). constexpr TypeParam MID = TestFixture::getMid(); constexpr TypeParam A = MID - TestFixture::fromInt(42 + 42); constexpr TypeParam B = MID - TestFixture::fromInt(42); @@ -347,3 +370,201 @@ // Check that delete of the point not from the range set works as expected. this->checkDelete(10, {{0, 5}, {20, 30}}, {{0, 5}, {20, 30}}); } + +TYPED_TEST(RangeSetTest, RangeSetUniteTest) { + + // Use next values of the range {MIN, A, B, MID, C, D, MAX}. + constexpr TypeParam MIN = TestFixture::getMin(); + constexpr TypeParam MAX = TestFixture::getMax(); + constexpr TypeParam MID = TestFixture::getMid(); + constexpr TypeParam A = MID - TestFixture::fromInt(42 + 42); + constexpr TypeParam B = MID - TestFixture::fromInt(42); + constexpr TypeParam C = -B; + constexpr TypeParam D = -A; + + // LHS and RHS is empty. + // RHS => + // LHS => = + // ___________________ ___________________ + this->checkUnite({}, {}, {}); + + // RHS is empty. + // RHS => + // LHS => _____ = _____ + // ______/_____\______ ______/_____\______ + this->checkUnite({{A, B}}, {}, {{A, B}}); + this->checkUnite({{A, B}, {C, D}}, {}, {{A, B}, {C, D}}); + + // LHS is empty. + // RHS => ___ + // LHS => / \ = _____ + // ______/_____\______ ______/_____\______ + this->checkUnite({}, B, {{B, B}}); + this->checkUnite({}, {B, C}, {{B, C}}); + this->checkUnite({}, {{MIN, B}, {C, MAX}}, {{MIN, B}, {C, MAX}}); + + // RHS is detached from LHS. + // RHS => ___ + // LHS => ___ / \ = ___ _____ + // __/___\___/_____\__ __/___\___/_____\__ + this->checkUnite({{A, C}}, D, {{A, C}, {D, D}}); + this->checkUnite({{MID, C}, {D, MAX}}, A, {{A, A}, {MID, C}, {D, MAX}}); + this->checkUnite({{A, B}}, {MID, D}, {{A, B}, {MID, D}}); + this->checkUnite({{MIN, A}, {D, MAX}}, {B, C}, {{MIN, A}, {B, C}, {D, MAX}}); + this->checkUnite({{B, MID}, {D, MAX}}, {{MIN, A}, {C, C}}, + {{MIN, A}, {B, MID}, {C, C}, {D, MAX}}); + this->checkUnite({{MIN, A}, {C, C}}, {{B, MID}, {D, MAX}}, + {{MIN, A}, {B, MID}, {C, C}, {D, MAX}}); + this->checkUnite({{MAX, MAX}}, {A, B}, {{A, B}, {MAX, MAX}}); + this->checkUnite({{MIN, MIN}}, {A, B}, {{MIN, MIN}, {A, B}}); + + // RHS is inside LHS. + // RHS => ___ + // LHS => ___/___\___ = ___________ + // ___/__/_____\__\___ ___/___________\___ + this->checkUnite({{A, C}}, MID, {{A, C}}); + this->checkUnite({{A, D}}, {B, C}, {{A, D}}); + + // RHS wraps LHS. + // RHS => _________ + // LHS => / _____ \ = ___________ + // ___/__/_____\__\___ ___/___________\___ + this->checkUnite({{MID, MID}}, {A, D}, {{A, D}}); + this->checkUnite({{B, C}}, {A, D}, {{A, D}}); + this->checkUnite({{A, B}}, {MIN, MAX}, {{MIN, MAX}}); + + // RHS equals to LHS. + // RHS => _________ + // LHS => /_________\ = ___________ + // ___/___________\___ ___/___________\___ + this->checkUnite({{MIN, MIN}}, MIN, {{MIN, MIN}}); + this->checkUnite({{A, B}}, {A, B}, {{A, B}}); + this->checkUnite({{MAX, MAX}}, {{MAX, MAX}}, {{MAX, MAX}}); + this->checkUnite({{MIN, MIN}}, {{MIN, MIN}}, {{MIN, MIN}}); + + // RHS intersects right of LHS. + // RHS => ______ + // LHS => ___/____ \ = ___________ + // ___/__/_____\__\___ ___/___________\___ + this->checkUnite({{A, C}}, C, {{A, C}}); + this->checkUnite({{A, C}}, {B, D}, {{A, D}}); + + // RHS intersects left of LHS. + // RHS => ______ + // LHS => / ____\___ = ___________ + // ___/__/_____\__\___ ___/___________\___ + this->checkUnite({{B, D}}, B, {{B, D}}); + this->checkUnite({{B, D}}, {A, C}, {{A, D}}); + + // RHS adjacent to LHS on right. + // RHS => _____ + // LHS => ______ / \ = _______________ + // _/______\/_______\_ _/_______________\_ + this->checkUnite({{A, B - 1}}, B, {{A, B}}); + this->checkUnite({{A, C}}, {C + 1, D}, {{A, D}}); + + // RHS adjacent to LHS on left. + // RHS => _____ + // LHS => / \ ______ = _______________ + // _/_______\/______\_ _/_______________\_ + this->checkUnite({{B + 1, C}}, B, {{B, C}}); + this->checkUnite({{B, D}}, {A, B - 1}, {{A, D}}); + + // RHS adjacent to LHS in between. + // RHS => ___ + // LHS => ___ / \ ___ = _______________ + // _/___\/_____\/___\_ _/_______________\_ + this->checkUnite({{A, MID - 1}, {MID + 1, D}}, MID, {{A, D}}); + this->checkUnite({{MIN, A}, {D, MAX}}, {A + 1, D - 1}, {{MIN, MAX}}); + + // RHS adjacent to LHS on the outside. + // RHS => __ __ + // LHS => / \ ___ / \ = _______________ + // _/____\/___\/____\_ _/_______________\_ + this->checkUnite({{C, C}}, {{A, C - 1}, {C + 1, D}}, {{A, D}}); + this->checkUnite({{B, MID}}, {{A, B - 1}, {MID + 1, D}}, {{A, D}}); + + // RHS wraps two subranges of LHS. + // RHS => ___________ + // LHS => / ___ ___ \ = _____________ + // __/_/___\_/___\_\__ __/_____________\__ + this->checkUnite({{B, B}, {MID, MID}, {C, C}}, {{A, D}}, {{A, D}}); + this->checkUnite({{A, B}, {MID, C}}, {{MIN, D}}, {{MIN, D}}); + + // RHS intersects two subranges of LHS. + // RHS => _________ + // LHS => __/__ _\__ = _______________ + // _/_/___\____/__\_\_ _/_______________\_ + this->checkUnite({{MIN, B}, {C, MAX}}, {{A, D}}, {{MIN, MAX}}); + + // Multiple intersections. + + // RHS => + // LHS => /\ /\ = __ __ + // _/__\_/__\_/\_/\_/\_ _/__\_/__\_/\_/\_/\_ + this->checkUnite({{MIN, A}, {A + 2, B}}, {{MID, C}, {C + 2, D - 2}, {D, MAX}}, + {{MIN, A}, {A + 2, B}, {MID, C}, {C + 2, D - 2}, {D, MAX}}); + this->checkUnite({{MIN, MIN}, {A, A}}, {{B, B}, {C, C}, {MAX, MAX}}, + {{MIN, MIN}, {A, A}, {B, B}, {C, C}, {MAX, MAX}}); + + // RHS => + // LHS => /\ /\ = __ __ + // _/\_/\_/\__/__\_/__\_ _/\_/\_/\_/__\_/__\_ + this->checkUnite({{C + 2, D - 2}, {D, MAX}}, {{MIN, A}, {A + 2, B}, {MID, C}}, + {{MIN, A}, {A + 2, B}, {MID, C}, {C + 2, D - 2}, {D, MAX}}); + this->checkUnite({{C, C}, {MAX, MAX}}, {{MIN, MIN}, {A, A}, {B, B}}, + {{MIN, MIN}, {A, A}, {B, B}, {C, C}, {MAX, MAX}}); + + // RHS => + // LHS => _ /\ _ /\ _ /\ = + // _/_\_/__\_/_\_/__\_/_\_/__\_ + // + // RSLT => _ __ _ __ _ __ + // _/_\_/__\_/_\_/__\_/_\_/__\_ + this->checkUnite({{A + 2, B}, {MID + 2, C}, {D + 2, MAX}}, + {{MIN, A}, {B + 2, MID}, {C + 2, D}}, + {{MIN, A}, + {A + 2, B}, + {B + 2, MID}, + {MID + 2, C}, + {C + 2, D}, + {D + 2, MAX}}); + this->checkUnite({{A, A}, {C, C}, {MAX, MAX}}, {{MIN, MIN}, {B, B}, {D, D}}, + {{MIN, MIN}, {A, A}, {B, B}, {C, C}, {D, D}, {MAX, MAX}}); + + // RHS => + // LHS => /\ _ /\ _ /\ _ = + // _/__\_/_\_/__\_/_\_/__\_/_\_ + // + // RSLT => __ _ __ _ __ _ + // _/__\_/_\_/__\_/_\_/__\_/_\_ + this->checkUnite({{MIN, A}, {B + 2, MID}, {C + 2, D}}, + {{A + 2, B}, {MID + 2, C}, {D + 2, MAX}}, + {{MIN, A}, + {A + 2, B}, + {B + 2, MID}, + {MID + 2, C}, + {C + 2, D}, + {D + 2, MAX}}); + this->checkUnite({{MIN, MIN}, {B, B}, {D, D}}, {{A, A}, {C, C}, {MAX, MAX}}, + {{MIN, MIN}, {A, A}, {B, B}, {C, C}, {D, D}, {MAX, MAX}}); + + // RHS => _ __ _ + // LHS => /_\ /_ \ _ / \ = ___ ____________ + // _/___\_/__\_\/_\/___\_ _/___\_/____________\_ + this->checkUnite({{MIN, A}, {B, C}, {D, MAX}}, + {{MIN, A}, {B, C - 2}, {C + 1, D - 1}}, + {{MIN, A}, {B, MAX}}); + this->checkUnite({{A, A}, {B, MID}, {D, D}}, + {{A, A}, {B, B}, {MID + 1, D - 1}}, {{A, A}, {B, D}}); + + // RHS => ___ ___ + // LHS => /\ _/_ \_ / _ \ /\ = + // _/\_/__\//__\ /\\_/_/_\_\_/__\_ + // + // RSLT => ___________ _____ __ + // _/\_/___________\_/_____\_/__\_ + this->checkUnite({{A, B - 1}, {B + 1, C - 1}, {C + 2, D}, {MAX - 1, MAX}}, + {{MIN, MIN}, {B, MID}, {MID + 1, C}, {C + 4, D - 1}}, + {{MIN, MIN}, {A, C}, {C + 2, D}, {MAX - 1, MAX}}); +}