[clang] [analyzer] Limit Store by region-store-binding-limit (PR #127602)
Balázs Benics via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 20 04:26:04 PST 2025
https://github.com/balazs-benics-sonarsource updated https://github.com/llvm/llvm-project/pull/127602
>From f5cd6b22fb83c0bfb584717cde6899cd65fc1274 Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Wed, 5 Feb 2025 17:13:34 +0100
Subject: [PATCH 01/12] [analyzer] Limit Store by region-store-binding-limit
In our test pool, the max entry point RT was improved by this change:
1'181 seconds (~19.7 minutes) -> 94 seconds (1.6 minutes)
BTW, the 1.6 minutes is still really bad. But a few orders of magnitude
better than it was before.
This was the most servere RT edge-case as you can see from the numbers.
There are are more known RT bottlenecks, such as:
- Large environment sizes, and `removeDead`. See more about the failed
attempt on improving it at:
https://discourse.llvm.org/t/unsuccessful-attempts-to-fix-a-slow-analysis-case-related-to-removedead-and-environment-size/84650
- Large chunk of time could be spend inside `assume`, to reach a fixed
point. This is something we want to look into a bit later if we have
time.
We have 3'075'607 entry points in our test set.
About 393'352 entry points ran longer than 1 second when measured.
To give a sense of the distribution, if we ignore the slowest 500
entry points, then the maximum entry point runs for about 14 seconds.
These 500 slow entry points are in 332 translation units.
By this patch, out of the slowest 500 entry points, 72 entry points
were improved by at least 10x after this change.
We measured no RT regression on the "usual" entry points.
CPP-6092
---
.../StaticAnalyzer/Core/AnalyzerOptions.def | 8 +
.../Core/PathSensitive/ExprEngine.h | 2 +-
.../StaticAnalyzer/Core/PathSensitive/Store.h | 10 +-
.../lib/StaticAnalyzer/Core/ProgramState.cpp | 18 +-
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 210 +++++++----
clang/lib/StaticAnalyzer/Core/Store.cpp | 7 +-
clang/test/Analysis/analyzer-config.c | 1 +
clang/test/Analysis/region-store.cpp | 336 +++++++++++++++++-
clang/unittests/StaticAnalyzer/StoreTest.cpp | 7 +-
9 files changed, 525 insertions(+), 74 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
index a9b8d0753673b..f05c8724d583d 100644
--- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
+++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
@@ -483,6 +483,14 @@ ANALYZER_OPTION(
"behavior, set the option to 0.",
5)
+ANALYZER_OPTION(
+ unsigned, RegionStoreMaxBindingFanOut, "region-store-max-binding-fanout",
+ "This option limits how many sub-bindings a single binding operation can "
+ "scatter into. For example, binding an array would scatter into binding "
+ "each individual element. Setting this to zero means unlimited, but then "
+ "modelling large array initializers may take proportional time to their "
+ "size.", 100)
+
//===----------------------------------------------------------------------===//
// String analyzer options.
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 20c446e33ef9a..9fd07ce47175c 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -659,13 +659,13 @@ class ExprEngine {
SVal Loc, SVal Val,
const LocationContext *LCtx);
+public:
/// A simple wrapper when you only need to notify checkers of pointer-escape
/// of some values.
ProgramStateRef escapeValues(ProgramStateRef State, ArrayRef<SVal> Vs,
PointerEscapeKind K,
const CallEvent *Call = nullptr) const;
-public:
// FIXME: 'tag' should be removed, and a LocationContext should be used
// instead.
// FIXME: Comment on the meaning of the arguments, when 'St' may not
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
index 332855a3c9c45..ebf00d49b6cc8 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
@@ -50,6 +50,14 @@ class SymbolReaper;
using InvalidatedSymbols = llvm::DenseSet<SymbolRef>;
+struct BindResult {
+ StoreRef ResultingStore;
+
+ // If during the bind operation we exhaust the allowed binding budget, we set
+ // this to the beginning of the escaped part of the region.
+ llvm::SmallVector<SVal, 0> FailedToBindValues;
+};
+
class StoreManager {
protected:
SValBuilder &svalBuilder;
@@ -105,7 +113,7 @@ class StoreManager {
/// \return A StoreRef object that contains the same
/// bindings as \c store with the addition of having the value specified
/// by \c val bound to the location given for \c loc.
- virtual StoreRef Bind(Store store, Loc loc, SVal val) = 0;
+ virtual BindResult Bind(Store store, Loc loc, SVal val) = 0;
/// Return a store with the specified value bound to all sub-regions of the
/// region. The region must not have previous bindings. If you need to
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 34ab2388cbd2f..325b44c9cb05c 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -116,13 +116,23 @@ ProgramStateRef ProgramState::bindLoc(Loc LV,
const LocationContext *LCtx,
bool notifyChanges) const {
ProgramStateManager &Mgr = getStateManager();
- ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
- LV, V));
+ ExprEngine &Eng = Mgr.getOwningEngine();
+ BindResult BindRes = Mgr.StoreMgr->Bind(getStore(), LV, V);
+ ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
const MemRegion *MR = LV.getAsRegion();
+
+ // We must always notify the checkers for failing binds because otherwise they
+ // may keep stale traits for these symbols.
+ // Eg., Malloc checker may report leaks if we failed to bind that symbol.
+ if (!BindRes.FailedToBindValues.empty()) {
+ State =
+ Eng.escapeValues(State, BindRes.FailedToBindValues, PSK_EscapeOnBind);
+ }
+
if (MR && notifyChanges)
- return Mgr.getOwningEngine().processRegionChange(newState, MR, LCtx);
+ return Eng.processRegionChange(State, MR, LCtx);
- return newState;
+ return State;
}
ProgramStateRef
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index d01b6ae55f611..ee821f9b19e73 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -164,6 +164,7 @@ namespace {
class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
ClusterBindings> {
ClusterBindings::Factory *CBFactory;
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind;
// This flag indicates whether the current bindings are within the analysis
// that has started from main(). It affects how we perform loads from
@@ -176,31 +177,59 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
// however that would have made the manager needlessly stateful.
bool IsMainAnalysis;
+ unsigned BindingsLeft;
+
public:
+ unsigned bindingsLeft() const { return BindingsLeft; }
+
+ bool hasExhaustedBindingLimit() const { return BindingsLeft == 0; }
+
+ RegionBindingsRef escapeValue(SVal V) const {
+ assert(EscapedValuesDuringBind);
+ EscapedValuesDuringBind->push_back(V);
+ return *this;
+ }
+ RegionBindingsRef escapeValues(nonloc::CompoundVal::iterator Begin,
+ nonloc::CompoundVal::iterator End) const {
+ for (SVal V : llvm::make_range(Begin, End))
+ escapeValue(V);
+ return *this;
+ }
+
typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>
ParentTy;
RegionBindingsRef(ClusterBindings::Factory &CBFactory,
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind,
const RegionBindings::TreeTy *T,
- RegionBindings::TreeTy::Factory *F,
- bool IsMainAnalysis)
- : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F),
- CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {}
-
- RegionBindingsRef(const ParentTy &P,
- ClusterBindings::Factory &CBFactory,
- bool IsMainAnalysis)
- : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P),
- CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {}
+ RegionBindings::TreeTy::Factory *F, bool IsMainAnalysis,
+ unsigned BindingsLeft)
+ : RegionBindingsRef(ParentTy(T, F), CBFactory, EscapedValuesDuringBind,
+ IsMainAnalysis, BindingsLeft) {}
+
+ RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory,
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind,
+ bool IsMainAnalysis, unsigned BindingsLeft)
+ : ParentTy(P), CBFactory(&CBFactory),
+ EscapedValuesDuringBind(EscapedValuesDuringBind),
+ IsMainAnalysis(IsMainAnalysis), BindingsLeft(BindingsLeft) {}
+
+ RegionBindingsRef add(key_type_ref K, data_type_ref D,
+ unsigned NewBindingsLeft) const {
+ return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
+ *CBFactory, EscapedValuesDuringBind,
+ IsMainAnalysis, NewBindingsLeft);
+ }
RegionBindingsRef add(key_type_ref K, data_type_ref D) const {
- return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
- *CBFactory, IsMainAnalysis);
+ unsigned NewBindingsLeft = BindingsLeft ? BindingsLeft - 1 : BindingsLeft;
+ return add(K, D, NewBindingsLeft);
}
RegionBindingsRef remove(key_type_ref K) const {
return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K),
- *CBFactory, IsMainAnalysis);
+ *CBFactory, EscapedValuesDuringBind,
+ IsMainAnalysis, BindingsLeft);
}
RegionBindingsRef addBinding(BindingKey K, SVal V) const;
@@ -345,14 +374,21 @@ RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
}
RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
+ // If we are about to exhaust the binding limit, highjack this bind call for
+ // the default binding.
+ if (BindingsLeft == 1) {
+ escapeValue(V);
+ K = BindingKey::Make(K.getRegion(), BindingKey::Default);
+ V = UnknownVal();
+ }
+
const MemRegion *Base = K.getBaseRegion();
const ClusterBindings *ExistingCluster = lookup(Base);
ClusterBindings Cluster =
(ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap());
- ClusterBindings NewCluster = CBFactory->add(Cluster, K, V);
- return add(Base, NewCluster);
+ return add(Base, CBFactory->add(Cluster, K, V));
}
@@ -417,7 +453,7 @@ class RegionStoreManager : public StoreManager {
///
/// This is controlled by 'region-store-small-struct-limit' option.
/// To disable all small-struct-dependent behavior, set the option to "0".
- unsigned SmallStructLimit;
+ const unsigned SmallStructLimit;
/// The largest number of element an array can have and still be
/// considered "small".
@@ -427,7 +463,13 @@ class RegionStoreManager : public StoreManager {
///
/// This is controlled by 'region-store-small-struct-limit' option.
/// To disable all small-struct-dependent behavior, set the option to "0".
- unsigned SmallArrayLimit;
+ const unsigned SmallArrayLimit;
+
+ /// The number of bindings a single bind operation can scatter into.
+ /// For example, binding the initializer-list of an array would recurse and
+ /// bind all the individual array elements, potentially causing scalability
+ /// issues.
+ const unsigned RegionStoreMaxBindingFanOut;
/// A helper used to populate the work list with the given set of
/// regions.
@@ -435,15 +477,21 @@ class RegionStoreManager : public StoreManager {
ArrayRef<SVal> Values,
InvalidatedRegions *TopLevelRegions);
+ const AnalyzerOptions &getOptions() {
+ return StateMgr.getOwningEngine().getAnalysisManager().options;
+ }
+
public:
RegionStoreManager(ProgramStateManager &mgr)
: StoreManager(mgr), RBFactory(mgr.getAllocator()),
- CBFactory(mgr.getAllocator()), SmallStructLimit(0), SmallArrayLimit(0) {
- ExprEngine &Eng = StateMgr.getOwningEngine();
- AnalyzerOptions &Options = Eng.getAnalysisManager().options;
- SmallStructLimit = Options.RegionStoreSmallStructLimit;
- SmallArrayLimit = Options.RegionStoreSmallArrayLimit;
- }
+ CBFactory(mgr.getAllocator()),
+ SmallStructLimit(getOptions().RegionStoreSmallStructLimit),
+ SmallArrayLimit(getOptions().RegionStoreSmallArrayLimit),
+ RegionStoreMaxBindingFanOut(
+ getOptions().RegionStoreMaxBindingFanOut == 0
+ ? -1U
+ : getOptions().RegionStoreMaxBindingFanOut +
+ /*for the default binding*/ 1) {}
/// setImplicitDefaultValue - Set the default binding for the provided
/// MemRegion to the value implicitly defined for compound literals when
@@ -465,9 +513,13 @@ class RegionStoreManager : public StoreManager {
bool IsMainAnalysis = false;
if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl()))
IsMainAnalysis = FD->isMain() && !Ctx.getLangOpts().CPlusPlus;
- return StoreRef(RegionBindingsRef(
- RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory),
- CBFactory, IsMainAnalysis).asStore(), *this);
+ return StoreRef(
+ RegionBindingsRef(
+ RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory),
+ CBFactory, /*EscapedValuesDuringBind=*/nullptr, IsMainAnalysis,
+ RegionStoreMaxBindingFanOut)
+ .asStore(),
+ *this);
}
//===-------------------------------------------------------------------===//
@@ -502,9 +554,13 @@ class RegionStoreManager : public StoreManager {
QualType ElemT);
public: // Part of public interface to class.
-
- StoreRef Bind(Store store, Loc LV, SVal V) override {
- return StoreRef(bind(getRegionBindings(store), LV, V).asStore(), *this);
+ BindResult Bind(Store store, Loc LV, SVal V) override {
+ llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
+ return BindResult{
+ StoreRef(bind(getRegionBindings(store, &EscapedValuesDuringBind), LV, V)
+ .asStore(),
+ *this),
+ EscapedValuesDuringBind};
}
RegionBindingsRef bind(RegionBindingsConstRef B, Loc LV, SVal V);
@@ -513,7 +569,7 @@ class RegionStoreManager : public StoreManager {
// a default value.
StoreRef BindDefaultInitial(Store store, const MemRegion *R,
SVal V) override {
- RegionBindingsRef B = getRegionBindings(store);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
// Use other APIs when you have to wipe the region that was initialized
// earlier.
assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) &&
@@ -538,7 +594,7 @@ class RegionStoreManager : public StoreManager {
if (BR->getDecl()->isEmpty())
return StoreRef(store, *this);
- RegionBindingsRef B = getRegionBindings(store);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
SVal V = svalBuilder.makeZeroVal(Ctx.CharTy);
B = removeSubRegionBindings(B, cast<SubRegion>(R));
B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
@@ -587,14 +643,14 @@ class RegionStoreManager : public StoreManager {
StoreRef killBinding(Store ST, Loc L) override;
void incrementReferenceCount(Store store) override {
- getRegionBindings(store).manualRetain();
+ getRegionBindingsWithUnboundedLimit(store).manualRetain();
}
/// If the StoreManager supports it, decrement the reference count of
/// the specified Store object. If the reference count hits 0, the memory
/// associated with the object is recycled.
void decrementReferenceCount(Store store) override {
- getRegionBindings(store).manualRelease();
+ getRegionBindingsWithUnboundedLimit(store).manualRelease();
}
bool includedInBindings(Store store, const MemRegion *region) const override;
@@ -613,7 +669,7 @@ class RegionStoreManager : public StoreManager {
/// else
/// return symbolic
SVal getBinding(Store S, Loc L, QualType T) override {
- return getBinding(getRegionBindings(S), L, T);
+ return getBinding(getRegionBindingsWithUnboundedLimit(S), L, T);
}
std::optional<SVal> getUniqueDefaultBinding(RegionBindingsConstRef B,
@@ -622,7 +678,7 @@ class RegionStoreManager : public StoreManager {
getUniqueDefaultBinding(nonloc::LazyCompoundVal LCV) const;
std::optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
- RegionBindingsRef B = getRegionBindings(S);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(S);
// Default bindings are always applied over a base region so look up the
// base region's default binding, otherwise the lookup will fail when R
// is at an offset from R->getBaseRegion().
@@ -700,21 +756,23 @@ class RegionStoreManager : public StoreManager {
// Utility methods.
//===------------------------------------------------------------------===//
- RegionBindingsRef getRegionBindings(Store store) const {
- llvm::PointerIntPair<Store, 1, bool> Ptr;
- Ptr.setFromOpaqueValue(const_cast<void *>(store));
- return RegionBindingsRef(
- CBFactory,
- static_cast<const RegionBindings::TreeTy *>(Ptr.getPointer()),
- RBFactory.getTreeFactory(),
- Ptr.getInt());
+ RegionBindingsRef
+ getRegionBindings(Store store,
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind) const {
+ return getRegionBindingsImpl(store, EscapedValuesDuringBind,
+ /*BindingsLeft=*/RegionStoreMaxBindingFanOut);
+ }
+
+ RegionBindingsRef getRegionBindingsWithUnboundedLimit(Store store) const {
+ return getRegionBindingsImpl(store, /*EscapedValuesDuringBind=*/nullptr,
+ /*BindingsLeft=*/-1U);
}
void printJson(raw_ostream &Out, Store S, const char *NL = "\n",
unsigned int Space = 0, bool IsDot = false) const override;
void iterBindings(Store store, BindingsHandler& f) override {
- RegionBindingsRef B = getRegionBindings(store);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
for (const auto &[Region, Cluster] : B) {
for (const auto &[Key, Value] : Cluster) {
if (!Key.isDirect())
@@ -727,6 +785,19 @@ class RegionStoreManager : public StoreManager {
}
}
}
+
+private:
+ RegionBindingsRef
+ getRegionBindingsImpl(Store store,
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind,
+ unsigned BindingsLeft) const {
+ llvm::PointerIntPair<Store, 1, bool> Ptr;
+ Ptr.setFromOpaqueValue(const_cast<void *>(store));
+ return RegionBindingsRef(
+ CBFactory, EscapedValuesDuringBind,
+ static_cast<const RegionBindings::TreeTy *>(Ptr.getPointer()),
+ RBFactory.getTreeFactory(), Ptr.getInt(), BindingsLeft);
+ }
};
} // end anonymous namespace
@@ -852,7 +923,7 @@ class ClusterAnalysis {
bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R,
ScanReachableSymbols &Callbacks) {
assert(R == R->getBaseRegion() && "Should only be called for base regions");
- RegionBindingsRef B = getRegionBindings(S);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(S);
const ClusterBindings *Cluster = B.lookup(R);
if (!Cluster)
@@ -1038,7 +1109,9 @@ RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
if (Result.isEmpty())
return B.remove(ClusterHead);
- return B.add(ClusterHead, Result.asImmutableMap());
+ // Make this "add" free by using the old "BindingsLeft".
+ return B.add(ClusterHead, Result.asImmutableMap(),
+ /*BindingsLeft=*/B.bindingsLeft());
}
namespace {
@@ -1375,7 +1448,7 @@ StoreRef RegionStoreManager::invalidateRegions(
GlobalsFilter = GFK_None;
}
- RegionBindingsRef B = getRegionBindings(store);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
InvalidateRegionsWorker W(*this, StateMgr, B, S, Count, LCtx, IS, ITraits,
Invalidated, GlobalsFilter);
@@ -2136,8 +2209,9 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
const SubRegion *lazyBindingRegion = nullptr;
std::tie(lazyBindingStore, lazyBindingRegion) = findLazyBinding(B, R, R);
if (lazyBindingRegion)
- return getLazyBinding(lazyBindingRegion,
- getRegionBindings(lazyBindingStore));
+ return getLazyBinding(
+ lazyBindingRegion,
+ getRegionBindingsWithUnboundedLimit(lazyBindingStore));
// Record whether or not we see a symbolic index. That can completely
// be out of scope of our lookup.
@@ -2314,7 +2388,7 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
SValListTy List;
const SubRegion *LazyR = LCV.getRegion();
- RegionBindingsRef B = getRegionBindings(LCV.getStore());
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(LCV.getStore());
// If this region had /no/ bindings at the time, there are no interesting
// values to return.
@@ -2377,7 +2451,7 @@ SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B,
bool RegionStoreManager::includedInBindings(Store store,
const MemRegion *region) const {
- RegionBindingsRef B = getRegionBindings(store);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
region = region->getBaseRegion();
// Quick path: if the base is the head of a cluster, the region is live.
@@ -2406,18 +2480,23 @@ bool RegionStoreManager::includedInBindings(Store store,
StoreRef RegionStoreManager::killBinding(Store ST, Loc L) {
if (std::optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>())
if (const MemRegion* R = LV->getRegion())
- return StoreRef(getRegionBindings(ST).removeBinding(R)
- .asImmutableMap()
- .getRootWithoutRetain(),
+ return StoreRef(getRegionBindingsWithUnboundedLimit(ST)
+ .removeBinding(R)
+ .asImmutableMap()
+ .getRootWithoutRetain(),
*this);
return StoreRef(ST, *this);
}
-RegionBindingsRef
-RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) {
+RegionBindingsRef RegionStoreManager::bind(RegionBindingsConstRef B, Loc L,
+ SVal V) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bind",
[&L]() { return locDescr(L); });
+
+ if (B.hasExhaustedBindingLimit())
+ return B.escapeValue(V);
+
// We only care about region locations.
auto MemRegVal = L.getAs<loc::MemRegionVal>();
if (!MemRegVal)
@@ -2511,7 +2590,8 @@ std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
auto Idx = svalBuilder.makeArrayIndex(i);
const ElementRegion *SrcER =
MRMgr.getElementRegion(Ty, Idx, LCV.getRegion(), Ctx);
- SVal V = getBindingForElement(getRegionBindings(LCV.getStore()), SrcER);
+ SVal V = getBindingForElement(
+ getRegionBindingsWithUnboundedLimit(LCV.getStore()), SrcER);
const ElementRegion *DstER = MRMgr.getElementRegion(Ty, Idx, R, Ctx);
NewB = bind(NewB, loc::MemRegionVal(DstER), V);
@@ -2566,6 +2646,8 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
// The init list might be shorter than the array length.
if (VI == VE)
break;
+ if (NewB.hasExhaustedBindingLimit())
+ return NewB.escapeValues(VI, VE);
NonLoc Idx = svalBuilder.makeArrayIndex(i);
const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx);
@@ -2645,7 +2727,7 @@ RegionStoreManager::getUniqueDefaultBinding(RegionBindingsConstRef B,
std::optional<SVal>
RegionStoreManager::getUniqueDefaultBinding(nonloc::LazyCompoundVal LCV) const {
- RegionBindingsConstRef B = getRegionBindings(LCV.getStore());
+ auto B = getRegionBindingsWithUnboundedLimit(LCV.getStore());
return getUniqueDefaultBinding(B, LCV.getRegion());
}
@@ -2702,7 +2784,8 @@ std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
for (const FieldDecl *Field : Fields) {
const FieldRegion *SourceFR = MRMgr.getFieldRegion(Field, LCV.getRegion());
- SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR);
+ SVal V = getBindingForField(
+ getRegionBindingsWithUnboundedLimit(LCV.getStore()), SourceFR);
const FieldRegion *DestFR = MRMgr.getFieldRegion(Field, R);
NewB = bind(NewB, loc::MemRegionVal(DestFR), V);
@@ -2782,6 +2865,8 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
if (VI == VE)
break;
+ if (NewB.hasExhaustedBindingLimit())
+ return NewB.escapeValues(VI, VE);
QualType BTy = B.getType();
assert(BTy->isStructureOrClassType() && "Base classes must be classes!");
@@ -2805,6 +2890,9 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
if (VI == VE)
break;
+ if (NewB.hasExhaustedBindingLimit())
+ return NewB.escapeValues(VI, VE);
+
// Skip any unnamed bitfields to stay in sync with the initializers.
if (FI->isUnnamedBitField())
continue;
@@ -2984,7 +3072,7 @@ bool RemoveDeadBindingsWorker::UpdatePostponed() {
StoreRef RegionStoreManager::removeDeadBindings(Store store,
const StackFrameContext *LCtx,
SymbolReaper& SymReaper) {
- RegionBindingsRef B = getRegionBindings(store);
+ RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx);
W.GenerateClusters();
@@ -3014,7 +3102,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
void RegionStoreManager::printJson(raw_ostream &Out, Store S, const char *NL,
unsigned int Space, bool IsDot) const {
- RegionBindingsRef Bindings = getRegionBindings(S);
+ RegionBindingsRef Bindings = getRegionBindingsWithUnboundedLimit(S);
Indent(Out, Space, IsDot) << "\"store\": ";
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index b436dd746d21f..bb1d7cb243474 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -51,8 +51,11 @@ StoreRef StoreManager::enterStackFrame(Store OldStore,
SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings;
Call.getInitialStackFrameContents(LCtx, InitialBindings);
- for (const auto &I : InitialBindings)
- Store = Bind(Store.getStore(), I.first.castAs<Loc>(), I.second);
+ for (const auto &[Location, Val] : InitialBindings) {
+ BindResult Res = Bind(Store.getStore(), Location.castAs<Loc>(), Val);
+ assert(Res.FailedToBindValues.empty());
+ Store = Res.ResultingStore;
+ }
return Store;
}
diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c
index f6a49680917ac..e3f276da57703 100644
--- a/clang/test/Analysis/analyzer-config.c
+++ b/clang/test/Analysis/analyzer-config.c
@@ -116,6 +116,7 @@
// CHECK-NEXT: osx.NumberObjectConversion:Pedantic = false
// CHECK-NEXT: osx.cocoa.RetainCount:TrackNSCFStartParam = false
// CHECK-NEXT: prune-paths = true
+// CHECK-NEXT: region-store-max-binding-fanout = 100
// CHECK-NEXT: region-store-small-array-limit = 5
// CHECK-NEXT: region-store-small-struct-limit = 2
// CHECK-NEXT: report-in-main-source-file = false
diff --git a/clang/test/Analysis/region-store.cpp b/clang/test/Analysis/region-store.cpp
index ab179ceb1acc8..cab0bd75edf9b 100644
--- a/clang/test/Analysis/region-store.cpp
+++ b/clang/test/Analysis/region-store.cpp
@@ -1,5 +1,15 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s
-// expected-no-diagnostics
+// DEFINE: %{analyzer} = %clang_analyze_cc1 -Wno-array-bounds %s \
+// DEFINE: -analyzer-checker=core,cplusplus,unix,debug.ExprInspection
+
+// RUN: %{analyzer} -verify=default
+// RUN: %{analyzer} -analyzer-config region-store-max-binding-fanout=10 -verify=limit10
+// RUN: %{analyzer} -analyzer-config region-store-max-binding-fanout=0 -verify=unlimited
+
+template <class T> void clang_analyzer_dump(T);
+void clang_analyzer_eval(bool);
+
+template <class... Ts> void escape(Ts...);
+bool coin();
class Loc {
int x;
@@ -26,3 +36,325 @@ int radar13445834(Derived *Builder, Loc l) {
return Builder->accessBase();
}
+
+void boundedNumberOfBindings() {
+ int array[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22};
+ clang_analyzer_dump(array[0]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{0 S32b}}
+ clang_analyzer_dump(array[1]); // default-warning {{1 S32b}} unlimited-warning {{1 S32b}} limit10-warning {{1 S32b}}
+ clang_analyzer_dump(array[2]); // default-warning {{2 S32b}} unlimited-warning {{2 S32b}} limit10-warning {{2 S32b}}
+ clang_analyzer_dump(array[3]); // default-warning {{3 S32b}} unlimited-warning {{3 S32b}} limit10-warning {{3 S32b}}
+ clang_analyzer_dump(array[4]); // default-warning {{4 S32b}} unlimited-warning {{4 S32b}} limit10-warning {{4 S32b}}
+ clang_analyzer_dump(array[5]); // default-warning {{5 S32b}} unlimited-warning {{5 S32b}} limit10-warning {{5 S32b}}
+ clang_analyzer_dump(array[6]); // default-warning {{6 S32b}} unlimited-warning {{6 S32b}} limit10-warning {{6 S32b}}
+ clang_analyzer_dump(array[7]); // default-warning {{7 S32b}} unlimited-warning {{7 S32b}} limit10-warning {{7 S32b}}
+ clang_analyzer_dump(array[8]); // default-warning {{8 S32b}} unlimited-warning {{8 S32b}} limit10-warning {{8 S32b}}
+ clang_analyzer_dump(array[9]); // default-warning {{9 S32b}} unlimited-warning {{9 S32b}} limit10-warning {{9 S32b}}
+ clang_analyzer_dump(array[10]); // default-warning {{10 S32b}} unlimited-warning {{10 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[11]); // default-warning {{11 S32b}} unlimited-warning {{11 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[12]); // default-warning {{12 S32b}} unlimited-warning {{12 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[13]); // default-warning {{13 S32b}} unlimited-warning {{13 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[14]); // default-warning {{14 S32b}} unlimited-warning {{14 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[15]); // default-warning {{15 S32b}} unlimited-warning {{15 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[16]); // default-warning {{16 S32b}} unlimited-warning {{16 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[17]); // default-warning {{17 S32b}} unlimited-warning {{17 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[18]); // default-warning {{18 S32b}} unlimited-warning {{18 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[19]); // default-warning {{19 S32b}} unlimited-warning {{19 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[20]); // default-warning {{20 S32b}} unlimited-warning {{20 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[21]); // default-warning {{21 S32b}} unlimited-warning {{21 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[22]); // default-warning {{22 S32b}} unlimited-warning {{22 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[23]); // see below see below limit10-warning {{Unknown}}
+ // default-warning at -1 {{1st function call argument is an uninitialized value}}
+ // unlimited-warning at -2 {{1st function call argument is an uninitialized value}}
+ // FIXME: The last dump at index 23 should be Undefined due to out of bounds access.
+}
+
+void incompleteInitList() {
+ int array[23] {0,1,2,3,4,5,6,7,8,9,10,11 /*rest are zeroes*/ };
+ clang_analyzer_dump(array[0]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{0 S32b}}
+ clang_analyzer_dump(array[1]); // default-warning {{1 S32b}} unlimited-warning {{1 S32b}} limit10-warning {{1 S32b}}
+ clang_analyzer_dump(array[2]); // default-warning {{2 S32b}} unlimited-warning {{2 S32b}} limit10-warning {{2 S32b}}
+ clang_analyzer_dump(array[3]); // default-warning {{3 S32b}} unlimited-warning {{3 S32b}} limit10-warning {{3 S32b}}
+ clang_analyzer_dump(array[4]); // default-warning {{4 S32b}} unlimited-warning {{4 S32b}} limit10-warning {{4 S32b}}
+ clang_analyzer_dump(array[5]); // default-warning {{5 S32b}} unlimited-warning {{5 S32b}} limit10-warning {{5 S32b}}
+ clang_analyzer_dump(array[6]); // default-warning {{6 S32b}} unlimited-warning {{6 S32b}} limit10-warning {{6 S32b}}
+ clang_analyzer_dump(array[7]); // default-warning {{7 S32b}} unlimited-warning {{7 S32b}} limit10-warning {{7 S32b}}
+ clang_analyzer_dump(array[8]); // default-warning {{8 S32b}} unlimited-warning {{8 S32b}} limit10-warning {{8 S32b}}
+ clang_analyzer_dump(array[9]); // default-warning {{9 S32b}} unlimited-warning {{9 S32b}} limit10-warning {{9 S32b}}
+ clang_analyzer_dump(array[10]); // default-warning {{10 S32b}} unlimited-warning {{10 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[11]); // default-warning {{11 S32b}} unlimited-warning {{11 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[12]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[13]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[14]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[15]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[16]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[17]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[18]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[19]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[20]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[21]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[22]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(array[23]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{Unknown}}
+ // FIXME: The last dump at index 23 should be Undefined due to out of bounds access.
+}
+
+struct Inner {
+ int first;
+ int second;
+};
+struct Outer {
+ Inner upper;
+ Inner lower;
+};
+
+void nestedStructInitLists() {
+ Outer array[]{ // 7*4: 28 values
+ {{00, 01}, {02, 03}},
+ {{10, 11}, {12, 13}},
+ {{20, 21}, {22, 23}},
+ {{30, 31}, {32, 33}},
+ {{40, 41}, {42, 43}},
+ {{50, 51}, {52, 53}},
+ {{60, 61}, {62, 63}},
+ };
+
+ int *p = (int*)array;
+ clang_analyzer_dump(p[0]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{0 S32b}}
+ clang_analyzer_dump(p[1]); // default-warning {{1 S32b}} unlimited-warning {{1 S32b}} limit10-warning {{1 S32b}}
+ clang_analyzer_dump(p[2]); // default-warning {{2 S32b}} unlimited-warning {{2 S32b}} limit10-warning {{2 S32b}}
+ clang_analyzer_dump(p[3]); // default-warning {{3 S32b}} unlimited-warning {{3 S32b}} limit10-warning {{3 S32b}}
+ clang_analyzer_dump(p[4]); // default-warning {{10 S32b}} unlimited-warning {{10 S32b}} limit10-warning {{10 S32b}}
+ clang_analyzer_dump(p[5]); // default-warning {{11 S32b}} unlimited-warning {{11 S32b}} limit10-warning {{11 S32b}}
+ clang_analyzer_dump(p[6]); // default-warning {{12 S32b}} unlimited-warning {{12 S32b}} limit10-warning {{12 S32b}}
+ clang_analyzer_dump(p[7]); // default-warning {{13 S32b}} unlimited-warning {{13 S32b}} limit10-warning {{13 S32b}}
+ clang_analyzer_dump(p[8]); // default-warning {{20 S32b}} unlimited-warning {{20 S32b}} limit10-warning {{20 S32b}}
+ clang_analyzer_dump(p[9]); // default-warning {{21 S32b}} unlimited-warning {{21 S32b}} limit10-warning {{21 S32b}}
+ clang_analyzer_dump(p[10]); // default-warning {{22 S32b}} unlimited-warning {{22 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[11]); // default-warning {{23 S32b}} unlimited-warning {{23 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[12]); // default-warning {{30 S32b}} unlimited-warning {{30 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[13]); // default-warning {{31 S32b}} unlimited-warning {{31 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[14]); // default-warning {{32 S32b}} unlimited-warning {{32 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[15]); // default-warning {{33 S32b}} unlimited-warning {{33 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[16]); // default-warning {{40 S32b}} unlimited-warning {{40 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[17]); // default-warning {{41 S32b}} unlimited-warning {{41 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[18]); // default-warning {{42 S32b}} unlimited-warning {{42 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[19]); // default-warning {{43 S32b}} unlimited-warning {{43 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[20]); // default-warning {{50 S32b}} unlimited-warning {{50 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[21]); // default-warning {{51 S32b}} unlimited-warning {{51 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[22]); // default-warning {{52 S32b}} unlimited-warning {{52 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[23]); // default-warning {{53 S32b}} unlimited-warning {{53 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[24]); // default-warning {{60 S32b}} unlimited-warning {{60 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[25]); // default-warning {{61 S32b}} unlimited-warning {{61 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[26]); // default-warning {{62 S32b}} unlimited-warning {{62 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[27]); // default-warning {{63 S32b}} unlimited-warning {{63 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(p[28]); // see below see below limit10-warning {{Unknown}}
+ // default-warning at -1 {{1st function call argument is an uninitialized value}}
+ // unlimited-warning at -2 {{1st function call argument is an uninitialized value}}
+ // FIXME: The last dump at index 28 should be Undefined due to out of bounds access.
+}
+
+void expectNoLeaksInWidenedInitLists() {
+ int *p[] {
+ new int(0),
+ new int(1),
+ new int(2),
+ new int(3),
+ new int(4),
+ new int(5),
+ new int(6),
+ new int(7),
+ new int(8),
+ new int(9),
+ new int(10),
+ new int(11),
+ new int(12),
+ new int(13),
+ new int(14),
+ new int(15),
+ new int(16),
+ new int(17),
+ new int(18),
+ new int(19),
+ new int(20),
+ new int(21),
+ new int(22),
+ new int(23),
+ new int(24),
+ };
+ clang_analyzer_dump(*p[0]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{0 S32b}}
+ clang_analyzer_dump(*p[12]); // default-warning {{12 S32b}} unlimited-warning {{12 S32b}} limit10-warning {{Unknown}}
+ escape(p); // no-leaks
+}
+
+void rawArrayWithSelfReference() {
+ // If a pointer to some object escapes, that pointed object should escape too.
+ // Consequently, if the 22th initializer would escape, then the p[6] should also escape - clobbering any loads from that location later.
+ int *p[25] = {
+ new int(0),
+ new int(1),
+ new int(2),
+ new int(3),
+ new int(4),
+ new int(5),
+ new int(6),
+ new int(7),
+ p[5], // Should be a pointer to the 6th array element, but we get Undefined as the analyzer thinks that "p" is not yet initialized, thus loading from index 5 is UB. This is wrong.
+ new int(9),
+ new int(10),
+ new int(11),
+ new int(12),
+ new int(13),
+ new int(14),
+ new int(15),
+ new int(16),
+ new int(17),
+ new int(18),
+ new int(19),
+ new int(20),
+ new int(21),
+ p[6], // Should be a pointer to the 6th array element, but we get Undefined as the analyzer thinks that "p" is not yet initialized, thus loading from index 5 is UB. This is wrong.
+ new int(23),
+ new int(24),
+ };
+ clang_analyzer_dump(*p[5]); // default-warning {{5 S32b}} unlimited-warning {{5 S32b}} limit10-warning {{5 S32b}}
+ clang_analyzer_dump(*p[6]); // default-warning {{6 S32b}} unlimited-warning {{6 S32b}} limit10-warning {{6 S32b}}
+
+ if (coin()) {
+ clang_analyzer_dump(*p[8]);
+ // default-warning at -1 {{Dereference of undefined pointer value}}
+ // unlimited-warning at -2 {{Dereference of undefined pointer value}}
+ // limit10-warning at -3 {{Dereference of undefined pointer value}}
+ }
+
+ clang_analyzer_dump(*p[12]); // default-warning {{12 S32b}} unlimited-warning {{12 S32b}} limit10-warning {{Unknown}}
+
+ if (coin()) {
+ clang_analyzer_dump(*p[22]);
+ // default-warning at -1 {{Dereference of undefined pointer value}}
+ // unlimited-warning at -2 {{Dereference of undefined pointer value}}
+ // limit10-warning at -3 {{Unknown}}
+ }
+
+ clang_analyzer_dump(*p[23]); // default-warning {{23 S32b}} unlimited-warning {{23 S32b}} limit10-warning {{Unknown}}
+
+ escape(p); // no-leaks
+}
+
+template <class T, unsigned Size> struct BigArray {
+ T array[Size];
+};
+
+void fieldArrayWithSelfReference() {
+ // Similar to "rawArrayWithSelfReference", but using an aggregate object and assignment operator to achieve the element-wise binds.
+ BigArray<int *, 25> p;
+ p = {
+ new int(0),
+ new int(1),
+ new int(2),
+ new int(3),
+ new int(4),
+ new int(5),
+ new int(6),
+ new int(7),
+ p.array[5], // Pointer to the 6th array element.
+ new int(9),
+ new int(10),
+ new int(11),
+ new int(12),
+ new int(13),
+ new int(14),
+ new int(15),
+ new int(16),
+ new int(17),
+ new int(18),
+ new int(19),
+ new int(20),
+ new int(21),
+ p.array[6], // Pointer to the 7th array element.
+ new int(23),
+ new int(24),
+ };
+ clang_analyzer_dump(*p.array[5]); // default-warning {{5 S32b}} unlimited-warning {{5 S32b}} limit10-warning {{5 S32b}}
+ clang_analyzer_dump(*p.array[6]); // default-warning {{6 S32b}} unlimited-warning {{6 S32b}} limit10-warning {{6 S32b}}
+
+ if (coin()) {
+ clang_analyzer_dump(*p.array[8]);
+ // default-warning at -1 {{Unknown}}
+ // unlimited-warning at -2 {{Unknown}}
+ // limit10-warning at -3 {{Unknown}}
+ }
+
+ clang_analyzer_dump(*p.array[12]); // default-warning {{12 S32b}} unlimited-warning {{12 S32b}} limit10-warning {{Unknown}}
+
+ if (coin()) {
+ clang_analyzer_dump(*p.array[22]);
+ // default-warning at -1 {{Unknown}}
+ // unlimited-warning at -2 {{Unknown}}
+ // limit10-warning at -3 {{Unknown}}
+ }
+
+ clang_analyzer_dump(*p.array[23]); // default-warning {{23 S32b}} unlimited-warning {{23 S32b}} limit10-warning {{Unknown}}
+
+ escape(p); // no-leaks
+}
+
+struct PtrHolderBase {
+ int *ptr;
+};
+struct BigStruct : BigArray<int, 1000>, PtrHolderBase {};
+void largeBaseClasses() {
+ BigStruct D{{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}, {new int(25)}};
+ (void)D; // no-leak here, the PtrHolderBase subobject is properly escaped.
+
+ clang_analyzer_dump(*D.ptr); // default-warning {{25 S32b}} unlimited-warning {{25 S32b}} limit10-warning {{Unknown}}
+ escape(D);
+}
+
+struct List {
+ int* ptr;
+ BigArray<int, 30> head;
+ List *tail;
+};
+void tempObjectMayEscapeArgumentsInAssignment() {
+ // This will be leaked after the assignment. However, we should not diagnose
+ // this because in the RHS of the assignment the temporary couldn't be really
+ // materialized due to the number of bindings, thus the address of `l` will
+ // escape there.
+ List l{new int(404)};
+
+ // ExprWithCleanups wraps the assignment operator call, which assigns a MaterializeTemporaryExpr.
+ l = List{new int(42), {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}, &l};
+ (void)l;
+ // default-warning at -1 {{Potential memory leak}} We detect the leak with the default settings.
+ // unlimited-warning at -2 {{Potential memory leak}} We detect the leak with the default settings.
+ // limit10 is missing! It's because in that case we escape `&l`, thus we assume freed. This is good.
+
+ clang_analyzer_dump(*l.ptr); // default-warning {{42 S32b}} unlimited-warning {{42 S32b}} limit10-warning {{42 S32b}}
+ escape(l);
+}
+
+void tempObjNotMaterializedThusDoesntEscapeAnything() {
+ List l{new int(404)};
+ // We have no ExprWithCleanups or MaterializeTemporaryExpr here, so `&l` is never escaped. This is good.
+ (void)List{new int(42), {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}, &l};
+ (void)l;
+ // default-warning at -1 {{Potential memory leak}} We detect the leak with the default settings.
+ // limit10-warning at -2 {{Potential memory leak}} We detect the leak with the default settings.
+ // unlimited-warning at -3 {{Potential memory leak}} We detect the leak with the default settings.
+
+ clang_analyzer_dump(*l.ptr); // default-warning {{404 S32b}} unlimited-warning {{404 S32b}} limit10-warning {{404 S32b}}
+ escape(l);
+}
+
+void theValueOfTheEscapedRegionRemainsTheSame() {
+ int *p = new int(404);
+ List l{p};
+ List l2{new int(42), {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}, &l};
+
+ // The value of l.ptr shouldn't be clobbered after a failing to copy `&l`.
+ clang_analyzer_eval(p == l.ptr); // default-warning {{TRUE}} unlimited-warning {{TRUE}} limit10-warning {{TRUE}}
+
+ // If the bindings fit, we will know that it aliases with `p`. Otherwise, it's unknown. This is good.
+ clang_analyzer_dump(*l2.tail->ptr); // default-warning {{404 S32b}} unlimited-warning {{404 S32b}} limit10-warning {{Unknown}}
+
+ escape(l, l2);
+}
diff --git a/clang/unittests/StaticAnalyzer/StoreTest.cpp b/clang/unittests/StaticAnalyzer/StoreTest.cpp
index 17b64ce622f89..a1a37cdcfad32 100644
--- a/clang/unittests/StaticAnalyzer/StoreTest.cpp
+++ b/clang/unittests/StaticAnalyzer/StoreTest.cpp
@@ -69,7 +69,7 @@ class VariableBindConsumer : public StoreTestConsumer {
SVal NarrowZero = Builder.makeZeroVal(ASTCtxt.CharTy);
// Bind(Zero)
- Store StX0 = SManager.Bind(StInit, LX0, Zero).getStore();
+ Store StX0 = SManager.Bind(StInit, LX0, Zero).ResultingStore.getStore();
EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy));
// BindDefaultInitial(Zero)
@@ -87,7 +87,7 @@ class VariableBindConsumer : public StoreTestConsumer {
EXPECT_EQ(NarrowZero, *SManager.getDefaultBinding(StZ0, LZ0.getAsRegion()));
// Bind(One)
- Store StX1 = SManager.Bind(StInit, LX1, One).getStore();
+ Store StX1 = SManager.Bind(StInit, LX1, One).ResultingStore.getStore();
EXPECT_EQ(One, SManager.getBinding(StX1, LX1, ASTCtxt.IntTy));
// BindDefaultInitial(One)
@@ -134,7 +134,8 @@ class LiteralCompoundConsumer : public StoreTestConsumer {
Store StInit = SManager.getInitialStore(SFC).getStore();
// Let's bind constant 1 to 'test[0]'
SVal One = Builder.makeIntVal(1, Int);
- Store StX = SManager.Bind(StInit, ZeroElement, One).getStore();
+ Store StX =
+ SManager.Bind(StInit, ZeroElement, One).ResultingStore.getStore();
// And make sure that we can read this binding back as it was
EXPECT_EQ(One, SManager.getBinding(StX, ZeroElement, Int));
>From dc9d8249d4f557fee3e38ca1ba91bfe768a700ef Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Wed, 19 Feb 2025 11:05:15 +0100
Subject: [PATCH 02/12] Use strong-type for representing a bounded store
---
.../StaticAnalyzer/Core/PathSensitive/Store.h | 6 +-
.../lib/StaticAnalyzer/Core/ProgramState.cpp | 34 +-
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 360 +++++++++---------
clang/unittests/StaticAnalyzer/StoreTest.cpp | 11 +-
4 files changed, 217 insertions(+), 194 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
index ebf00d49b6cc8..05fd01ee0e5b8 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
@@ -118,12 +118,12 @@ class StoreManager {
/// Return a store with the specified value bound to all sub-regions of the
/// region. The region must not have previous bindings. If you need to
/// invalidate existing bindings, consider invalidateRegions().
- virtual StoreRef BindDefaultInitial(Store store, const MemRegion *R,
- SVal V) = 0;
+ virtual BindResult BindDefaultInitial(Store store, const MemRegion *R,
+ SVal V) = 0;
/// Return a store with in which all values within the given region are
/// reset to zero. This method is allowed to overwrite previous bindings.
- virtual StoreRef BindDefaultZero(Store store, const MemRegion *R) = 0;
+ virtual BindResult BindDefaultZero(Store store, const MemRegion *R) = 0;
/// Create a new store with the specified binding removed.
/// \param ST the original store, that is the basis for the new store.
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 325b44c9cb05c..4b550887978ca 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -111,6 +111,17 @@ ProgramStateRef ProgramStateManager::removeDeadBindingsFromEnvironmentAndStore(
return getPersistentState(NewState);
}
+static ProgramStateRef escapeFailedToBindValues(ExprEngine &Eng,
+ ProgramStateRef State,
+ const BindResult &BindRes) {
+ // We must always notify the checkers for failing binds because otherwise they
+ // may keep stale traits for these symbols.
+ // Eg., Malloc checker may report leaks if we failed to bind that symbol.
+ if (BindRes.FailedToBindValues.empty())
+ return State;
+ return Eng.escapeValues(State, BindRes.FailedToBindValues, PSK_EscapeOnBind);
+}
+
ProgramStateRef ProgramState::bindLoc(Loc LV,
SVal V,
const LocationContext *LCtx,
@@ -119,16 +130,9 @@ ProgramStateRef ProgramState::bindLoc(Loc LV,
ExprEngine &Eng = Mgr.getOwningEngine();
BindResult BindRes = Mgr.StoreMgr->Bind(getStore(), LV, V);
ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
+ State = escapeFailedToBindValues(Eng, State, BindRes);
const MemRegion *MR = LV.getAsRegion();
- // We must always notify the checkers for failing binds because otherwise they
- // may keep stale traits for these symbols.
- // Eg., Malloc checker may report leaks if we failed to bind that symbol.
- if (!BindRes.FailedToBindValues.empty()) {
- State =
- Eng.escapeValues(State, BindRes.FailedToBindValues, PSK_EscapeOnBind);
- }
-
if (MR && notifyChanges)
return Eng.processRegionChange(State, MR, LCtx);
@@ -139,19 +143,21 @@ ProgramStateRef
ProgramState::bindDefaultInitial(SVal loc, SVal V,
const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
+ ExprEngine &Eng = Mgr.getOwningEngine();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
- const StoreRef &newStore = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V);
- ProgramStateRef new_state = makeWithStore(newStore);
- return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx);
+ BindResult BindRes = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V);
+ ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
+ State = escapeFailedToBindValues(Eng, State, BindRes);
+ return Mgr.getOwningEngine().processRegionChange(State, R, LCtx);
}
ProgramStateRef
ProgramState::bindDefaultZero(SVal loc, const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
- const StoreRef &newStore = Mgr.StoreMgr->BindDefaultZero(getStore(), R);
- ProgramStateRef new_state = makeWithStore(newStore);
- return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx);
+ BindResult BindRes = Mgr.StoreMgr->BindDefaultZero(getStore(), R);
+ ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
+ return Mgr.getOwningEngine().processRegionChange(State, R, LCtx);
}
typedef ArrayRef<const MemRegion *> RegionList;
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index ee821f9b19e73..9cb8bc586f5a5 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -164,7 +164,6 @@ namespace {
class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
ClusterBindings> {
ClusterBindings::Factory *CBFactory;
- SmallVectorImpl<SVal> *EscapedValuesDuringBind;
// This flag indicates whether the current bindings are within the analysis
// that has started from main(). It affects how we perform loads from
@@ -177,59 +176,27 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
// however that would have made the manager needlessly stateful.
bool IsMainAnalysis;
- unsigned BindingsLeft;
-
public:
- unsigned bindingsLeft() const { return BindingsLeft; }
-
- bool hasExhaustedBindingLimit() const { return BindingsLeft == 0; }
-
- RegionBindingsRef escapeValue(SVal V) const {
- assert(EscapedValuesDuringBind);
- EscapedValuesDuringBind->push_back(V);
- return *this;
- }
- RegionBindingsRef escapeValues(nonloc::CompoundVal::iterator Begin,
- nonloc::CompoundVal::iterator End) const {
- for (SVal V : llvm::make_range(Begin, End))
- escapeValue(V);
- return *this;
- }
-
typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>
ParentTy;
RegionBindingsRef(ClusterBindings::Factory &CBFactory,
- SmallVectorImpl<SVal> *EscapedValuesDuringBind,
const RegionBindings::TreeTy *T,
- RegionBindings::TreeTy::Factory *F, bool IsMainAnalysis,
- unsigned BindingsLeft)
- : RegionBindingsRef(ParentTy(T, F), CBFactory, EscapedValuesDuringBind,
- IsMainAnalysis, BindingsLeft) {}
+ RegionBindings::TreeTy::Factory *F, bool IsMainAnalysis)
+ : RegionBindingsRef(ParentTy(T, F), CBFactory, IsMainAnalysis) {}
RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory,
- SmallVectorImpl<SVal> *EscapedValuesDuringBind,
- bool IsMainAnalysis, unsigned BindingsLeft)
- : ParentTy(P), CBFactory(&CBFactory),
- EscapedValuesDuringBind(EscapedValuesDuringBind),
- IsMainAnalysis(IsMainAnalysis), BindingsLeft(BindingsLeft) {}
-
- RegionBindingsRef add(key_type_ref K, data_type_ref D,
- unsigned NewBindingsLeft) const {
- return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
- *CBFactory, EscapedValuesDuringBind,
- IsMainAnalysis, NewBindingsLeft);
- }
+ bool IsMainAnalysis)
+ : ParentTy(P), CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {}
RegionBindingsRef add(key_type_ref K, data_type_ref D) const {
- unsigned NewBindingsLeft = BindingsLeft ? BindingsLeft - 1 : BindingsLeft;
- return add(K, D, NewBindingsLeft);
+ return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
+ *CBFactory, IsMainAnalysis);
}
RegionBindingsRef remove(key_type_ref K) const {
return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K),
- *CBFactory, EscapedValuesDuringBind,
- IsMainAnalysis, BindingsLeft);
+ *CBFactory, IsMainAnalysis);
}
RegionBindingsRef addBinding(BindingKey K, SVal V) const;
@@ -359,7 +326,80 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
};
} // end anonymous namespace
+/// This class represents the same as \c RegionBindingsRef, but with a limit on
+/// the number of bindings that can be added.
+class BoundedRegionBindingsRef : public RegionBindingsRef {
+public:
+ static BoundedRegionBindingsRef
+ createWithLimit(RegionBindingsRef Base,
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind,
+ unsigned BindingsLeft) {
+ return BoundedRegionBindingsRef{Base, EscapedValuesDuringBind,
+ BindingsLeft};
+ }
+
+ unsigned bindingsLeft() const { return BindingsLeft; }
+
+ bool hasExhaustedBindingLimit() const { return BindingsLeft == 0; }
+
+ BoundedRegionBindingsRef escapeValue(SVal V) const {
+ assert(EscapedValuesDuringBind);
+ EscapedValuesDuringBind->push_back(V);
+ return *this;
+ }
+
+ BoundedRegionBindingsRef
+ escapeValues(nonloc::CompoundVal::iterator Begin,
+ nonloc::CompoundVal::iterator End) const {
+ for (SVal V : llvm::make_range(Begin, End))
+ escapeValue(V);
+ return *this;
+ }
+
+ BoundedRegionBindingsRef
+ addWithoutDecreasingLimit(const MemRegion *BaseRegion,
+ data_type_ref BindingKeyAndValue) const {
+ return BoundedRegionBindingsRef{
+ RegionBindingsRef::add(BaseRegion, BindingKeyAndValue),
+ EscapedValuesDuringBind, BindingsLeft};
+ }
+
+ BoundedRegionBindingsRef remove(key_type_ref K) const {
+ return BoundedRegionBindingsRef{RegionBindingsRef::remove(K),
+ EscapedValuesDuringBind, BindingsLeft};
+ }
+
+ BoundedRegionBindingsRef addBinding(BindingKey K, SVal V) const {
+ // If we are about to exhaust the binding limit, highjack this bind call for
+ // the default binding.
+ if (BindingsLeft == 1) {
+ escapeValue(V);
+ K = BindingKey::Make(K.getRegion(), BindingKey::Default);
+ V = UnknownVal();
+ }
+ return BoundedRegionBindingsRef{RegionBindingsRef::addBinding(K, V),
+ EscapedValuesDuringBind, BindingsLeft - 1};
+ }
+
+ BoundedRegionBindingsRef addBinding(const MemRegion *R, BindingKey::Kind k,
+ SVal V) const {
+ return addBinding(BindingKey::Make(R, k), V);
+ }
+
+private:
+ BoundedRegionBindingsRef(RegionBindingsRef Base,
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind,
+ unsigned BindingsLeft)
+ : RegionBindingsRef(Base),
+ EscapedValuesDuringBind(EscapedValuesDuringBind),
+ BindingsLeft(BindingsLeft) {}
+
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind;
+ unsigned BindingsLeft;
+};
+
typedef const RegionBindingsRef& RegionBindingsConstRef;
+typedef const BoundedRegionBindingsRef &BoundedRegionBindingsConstRef;
std::optional<SVal>
RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
@@ -374,14 +414,6 @@ RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
}
RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
- // If we are about to exhaust the binding limit, highjack this bind call for
- // the default binding.
- if (BindingsLeft == 1) {
- escapeValue(V);
- K = BindingKey::Make(K.getRegion(), BindingKey::Default);
- V = UnknownVal();
- }
-
const MemRegion *Base = K.getBaseRegion();
const ClusterBindings *ExistingCluster = lookup(Base);
@@ -391,7 +423,6 @@ RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
return add(Base, CBFactory->add(Cluster, K, V));
}
-
RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R,
BindingKey::Kind k,
SVal V) const {
@@ -496,8 +527,9 @@ class RegionStoreManager : public StoreManager {
/// setImplicitDefaultValue - Set the default binding for the provided
/// MemRegion to the value implicitly defined for compound literals when
/// the value is not specified.
- RegionBindingsRef setImplicitDefaultValue(RegionBindingsConstRef B,
- const MemRegion *R, QualType T);
+ BoundedRegionBindingsRef
+ setImplicitDefaultValue(BoundedRegionBindingsConstRef B, const MemRegion *R,
+ QualType T);
/// ArrayToPointer - Emulates the "decay" of an array to a pointer
/// type. 'Array' represents the lvalue of the array being decayed
@@ -513,13 +545,11 @@ class RegionStoreManager : public StoreManager {
bool IsMainAnalysis = false;
if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl()))
IsMainAnalysis = FD->isMain() && !Ctx.getLangOpts().CPlusPlus;
- return StoreRef(
- RegionBindingsRef(
- RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory),
- CBFactory, /*EscapedValuesDuringBind=*/nullptr, IsMainAnalysis,
- RegionStoreMaxBindingFanOut)
- .asStore(),
- *this);
+ return StoreRef(RegionBindingsRef(RegionBindingsRef::ParentTy(
+ RBFactory.getEmptyMap(), RBFactory),
+ CBFactory, IsMainAnalysis)
+ .asStore(),
+ *this);
}
//===-------------------------------------------------------------------===//
@@ -541,8 +571,8 @@ class RegionStoreManager : public StoreManager {
bool scanReachableSymbols(Store S, const MemRegion *R,
ScanReachableSymbols &Callbacks) override;
- RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
- const SubRegion *R);
+ BoundedRegionBindingsRef
+ removeSubRegionBindings(BoundedRegionBindingsConstRef B, const SubRegion *R);
std::optional<SVal>
getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
const ElementRegion *R);
@@ -556,31 +586,35 @@ class RegionStoreManager : public StoreManager {
public: // Part of public interface to class.
BindResult Bind(Store store, Loc LV, SVal V) override {
llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
- return BindResult{
- StoreRef(bind(getRegionBindings(store, &EscapedValuesDuringBind), LV, V)
- .asStore(),
- *this),
- EscapedValuesDuringBind};
+ BoundedRegionBindingsRef BoundedBindings =
+ getRegionBindings(store, &EscapedValuesDuringBind);
+ return BindResult{StoreRef(bind(BoundedBindings, LV, V).asStore(), *this),
+ std::move(EscapedValuesDuringBind)};
}
- RegionBindingsRef bind(RegionBindingsConstRef B, Loc LV, SVal V);
+ BoundedRegionBindingsRef bind(BoundedRegionBindingsConstRef B, Loc LV,
+ SVal V);
// BindDefaultInitial is only used to initialize a region with
// a default value.
- StoreRef BindDefaultInitial(Store store, const MemRegion *R,
- SVal V) override {
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
+ BindResult BindDefaultInitial(Store store, const MemRegion *R,
+ SVal V) override {
+ llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
+ BoundedRegionBindingsRef B =
+ getRegionBindings(store, &EscapedValuesDuringBind);
// Use other APIs when you have to wipe the region that was initialized
// earlier.
assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) &&
"Double initialization!");
B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
- return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this);
+ return BindResult{
+ StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this),
+ std::move(EscapedValuesDuringBind)};
}
// BindDefaultZero is used for zeroing constructors that may accidentally
// overwrite existing bindings.
- StoreRef BindDefaultZero(Store store, const MemRegion *R) override {
+ BindResult BindDefaultZero(Store store, const MemRegion *R) override {
// FIXME: The offsets of empty bases can be tricky because of
// of the so called "empty base class optimization".
// If a base class has been optimized out
@@ -592,13 +626,17 @@ class RegionStoreManager : public StoreManager {
// As a temporary mitigation we don't create bindings for empty bases.
if (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R))
if (BR->getDecl()->isEmpty())
- return StoreRef(store, *this);
+ return BindResult{StoreRef(store, *this), {}};
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
+ llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
+ BoundedRegionBindingsRef B =
+ getRegionBindings(store, &EscapedValuesDuringBind);
SVal V = svalBuilder.makeZeroVal(Ctx.CharTy);
B = removeSubRegionBindings(B, cast<SubRegion>(R));
B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
- return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this);
+ return BindResult{
+ StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this),
+ std::move(EscapedValuesDuringBind)};
}
/// Attempt to extract the fields of \p LCV and bind them to the struct region
@@ -611,31 +649,29 @@ class RegionStoreManager : public StoreManager {
///
/// \returns The updated store bindings, or \c std::nullopt if binding
/// non-lazily would be too expensive.
- std::optional<RegionBindingsRef>
- tryBindSmallStruct(RegionBindingsConstRef B, const TypedValueRegion *R,
+ std::optional<BoundedRegionBindingsRef>
+ tryBindSmallStruct(BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
const RecordDecl *RD, nonloc::LazyCompoundVal LCV);
/// BindStruct - Bind a compound value to a structure.
- RegionBindingsRef bindStruct(RegionBindingsConstRef B,
- const TypedValueRegion* R, SVal V);
+ BoundedRegionBindingsRef bindStruct(BoundedRegionBindingsConstRef B,
+ const TypedValueRegion *R, SVal V);
/// BindVector - Bind a compound value to a vector.
- RegionBindingsRef bindVector(RegionBindingsConstRef B,
- const TypedValueRegion* R, SVal V);
+ BoundedRegionBindingsRef bindVector(BoundedRegionBindingsConstRef B,
+ const TypedValueRegion *R, SVal V);
- std::optional<RegionBindingsRef>
- tryBindSmallArray(RegionBindingsConstRef B, const TypedValueRegion *R,
+ std::optional<BoundedRegionBindingsRef>
+ tryBindSmallArray(BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
const ArrayType *AT, nonloc::LazyCompoundVal LCV);
- RegionBindingsRef bindArray(RegionBindingsConstRef B,
- const TypedValueRegion* R,
- SVal V);
+ BoundedRegionBindingsRef bindArray(BoundedRegionBindingsConstRef B,
+ const TypedValueRegion *R, SVal V);
/// Clears out all bindings in the given region and assigns a new value
/// as a Default binding.
- RegionBindingsRef bindAggregate(RegionBindingsConstRef B,
- const TypedRegion *R,
- SVal DefaultVal);
+ BoundedRegionBindingsRef bindAggregate(BoundedRegionBindingsConstRef B,
+ const TypedRegion *R, SVal DefaultVal);
/// Create a new store with the specified binding removed.
/// \param ST the original store, that is the basis for the new store.
@@ -643,14 +679,14 @@ class RegionStoreManager : public StoreManager {
StoreRef killBinding(Store ST, Loc L) override;
void incrementReferenceCount(Store store) override {
- getRegionBindingsWithUnboundedLimit(store).manualRetain();
+ getRegionBindings(store).manualRetain();
}
/// If the StoreManager supports it, decrement the reference count of
/// the specified Store object. If the reference count hits 0, the memory
/// associated with the object is recycled.
void decrementReferenceCount(Store store) override {
- getRegionBindingsWithUnboundedLimit(store).manualRelease();
+ getRegionBindings(store).manualRelease();
}
bool includedInBindings(Store store, const MemRegion *region) const override;
@@ -669,7 +705,7 @@ class RegionStoreManager : public StoreManager {
/// else
/// return symbolic
SVal getBinding(Store S, Loc L, QualType T) override {
- return getBinding(getRegionBindingsWithUnboundedLimit(S), L, T);
+ return getBinding(getRegionBindings(S), L, T);
}
std::optional<SVal> getUniqueDefaultBinding(RegionBindingsConstRef B,
@@ -678,7 +714,7 @@ class RegionStoreManager : public StoreManager {
getUniqueDefaultBinding(nonloc::LazyCompoundVal LCV) const;
std::optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(S);
+ RegionBindingsRef B = getRegionBindings(S);
// Default bindings are always applied over a base region so look up the
// base region's default binding, otherwise the lookup will fail when R
// is at an offset from R->getBaseRegion().
@@ -756,23 +792,27 @@ class RegionStoreManager : public StoreManager {
// Utility methods.
//===------------------------------------------------------------------===//
- RegionBindingsRef
- getRegionBindings(Store store,
- SmallVectorImpl<SVal> *EscapedValuesDuringBind) const {
- return getRegionBindingsImpl(store, EscapedValuesDuringBind,
- /*BindingsLeft=*/RegionStoreMaxBindingFanOut);
+ RegionBindingsRef getRegionBindings(Store store) const {
+ llvm::PointerIntPair<Store, 1, bool> Ptr;
+ Ptr.setFromOpaqueValue(const_cast<void *>(store));
+ return {CBFactory,
+ static_cast<const RegionBindings::TreeTy *>(Ptr.getPointer()),
+ RBFactory.getTreeFactory(), Ptr.getInt()};
}
- RegionBindingsRef getRegionBindingsWithUnboundedLimit(Store store) const {
- return getRegionBindingsImpl(store, /*EscapedValuesDuringBind=*/nullptr,
- /*BindingsLeft=*/-1U);
+ BoundedRegionBindingsRef
+ getRegionBindings(Store store,
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind) const {
+ return BoundedRegionBindingsRef::createWithLimit(
+ getRegionBindings(store), EscapedValuesDuringBind,
+ /*BindingsLeft=*/RegionStoreMaxBindingFanOut);
}
void printJson(raw_ostream &Out, Store S, const char *NL = "\n",
unsigned int Space = 0, bool IsDot = false) const override;
void iterBindings(Store store, BindingsHandler& f) override {
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
+ RegionBindingsRef B = getRegionBindings(store);
for (const auto &[Region, Cluster] : B) {
for (const auto &[Key, Value] : Cluster) {
if (!Key.isDirect())
@@ -785,19 +825,6 @@ class RegionStoreManager : public StoreManager {
}
}
}
-
-private:
- RegionBindingsRef
- getRegionBindingsImpl(Store store,
- SmallVectorImpl<SVal> *EscapedValuesDuringBind,
- unsigned BindingsLeft) const {
- llvm::PointerIntPair<Store, 1, bool> Ptr;
- Ptr.setFromOpaqueValue(const_cast<void *>(store));
- return RegionBindingsRef(
- CBFactory, EscapedValuesDuringBind,
- static_cast<const RegionBindings::TreeTy *>(Ptr.getPointer()),
- RBFactory.getTreeFactory(), Ptr.getInt(), BindingsLeft);
- }
};
} // end anonymous namespace
@@ -923,7 +950,7 @@ class ClusterAnalysis {
bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R,
ScanReachableSymbols &Callbacks) {
assert(R == R->getBaseRegion() && "Should only be called for base regions");
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(S);
+ RegionBindingsRef B = getRegionBindings(S);
const ClusterBindings *Cluster = B.lookup(R);
if (!Cluster)
@@ -1067,8 +1094,8 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
IncludeAllDefaultBindings);
}
-RegionBindingsRef
-RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
+BoundedRegionBindingsRef
+RegionStoreManager::removeSubRegionBindings(BoundedRegionBindingsConstRef B,
const SubRegion *Top) {
BindingKey TopKey = BindingKey::Make(Top, BindingKey::Default);
const MemRegion *ClusterHead = TopKey.getBaseRegion();
@@ -1109,9 +1136,7 @@ RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
if (Result.isEmpty())
return B.remove(ClusterHead);
- // Make this "add" free by using the old "BindingsLeft".
- return B.add(ClusterHead, Result.asImmutableMap(),
- /*BindingsLeft=*/B.bindingsLeft());
+ return B.addWithoutDecreasingLimit(ClusterHead, Result.asImmutableMap());
}
namespace {
@@ -1448,7 +1473,7 @@ StoreRef RegionStoreManager::invalidateRegions(
GlobalsFilter = GFK_None;
}
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
+ RegionBindingsRef B = getRegionBindings(store);
InvalidateRegionsWorker W(*this, StateMgr, B, S, Count, LCtx, IS, ITraits,
Invalidated, GlobalsFilter);
@@ -2209,9 +2234,8 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
const SubRegion *lazyBindingRegion = nullptr;
std::tie(lazyBindingStore, lazyBindingRegion) = findLazyBinding(B, R, R);
if (lazyBindingRegion)
- return getLazyBinding(
- lazyBindingRegion,
- getRegionBindingsWithUnboundedLimit(lazyBindingStore));
+ return getLazyBinding(lazyBindingRegion,
+ getRegionBindings(lazyBindingStore));
// Record whether or not we see a symbolic index. That can completely
// be out of scope of our lookup.
@@ -2388,7 +2412,7 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
SValListTy List;
const SubRegion *LazyR = LCV.getRegion();
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(LCV.getStore());
+ RegionBindingsRef B = getRegionBindings(LCV.getStore());
// If this region had /no/ bindings at the time, there are no interesting
// values to return.
@@ -2451,7 +2475,7 @@ SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B,
bool RegionStoreManager::includedInBindings(Store store,
const MemRegion *region) const {
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
+ RegionBindingsRef B = getRegionBindings(store);
region = region->getBaseRegion();
// Quick path: if the base is the head of a cluster, the region is live.
@@ -2480,7 +2504,7 @@ bool RegionStoreManager::includedInBindings(Store store,
StoreRef RegionStoreManager::killBinding(Store ST, Loc L) {
if (std::optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>())
if (const MemRegion* R = LV->getRegion())
- return StoreRef(getRegionBindingsWithUnboundedLimit(ST)
+ return StoreRef(getRegionBindings(ST)
.removeBinding(R)
.asImmutableMap()
.getRootWithoutRetain(),
@@ -2489,8 +2513,8 @@ StoreRef RegionStoreManager::killBinding(Store ST, Loc L) {
return StoreRef(ST, *this);
}
-RegionBindingsRef RegionStoreManager::bind(RegionBindingsConstRef B, Loc L,
- SVal V) {
+BoundedRegionBindingsRef
+RegionStoreManager::bind(BoundedRegionBindingsConstRef B, Loc L, SVal V) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bind",
[&L]() { return locDescr(L); });
@@ -2530,7 +2554,7 @@ RegionBindingsRef RegionStoreManager::bind(RegionBindingsConstRef B, Loc L,
"'this' pointer is not an l-value and is not assignable");
// Clear out bindings that may overlap with this binding.
- RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R));
+ auto NewB = removeSubRegionBindings(B, cast<SubRegion>(R));
// LazyCompoundVals should be always bound as 'default' bindings.
auto KeyKind = isa<nonloc::LazyCompoundVal>(V) ? BindingKey::Default
@@ -2538,10 +2562,9 @@ RegionBindingsRef RegionStoreManager::bind(RegionBindingsConstRef B, Loc L,
return NewB.addBinding(BindingKey::Make(R, KeyKind), V);
}
-RegionBindingsRef
-RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B,
- const MemRegion *R,
- QualType T) {
+BoundedRegionBindingsRef
+RegionStoreManager::setImplicitDefaultValue(BoundedRegionBindingsConstRef B,
+ const MemRegion *R, QualType T) {
SVal V;
if (Loc::isLocType(T))
@@ -2565,9 +2588,9 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B,
return B.addBinding(R, BindingKey::Default, V);
}
-std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
- RegionBindingsConstRef B, const TypedValueRegion *R, const ArrayType *AT,
- nonloc::LazyCompoundVal LCV) {
+std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallArray(
+ BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
+ const ArrayType *AT, nonloc::LazyCompoundVal LCV) {
auto CAT = dyn_cast<ConstantArrayType>(AT);
@@ -2584,14 +2607,13 @@ std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
if (ArrSize > SmallArrayLimit)
return std::nullopt;
- RegionBindingsRef NewB = B;
+ BoundedRegionBindingsRef NewB = B;
for (uint64_t i = 0; i < ArrSize; ++i) {
auto Idx = svalBuilder.makeArrayIndex(i);
const ElementRegion *SrcER =
MRMgr.getElementRegion(Ty, Idx, LCV.getRegion(), Ctx);
- SVal V = getBindingForElement(
- getRegionBindingsWithUnboundedLimit(LCV.getStore()), SrcER);
+ SVal V = getBindingForElement(getRegionBindings(LCV.getStore()), SrcER);
const ElementRegion *DstER = MRMgr.getElementRegion(Ty, Idx, R, Ctx);
NewB = bind(NewB, loc::MemRegionVal(DstER), V);
@@ -2600,10 +2622,9 @@ std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
return NewB;
}
-RegionBindingsRef
-RegionStoreManager::bindArray(RegionBindingsConstRef B,
- const TypedValueRegion* R,
- SVal Init) {
+BoundedRegionBindingsRef
+RegionStoreManager::bindArray(BoundedRegionBindingsConstRef B,
+ const TypedValueRegion *R, SVal Init) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindArray",
[R]() { return R->getDescriptiveName(); });
@@ -2623,10 +2644,8 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
}
// Handle lazy compound values.
- if (std::optional<nonloc::LazyCompoundVal> LCV =
- Init.getAs<nonloc::LazyCompoundVal>()) {
- if (std::optional<RegionBindingsRef> NewB =
- tryBindSmallArray(B, R, AT, *LCV))
+ if (std::optional LCV = Init.getAs<nonloc::LazyCompoundVal>()) {
+ if (std::optional NewB = tryBindSmallArray(B, R, AT, *LCV))
return *NewB;
return bindAggregate(B, R, Init);
@@ -2640,7 +2659,7 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
uint64_t i = 0;
- RegionBindingsRef NewB(B);
+ BoundedRegionBindingsRef NewB = B;
for (; Size ? i < *Size : true; ++i, ++VI) {
// The init list might be shorter than the array length.
@@ -2669,9 +2688,9 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
return NewB;
}
-RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
- const TypedValueRegion* R,
- SVal V) {
+BoundedRegionBindingsRef
+RegionStoreManager::bindVector(BoundedRegionBindingsConstRef B,
+ const TypedValueRegion *R, SVal V) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindVector",
[R]() { return R->getDescriptiveName(); });
QualType T = R->getValueType();
@@ -2692,7 +2711,7 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
nonloc::CompoundVal CV = V.castAs<nonloc::CompoundVal>();
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
unsigned index = 0, numElements = VT->getNumElements();
- RegionBindingsRef NewB(B);
+ BoundedRegionBindingsRef NewB = B;
for ( ; index != numElements ; ++index) {
if (VI == VE)
@@ -2727,13 +2746,13 @@ RegionStoreManager::getUniqueDefaultBinding(RegionBindingsConstRef B,
std::optional<SVal>
RegionStoreManager::getUniqueDefaultBinding(nonloc::LazyCompoundVal LCV) const {
- auto B = getRegionBindingsWithUnboundedLimit(LCV.getStore());
+ auto B = getRegionBindings(LCV.getStore());
return getUniqueDefaultBinding(B, LCV.getRegion());
}
-std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
- RegionBindingsConstRef B, const TypedValueRegion *R, const RecordDecl *RD,
- nonloc::LazyCompoundVal LCV) {
+std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
+ BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
+ const RecordDecl *RD, nonloc::LazyCompoundVal LCV) {
// If we try to copy a Conjured value representing the value of the whole
// struct, don't try to element-wise copy each field.
// That would unnecessarily bind Derived symbols slicing off the subregion for
@@ -2780,12 +2799,11 @@ std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
Fields.push_back(FD);
}
- RegionBindingsRef NewB = B;
+ BoundedRegionBindingsRef NewB = B;
for (const FieldDecl *Field : Fields) {
const FieldRegion *SourceFR = MRMgr.getFieldRegion(Field, LCV.getRegion());
- SVal V = getBindingForField(
- getRegionBindingsWithUnboundedLimit(LCV.getStore()), SourceFR);
+ SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR);
const FieldRegion *DestFR = MRMgr.getFieldRegion(Field, R);
NewB = bind(NewB, loc::MemRegionVal(DestFR), V);
@@ -2794,9 +2812,9 @@ std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
return NewB;
}
-RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
- const TypedValueRegion *R,
- SVal V) {
+BoundedRegionBindingsRef
+RegionStoreManager::bindStruct(BoundedRegionBindingsConstRef B,
+ const TypedValueRegion *R, SVal V) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindStruct",
[R]() { return R->getDescriptiveName(); });
QualType T = R->getValueType();
@@ -2811,8 +2829,7 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
// Handle lazy compound values and symbolic values.
if (std::optional<nonloc::LazyCompoundVal> LCV =
V.getAs<nonloc::LazyCompoundVal>()) {
- if (std::optional<RegionBindingsRef> NewB =
- tryBindSmallStruct(B, R, RD, *LCV))
+ if (std::optional NewB = tryBindSmallStruct(B, R, RD, *LCV))
return *NewB;
return bindAggregate(B, R, V);
}
@@ -2844,7 +2861,7 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>();
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
- RegionBindingsRef NewB(B);
+ BoundedRegionBindingsRef NewB = B;
// In C++17 aggregates may have base classes, handle those as well.
// They appear before fields in the initializer list / compound value.
@@ -2918,10 +2935,9 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
return NewB;
}
-RegionBindingsRef
-RegionStoreManager::bindAggregate(RegionBindingsConstRef B,
- const TypedRegion *R,
- SVal Val) {
+BoundedRegionBindingsRef
+RegionStoreManager::bindAggregate(BoundedRegionBindingsConstRef B,
+ const TypedRegion *R, SVal Val) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindAggregate",
[R]() { return R->getDescriptiveName(); });
// Remove the old bindings, using 'R' as the root of all regions
@@ -3072,7 +3088,7 @@ bool RemoveDeadBindingsWorker::UpdatePostponed() {
StoreRef RegionStoreManager::removeDeadBindings(Store store,
const StackFrameContext *LCtx,
SymbolReaper& SymReaper) {
- RegionBindingsRef B = getRegionBindingsWithUnboundedLimit(store);
+ RegionBindingsRef B = getRegionBindings(store);
RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx);
W.GenerateClusters();
@@ -3102,7 +3118,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
void RegionStoreManager::printJson(raw_ostream &Out, Store S, const char *NL,
unsigned int Space, bool IsDot) const {
- RegionBindingsRef Bindings = getRegionBindingsWithUnboundedLimit(S);
+ RegionBindingsRef Bindings = getRegionBindings(S);
Indent(Out, Space, IsDot) << "\"store\": ";
diff --git a/clang/unittests/StaticAnalyzer/StoreTest.cpp b/clang/unittests/StaticAnalyzer/StoreTest.cpp
index a1a37cdcfad32..1448c2b411fdb 100644
--- a/clang/unittests/StaticAnalyzer/StoreTest.cpp
+++ b/clang/unittests/StaticAnalyzer/StoreTest.cpp
@@ -73,13 +73,14 @@ class VariableBindConsumer : public StoreTestConsumer {
EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy));
// BindDefaultInitial(Zero)
- Store StY0 =
- SManager.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore();
+ Store StY0 = SManager.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero)
+ .ResultingStore.getStore();
EXPECT_EQ(Zero, SManager.getBinding(StY0, LY0, ASTCtxt.IntTy));
EXPECT_EQ(Zero, *SManager.getDefaultBinding(StY0, LY0.getAsRegion()));
// BindDefaultZero()
- Store StZ0 = SManager.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore();
+ Store StZ0 = SManager.BindDefaultZero(StInit, LZ0.getAsRegion())
+ .ResultingStore.getStore();
// BindDefaultZero wipes the region with '0 S8b', not with out Zero.
// Direct load, however, does give us back the object of the type
// that we specify for loading.
@@ -91,8 +92,8 @@ class VariableBindConsumer : public StoreTestConsumer {
EXPECT_EQ(One, SManager.getBinding(StX1, LX1, ASTCtxt.IntTy));
// BindDefaultInitial(One)
- Store StY1 =
- SManager.BindDefaultInitial(StInit, LY1.getAsRegion(), One).getStore();
+ Store StY1 = SManager.BindDefaultInitial(StInit, LY1.getAsRegion(), One)
+ .ResultingStore.getStore();
EXPECT_EQ(One, SManager.getBinding(StY1, LY1, ASTCtxt.IntTy));
EXPECT_EQ(One, *SManager.getDefaultBinding(StY1, LY1.getAsRegion()));
}
>From 2e685b9668055f8e8c0d9ebf2ae4bf345e61e7f4 Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Wed, 19 Feb 2025 11:07:02 +0100
Subject: [PATCH 03/12] NFC Rename to withValuesEscaped
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 9cb8bc586f5a5..e72e15f51eca4 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -342,17 +342,17 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
bool hasExhaustedBindingLimit() const { return BindingsLeft == 0; }
- BoundedRegionBindingsRef escapeValue(SVal V) const {
+ BoundedRegionBindingsRef withValuesEscaped(SVal V) const {
assert(EscapedValuesDuringBind);
EscapedValuesDuringBind->push_back(V);
return *this;
}
BoundedRegionBindingsRef
- escapeValues(nonloc::CompoundVal::iterator Begin,
- nonloc::CompoundVal::iterator End) const {
+ withValuesEscaped(nonloc::CompoundVal::iterator Begin,
+ nonloc::CompoundVal::iterator End) const {
for (SVal V : llvm::make_range(Begin, End))
- escapeValue(V);
+ withValuesEscaped(V);
return *this;
}
@@ -373,7 +373,7 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
// If we are about to exhaust the binding limit, highjack this bind call for
// the default binding.
if (BindingsLeft == 1) {
- escapeValue(V);
+ withValuesEscaped(V);
K = BindingKey::Make(K.getRegion(), BindingKey::Default);
V = UnknownVal();
}
@@ -2519,7 +2519,7 @@ RegionStoreManager::bind(BoundedRegionBindingsConstRef B, Loc L, SVal V) {
[&L]() { return locDescr(L); });
if (B.hasExhaustedBindingLimit())
- return B.escapeValue(V);
+ return B.withValuesEscaped(V);
// We only care about region locations.
auto MemRegVal = L.getAs<loc::MemRegionVal>();
@@ -2666,7 +2666,7 @@ RegionStoreManager::bindArray(BoundedRegionBindingsConstRef B,
if (VI == VE)
break;
if (NewB.hasExhaustedBindingLimit())
- return NewB.escapeValues(VI, VE);
+ return NewB.withValuesEscaped(VI, VE);
NonLoc Idx = svalBuilder.makeArrayIndex(i);
const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx);
@@ -2883,7 +2883,7 @@ RegionStoreManager::bindStruct(BoundedRegionBindingsConstRef B,
if (VI == VE)
break;
if (NewB.hasExhaustedBindingLimit())
- return NewB.escapeValues(VI, VE);
+ return NewB.withValuesEscaped(VI, VE);
QualType BTy = B.getType();
assert(BTy->isStructureOrClassType() && "Base classes must be classes!");
@@ -2908,7 +2908,7 @@ RegionStoreManager::bindStruct(BoundedRegionBindingsConstRef B,
break;
if (NewB.hasExhaustedBindingLimit())
- return NewB.escapeValues(VI, VE);
+ return NewB.withValuesEscaped(VI, VE);
// Skip any unnamed bitfields to stay in sync with the initializers.
if (FI->isUnnamedBitField())
>From 067de3d33524ff7050de41c933f3bf18dee7b3fd Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Wed, 19 Feb 2025 11:09:24 +0100
Subject: [PATCH 04/12] Use default value of 128, like MAXIMUM_STEP_UNROLLED
does
---
clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def | 2 +-
clang/test/Analysis/analyzer-config.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
index f05c8724d583d..b087ca8860690 100644
--- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
+++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
@@ -489,7 +489,7 @@ ANALYZER_OPTION(
"scatter into. For example, binding an array would scatter into binding "
"each individual element. Setting this to zero means unlimited, but then "
"modelling large array initializers may take proportional time to their "
- "size.", 100)
+ "size.", 128)
//===----------------------------------------------------------------------===//
// String analyzer options.
diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c
index e3f276da57703..06378b9814886 100644
--- a/clang/test/Analysis/analyzer-config.c
+++ b/clang/test/Analysis/analyzer-config.c
@@ -116,7 +116,7 @@
// CHECK-NEXT: osx.NumberObjectConversion:Pedantic = false
// CHECK-NEXT: osx.cocoa.RetainCount:TrackNSCFStartParam = false
// CHECK-NEXT: prune-paths = true
-// CHECK-NEXT: region-store-max-binding-fanout = 100
+// CHECK-NEXT: region-store-max-binding-fanout = 128
// CHECK-NEXT: region-store-small-array-limit = 5
// CHECK-NEXT: region-store-small-struct-limit = 2
// CHECK-NEXT: report-in-main-source-file = false
>From a1f4eba94418ceb5ed59f994d7006ad76f0dd602 Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Wed, 19 Feb 2025 11:20:11 +0100
Subject: [PATCH 05/12] Rename the generic "remove" to more descriptive
"removeCluster"
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 23 ++++++++++---------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index e72e15f51eca4..75cba546b2034 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -194,9 +194,9 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
*CBFactory, IsMainAnalysis);
}
- RegionBindingsRef remove(key_type_ref K) const {
- return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K),
- *CBFactory, IsMainAnalysis);
+ RegionBindingsRef removeCluster(const MemRegion *BaseRegion) const {
+ return RegionBindingsRef(ParentTy::remove(BaseRegion), *CBFactory,
+ IsMainAnalysis);
}
RegionBindingsRef addBinding(BindingKey K, SVal V) const;
@@ -364,9 +364,10 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
EscapedValuesDuringBind, BindingsLeft};
}
- BoundedRegionBindingsRef remove(key_type_ref K) const {
- return BoundedRegionBindingsRef{RegionBindingsRef::remove(K),
- EscapedValuesDuringBind, BindingsLeft};
+ BoundedRegionBindingsRef removeCluster(const MemRegion *BaseRegion) const {
+ return BoundedRegionBindingsRef{
+ RegionBindingsRef::removeCluster(BaseRegion), EscapedValuesDuringBind,
+ BindingsLeft};
}
BoundedRegionBindingsRef addBinding(BindingKey K, SVal V) const {
@@ -449,7 +450,7 @@ RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) {
ClusterBindings NewCluster = CBFactory->remove(*Cluster, K);
if (NewCluster.isEmpty())
- return remove(Base);
+ return removeCluster(Base);
return add(Base, NewCluster);
}
@@ -1102,7 +1103,7 @@ RegionStoreManager::removeSubRegionBindings(BoundedRegionBindingsConstRef B,
if (Top == ClusterHead) {
// We can remove an entire cluster's bindings all in one go.
- return B.remove(Top);
+ return B.removeCluster(Top);
}
const ClusterBindings *Cluster = B.lookup(ClusterHead);
@@ -1135,7 +1136,7 @@ RegionStoreManager::removeSubRegionBindings(BoundedRegionBindingsConstRef B,
}
if (Result.isEmpty())
- return B.remove(ClusterHead);
+ return B.removeCluster(ClusterHead);
return B.addWithoutDecreasingLimit(ClusterHead, Result.asImmutableMap());
}
@@ -1221,7 +1222,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
// Invalidate regions contents.
if (!PreserveRegionsContents)
- B = B.remove(baseR);
+ B = B.removeCluster(baseR);
}
if (const auto *TO = dyn_cast<TypedValueRegion>(baseR)) {
@@ -3106,7 +3107,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
// If the cluster has been visited, we know the region has been marked.
// Otherwise, remove the dead entry.
if (!W.isVisited(Base))
- B = B.remove(Base);
+ B = B.removeCluster(Base);
}
return StoreRef(B.asStore(), *this);
>From 6eb8395a25b2c65440ca2451bf072625d0d93cb7 Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Wed, 19 Feb 2025 11:37:23 +0100
Subject: [PATCH 06/12] Move raw "add" into a protected section, rename it to
internal to signify that it should be avoided
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 22 ++++++++++++-------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 75cba546b2034..34efd179cb8e0 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -189,11 +189,6 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
bool IsMainAnalysis)
: ParentTy(P), CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {}
- RegionBindingsRef add(key_type_ref K, data_type_ref D) const {
- return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
- *CBFactory, IsMainAnalysis);
- }
-
RegionBindingsRef removeCluster(const MemRegion *BaseRegion) const {
return RegionBindingsRef(ParentTy::remove(BaseRegion), *CBFactory,
IsMainAnalysis);
@@ -323,6 +318,10 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
}
LLVM_DUMP_METHOD void dump() const { printJson(llvm::errs()); }
+
+protected:
+ RegionBindingsRef addBindingInternal(const MemRegion *BaseRegion,
+ data_type_ref D) const;
};
} // end anonymous namespace
@@ -360,7 +359,7 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
addWithoutDecreasingLimit(const MemRegion *BaseRegion,
data_type_ref BindingKeyAndValue) const {
return BoundedRegionBindingsRef{
- RegionBindingsRef::add(BaseRegion, BindingKeyAndValue),
+ RegionBindingsRef::addBindingInternal(BaseRegion, BindingKeyAndValue),
EscapedValuesDuringBind, BindingsLeft};
}
@@ -414,6 +413,13 @@ RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
return V ? std::optional<SVal>(*V) : std::nullopt;
}
+RegionBindingsRef
+RegionBindingsRef::addBindingInternal(const MemRegion *BaseRegion,
+ data_type_ref D) const {
+ return RegionBindingsRef(ParentTy::add(BaseRegion, D), *CBFactory,
+ IsMainAnalysis);
+}
+
RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
const MemRegion *Base = K.getBaseRegion();
@@ -421,7 +427,7 @@ RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
ClusterBindings Cluster =
(ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap());
- return add(Base, CBFactory->add(Cluster, K, V));
+ return addBindingInternal(Base, CBFactory->add(Cluster, K, V));
}
RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R,
@@ -451,7 +457,7 @@ RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) {
ClusterBindings NewCluster = CBFactory->remove(*Cluster, K);
if (NewCluster.isEmpty())
return removeCluster(Base);
- return add(Base, NewCluster);
+ return addBindingInternal(Base, NewCluster);
}
RegionBindingsRef RegionBindingsRef::removeBinding(const MemRegion *R,
>From 25bb87564f7469d82e1255f974805cb016dc904d Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Wed, 19 Feb 2025 11:54:01 +0100
Subject: [PATCH 07/12] Apply further renamings to clarify their intent
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 26 +++++++++----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 34efd179cb8e0..433dbf9686152 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -320,8 +320,9 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
LLVM_DUMP_METHOD void dump() const { printJson(llvm::errs()); }
protected:
- RegionBindingsRef addBindingInternal(const MemRegion *BaseRegion,
- data_type_ref D) const;
+ RegionBindingsRef
+ commitBindingsToCluster(const MemRegion *BaseRegion,
+ const ClusterBindings &Bindings) const;
};
} // end anonymous namespace
@@ -358,9 +359,9 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
BoundedRegionBindingsRef
addWithoutDecreasingLimit(const MemRegion *BaseRegion,
data_type_ref BindingKeyAndValue) const {
- return BoundedRegionBindingsRef{
- RegionBindingsRef::addBindingInternal(BaseRegion, BindingKeyAndValue),
- EscapedValuesDuringBind, BindingsLeft};
+ return BoundedRegionBindingsRef{RegionBindingsRef::commitBindingsToCluster(
+ BaseRegion, BindingKeyAndValue),
+ EscapedValuesDuringBind, BindingsLeft};
}
BoundedRegionBindingsRef removeCluster(const MemRegion *BaseRegion) const {
@@ -413,10 +414,9 @@ RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
return V ? std::optional<SVal>(*V) : std::nullopt;
}
-RegionBindingsRef
-RegionBindingsRef::addBindingInternal(const MemRegion *BaseRegion,
- data_type_ref D) const {
- return RegionBindingsRef(ParentTy::add(BaseRegion, D), *CBFactory,
+RegionBindingsRef RegionBindingsRef::commitBindingsToCluster(
+ const MemRegion *BaseRegion, const ClusterBindings &Bindings) const {
+ return RegionBindingsRef(ParentTy::add(BaseRegion, Bindings), *CBFactory,
IsMainAnalysis);
}
@@ -424,10 +424,10 @@ RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
const MemRegion *Base = K.getBaseRegion();
const ClusterBindings *ExistingCluster = lookup(Base);
- ClusterBindings Cluster =
+ ClusterBindings Bindings =
(ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap());
-
- return addBindingInternal(Base, CBFactory->add(Cluster, K, V));
+ Bindings = CBFactory->add(Bindings, K, V);
+ return commitBindingsToCluster(Base, Bindings);
}
RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R,
@@ -457,7 +457,7 @@ RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) {
ClusterBindings NewCluster = CBFactory->remove(*Cluster, K);
if (NewCluster.isEmpty())
return removeCluster(Base);
- return addBindingInternal(Base, NewCluster);
+ return commitBindingsToCluster(Base, NewCluster);
}
RegionBindingsRef RegionBindingsRef::removeBinding(const MemRegion *R,
>From 534da56e745aa746ed5a01738a5f5ef84a4efe6a Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Thu, 20 Feb 2025 11:28:21 +0100
Subject: [PATCH 08/12] Fix complex argument passing
---
.../Core/PathSensitive/ProgramState.h | 1 +
.../StaticAnalyzer/Core/PathSensitive/Store.h | 5 +--
.../lib/StaticAnalyzer/Core/ProgramState.cpp | 38 +++++++++----------
clang/lib/StaticAnalyzer/Core/Store.cpp | 17 +++++----
clang/test/Analysis/region-store.cpp | 28 ++++++++++++++
5 files changed, 57 insertions(+), 32 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index a20516b003c7d..208b32e376883 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -126,6 +126,7 @@ class ProgramState : public llvm::FoldingSetNode {
/// makeWithStore - Return a ProgramState with the same values as the current
/// state with the exception of using the specified Store.
ProgramStateRef makeWithStore(const StoreRef &store) const;
+ ProgramStateRef makeWithStore(const BindResult &BindRes) const;
void setStore(const StoreRef &storeRef);
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
index 05fd01ee0e5b8..cf7623c7be409 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
@@ -248,9 +248,8 @@ class StoreManager {
/// enterStackFrame - Let the StoreManager to do something when execution
/// engine is about to execute into a callee.
- StoreRef enterStackFrame(Store store,
- const CallEvent &Call,
- const StackFrameContext *CalleeCtx);
+ BindResult enterStackFrame(Store store, const CallEvent &Call,
+ const StackFrameContext *CalleeCtx);
/// Finds the transitive closure of symbols within the given region.
///
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 4b550887978ca..492209d4d2bf0 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -111,26 +111,13 @@ ProgramStateRef ProgramStateManager::removeDeadBindingsFromEnvironmentAndStore(
return getPersistentState(NewState);
}
-static ProgramStateRef escapeFailedToBindValues(ExprEngine &Eng,
- ProgramStateRef State,
- const BindResult &BindRes) {
- // We must always notify the checkers for failing binds because otherwise they
- // may keep stale traits for these symbols.
- // Eg., Malloc checker may report leaks if we failed to bind that symbol.
- if (BindRes.FailedToBindValues.empty())
- return State;
- return Eng.escapeValues(State, BindRes.FailedToBindValues, PSK_EscapeOnBind);
-}
-
ProgramStateRef ProgramState::bindLoc(Loc LV,
SVal V,
const LocationContext *LCtx,
bool notifyChanges) const {
ProgramStateManager &Mgr = getStateManager();
ExprEngine &Eng = Mgr.getOwningEngine();
- BindResult BindRes = Mgr.StoreMgr->Bind(getStore(), LV, V);
- ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
- State = escapeFailedToBindValues(Eng, State, BindRes);
+ ProgramStateRef State = makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V));
const MemRegion *MR = LV.getAsRegion();
if (MR && notifyChanges)
@@ -143,11 +130,9 @@ ProgramStateRef
ProgramState::bindDefaultInitial(SVal loc, SVal V,
const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
- ExprEngine &Eng = Mgr.getOwningEngine();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
BindResult BindRes = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V);
- ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
- State = escapeFailedToBindValues(Eng, State, BindRes);
+ ProgramStateRef State = makeWithStore(BindRes);
return Mgr.getOwningEngine().processRegionChange(State, R, LCtx);
}
@@ -156,7 +141,7 @@ ProgramState::bindDefaultZero(SVal loc, const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
BindResult BindRes = Mgr.StoreMgr->BindDefaultZero(getStore(), R);
- ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
+ ProgramStateRef State = makeWithStore(BindRes);
return Mgr.getOwningEngine().processRegionChange(State, R, LCtx);
}
@@ -248,9 +233,8 @@ SVal ProgramState::wrapSymbolicRegion(SVal Val) const {
ProgramStateRef
ProgramState::enterStackFrame(const CallEvent &Call,
const StackFrameContext *CalleeCtx) const {
- const StoreRef &NewStore =
- getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx);
- return makeWithStore(NewStore);
+ return makeWithStore(
+ getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx));
}
SVal ProgramState::getSelfSVal(const LocationContext *LCtx) const {
@@ -453,6 +437,18 @@ ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const {
return getStateManager().getPersistentState(NewSt);
}
+ProgramStateRef ProgramState::makeWithStore(const BindResult &BindRes) const {
+ ExprEngine &Eng = getStateManager().getOwningEngine();
+ ProgramStateRef State = makeWithStore(BindRes.ResultingStore);
+
+ // We must always notify the checkers for failing binds because otherwise they
+ // may keep stale traits for these symbols.
+ // Eg., Malloc checker may report leaks if we failed to bind that symbol.
+ if (BindRes.FailedToBindValues.empty())
+ return State;
+ return Eng.escapeValues(State, BindRes.FailedToBindValues, PSK_EscapeOnBind);
+}
+
ProgramStateRef ProgramState::cloneAsPosteriorlyOverconstrained() const {
ProgramState NewSt(*this);
NewSt.PosteriorlyOverconstrained = true;
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index bb1d7cb243474..5f30fae4b7047 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -29,6 +29,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
@@ -43,21 +44,21 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr)
: svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
-StoreRef StoreManager::enterStackFrame(Store OldStore,
- const CallEvent &Call,
- const StackFrameContext *LCtx) {
- StoreRef Store = StoreRef(OldStore, *this);
+BindResult StoreManager::enterStackFrame(Store OldStore, const CallEvent &Call,
+ const StackFrameContext *LCtx) {
+ BindResult Result{StoreRef(OldStore, *this), {}};
SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings;
Call.getInitialStackFrameContents(LCtx, InitialBindings);
for (const auto &[Location, Val] : InitialBindings) {
- BindResult Res = Bind(Store.getStore(), Location.castAs<Loc>(), Val);
- assert(Res.FailedToBindValues.empty());
- Store = Res.ResultingStore;
+ Store S = Result.ResultingStore.getStore();
+ BindResult Curr = Bind(S, Location.castAs<Loc>(), Val);
+ Result.ResultingStore = Curr.ResultingStore;
+ llvm::append_range(Result.FailedToBindValues, Curr.FailedToBindValues);
}
- return Store;
+ return Result;
}
const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base,
diff --git a/clang/test/Analysis/region-store.cpp b/clang/test/Analysis/region-store.cpp
index cab0bd75edf9b..9e80a2e688575 100644
--- a/clang/test/Analysis/region-store.cpp
+++ b/clang/test/Analysis/region-store.cpp
@@ -358,3 +358,31 @@ void theValueOfTheEscapedRegionRemainsTheSame() {
escape(l, l2);
}
+
+void calleeWithManyParms(BigArray<int, 7> arr7, BigArray<int, 100> arr100) {
+ clang_analyzer_dump(arr7.array[0]); // default-warning {{0 S32b}} unlimited-warning {{0 S32b}} limit10-warning {{0 S32b}}
+ clang_analyzer_dump(arr7.array[6]); // default-warning {{6 S32b}} unlimited-warning {{6 S32b}} limit10-warning {{6 S32b}}
+
+ clang_analyzer_dump(arr100.array[0]); // default-warning {{10 S32b}} unlimited-warning {{10 S32b}} limit10-warning {{10 S32b}}
+ clang_analyzer_dump(arr100.array[6]); // default-warning {{16 S32b}} unlimited-warning {{16 S32b}} limit10-warning {{16 S32b}}
+
+ clang_analyzer_dump(arr100.array[8]); // default-warning {{18 S32b}} unlimited-warning {{18 S32b}} limit10-warning {{18 S32b}}
+ clang_analyzer_dump(arr100.array[9]); // default-warning {{19 S32b}} unlimited-warning {{19 S32b}} limit10-warning {{19 S32b}}
+ clang_analyzer_dump(arr100.array[10]); // default-warning {{20 S32b}} unlimited-warning {{20 S32b}} limit10-warning {{Unknown}}
+ clang_analyzer_dump(arr100.array[99]); // default-warning {{19 S32b}} unlimited-warning {{19 S32b}} limit10-warning {{Unknown}}
+}
+
+void tooManyFnArgumentsWhenInlining() {
+ calleeWithManyParms({0,1,2,3,4,5,6}, {
+ 10,11,12,13,14,15,16,17,18,19,
+ 20,21,22,23,24,25,26,27,28,29,
+ 30,31,32,33,34,35,36,37,38,39,
+ 40,41,42,43,44,45,46,47,48,49,
+ 50,51,52,53,54,55,56,57,58,59,
+ 60,61,62,63,64,65,66,67,68,69,
+ 70,71,72,73,74,75,76,77,78,79,
+ 80,81,82,83,84,85,86,87,88,89,
+ 90,91,92,93,94,95,96,97,98,99,
+ 10,11,12,13,14,15,16,17,18,19,
+ });
+}
>From 87f27a6e57fbc9afa51e59adc9f265a18dfe09b3 Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Thu, 20 Feb 2025 11:39:56 +0100
Subject: [PATCH 09/12] Simplify code
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 38 ++++++++-----------
1 file changed, 15 insertions(+), 23 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 433dbf9686152..cc930504e396b 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -330,13 +330,12 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
/// the number of bindings that can be added.
class BoundedRegionBindingsRef : public RegionBindingsRef {
public:
- static BoundedRegionBindingsRef
- createWithLimit(RegionBindingsRef Base,
- SmallVectorImpl<SVal> *EscapedValuesDuringBind,
- unsigned BindingsLeft) {
- return BoundedRegionBindingsRef{Base, EscapedValuesDuringBind,
- BindingsLeft};
- }
+ BoundedRegionBindingsRef(RegionBindingsRef Base,
+ SmallVectorImpl<SVal> &EscapedValuesDuringBind,
+ unsigned BindingsLeft)
+ : RegionBindingsRef(Base),
+ EscapedValuesDuringBind(&EscapedValuesDuringBind),
+ BindingsLeft(BindingsLeft) {}
unsigned bindingsLeft() const { return BindingsLeft; }
@@ -361,12 +360,12 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
data_type_ref BindingKeyAndValue) const {
return BoundedRegionBindingsRef{RegionBindingsRef::commitBindingsToCluster(
BaseRegion, BindingKeyAndValue),
- EscapedValuesDuringBind, BindingsLeft};
+ *EscapedValuesDuringBind, BindingsLeft};
}
BoundedRegionBindingsRef removeCluster(const MemRegion *BaseRegion) const {
return BoundedRegionBindingsRef{
- RegionBindingsRef::removeCluster(BaseRegion), EscapedValuesDuringBind,
+ RegionBindingsRef::removeCluster(BaseRegion), *EscapedValuesDuringBind,
BindingsLeft};
}
@@ -379,7 +378,7 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
V = UnknownVal();
}
return BoundedRegionBindingsRef{RegionBindingsRef::addBinding(K, V),
- EscapedValuesDuringBind, BindingsLeft - 1};
+ *EscapedValuesDuringBind, BindingsLeft - 1};
}
BoundedRegionBindingsRef addBinding(const MemRegion *R, BindingKey::Kind k,
@@ -388,14 +387,7 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
}
private:
- BoundedRegionBindingsRef(RegionBindingsRef Base,
- SmallVectorImpl<SVal> *EscapedValuesDuringBind,
- unsigned BindingsLeft)
- : RegionBindingsRef(Base),
- EscapedValuesDuringBind(EscapedValuesDuringBind),
- BindingsLeft(BindingsLeft) {}
-
- SmallVectorImpl<SVal> *EscapedValuesDuringBind;
+ SmallVectorImpl<SVal> *EscapedValuesDuringBind; // nonnull
unsigned BindingsLeft;
};
@@ -594,7 +586,7 @@ class RegionStoreManager : public StoreManager {
BindResult Bind(Store store, Loc LV, SVal V) override {
llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
BoundedRegionBindingsRef BoundedBindings =
- getRegionBindings(store, &EscapedValuesDuringBind);
+ getRegionBindings(store, EscapedValuesDuringBind);
return BindResult{StoreRef(bind(BoundedBindings, LV, V).asStore(), *this),
std::move(EscapedValuesDuringBind)};
}
@@ -608,7 +600,7 @@ class RegionStoreManager : public StoreManager {
SVal V) override {
llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
BoundedRegionBindingsRef B =
- getRegionBindings(store, &EscapedValuesDuringBind);
+ getRegionBindings(store, EscapedValuesDuringBind);
// Use other APIs when you have to wipe the region that was initialized
// earlier.
assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) &&
@@ -637,7 +629,7 @@ class RegionStoreManager : public StoreManager {
llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
BoundedRegionBindingsRef B =
- getRegionBindings(store, &EscapedValuesDuringBind);
+ getRegionBindings(store, EscapedValuesDuringBind);
SVal V = svalBuilder.makeZeroVal(Ctx.CharTy);
B = removeSubRegionBindings(B, cast<SubRegion>(R));
B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
@@ -809,8 +801,8 @@ class RegionStoreManager : public StoreManager {
BoundedRegionBindingsRef
getRegionBindings(Store store,
- SmallVectorImpl<SVal> *EscapedValuesDuringBind) const {
- return BoundedRegionBindingsRef::createWithLimit(
+ SmallVectorImpl<SVal> &EscapedValuesDuringBind) const {
+ return BoundedRegionBindingsRef(
getRegionBindings(store), EscapedValuesDuringBind,
/*BindingsLeft=*/RegionStoreMaxBindingFanOut);
}
>From e9444298775ab03912ba015b567fdaf1ae2bc435 Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Thu, 20 Feb 2025 11:58:21 +0100
Subject: [PATCH 10/12] Use optional unsigned for BindLimit
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 51 +++++++++++--------
1 file changed, 30 insertions(+), 21 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index cc930504e396b..e3d013244fe01 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -31,6 +31,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
+#include <limits>
#include <optional>
#include <utility>
@@ -332,17 +333,16 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
public:
BoundedRegionBindingsRef(RegionBindingsRef Base,
SmallVectorImpl<SVal> &EscapedValuesDuringBind,
- unsigned BindingsLeft)
+ std::optional<unsigned> BindingsLeft)
: RegionBindingsRef(Base),
EscapedValuesDuringBind(&EscapedValuesDuringBind),
BindingsLeft(BindingsLeft) {}
- unsigned bindingsLeft() const { return BindingsLeft; }
-
- bool hasExhaustedBindingLimit() const { return BindingsLeft == 0; }
+ bool hasExhaustedBindingLimit() const {
+ return BindingsLeft.has_value() && BindingsLeft.value() == 0;
+ }
BoundedRegionBindingsRef withValuesEscaped(SVal V) const {
- assert(EscapedValuesDuringBind);
EscapedValuesDuringBind->push_back(V);
return *this;
}
@@ -370,15 +370,22 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
}
BoundedRegionBindingsRef addBinding(BindingKey K, SVal V) const {
- // If we are about to exhaust the binding limit, highjack this bind call for
- // the default binding.
- if (BindingsLeft == 1) {
- withValuesEscaped(V);
- K = BindingKey::Make(K.getRegion(), BindingKey::Default);
- V = UnknownVal();
+ std::optional<unsigned> NewBindingsLeft = BindingsLeft;
+ if (NewBindingsLeft.has_value()) {
+ assert(NewBindingsLeft.value() != 0);
+ NewBindingsLeft.value() -= 1;
+
+ // If we just exhausted the binding limit, highjack
+ // this bind call for the default binding.
+ if (NewBindingsLeft.value() == 0) {
+ withValuesEscaped(V);
+ K = BindingKey::Make(K.getRegion(), BindingKey::Default);
+ V = UnknownVal();
+ }
}
+
return BoundedRegionBindingsRef{RegionBindingsRef::addBinding(K, V),
- *EscapedValuesDuringBind, BindingsLeft - 1};
+ *EscapedValuesDuringBind, NewBindingsLeft};
}
BoundedRegionBindingsRef addBinding(const MemRegion *R, BindingKey::Kind k,
@@ -388,7 +395,7 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
private:
SmallVectorImpl<SVal> *EscapedValuesDuringBind; // nonnull
- unsigned BindingsLeft;
+ std::optional<unsigned> BindingsLeft;
};
typedef const RegionBindingsRef& RegionBindingsConstRef;
@@ -498,8 +505,8 @@ class RegionStoreManager : public StoreManager {
/// The number of bindings a single bind operation can scatter into.
/// For example, binding the initializer-list of an array would recurse and
/// bind all the individual array elements, potentially causing scalability
- /// issues.
- const unsigned RegionStoreMaxBindingFanOut;
+ /// issues. Nullopt if the limit is disabled.
+ const std::optional<unsigned> RegionStoreMaxBindingFanOutPlusOne;
/// A helper used to populate the work list with the given set of
/// regions.
@@ -517,11 +524,13 @@ class RegionStoreManager : public StoreManager {
CBFactory(mgr.getAllocator()),
SmallStructLimit(getOptions().RegionStoreSmallStructLimit),
SmallArrayLimit(getOptions().RegionStoreSmallArrayLimit),
- RegionStoreMaxBindingFanOut(
- getOptions().RegionStoreMaxBindingFanOut == 0
- ? -1U
- : getOptions().RegionStoreMaxBindingFanOut +
- /*for the default binding*/ 1) {}
+ RegionStoreMaxBindingFanOutPlusOne([&]() -> std::optional<unsigned> {
+ unsigned FanOut = getOptions().RegionStoreMaxBindingFanOut;
+ assert(FanOut != std::numeric_limits<unsigned>::max());
+ if (FanOut == 0)
+ return std::nullopt;
+ return FanOut + 1 /*for the default binding*/;
+ }()) {}
/// setImplicitDefaultValue - Set the default binding for the provided
/// MemRegion to the value implicitly defined for compound literals when
@@ -804,7 +813,7 @@ class RegionStoreManager : public StoreManager {
SmallVectorImpl<SVal> &EscapedValuesDuringBind) const {
return BoundedRegionBindingsRef(
getRegionBindings(store), EscapedValuesDuringBind,
- /*BindingsLeft=*/RegionStoreMaxBindingFanOut);
+ /*BindingsLeft=*/RegionStoreMaxBindingFanOutPlusOne);
}
void printJson(raw_ostream &Out, Store S, const char *NL = "\n",
>From 3d1d1b25dde0c5ac3061bc3d92ad6a80472efe02 Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Thu, 20 Feb 2025 12:59:54 +0100
Subject: [PATCH 11/12] Simplify BindDefaultInitial
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index e3d013244fe01..557e920c711ad 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -607,17 +607,14 @@ class RegionStoreManager : public StoreManager {
// a default value.
BindResult BindDefaultInitial(Store store, const MemRegion *R,
SVal V) override {
- llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
- BoundedRegionBindingsRef B =
- getRegionBindings(store, EscapedValuesDuringBind);
+ RegionBindingsRef B = getRegionBindings(store);
// Use other APIs when you have to wipe the region that was initialized
// earlier.
assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) &&
"Double initialization!");
B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
return BindResult{
- StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this),
- std::move(EscapedValuesDuringBind)};
+ StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this), {}};
}
// BindDefaultZero is used for zeroing constructors that may accidentally
>From 83131d89d17a62b9d41f72481e9561feb833931f Mon Sep 17 00:00:00 2001
From: Balazs Benics <balazs.benics at sonarsource.com>
Date: Thu, 20 Feb 2025 13:25:33 +0100
Subject: [PATCH 12/12] Rename to LimitedRegionBindingsRef
---
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 104 +++++++++---------
1 file changed, 52 insertions(+), 52 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 557e920c711ad..bb26fd727a630 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -329,9 +329,9 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
/// This class represents the same as \c RegionBindingsRef, but with a limit on
/// the number of bindings that can be added.
-class BoundedRegionBindingsRef : public RegionBindingsRef {
+class LimitedRegionBindingsRef : public RegionBindingsRef {
public:
- BoundedRegionBindingsRef(RegionBindingsRef Base,
+ LimitedRegionBindingsRef(RegionBindingsRef Base,
SmallVectorImpl<SVal> &EscapedValuesDuringBind,
std::optional<unsigned> BindingsLeft)
: RegionBindingsRef(Base),
@@ -342,12 +342,12 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
return BindingsLeft.has_value() && BindingsLeft.value() == 0;
}
- BoundedRegionBindingsRef withValuesEscaped(SVal V) const {
+ LimitedRegionBindingsRef withValuesEscaped(SVal V) const {
EscapedValuesDuringBind->push_back(V);
return *this;
}
- BoundedRegionBindingsRef
+ LimitedRegionBindingsRef
withValuesEscaped(nonloc::CompoundVal::iterator Begin,
nonloc::CompoundVal::iterator End) const {
for (SVal V : llvm::make_range(Begin, End))
@@ -355,21 +355,21 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
return *this;
}
- BoundedRegionBindingsRef
+ LimitedRegionBindingsRef
addWithoutDecreasingLimit(const MemRegion *BaseRegion,
data_type_ref BindingKeyAndValue) const {
- return BoundedRegionBindingsRef{RegionBindingsRef::commitBindingsToCluster(
+ return LimitedRegionBindingsRef{RegionBindingsRef::commitBindingsToCluster(
BaseRegion, BindingKeyAndValue),
*EscapedValuesDuringBind, BindingsLeft};
}
- BoundedRegionBindingsRef removeCluster(const MemRegion *BaseRegion) const {
- return BoundedRegionBindingsRef{
+ LimitedRegionBindingsRef removeCluster(const MemRegion *BaseRegion) const {
+ return LimitedRegionBindingsRef{
RegionBindingsRef::removeCluster(BaseRegion), *EscapedValuesDuringBind,
BindingsLeft};
}
- BoundedRegionBindingsRef addBinding(BindingKey K, SVal V) const {
+ LimitedRegionBindingsRef addBinding(BindingKey K, SVal V) const {
std::optional<unsigned> NewBindingsLeft = BindingsLeft;
if (NewBindingsLeft.has_value()) {
assert(NewBindingsLeft.value() != 0);
@@ -384,11 +384,11 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
}
}
- return BoundedRegionBindingsRef{RegionBindingsRef::addBinding(K, V),
+ return LimitedRegionBindingsRef{RegionBindingsRef::addBinding(K, V),
*EscapedValuesDuringBind, NewBindingsLeft};
}
- BoundedRegionBindingsRef addBinding(const MemRegion *R, BindingKey::Kind k,
+ LimitedRegionBindingsRef addBinding(const MemRegion *R, BindingKey::Kind k,
SVal V) const {
return addBinding(BindingKey::Make(R, k), V);
}
@@ -399,7 +399,7 @@ class BoundedRegionBindingsRef : public RegionBindingsRef {
};
typedef const RegionBindingsRef& RegionBindingsConstRef;
-typedef const BoundedRegionBindingsRef &BoundedRegionBindingsConstRef;
+typedef const LimitedRegionBindingsRef &LimitedRegionBindingsConstRef;
std::optional<SVal>
RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
@@ -535,8 +535,8 @@ class RegionStoreManager : public StoreManager {
/// setImplicitDefaultValue - Set the default binding for the provided
/// MemRegion to the value implicitly defined for compound literals when
/// the value is not specified.
- BoundedRegionBindingsRef
- setImplicitDefaultValue(BoundedRegionBindingsConstRef B, const MemRegion *R,
+ LimitedRegionBindingsRef
+ setImplicitDefaultValue(LimitedRegionBindingsConstRef B, const MemRegion *R,
QualType T);
/// ArrayToPointer - Emulates the "decay" of an array to a pointer
@@ -579,8 +579,8 @@ class RegionStoreManager : public StoreManager {
bool scanReachableSymbols(Store S, const MemRegion *R,
ScanReachableSymbols &Callbacks) override;
- BoundedRegionBindingsRef
- removeSubRegionBindings(BoundedRegionBindingsConstRef B, const SubRegion *R);
+ LimitedRegionBindingsRef
+ removeSubRegionBindings(LimitedRegionBindingsConstRef B, const SubRegion *R);
std::optional<SVal>
getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
const ElementRegion *R);
@@ -594,13 +594,13 @@ class RegionStoreManager : public StoreManager {
public: // Part of public interface to class.
BindResult Bind(Store store, Loc LV, SVal V) override {
llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
- BoundedRegionBindingsRef BoundedBindings =
+ LimitedRegionBindingsRef BoundedBindings =
getRegionBindings(store, EscapedValuesDuringBind);
return BindResult{StoreRef(bind(BoundedBindings, LV, V).asStore(), *this),
std::move(EscapedValuesDuringBind)};
}
- BoundedRegionBindingsRef bind(BoundedRegionBindingsConstRef B, Loc LV,
+ LimitedRegionBindingsRef bind(LimitedRegionBindingsConstRef B, Loc LV,
SVal V);
// BindDefaultInitial is only used to initialize a region with
@@ -634,7 +634,7 @@ class RegionStoreManager : public StoreManager {
return BindResult{StoreRef(store, *this), {}};
llvm::SmallVector<SVal, 0> EscapedValuesDuringBind;
- BoundedRegionBindingsRef B =
+ LimitedRegionBindingsRef B =
getRegionBindings(store, EscapedValuesDuringBind);
SVal V = svalBuilder.makeZeroVal(Ctx.CharTy);
B = removeSubRegionBindings(B, cast<SubRegion>(R));
@@ -654,28 +654,28 @@ class RegionStoreManager : public StoreManager {
///
/// \returns The updated store bindings, or \c std::nullopt if binding
/// non-lazily would be too expensive.
- std::optional<BoundedRegionBindingsRef>
- tryBindSmallStruct(BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
+ std::optional<LimitedRegionBindingsRef>
+ tryBindSmallStruct(LimitedRegionBindingsConstRef B, const TypedValueRegion *R,
const RecordDecl *RD, nonloc::LazyCompoundVal LCV);
/// BindStruct - Bind a compound value to a structure.
- BoundedRegionBindingsRef bindStruct(BoundedRegionBindingsConstRef B,
+ LimitedRegionBindingsRef bindStruct(LimitedRegionBindingsConstRef B,
const TypedValueRegion *R, SVal V);
/// BindVector - Bind a compound value to a vector.
- BoundedRegionBindingsRef bindVector(BoundedRegionBindingsConstRef B,
+ LimitedRegionBindingsRef bindVector(LimitedRegionBindingsConstRef B,
const TypedValueRegion *R, SVal V);
- std::optional<BoundedRegionBindingsRef>
- tryBindSmallArray(BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
+ std::optional<LimitedRegionBindingsRef>
+ tryBindSmallArray(LimitedRegionBindingsConstRef B, const TypedValueRegion *R,
const ArrayType *AT, nonloc::LazyCompoundVal LCV);
- BoundedRegionBindingsRef bindArray(BoundedRegionBindingsConstRef B,
+ LimitedRegionBindingsRef bindArray(LimitedRegionBindingsConstRef B,
const TypedValueRegion *R, SVal V);
/// Clears out all bindings in the given region and assigns a new value
/// as a Default binding.
- BoundedRegionBindingsRef bindAggregate(BoundedRegionBindingsConstRef B,
+ LimitedRegionBindingsRef bindAggregate(LimitedRegionBindingsConstRef B,
const TypedRegion *R, SVal DefaultVal);
/// Create a new store with the specified binding removed.
@@ -805,10 +805,10 @@ class RegionStoreManager : public StoreManager {
RBFactory.getTreeFactory(), Ptr.getInt()};
}
- BoundedRegionBindingsRef
+ LimitedRegionBindingsRef
getRegionBindings(Store store,
SmallVectorImpl<SVal> &EscapedValuesDuringBind) const {
- return BoundedRegionBindingsRef(
+ return LimitedRegionBindingsRef(
getRegionBindings(store), EscapedValuesDuringBind,
/*BindingsLeft=*/RegionStoreMaxBindingFanOutPlusOne);
}
@@ -1099,8 +1099,8 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
IncludeAllDefaultBindings);
}
-BoundedRegionBindingsRef
-RegionStoreManager::removeSubRegionBindings(BoundedRegionBindingsConstRef B,
+LimitedRegionBindingsRef
+RegionStoreManager::removeSubRegionBindings(LimitedRegionBindingsConstRef B,
const SubRegion *Top) {
BindingKey TopKey = BindingKey::Make(Top, BindingKey::Default);
const MemRegion *ClusterHead = TopKey.getBaseRegion();
@@ -2518,8 +2518,8 @@ StoreRef RegionStoreManager::killBinding(Store ST, Loc L) {
return StoreRef(ST, *this);
}
-BoundedRegionBindingsRef
-RegionStoreManager::bind(BoundedRegionBindingsConstRef B, Loc L, SVal V) {
+LimitedRegionBindingsRef
+RegionStoreManager::bind(LimitedRegionBindingsConstRef B, Loc L, SVal V) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bind",
[&L]() { return locDescr(L); });
@@ -2567,8 +2567,8 @@ RegionStoreManager::bind(BoundedRegionBindingsConstRef B, Loc L, SVal V) {
return NewB.addBinding(BindingKey::Make(R, KeyKind), V);
}
-BoundedRegionBindingsRef
-RegionStoreManager::setImplicitDefaultValue(BoundedRegionBindingsConstRef B,
+LimitedRegionBindingsRef
+RegionStoreManager::setImplicitDefaultValue(LimitedRegionBindingsConstRef B,
const MemRegion *R, QualType T) {
SVal V;
@@ -2593,8 +2593,8 @@ RegionStoreManager::setImplicitDefaultValue(BoundedRegionBindingsConstRef B,
return B.addBinding(R, BindingKey::Default, V);
}
-std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallArray(
- BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
+std::optional<LimitedRegionBindingsRef> RegionStoreManager::tryBindSmallArray(
+ LimitedRegionBindingsConstRef B, const TypedValueRegion *R,
const ArrayType *AT, nonloc::LazyCompoundVal LCV) {
auto CAT = dyn_cast<ConstantArrayType>(AT);
@@ -2612,7 +2612,7 @@ std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallArray(
if (ArrSize > SmallArrayLimit)
return std::nullopt;
- BoundedRegionBindingsRef NewB = B;
+ LimitedRegionBindingsRef NewB = B;
for (uint64_t i = 0; i < ArrSize; ++i) {
auto Idx = svalBuilder.makeArrayIndex(i);
@@ -2627,8 +2627,8 @@ std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallArray(
return NewB;
}
-BoundedRegionBindingsRef
-RegionStoreManager::bindArray(BoundedRegionBindingsConstRef B,
+LimitedRegionBindingsRef
+RegionStoreManager::bindArray(LimitedRegionBindingsConstRef B,
const TypedValueRegion *R, SVal Init) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindArray",
[R]() { return R->getDescriptiveName(); });
@@ -2664,7 +2664,7 @@ RegionStoreManager::bindArray(BoundedRegionBindingsConstRef B,
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
uint64_t i = 0;
- BoundedRegionBindingsRef NewB = B;
+ LimitedRegionBindingsRef NewB = B;
for (; Size ? i < *Size : true; ++i, ++VI) {
// The init list might be shorter than the array length.
@@ -2693,8 +2693,8 @@ RegionStoreManager::bindArray(BoundedRegionBindingsConstRef B,
return NewB;
}
-BoundedRegionBindingsRef
-RegionStoreManager::bindVector(BoundedRegionBindingsConstRef B,
+LimitedRegionBindingsRef
+RegionStoreManager::bindVector(LimitedRegionBindingsConstRef B,
const TypedValueRegion *R, SVal V) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindVector",
[R]() { return R->getDescriptiveName(); });
@@ -2716,7 +2716,7 @@ RegionStoreManager::bindVector(BoundedRegionBindingsConstRef B,
nonloc::CompoundVal CV = V.castAs<nonloc::CompoundVal>();
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
unsigned index = 0, numElements = VT->getNumElements();
- BoundedRegionBindingsRef NewB = B;
+ LimitedRegionBindingsRef NewB = B;
for ( ; index != numElements ; ++index) {
if (VI == VE)
@@ -2755,8 +2755,8 @@ RegionStoreManager::getUniqueDefaultBinding(nonloc::LazyCompoundVal LCV) const {
return getUniqueDefaultBinding(B, LCV.getRegion());
}
-std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
- BoundedRegionBindingsConstRef B, const TypedValueRegion *R,
+std::optional<LimitedRegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
+ LimitedRegionBindingsConstRef B, const TypedValueRegion *R,
const RecordDecl *RD, nonloc::LazyCompoundVal LCV) {
// If we try to copy a Conjured value representing the value of the whole
// struct, don't try to element-wise copy each field.
@@ -2804,7 +2804,7 @@ std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
Fields.push_back(FD);
}
- BoundedRegionBindingsRef NewB = B;
+ LimitedRegionBindingsRef NewB = B;
for (const FieldDecl *Field : Fields) {
const FieldRegion *SourceFR = MRMgr.getFieldRegion(Field, LCV.getRegion());
@@ -2817,8 +2817,8 @@ std::optional<BoundedRegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
return NewB;
}
-BoundedRegionBindingsRef
-RegionStoreManager::bindStruct(BoundedRegionBindingsConstRef B,
+LimitedRegionBindingsRef
+RegionStoreManager::bindStruct(LimitedRegionBindingsConstRef B,
const TypedValueRegion *R, SVal V) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindStruct",
[R]() { return R->getDescriptiveName(); });
@@ -2866,7 +2866,7 @@ RegionStoreManager::bindStruct(BoundedRegionBindingsConstRef B,
const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>();
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
- BoundedRegionBindingsRef NewB = B;
+ LimitedRegionBindingsRef NewB = B;
// In C++17 aggregates may have base classes, handle those as well.
// They appear before fields in the initializer list / compound value.
@@ -2940,8 +2940,8 @@ RegionStoreManager::bindStruct(BoundedRegionBindingsConstRef B,
return NewB;
}
-BoundedRegionBindingsRef
-RegionStoreManager::bindAggregate(BoundedRegionBindingsConstRef B,
+LimitedRegionBindingsRef
+RegionStoreManager::bindAggregate(LimitedRegionBindingsConstRef B,
const TypedRegion *R, SVal Val) {
llvm::TimeTraceScope TimeScope("RegionStoreManager::bindAggregate",
[R]() { return R->getDescriptiveName(); });
More information about the cfe-commits
mailing list