diff options
author | Ted Kremenek <kremenek@apple.com> | 2008-11-12 19:24:17 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2008-11-12 19:24:17 +0000 |
commit | af3374187c47acea45706eab6744be6b1c66a856 (patch) | |
tree | aba0e93182f1d80e87d2f39b5211c2a7a4f1a69d /lib/Analysis | |
parent | b5abb429e7aac731c3b4363a6118a6b3a6e27abc (diff) |
Add (preliminary) transfer function support for ObjCForCollectionStmt. Still need to flesh out some logic.
When processing DeclStmt, use the new interface to StateManager::BindDecl. Conjuring of symbols is now done in VisitDeclStmt.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59155 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis')
-rw-r--r-- | lib/Analysis/GRCoreEngine.cpp | 21 | ||||
-rw-r--r-- | lib/Analysis/GRExprEngine.cpp | 110 |
2 files changed, 123 insertions, 8 deletions
diff --git a/lib/Analysis/GRCoreEngine.cpp b/lib/Analysis/GRCoreEngine.cpp index 99e2a7f06e..44c9b4871f 100644 --- a/lib/Analysis/GRCoreEngine.cpp +++ b/lib/Analysis/GRCoreEngine.cpp @@ -1,4 +1,4 @@ -//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ----------------*- C++ -*-// +//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-// // // The LLVM Compiler Infrastructure // @@ -212,6 +212,21 @@ void GRCoreEngineImpl::HandleBlockExit(CFGBlock * B, ExplodedNodeImpl* Pred) { return; } + case Stmt::ObjCForCollectionStmtClass: { + // In the case of ObjCForCollectionStmt, it appears twice in a CFG: + // + // (1) inside a basic block, which represents the binding of the + // 'element' variable to a value. + // (2) in a terminator, which represents the branch. + // + // For (1), subengines will bind a value (i.e., 0 or 1) indicating + // whether or not collection contains any more elements. We cannot + // just test to see if the element is nil because a container can + // contain nil elements. + HandleBranch(Term, Term, B, Pred); + return; + } + case Stmt::SwitchStmtClass: { GRSwitchNodeBuilderImpl builder(Pred, B, cast<SwitchStmt>(Term)->getCond(), @@ -233,8 +248,8 @@ void GRCoreEngineImpl::HandleBlockExit(CFGBlock * B, ExplodedNodeImpl* Pred) { GenerateNode(BlockEdge(B, *(B->succ_begin())), Pred->State, Pred); } -void GRCoreEngineImpl::HandleBranch(Expr* Cond, Stmt* Term, CFGBlock * B, - ExplodedNodeImpl* Pred) { +void GRCoreEngineImpl::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, + ExplodedNodeImpl* Pred) { assert (B->succ_size() == 2); GRBranchNodeBuilderImpl Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 8e6d5ab5cc..2fa163ad23 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -366,6 +366,10 @@ void GRExprEngine::Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst) { case Stmt::ObjCIvarRefExprClass: VisitObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst, false); break; + + case Stmt::ObjCForCollectionStmtClass: + VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); + break; case Stmt::ObjCMessageExprClass: { VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), Pred, Dst); @@ -547,7 +551,7 @@ const GRState* GRExprEngine::MarkBranch(const GRState* St, } } -void GRExprEngine::ProcessBranch(Expr* Condition, Stmt* Term, +void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, BranchNodeBuilder& builder) { // Remove old bindings for subexpressions. @@ -1346,6 +1350,79 @@ void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, } //===----------------------------------------------------------------------===// +// Transfer function: Objective-C fast enumeration 'for' statements. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, + NodeTy* Pred, NodeSet& Dst) { + + // ObjCForCollectionStmts are processed in two places. This method + // handles the case where an ObjCForCollectionStmt* occurs as one of the + // statements within a basic block. This transfer function does two things: + // + // (1) binds the next container value to 'element'. This creates a new + // node in the ExplodedGraph. + // + // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating + // whether or not the container has any more elements. This value + // will be tested in ProcessBranch. We need to explicitly bind + // this value because a container can contain nil elements. + // + // FIXME: Eventually this logic should actually do dispatches to + // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). + // This will require simulating a temporary NSFastEnumerationState, either + // through an SVal or through the use of MemRegions. This value can + // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop + // terminates we reclaim the temporary (it goes out of scope) and we + // we can test if the SVal is 0 or if the MemRegion is null (depending + // on what approach we take). + // + // For now: simulate (1) by assigning either a symbol or nil if the + // container is empty. Thus this transfer function will by default + // result in state splitting. + + Stmt* elem = S->getElement(); + VarDecl* ElemD; + bool bindDecl = false; + + if (DeclStmt* DS = dyn_cast<DeclStmt>(elem)) { + ElemD = cast<VarDecl>(DS->getSolitaryDecl()); + assert (ElemD->getInit() == 0); + bindDecl = true; + } + else + ElemD = cast<VarDecl>(cast<DeclRefExpr>(elem)->getDecl()); + + + // Get the current state, as well as the QualType for 'int' and the + // type of the element. + GRStateRef state = GRStateRef(GetState(Pred), getStateManager()); + QualType IntTy = getContext().IntTy; + QualType ElemTy = ElemD->getType(); + + // Handle the case where the container still has elements. + SVal TrueV = NonLoc::MakeVal(getBasicVals(), 1, IntTy); + GRStateRef hasElems = state.BindExpr(S, TrueV); + + assert (Loc::IsLocType(ElemTy)); + unsigned Count = Builder->getCurrentBlockCount(); + loc::SymbolVal SymV(SymMgr.getConjuredSymbol(elem, ElemTy, Count)); + + if (bindDecl) + hasElems = hasElems.BindDecl(ElemD, &SymV, Count); + else + hasElems = hasElems.BindLoc(hasElems.GetLValue(ElemD), SymV); + + + // Handle the case where the container has no elements. + + + + + +} + +//===----------------------------------------------------------------------===// // Transfer function: Objective-C message expressions. //===----------------------------------------------------------------------===// @@ -1630,21 +1707,44 @@ void GRExprEngine::VisitDeclStmt(DeclStmt* DS, NodeTy* Pred, NodeSet& Dst) { const VarDecl* VD = dyn_cast<VarDecl>(D); - Expr* Ex = const_cast<Expr*>(VD->getInit()); + Expr* InitEx = const_cast<Expr*>(VD->getInit()); // FIXME: static variables may have an initializer, but the second // time a function is called those values may not be current. NodeSet Tmp; - if (Ex) - Visit(Ex, Pred, Tmp); + if (InitEx) + Visit(InitEx, Pred, Tmp); if (Tmp.empty()) Tmp.Add(Pred); for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { const GRState* St = GetState(*I); - St = StateMgr.BindDecl(St, VD, Ex, Builder->getCurrentBlockCount()); + unsigned Count = Builder->getCurrentBlockCount(); + + if (InitEx) { + SVal InitVal = GetSVal(St, InitEx); + QualType T = VD->getType(); + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown()) { + if (Loc::IsLocType(T)) { + SymbolID Sym = SymMgr.getConjuredSymbol(InitEx, Count); + InitVal = loc::SymbolVal(Sym); + } + else if (T->isIntegerType()) { + SymbolID Sym = SymMgr.getConjuredSymbol(InitEx, Count); + InitVal = nonloc::SymbolVal(Sym); + } + } + + St = StateMgr.BindDecl(St, VD, &InitVal, Count); + } + else + St = StateMgr.BindDecl(St, VD, 0, Count); + MakeNode(Dst, DS, *I, St); } } |