[analyzer] Revamp RegionStore to distinguish regions with symbolic offsets.

RegionStore currently uses a (Region, Offset) pair to describe the locations
of memory bindings. However, this representation breaks down when we have
regions like 'array[index]', where 'index' is unknown. We used to store this
as (SubRegion, 0); now we mark them specially as (SubRegion, SYMBOLIC).

Furthermore, ProgramState::scanReachableSymbols depended on the existence of
a sub-region map, but RegionStore's implementation doesn't provide for such
a thing. Moving the store-traversing logic of scanReachableSymbols into the
StoreManager allows us to eliminate the notion of SubRegionMap altogether.

This fixes some particularly awkward broken test cases, now in
array-struct-region.c.

llvm-svn: 161510
This commit is contained in:
Jordan Rose 2012-08-08 18:23:27 +00:00
parent 55edf5ff14
commit 3a80cec5e9
8 changed files with 287 additions and 202 deletions

View File

@ -52,11 +52,13 @@ class RegionOffset {
int64_t Offset;
public:
RegionOffset(const MemRegion *r) : R(r), Offset(0) {}
RegionOffset() : R(0) {}
RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {}
const MemRegion *getRegion() const { return R; }
int64_t getOffset() const { return Offset; }
bool isValid() const { return R; }
};
//===----------------------------------------------------------------------===//
@ -490,6 +492,8 @@ public:
return T.getTypePtrOrNull() ? T.getDesugaredType(Context) : T;
}
DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const;
static bool classof(const MemRegion* R) {
unsigned k = R->getKind();
return k >= BEG_TYPED_VALUE_REGIONS && k <= END_TYPED_VALUE_REGIONS;
@ -806,8 +810,6 @@ public:
const Decl *getDecl() const { return D; }
void Profile(llvm::FoldingSetNodeID& ID) const;
DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const;
static bool classof(const MemRegion* R) {
unsigned k = R->getKind();
return k >= BEG_DECL_REGIONS && k <= END_DECL_REGIONS;

View File

@ -795,14 +795,12 @@ CB ProgramState::scanReachableSymbols(const MemRegion * const *beg,
/// \class ScanReachableSymbols
/// A Utility class that allows to visit the reachable symbols using a custom
/// SymbolVisitor.
class ScanReachableSymbols : public SubRegionMap::Visitor {
virtual void anchor();
class ScanReachableSymbols {
typedef llvm::DenseMap<const void*, unsigned> VisitedItems;
VisitedItems visited;
ProgramStateRef state;
SymbolVisitor &visitor;
OwningPtr<SubRegionMap> SRM;
public:
ScanReachableSymbols(ProgramStateRef st, SymbolVisitor& v)
@ -812,11 +810,6 @@ public:
bool scan(SVal val);
bool scan(const MemRegion *R);
bool scan(const SymExpr *sym);
// From SubRegionMap::Visitor.
bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) {
return scan(SubRegion);
}
};
} // end GR namespace

View File

@ -32,7 +32,7 @@ namespace ento {
class CallEvent;
class ProgramState;
class ProgramStateManager;
class SubRegionMap;
class ScanReachableSymbols;
class StoreManager {
protected:
@ -85,11 +85,6 @@ public:
/// used to query and manipulate MemRegion objects.
MemRegionManager& getRegionManager() { return MRMgr; }
/// getSubRegionMap - Returns an opaque map object that clients can query
/// to get the subregions of a given MemRegion object. It is the
// caller's responsibility to 'delete' the returned map.
virtual SubRegionMap *getSubRegionMap(Store store) = 0;
virtual Loc getLValueVar(const VarDecl *VD, const LocationContext *LC) {
return svalBuilder.makeLoc(MRMgr.getVarRegion(VD, LC));
}
@ -203,6 +198,12 @@ public:
const CallEvent &Call,
const StackFrameContext *CalleeCtx);
/// Finds the transitive closure of symbols within the given region.
///
/// Returns false if the visitor aborted the scan.
virtual bool scanReachableSymbols(Store S, const MemRegion *R,
ScanReachableSymbols &Visitor) = 0;
virtual void print(Store store, raw_ostream &Out,
const char* nl, const char *sep) = 0;
@ -274,24 +275,6 @@ inline StoreRef &StoreRef::operator=(StoreRef const &newStore) {
return *this;
}
// FIXME: Do we still need this?
/// SubRegionMap - An abstract interface that represents a queryable map
/// between MemRegion objects and their subregions.
class SubRegionMap {
virtual void anchor();
public:
virtual ~SubRegionMap() {}
class Visitor {
virtual void anchor();
public:
virtual ~Visitor() {}
virtual bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) = 0;
};
virtual bool iterSubRegions(const MemRegion *region, Visitor& V) const = 0;
};
// FIXME: Do we need to pass ProgramStateManager anymore?
StoreManager *CreateRegionStoreManager(ProgramStateManager& StMgr);
StoreManager *CreateFieldsOnlyRegionStoreManager(ProgramStateManager& StMgr);

View File

@ -179,7 +179,7 @@ const StackFrameContext *VarRegion::getStackFrame() const {
// Region extents.
//===----------------------------------------------------------------------===//
DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const {
DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const {
ASTContext &Ctx = svalBuilder.getContext();
QualType T = getDesugaredValueType(Ctx);
@ -470,7 +470,7 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
}
void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
os << "base " << decl->getName();
os << "base{" << superRegion << ',' << decl->getName() << '}';
}
void CXXThisRegion::dumpToStream(raw_ostream &os) const {
@ -1031,30 +1031,63 @@ RegionOffset MemRegion::getAsOffset() const {
while (1) {
switch (R->getKind()) {
default:
return RegionOffset(0);
return RegionOffset();
case SymbolicRegionKind:
case AllocaRegionKind:
case CompoundLiteralRegionKind:
case CXXThisRegionKind:
case StringRegionKind:
case VarRegionKind:
case ObjCIvarRegionKind:
case CXXTempObjectRegionKind:
goto Finish;
case CXXBaseObjectRegionKind: {
const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R);
R = BOR->getSuperRegion();
QualType Ty;
if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) {
Ty = TVR->getDesugaredValueType(getContext());
} else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
// If our base region is symbolic, we don't know what type it really is.
// Pretend the type of the symbol is the true dynamic type.
// (This will at least be self-consistent for the life of the symbol.)
Ty = SR->getSymbol()->getType(getContext())->getPointeeType();
}
const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl();
if (!Child) {
// We cannot compute the offset of the base class.
return RegionOffset();
}
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child);
CharUnits BaseOffset;
const CXXRecordDecl *Base = BOR->getDecl();
if (Child->isVirtuallyDerivedFrom(Base))
BaseOffset = Layout.getVBaseClassOffset(Base);
else
BaseOffset = Layout.getBaseClassOffset(Base);
// The base offset is in chars, not in bits.
Offset += BaseOffset.getQuantity() * getContext().getCharWidth();
break;
}
case ElementRegionKind: {
const ElementRegion *ER = cast<ElementRegion>(R);
QualType EleTy = ER->getValueType();
if (!IsCompleteType(getContext(), EleTy))
return RegionOffset(0);
return RegionOffset();
SVal Index = ER->getIndex();
if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) {
int64_t i = CI->getValue().getSExtValue();
CharUnits Size = getContext().getTypeSizeInChars(EleTy);
Offset += i * Size.getQuantity() * 8;
// This type size is in bits.
Offset += i * getContext().getTypeSize(EleTy);
} else {
// We cannot compute offset for non-concrete index.
return RegionOffset(0);
return RegionOffset();
}
R = ER->getSuperRegion();
break;
@ -1064,7 +1097,7 @@ RegionOffset MemRegion::getAsOffset() const {
const RecordDecl *RD = FR->getDecl()->getParent();
if (!RD->isCompleteDefinition())
// We cannot compute offset for incomplete type.
return RegionOffset(0);
return RegionOffset();
// Get the field number.
unsigned idx = 0;
for (RecordDecl::field_iterator FI = RD->field_begin(),

View File

@ -499,8 +499,6 @@ ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key)
return getPersistentState(NewState);
}
void ScanReachableSymbols::anchor() { }
bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I)
if (!scan(*I))
@ -578,10 +576,19 @@ bool ScanReachableSymbols::scan(const MemRegion *R) {
return false;
// If this is a subregion, also visit the parent regions.
if (const SubRegion *SR = dyn_cast<SubRegion>(R))
if (!scan(SR->getSuperRegion()))
if (const SubRegion *SR = dyn_cast<SubRegion>(R)) {
const MemRegion *Super = SR->getSuperRegion();
if (!scan(Super))
return false;
// When we reach the topmost region, scan all symbols in it.
if (isa<MemSpaceRegion>(Super)) {
StoreManager &StoreMgr = state->getStateManager().getStoreManager();
if (!StoreMgr.scanReachableSymbols(state->getStore(), SR, *this))
return false;
}
}
// Regions captured by a block are also implicitly reachable.
if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) {
BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
@ -592,16 +599,7 @@ bool ScanReachableSymbols::scan(const MemRegion *R) {
}
}
// Now look at the binding to this region (if any).
if (!scan(state->getSValAsScalarOrLoc(R)))
return false;
// Now look at the subregions.
if (!SRM.get())
SRM.reset(state->getStateManager().getStoreManager().
getSubRegionMap(state->getStore()));
return SRM->iterSubRegions(R, *this);
return true;
}
bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const {

View File

@ -42,17 +42,27 @@ class BindingKey {
public:
enum Kind { Direct = 0x0, Default = 0x1 };
private:
llvm ::PointerIntPair<const MemRegion*, 1> P;
enum { SYMBOLIC = UINT64_MAX };
llvm::PointerIntPair<const MemRegion *, 1, Kind> P;
uint64_t Offset;
explicit BindingKey(const MemRegion *r, Kind k)
: P(r, k), Offset(SYMBOLIC) {}
explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k)
: P(r, (unsigned) k), Offset(offset) {}
: P(r, k), Offset(offset) {}
public:
bool isDirect() const { return P.getInt() == Direct; }
bool hasSymbolicOffset() const { return Offset == SYMBOLIC; }
const MemRegion *getRegion() const { return P.getPointer(); }
uint64_t getOffset() const { return Offset; }
uint64_t getOffset() const {
assert(!hasSymbolicOffset());
return Offset;
}
const MemRegion *getConcreteOffsetRegion() const;
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddPointer(P.getOpaqueValue());
@ -82,17 +92,36 @@ public:
BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
const RegionOffset &RO = R->getAsOffset();
if (RO.getRegion())
if (RO.isValid())
return BindingKey(RO.getRegion(), RO.getOffset(), k);
return BindingKey(R, 0, k);
return BindingKey(R, k);
}
const MemRegion *BindingKey::getConcreteOffsetRegion() const {
const MemRegion *R = getRegion();
if (!hasSymbolicOffset())
return R;
RegionOffset RO;
do {
const SubRegion *SR = dyn_cast<SubRegion>(R);
if (!SR)
break;
R = SR->getSuperRegion();
RO = R->getAsOffset();
} while (!RO.isValid());
return R;
}
namespace llvm {
static inline
raw_ostream &operator<<(raw_ostream &os, BindingKey K) {
os << '(' << K.getRegion() << ',' << K.getOffset()
<< ',' << (K.isDirect() ? "direct" : "default")
os << '(' << K.getRegion();
if (!K.hasSymbolicOffset())
os << ',' << K.getOffset();
os << ',' << (K.isDirect() ? "direct" : "default")
<< ')';
return os;
}
@ -133,60 +162,6 @@ public:
namespace {
class RegionStoreSubRegionMap : public SubRegionMap {
public:
typedef llvm::ImmutableSet<const MemRegion*> Set;
typedef llvm::DenseMap<const MemRegion*, Set> Map;
private:
Set::Factory F;
Map M;
public:
bool add(const MemRegion* Parent, const MemRegion* SubRegion) {
Map::iterator I = M.find(Parent);
if (I == M.end()) {
M.insert(std::make_pair(Parent, F.add(F.getEmptySet(), SubRegion)));
return true;
}
I->second = F.add(I->second, SubRegion);
return false;
}
void process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R);
~RegionStoreSubRegionMap() {}
const Set *getSubRegions(const MemRegion *Parent) const {
Map::const_iterator I = M.find(Parent);
return I == M.end() ? NULL : &I->second;
}
bool iterSubRegions(const MemRegion* Parent, Visitor& V) const {
Map::const_iterator I = M.find(Parent);
if (I == M.end())
return true;
Set S = I->second;
for (Set::iterator SI=S.begin(),SE=S.end(); SI != SE; ++SI) {
if (!V.Visit(Parent, *SI))
return false;
}
return true;
}
};
void
RegionStoreSubRegionMap::process(SmallVectorImpl<const SubRegion*> &WL,
const SubRegion *R) {
const MemRegion *superR = R->getSuperRegion();
if (add(superR, R))
if (const SubRegion *sr = dyn_cast<SubRegion>(superR))
WL.push_back(sr);
}
class RegionStoreManager : public StoreManager {
const RegionStoreFeatures Features;
RegionBindings::Factory RBFactory;
@ -197,12 +172,6 @@ public:
Features(f),
RBFactory(mgr.getAllocator()) {}
SubRegionMap *getSubRegionMap(Store store) {
return getRegionStoreSubRegionMap(store);
}
RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store);
Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R);
/// getDefaultBinding - Returns an SVal* representing an optional default
/// binding associated with a region and its subregions.
@ -255,10 +224,12 @@ public:
const CallEvent *Call,
InvalidatedRegions *Invalidated);
bool scanReachableSymbols(Store S, const MemRegion *R,
ScanReachableSymbols &Callbacks);
public: // Made public for helper classes.
void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R,
RegionStoreSubRegionMap &M);
RegionBindings removeSubRegionBindings(RegionBindings B, const SubRegion *R);
RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V);
@ -443,28 +414,6 @@ ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
}
RegionStoreSubRegionMap*
RegionStoreManager::getRegionStoreSubRegionMap(Store store) {
RegionBindings B = GetRegionBindings(store);
RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap();
SmallVector<const SubRegion*, 10> WL;
for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I)
if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion()))
M->process(WL, R);
// We also need to record in the subregion map "intermediate" regions that
// don't have direct bindings but are super regions of those that do.
while (!WL.empty()) {
const SubRegion *R = WL.back();
WL.pop_back();
M->process(WL, R);
}
return M;
}
//===----------------------------------------------------------------------===//
// Region Cluster analysis.
//===----------------------------------------------------------------------===//
@ -584,16 +533,88 @@ public:
// Binding invalidation.
//===----------------------------------------------------------------------===//
void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B,
const MemRegion *R,
RegionStoreSubRegionMap &M) {
bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R,
ScanReachableSymbols &Callbacks) {
// FIXME: This linear scan through all bindings could possibly be optimized
// by changing the data structure used for RegionBindings.
if (const RegionStoreSubRegionMap::Set *S = M.getSubRegions(R))
for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end();
I != E; ++I)
RemoveSubRegionBindings(B, *I, M);
RegionBindings B = GetRegionBindings(S);
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) {
BindingKey Key = RI.getKey();
if (Key.getRegion() == R) {
if (!Callbacks.scan(RI.getData()))
return false;
} else if (Key.hasSymbolicOffset()) {
if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Key.getRegion()))
if (BaseSR->isSubRegionOf(R))
if (!Callbacks.scan(RI.getData()))
return false;
}
}
B = removeBinding(B, R);
return true;
}
RegionBindings RegionStoreManager::removeSubRegionBindings(RegionBindings B,
const SubRegion *R) {
// FIXME: This linear scan through all bindings could possibly be optimized
// by changing the data structure used for RegionBindings.
BindingKey SRKey = BindingKey::Make(R, BindingKey::Default);
assert(SRKey.isValid());
if (SRKey.hasSymbolicOffset()) {
const SubRegion *Base = cast<SubRegion>(SRKey.getConcreteOffsetRegion());
B = removeSubRegionBindings(B, Base);
return addBinding(B, Base, BindingKey::Default, UnknownVal());
}
// FIXME: This does the wrong thing for bitfields.
uint64_t Length = UINT64_MAX;
SVal Extent = R->getExtent(svalBuilder);
if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) {
const llvm::APSInt &ExtentInt = ExtentCI->getValue();
assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned());
// Extents are in bytes but region offsets are in bits. Be careful!
Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth();
}
// It is safe to iterate over the bindings as they are being changed
// because they are in an ImmutableMap.
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) {
BindingKey NextKey = RI.getKey();
if (NextKey.getRegion() == SRKey.getRegion()) {
// Case 1: The next binding is inside the region we're invalidating.
// Remove it.
if (NextKey.getOffset() > SRKey.getOffset() &&
NextKey.getOffset() - SRKey.getOffset() < Length)
B = removeBinding(B, NextKey);
// Case 2: The next binding is at the same offset as the region we're
// invalidating. In this case, we need to leave default bindings alone,
// since they may be providing a default value for a regions beyond what
// we're invalidating.
// FIXME: This is probably incorrect; consider invalidating an outer
// struct whose first field is bound to a LazyCompoundVal.
else if (NextKey.getOffset() == SRKey.getOffset())
if (NextKey.isDirect())
B = removeBinding(B, NextKey);
} else if (NextKey.hasSymbolicOffset()) {
const MemRegion *Base = NextKey.getConcreteOffsetRegion();
// Case 3: The next key is symbolic and we just changed something within
// its concrete region. We don't know if the binding is still valid, so
// we'll be conservative and remove it.
if (R->isSubRegionOf(Base))
B = removeBinding(B, NextKey);
// Case 4: The next key is symbolic, but we changed a known super-region.
// In this case the binding is certainly no longer valid.
else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base))
if (BaseSR->isSubRegionOf(R))
B = removeBinding(B, NextKey);
}
}
return B;
}
namespace {
@ -1542,20 +1563,7 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) {
return BindVector(store, TR, V);
}
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
if (ER->getIndex().isZeroConstant()) {
if (const TypedValueRegion *superR =
dyn_cast<TypedValueRegion>(ER->getSuperRegion())) {
QualType superTy = superR->getValueType();
// For now, just invalidate the fields of the struct/union/class.
// This is for test rdar_test_7185607 in misc-ps-region-store.m.
// FIXME: Precisely handle the fields of the record.
if (superTy->isStructureOrClassType())
return KillStruct(store, superR, UnknownVal());
}
}
}
else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
// Binding directly to a symbolic region should be treated as binding
// to element 0.
QualType T = SR->getSymbol()->getType(Ctx);
@ -1569,10 +1577,13 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) {
R = GetElementZeroRegion(SR, T);
}
// Clear out bindings that may overlap with this binding.
// Perform the binding.
RegionBindings B = GetRegionBindings(store);
return StoreRef(addBinding(B, R, BindingKey::Direct,
V).getRootWithoutRetain(), *this);
B = removeSubRegionBindings(B, cast<SubRegion>(R));
BindingKey Key = BindingKey::Make(R, BindingKey::Direct);
return StoreRef(addBinding(B, Key, V).getRootWithoutRetain(), *this);
}
StoreRef RegionStoreManager::BindDecl(Store store, const VarRegion *VR,
@ -1792,30 +1803,11 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R,
SVal DefaultVal) {
BindingKey key = BindingKey::Make(R, BindingKey::Default);
// The BindingKey may be "invalid" if we cannot handle the region binding
// explicitly. One example is something like array[index], where index
// is a symbolic value. In such cases, we want to invalidate the entire
// array, as the index assignment could have been to any element. In
// the case of nested symbolic indices, we need to march up the region
// hierarchy untile we reach a region whose binding we can reason about.
const SubRegion *subReg = R;
while (!key.isValid()) {
if (const SubRegion *tmp = dyn_cast<SubRegion>(subReg->getSuperRegion())) {
subReg = tmp;
key = BindingKey::Make(tmp, BindingKey::Default);
}
else
break;
}
// Remove the old bindings, using 'subReg' as the root of all regions
// Remove the old bindings, using 'R' as the root of all regions
// we will invalidate.
RegionBindings B = GetRegionBindings(store);
OwningPtr<RegionStoreSubRegionMap>
SubRegions(getRegionStoreSubRegionMap(store));
RemoveSubRegionBindings(B, subReg, *SubRegions);
B = removeSubRegionBindings(B, R);
// Set the default value of the struct region to "unknown".
if (!key.isValid())
@ -1830,12 +1822,7 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
// Nuke the old bindings stemming from R.
RegionBindings B = GetRegionBindings(store);
OwningPtr<RegionStoreSubRegionMap>
SubRegions(getRegionStoreSubRegionMap(store));
// B and DVM are updated after the call to RemoveSubRegionBindings.
RemoveSubRegionBindings(B, R, *SubRegions.get());
B = removeSubRegionBindings(B, R);
// Now copy the bindings. This amounts to just binding 'V' to 'R'. This
// results in a zero-copy algorithm.

View File

@ -369,6 +369,3 @@ bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr,
return true;
}
void SubRegionMap::anchor() { }
void SubRegionMap::Visitor::anchor() { }

View File

@ -92,3 +92,95 @@ float struct_in_struct_f() {
return c.r; // no-warning
}
int randomInt();
int testSymbolicInvalidation(int index) {
int vals[10];
vals[0] = 42;
clang_analyzer_eval(vals[0] == 42); // expected-warning{{TRUE}}
vals[index] = randomInt();
clang_analyzer_eval(vals[0] == 42); // expected-warning{{UNKNOWN}}
return vals[index]; // no-warning
}
int testConcreteInvalidation(int index) {
int vals[10];
vals[index] = 42;
clang_analyzer_eval(vals[index] == 42); // expected-warning{{TRUE}}
vals[0] = randomInt();
clang_analyzer_eval(vals[index] == 42); // expected-warning{{UNKNOWN}}
return vals[0]; // no-warning
}
typedef struct {
int x, y, z;
} S;
S makeS();
int testSymbolicInvalidationStruct(int index) {
S vals[10];
vals[0].x = 42;
clang_analyzer_eval(vals[0].x == 42); // expected-warning{{TRUE}}
vals[index] = makeS();
clang_analyzer_eval(vals[0].x == 42); // expected-warning{{UNKNOWN}}
return vals[index].x; // no-warning
}
int testConcreteInvalidationStruct(int index) {
S vals[10];
vals[index].x = 42;
clang_analyzer_eval(vals[index].x == 42); // expected-warning{{TRUE}}
vals[0] = makeS();
clang_analyzer_eval(vals[index].x == 42); // expected-warning{{UNKNOWN}}
return vals[0].x; // no-warning
}
typedef struct {
S a[5];
S b[5];
} SS;
int testSymbolicInvalidationDoubleStruct(int index) {
SS vals;
vals.a[0].x = 42;
vals.b[0].x = 42;
clang_analyzer_eval(vals.a[0].x == 42); // expected-warning{{TRUE}}
clang_analyzer_eval(vals.b[0].x == 42); // expected-warning{{TRUE}}
vals.a[index] = makeS();
clang_analyzer_eval(vals.a[0].x == 42); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(vals.b[0].x == 42); // expected-warning{{TRUE}}
return vals.b[index].x; // no-warning
}
int testConcreteInvalidationDoubleStruct(int index) {
SS vals;
vals.a[index].x = 42;
vals.b[index].x = 42;
clang_analyzer_eval(vals.a[index].x == 42); // expected-warning{{TRUE}}
clang_analyzer_eval(vals.b[index].x == 42); // expected-warning{{TRUE}}
vals.a[0] = makeS();
clang_analyzer_eval(vals.a[index].x == 42); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(vals.b[index].x == 42); // expected-warning{{TRUE}}
return vals.b[0].x; // no-warning
}