<div class="gmail_quote">Thanks for looking through the patch. Commented on some of your comments inline.</div><div class="gmail_quote"><br></div><div class="gmail_quote">W dniu 15 września 2010 06:52 użytkownik Ted Kremenek <span dir="ltr"><<a href="mailto:kremenek@apple.com">kremenek@apple.com</a>></span> napisał:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"><div><div></div><div class="h5">On Sep 2, 2010, at 3:29 PM, Marcin Świderski wrote:<br>
<br>
> Hi<br>
><br>
> I'm sending a patch with implementation of C++<br>
> - initializers from constructor initialization list,<br>
> - implicit destructors for objects with automatic storage duration.<br>
><br>
> For destructors I've taken care of:<br>
> - block local scopes,<br>
> - if/switch/for/while/do local scopes (in case there's no block),<br>
> - if/switch/for/while condition variables,<br>
> - catch exception variable,<br>
> - temporaries bound to const reference.<br>
><br>
> Is there something that I've missed?<br>
><br>
> As it have been suggested I've created hierarchy of CFGElements. Currently there're two unused types for implicit destructor calls in destructor. I've did not revert ability of CFGElement to cast/dyn_cast to Stmt, because it would lead to situation when cast<Stmt>(SomeCFGElement) would return null. I did however add method for downcasting CFGElement object to object of its implemntation class (returned by value, not pointer) with returning invalid object on invalid cast.<br>
><br>
> I did not add CFGElement for destructors of temporary objects. After giving it some thought I came to a conclusion that it shouldn't be covered by CFG, because it clearly needs path-sensitiveness.<br>
><br>
> Cheers<br>
> Marcin<br>
><br>
<br>
</div></div>Comments inline below on the patch. Is the plan to still ad the CFGElements for the destructors of temporary objects? Overall, this is truly fantastic work. Sorry I took so long to look it over.<br>
<br>
One thing that would help is a general big comment (above LocalScopes perhaps) that explains the algorithm for adding destructors. I was able to tease it out of the code, but a high-level description would be really useful.<br>
<br>
One thing that occurred to me that while I argued against putting the scope information in the CFG itself, it looks like we do all the work to reconstruct scope information (via the LocalScope objects). Perhaps that information should be exposed as a public API? Just thinking (and not something to immediately consider).<br>
<br></blockquote><div>This could be done. However LocalScope graph construction process works only on variables that will need destructor to be called. Adding all variables for creating full scope info could break the graph. I'll include comment in code describing the process, so it will be clear why.</div>
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
Index: include/clang/Analysis/FlowSensitive/DataflowSolver.h<br>
===================================================================<br>
--- include/clang/Analysis/FlowSensitive/DataflowSolver.h (revision 112851)<br>
+++ include/clang/Analysis/FlowSensitive/DataflowSolver.h (working copy)<br>
@@ -273,8 +273,15 @@<br>
void ProcessBlock(const CFGBlock* B, bool recordStmtValues,<br>
dataflow::forward_analysis_tag) {<br>
<br>
- for (StmtItr I=ItrTraits::StmtBegin(B), E=ItrTraits::StmtEnd(B); I!=E;++I)<br>
- ProcessStmt(*I, recordStmtValues, AnalysisDirTag());<br>
+ for (StmtItr I=ItrTraits::StmtBegin(B), E=ItrTraits::StmtEnd(B); I!=E;++I) {<br>
+ if (const CFGStmt* SE = dyn_cast<const CFGStmt>(&*I))<br>
+ ProcessStmt(*SE, recordStmtValues, AnalysisDirTag());<br>
+ // FIXME: (CFGElement) Should handle other cases.<br>
+ else if (const CFGInitializer* IE = dyn_cast<const CFGInitializer>(&*I))<br>
+ (void)IE;<br>
+ else if (const CFGImplicitDtor* DE = dyn_cast<const CFGImplicitDtor>(&*I))<br>
+ (void)DE;<br>
+ }<br>
<br>
TF.VisitTerminator(const_cast<CFGBlock*>(B));<br>
}<br>
@@ -284,8 +291,15 @@<br>
<br>
TF.VisitTerminator(const_cast<CFGBlock*>(B));<br>
<br>
- for (StmtItr I=ItrTraits::StmtBegin(B), E=ItrTraits::StmtEnd(B); I!=E;++I)<br>
- ProcessStmt(*I, recordStmtValues, AnalysisDirTag());<br>
+ for (StmtItr I=ItrTraits::StmtBegin(B), E=ItrTraits::StmtEnd(B); I!=E;++I) {<br>
+ if (const CFGStmt* SE = dyn_cast<const CFGStmt>(&*I))<br>
+ ProcessStmt(*SE, recordStmtValues, AnalysisDirTag());<br>
+ // FIXME: (CFGElement) Should handle other cases.<br>
+ else if (const CFGInitializer* IE = dyn_cast<const CFGInitializer>(&*I))<br>
+ (void)IE;<br>
+ else if (const CFGImplicitDtor* DE = dyn_cast<const CFGImplicitDtor>(&*I))<br>
+ (void)DE;<br>
+ }<br>
}<br>
<br>
<br>
It would probably be more succinct to introduce a ProcessCFGElement(), which then in turn called ProcessStmt(). That way the two loops look like:<br>
<br>
- for (StmtItr I=ItrTraits::StmtBegin(B), E=ItrTraits::StmtEnd(B); I!=E;++I)<br>
- ProcessCFGElement(*I, recordStmtValues, AnalysisDirTag());<br>
<br>
and then have ProcessCFGElement do the dispatch logic. Right now this is basically copy-paste for the forward/backward analyses.<br>
<br>
<br>
<br>
void ProcessStmt(const Stmt* S, bool record, dataflow::forward_analysis_tag) {<br>
Index: include/clang/Analysis/Visitors/CFGRecStmtVisitor.h<br>
===================================================================<br>
--- include/clang/Analysis/Visitors/CFGRecStmtVisitor.h (revision 112851)<br>
+++ include/clang/Analysis/Visitors/CFGRecStmtVisitor.h (working copy)<br>
@@ -47,6 +47,9 @@<br>
<br>
// Defining operator() allows the visitor to be used as a C++ style functor.<br>
void operator()(Stmt* S) { static_cast<ImplClass*>(this)->BlockStmt_Visit(S);}<br>
+ void operator()(CFGElement E) {<br>
+ static_cast<ImplClass*>(this)->VisitCFGElement(E);<br>
+ }<br>
};<br>
<br>
Looks good.<br>
<br>
<br>
<br>
} // end namespace clang<br>
Index: include/clang/Analysis/Visitors/CFGStmtVisitor.h<br>
===================================================================<br>
--- include/clang/Analysis/Visitors/CFGStmtVisitor.h (revision 112851)<br>
+++ include/clang/Analysis/Visitors/CFGStmtVisitor.h (working copy)<br>
@@ -61,6 +61,16 @@<br>
RetTy VisitConditionVariableInit(Stmt *S) {<br>
return RetTy();<br>
}<br>
+<br>
+ /// VisitCFGElement - Handle CFGElements: Statements, Initializers and<br>
+ /// ImplicitDtors. Those CFGElements act like statements from CFG point of<br>
+ /// view.<br>
+ RetTy VisitCFGElement(CFGElement E) {<br>
+ if (CFGStmt S = E.getAs<CFGStmt>())<br>
+ return BlockStmt_Visit(S);<br>
+ // FIXME: (CFGElement) Handle Initializers and ImplicitDtors<br>
+ return RetTy();<br>
+ }<br>
<br>
Looks good.<br>
<br>
/// BlockVisit_XXX - Visitor methods for visiting the "root" statements in<br>
/// CFGBlocks. Root statements are the statements that appear explicitly in<br>
Index: include/clang/Analysis/CFG.h<br>
===================================================================<br>
--- include/clang/Analysis/CFG.h (revision 112851)<br>
+++ include/clang/Analysis/CFG.h (working copy)<br>
@@ -28,30 +28,234 @@<br>
}<br>
namespace clang {<br>
class Decl;<br>
+ class VarDecl;<br>
class Stmt;<br>
class Expr;<br>
+ class CXXBaseOrMemberInitializer;<br>
+ class CXXBindTemporaryExpr;<br>
+ class CXXDestructorDecl;<br>
+ class CXXExprWithTemporaries;<br>
+ class FieldDecl;<br>
+ class TypeSourceInfo;<br>
class CFG;<br>
+ class CFGBlock;<br>
class PrinterHelper;<br>
class LangOptions;<br>
class ASTContext;<br>
<br>
-/// CFGElement - Represents a top-level expression in a basic block.<br>
+/// CFGElement - Represents a top-level expression or other language construct<br>
+/// in a basic block.<br>
class CFGElement {<br>
- llvm::PointerIntPair<Stmt *, 2> Data;<br>
+protected:<br>
+ // ExternalData - External data sturcture used by implemntations to store<br>
+ // more data then one pointer.<br>
+ struct ExternalData {<br>
+ llvm::PointerIntPair<void*, 2> PrimData;<br>
+ llvm::PointerIntPair<void*, 2> SecData;<br>
+ };<br>
+<br>
+ ExternalData& getExternalData() {<br>
+ return *static_cast<ExternalData*>(Data.getPointer());<br>
+ }<br>
+ const ExternalData& getExternalData() const {<br>
+ return *static_cast<ExternalData*>(Data.getPointer());<br>
+ }<br>
+<br>
+ llvm::PointerIntPair<void*, 2> Data;<br>
+<br>
public:<br>
- enum Type { StartScope, EndScope };<br>
- explicit CFGElement() {}<br>
- CFGElement(Stmt *S, bool lvalue) : Data(S, lvalue ? 1 : 0) {}<br>
- CFGElement(Stmt *S, Type t) : Data(S, t == StartScope ? 2 : 3) {}<br>
- Stmt *getStmt() const { return Data.getPointer(); }<br>
- bool asLValue() const { return Data.getInt() == 1; }<br>
- bool asStartScope() const { return Data.getInt() == 2; }<br>
- bool asEndScope() const { return Data.getInt() == 3; }<br>
- bool asDtor() const { return Data.getInt() == 4; }<br>
+ enum Kind {<br>
+ Statement,<br>
+ LvalStatement,<br>
+ BEGIN_STATEMENTS = Statement,<br>
+ END_STATEMENTS = LvalStatement,<br>
+<br>
+ Initializer,<br>
+<br>
+ AutomaticObjectDtor,<br>
+ BaseDtor,<br>
+ MemberDtor,<br>
+ BEGIN_DTORS = AutomaticObjectDtor,<br>
+ END_DTORS = MemberDtor,<br>
+<br>
+ BEGIN_EXTERNALS = BEGIN_DTORS<br>
+ };<br>
+<br>
+ // Construct invalid CFGElement. Each implementation will provide default<br>
+ // constructor for constructing invalid instance. Invalid instance won't<br>
+ // preserve it's kind.<br>
+ CFGElement() {}<br>
+<br>
+ Kind getKind() const {<br>
+ if (Data.getInt() != BEGIN_EXTERNALS)<br>
+ return Kind(Data.getInt());<br>
+ return Kind(BEGIN_EXTERNALS + getExternalData().PrimData.getInt());<br>
+ }<br>
+<br>
+ bool isValid() const { return Data.getPointer(); }<br>
+ operator bool() const { return isValid(); };<br>
+<br>
+ bool isStmt() const {<br>
+ Kind K = Kind(Data.getInt());<br>
+ return K >= BEGIN_STATEMENTS && K <= END_STATEMENTS;<br>
+ }<br>
+<br>
+ bool isInitializer() const {<br>
+ return Data.getInt() == Initializer;<br>
+ }<br>
+<br>
+ bool isImplicitDtor() const {<br>
+ if (Data.getInt() != BEGIN_EXTERNALS)<br>
+ return false;<br>
+ Kind K = Kind(BEGIN_EXTERNALS + getExternalData().PrimData.getInt());<br>
+ return K >= BEGIN_DTORS && K <= END_DTORS;<br>
+ }<br>
+<br>
+ template<class ElemTy> ElemTy getAs() const {<br>
+ if (llvm::isa<ElemTy>(this))<br>
+ return *static_cast<const ElemTy*>(this);<br>
<br>
Could we use llvm::dyn_cast<> here? You already implement the classof() functions.<br>
<br></blockquote><div>You mean?:</div><div><br></div><div> if (ElemTy *E = llvm::dyn_cast<ElemTy>(this))</div><div> return *E;</div><div><br></div><div>We could, no real difference for me.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
+ return ElemTy();<br>
+ }<br>
<br>
<br>
+<br>
+ static bool classof(const CFGElement* E) { return true; }<br>
+<br>
+protected:<br>
+ CFGElement(Kind K, void* P)<br>
+ : Data(P, K) {<br>
+ assert (K < BEGIN_EXTERNALS && "CFGElement needs external data.");<br>
+ }<br>
+<br>
+ CFGElement(Kind K, void* P, void* S, llvm::BumpPtrAllocator& A)<br>
+ : Data(new (A.Allocate<ExternalData>()) ExternalData(), BEGIN_EXTERNALS) {<br>
+ assert (K >= BEGIN_EXTERNALS && "CFGElement doesn't need external data.");<br>
+ getExternalData().PrimData.setInt(K - BEGIN_EXTERNALS);<br>
+ getExternalData().PrimData.setPointer(P);<br>
+ getExternalData().SecData.setPointer(S);<br>
+ }<br>
+};<br>
<br>
Looks great.<br>
<br>
<br>
+<br>
+/// CFGStmt - Represents a top-level expression in a basic block.<br>
+class CFGStmt : public CFGElement {<br>
+public:<br>
+ CFGStmt() {}<br>
+ CFGStmt(Stmt* S, bool asLValue)<br>
+ : CFGElement(asLValue ? LvalStatement : Statement, S) {}<br>
+<br>
+ Stmt* getStmt() const { return static_cast<Stmt*>(Data.getPointer()); }<br>
operator Stmt*() const { return getStmt(); }<br>
- operator bool() const { return getStmt() != 0; }<br>
+<br>
+ bool asLValue() const { return getKind() == LvalStatement; }<br>
+<br>
+ static bool classof(const CFGElement* E) { return E->isStmt(); }<br>
};<br>
<br>
Looks great.<br>
<br>
<br>
+/// CFGInitializer - Represents a C++ initializer in constructor initialization<br>
+/// list.<br>
+class CFGInitializer : public CFGElement {<br>
+public:<br>
+ CFGInitializer() {}<br>
+ CFGInitializer(CXXBaseOrMemberInitializer* I)<br>
+ : CFGElement(Initializer, I) {}<br>
+<br>
+ CXXBaseOrMemberInitializer* getInitializer() const {<br>
+ return static_cast<CXXBaseOrMemberInitializer*>(Data.getPointer());<br>
+ }<br>
+ operator CXXBaseOrMemberInitializer*() const { return getInitializer(); }<br>
+<br>
+ static bool classof(const CFGElement* E) { return E->isInitializer(); }<br>
+};<br>
<br>
Looks great.<br>
<br>
+<br>
+/// CFGImplicitDtor - Represents implicit call to C++ object's destructor. This<br>
+/// is a base class for more specific implementations.<br>
+class CFGImplicitDtor : public CFGElement {<br>
+public:<br>
+ CFGImplicitDtor() {}<br>
+<br>
+ CXXDestructorDecl* getDestructor() const;<br>
+ SourceLocation getCallLoc() const;<br>
+ SourceLocation getObjectLoc() const;<br>
<br>
Please add doxygen comments for these two methods that return SourceLocations.<br>
<br>
+<br>
+ static bool classof(const CFGElement* E) { return E->isImplicitDtor(); }<br>
+<br>
+protected:<br>
+ CFGImplicitDtor(Kind K, void* P, void* S, llvm::BumpPtrAllocator& A)<br>
+ : CFGElement(K, P, S, A) {}<br>
+};<br>
<br>
Looks good.<br>
<br>
<br>
+<br>
+/// CFGAutomaticObjDtor - Represents implicit call to destructor of C++ object<br>
+/// with automatic storage duration.<br>
+class CFGAutomaticObjDtor : public CFGImplicitDtor {<br>
+public:<br>
+ CFGAutomaticObjDtor() {}<br>
+ CFGAutomaticObjDtor(VarDecl* VD, Stmt* S, llvm::BumpPtrAllocator& A)<br>
+ : CFGImplicitDtor(AutomaticObjectDtor, VD, S, A) {}<br>
+<br>
+ CXXDestructorDecl* getDestructor() const;<br>
+ SourceLocation getCallLoc() const;<br>
+ SourceLocation getObjectLoc() const;<br>
<br>
Please comment that we use our own dynamic-dispatch here for these methods since they are non-virtual and we don't want a vptr. When I first looked at this I thought this was a bug. I think it is very important to document the design decision here.<br>
<br>
+<br>
+ VarDecl* getVarDecl() const {<br>
+ return static_cast<VarDecl*>(getExternalData().PrimData.getPointer());<br>
+ }<br>
<br>
Looks great.<br>
<br>
+<br>
+ // Get statement end of which triggered the destructor call.<br>
+ Stmt* getTriggerStmt() const {<br>
+ return static_cast<Stmt*>(getExternalData().SecData.getPointer());<br>
+ }<br>
<br>
Looks great. Make the comment a doxygen comment.<br>
<br>
+<br>
+ static bool classof(const CFGElement* E) {<br>
+ return E->getKind() == AutomaticObjectDtor;<br>
+ }<br>
+};<br>
+<br>
+/// CFGBaseDtor - Represents implicit call to destructor of base class in<br>
+/// C++ class destructor.<br>
+class CFGBaseDtor : public CFGImplicitDtor {<br>
+public:<br>
+ CFGBaseDtor() {}<br>
+<br>
+ CXXDestructorDecl* getDestructor() const;<br>
+ SourceLocation getCallLoc() const;<br>
+ SourceLocation getObjectLoc() const;<br>
<br>
Please comment that we use our own dynamic-dispatch here for these methods since they are non-virtual and we don't want a vptr. When I first looked at this I thought this was a bug.<br>
<br>
+};<br>
+<br>
+/// CFGMemberDtor - Represents implicit call to destructor of member in<br>
+/// C++ class destructor.<br>
+class CFGMemberDtor : public CFGImplicitDtor {<br>
+public:<br>
+ CFGMemberDtor() {}<br>
+<br>
+ CXXDestructorDecl* getDestructor() const;<br>
+ SourceLocation getCallLoc() const;<br>
+ SourceLocation getObjectLoc() const;<br>
<br>
Please comment that we use our own dynamic-dispatch here for these methods since they are non-virtual and we don't want a vptr. When I first looked at this I thought this was a bug.<br>
<br>
<br>
+};<br>
+<br>
+// Dispatch methods from CFGImplicitDtor's interface.<br>
+#define CFG_IMPLICIT_DTOR_DISPATCH(Meth) \<br>
+ assert (Data.getInt() == BEGIN_EXTERNALS && "Bad CFGElement kind"); \<br>
+ Kind K = Kind(getExternalData().PrimData.getInt() + BEGIN_EXTERNALS); \<br>
+ switch (K) { \<br>
+ case AutomaticObjectDtor: \<br>
+ return static_cast<const CFGAutomaticObjDtor*>(this)->Meth(); \<br>
+ case BaseDtor: \<br>
+ return static_cast<const CFGBaseDtor*>(this)->Meth(); \<br>
+ default: \<br>
+ assert (K == MemberDtor && "CFGElement not of a destructor kind");\<br>
+ return static_cast<const CFGMemberDtor*>(this)->Meth(); \<br>
+ }<br>
+<br>
+inline CXXDestructorDecl* CFGImplicitDtor::getDestructor() const {<br>
+ CFG_IMPLICIT_DTOR_DISPATCH(getDestructor);<br>
+}<br>
+inline SourceLocation CFGImplicitDtor::getCallLoc() const {<br>
+ CFG_IMPLICIT_DTOR_DISPATCH(getCallLoc);<br>
+}<br>
+inline SourceLocation CFGImplicitDtor::getObjectLoc() const {<br>
+ CFG_IMPLICIT_DTOR_DISPATCH(getObjectLoc);<br>
+}<br>
+<br>
+#undef CFG_IMPLICIT_DTOR_DISPATCH<br>
+<br>
<br>
Cute.<br>
<br>
/// CFGBlock - Represents a single basic block in a source-level CFG.<br>
/// It consists of:<br>
///<br>
@@ -77,11 +281,11 @@<br>
/// &&, || expression that uses result of && or ||, RHS<br>
///<br>
class CFGBlock {<br>
- class StatementList {<br>
+ class ElementList {<br>
typedef BumpVector<CFGElement> ImplTy;<br>
ImplTy Impl;<br>
public:<br>
- StatementList(BumpVectorContext &C) : Impl(C, 4) {}<br>
+ ElementList(BumpVectorContext &C) : Impl(C, 4) {}<br>
<br>
Looks good.<br>
<br>
typedef std::reverse_iterator<ImplTy::iterator> iterator;<br>
typedef std::reverse_iterator<ImplTy::const_iterator> const_iterator;<br>
@@ -89,6 +293,11 @@<br>
typedef ImplTy::const_iterator const_reverse_iterator;<br>
<br>
void push_back(CFGElement e, BumpVectorContext &C) { Impl.push_back(e, C); }<br>
+ reverse_iterator insert(reverse_iterator I, size_t Cnt, CFGElement E,<br>
+ BumpVectorContext& C) {<br>
+ return Impl.insert(I, Cnt, E, C);<br>
+ }<br>
+<br>
CFGElement front() const { return Impl.back(); }<br>
CFGElement back() const { return Impl.front(); }<br>
<br>
@@ -110,8 +319,8 @@<br>
bool empty() const { return Impl.empty(); }<br>
};<br>
<br>
- /// Stmts - The set of statements in the basic block.<br>
- StatementList Stmts;<br>
+ /// Elements - The set of elements in the basic block.<br>
+ ElementList Elements;<br>
<br>
/// Label - An (optional) label that prefixes the executable<br>
/// statements in the block. When this variable is non-NULL, it is<br>
@@ -140,33 +349,33 @@<br>
<br>
public:<br>
explicit CFGBlock(unsigned blockid, BumpVectorContext &C)<br>
- : Stmts(C), Label(NULL), Terminator(NULL), LoopTarget(NULL),<br>
+ : Elements(C), Label(NULL), Terminator(NULL), LoopTarget(NULL),<br>
BlockID(blockid), Preds(C, 1), Succs(C, 1) {}<br>
~CFGBlock() {}<br>
<br>
- // Statement iterators<br>
- typedef StatementList::iterator iterator;<br>
- typedef StatementList::const_iterator const_iterator;<br>
- typedef StatementList::reverse_iterator reverse_iterator;<br>
- typedef StatementList::const_reverse_iterator const_reverse_iterator;<br>
+ // Element iterators<br>
+ typedef ElementList::iterator iterator;<br>
+ typedef ElementList::const_iterator const_iterator;<br>
+ typedef ElementList::reverse_iterator reverse_iterator;<br>
+ typedef ElementList::const_reverse_iterator const_reverse_iterator;<br>
<br>
- CFGElement front() const { return Stmts.front(); }<br>
- CFGElement back() const { return Stmts.back(); }<br>
+ CFGElement front() const { return Elements.front(); }<br>
+ CFGElement back() const { return Elements.back(); }<br>
<br>
- iterator begin() { return Stmts.begin(); }<br>
- iterator end() { return Stmts.end(); }<br>
- const_iterator begin() const { return Stmts.begin(); }<br>
- const_iterator end() const { return Stmts.end(); }<br>
+ iterator begin() { return Elements.begin(); }<br>
+ iterator end() { return Elements.end(); }<br>
+ const_iterator begin() const { return Elements.begin(); }<br>
+ const_iterator end() const { return Elements.end(); }<br>
<br>
- reverse_iterator rbegin() { return Stmts.rbegin(); }<br>
- reverse_iterator rend() { return Stmts.rend(); }<br>
- const_reverse_iterator rbegin() const { return Stmts.rbegin(); }<br>
- const_reverse_iterator rend() const { return Stmts.rend(); }<br>
+ reverse_iterator rbegin() { return Elements.rbegin(); }<br>
+ reverse_iterator rend() { return Elements.rend(); }<br>
+ const_reverse_iterator rbegin() const { return Elements.rbegin(); }<br>
+ const_reverse_iterator rend() const { return Elements.rend(); }<br>
<br>
- unsigned size() const { return Stmts.size(); }<br>
- bool empty() const { return Stmts.empty(); }<br>
+ unsigned size() const { return Elements.size(); }<br>
+ bool empty() const { return Elements.empty(); }<br>
<br>
- CFGElement operator[](size_t i) const { return Stmts[i]; }<br>
+ CFGElement operator[](size_t i) const { return Elements[i]; }<br>
<br>
<br>
All looks great.<br>
<br>
<br>
// CFG iterators<br>
typedef AdjacentBlocks::iterator pred_iterator;<br>
@@ -240,14 +449,24 @@<br>
}<br>
<br>
void appendStmt(Stmt* Statement, BumpVectorContext &C, bool asLValue) {<br>
- Stmts.push_back(CFGElement(Statement, asLValue), C);<br>
- }<br>
- void StartScope(Stmt* S, BumpVectorContext &C) {<br>
- Stmts.push_back(CFGElement(S, CFGElement::StartScope), C);<br>
+ Elements.push_back(CFGStmt(Statement, asLValue), C);<br>
}<br>
- void EndScope(Stmt* S, BumpVectorContext &C) {<br>
- Stmts.push_back(CFGElement(S, CFGElement::EndScope), C);<br>
+ void appendInitializer(CXXBaseOrMemberInitializer* I, BumpVectorContext& C) {<br>
+ Elements.push_back(CFGInitializer(I), C);<br>
}<br>
+<br>
+ // Destructors must be inserted in reversed order. So insertion is in two<br>
+ // steps. First we prepare space for some number of elements, then we insert<br>
+ // the elements begining at the last position in prepared space.<br>
+ iterator beginAutomaticObjDtorsInsert(iterator I, size_t Cnt,<br>
+ BumpVectorContext& C) {<br>
+ return iterator(Elements.insert(I.base(), Cnt, CFGElement(), C));<br>
+ }<br>
+ iterator insertAutomaticObjDtor(iterator I, VarDecl* VD, Stmt* S,<br>
+ BumpVectorContext& C) {<br>
+ *I = CFGAutomaticObjDtor(VD, S, C.getAllocator());<br>
+ return ++I;<br>
+ }<br>
<br>
Looks great.<br>
<br>
};<br>
<br>
<br>
/// createBlock - Create a new block in the CFG. The CFG owns the block;<br>
/// the caller should not directly free it.<br>
@@ -321,11 +568,12 @@<br>
//===--------------------------------------------------------------------===//<br>
<br>
template <typename CALLBACK><br>
- void VisitBlockStmts(CALLBACK& O) const {<br>
+ void VisitCFGElements(CALLBACK& O) const {<br>
for (const_iterator I=begin(), E=end(); I != E; ++I)<br>
for (CFGBlock::const_iterator BI=(*I)->begin(), BE=(*I)->end();<br>
- BI != BE; ++BI)<br>
+ BI != BE; ++BI) {<br>
O(*BI);<br>
+ }<br>
}<br>
<br>
Cool.<br>
<br>
<br>
//===--------------------------------------------------------------------===//<br>
@@ -398,18 +646,6 @@<br>
<br>
namespace llvm {<br>
<br>
-/// Implement simplify_type for CFGElement, so that we can dyn_cast from<br>
-/// CFGElement to a specific Stmt class.<br>
-template <> struct simplify_type<const ::clang::CFGElement> {<br>
- typedef ::clang::Stmt* SimpleType;<br>
- static SimpleType getSimplifiedValue(const ::clang::CFGElement &Val) {<br>
- return Val.getStmt();<br>
- }<br>
-};<br>
-<br>
-template <> struct simplify_type< ::clang::CFGElement><br>
- : public simplify_type<const ::clang::CFGElement> {};<br>
-<br>
<br>
Makes sense to remove, since we have getAs<> now and a much richer CFGElement hierarchy.<br>
<br>
<br>
// Traits for: CFGBlock<br>
<br>
template <> struct GraphTraits< ::clang::CFGBlock* > {<br>
Index: include/clang/Analysis/ProgramPoint.h<br>
===================================================================<br>
--- include/clang/Analysis/ProgramPoint.h (revision 112851)<br>
+++ include/clang/Analysis/ProgramPoint.h (working copy)<br>
@@ -117,10 +117,6 @@<br>
const CFGBlock* B = getBlock();<br>
return B->empty() ? CFGElement() : B->front();<br>
}<br>
-<br>
- const Stmt *getFirstStmt() const {<br>
- return getFirstElement().getStmt();<br>
- }<br>
<br>
static bool classof(const ProgramPoint* Location) {<br>
return Location->getKind() == BlockEntranceKind;<br>
@@ -136,11 +132,6 @@<br>
return reinterpret_cast<const CFGBlock*>(getData1());<br>
}<br>
<br>
- const Stmt* getLastStmt() const {<br>
- const CFGBlock* B = getBlock();<br>
- return B->empty() ? CFGElement() : B->back();<br>
- }<br>
-<br>
const Stmt* getTerminator() const {<br>
return getBlock()->getTerminator();<br>
}<br>
<br>
Index: include/clang/Checker/PathSensitive/GRCoreEngine.h<br>
===================================================================<br>
--- include/clang/Checker/PathSensitive/GRCoreEngine.h (revision 112851)<br>
+++ include/clang/Checker/PathSensitive/GRCoreEngine.h (working copy)<br>
@@ -259,7 +259,9 @@<br>
<br>
/// getStmt - Return the current block-level expression associated with<br>
/// this builder.<br>
- const Stmt* getStmt() const { return B[Idx]; }<br>
+ const Stmt* getStmt() const {<br>
+ return B[Idx].isStmt() ? B[Idx].getAs<CFGStmt>().getStmt() : NULL;<br>
+ }<br>
<br>
Looks great.<br>
<br>
<br>
/// getBlock - Return the CFGBlock associated with the block-level expression<br>
/// of this builder.<br>
Index: lib/Analysis/CFG.cpp<br>
===================================================================<br>
--- lib/Analysis/CFG.cpp (revision 112851)<br>
+++ lib/Analysis/CFG.cpp (working copy)<br>
@@ -52,6 +52,113 @@<br>
Kind k;<br>
};<br>
<br>
+/// LocalScope - List of automatic variables declared in current<br>
+/// local scope and position in previous local scope.<br>
+class LocalScope {<br>
+public:<br>
+ typedef std::vector<VarDecl*> AutomaticVarsTy;<br>
<br>
A SmallVector might reduce overall malloc() traffic.<br>
<br>
+ typedef AutomaticVarsTy::const_reverse_iterator AutomaticVarsIter;<br>
+<br>
+ class const_iterator {<br>
+ const LocalScope* Scope;<br>
+ AutomaticVarsIter VarIter;<br>
+<br>
+ // Guard for "valid" invalid operator.<br>
+ static LocalScope GuardScope;<br>
+<br>
+ public:<br>
+ /// Create invalid iterator.<br>
+ const_iterator()<br>
+ : Scope(&GuardScope), VarIter(Scope->Vars.rbegin()) {}<br>
+<br>
+ /// Create valid iterator.<br>
+ const_iterator(const LocalScope* Scope, AutomaticVarsIter VarIter)<br>
+ : Scope(Scope), VarIter(VarIter) {}<br>
+<br>
+ VarDecl* operator*() const { return *VarIter; }<br>
+ VarDecl* const* operator->() const { return &*VarIter; }<br>
+<br>
+ const_iterator& operator++() {<br>
+ ++VarIter;<br>
+ if (VarIter == Scope->Vars.rend())<br>
+ *this = Scope->Prev;<br>
+ return *this;<br>
+ }<br>
+ const_iterator operator++(int) {<br>
+ const_iterator prev = *this;<br>
+ ++*this;<br>
+ return prev;<br>
+ }<br>
+<br>
+ bool operator==(const const_iterator& rhs) const {<br>
+ return Scope == rhs.Scope && VarIter == rhs.VarIter;<br>
+ }<br>
+ bool operator!=(const const_iterator& rhs) const {<br>
+ return !(*this == rhs);<br>
+ }<br>
+<br>
+ int distance(const_iterator L);<br>
+ friend int distance(const_iterator F, const_iterator L);<br>
+ };<br>
+<br>
+ friend class const_iterator;<br>
+<br>
+private:<br>
+ // Automatic variables in order of declaration.<br>
+ AutomaticVarsTy Vars;<br>
+ // Iterator to variable in previous scope that was declared just before<br>
+ // begin of this scope.<br>
+ const_iterator Prev;<br>
+<br>
+ // Creates invalid scope that can serve as guard scope for iterator.<br>
+ LocalScope()<br>
+ : Vars(1, NULL)<br>
+ , Prev(this, Vars.rbegin()) {}<br>
+<br>
+public:<br>
+ LocalScope(const_iterator P)<br>
+ : Vars()<br>
+ , Prev(P) {}<br>
+<br>
+ // Begin of scope in direction of CFG building (backwards).<br>
+ const_iterator begin() const { return const_iterator(this, Vars.rbegin()); }<br>
+<br>
+ void addVar(VarDecl* VD) {<br>
+ Vars.push_back(VD);<br>
+ }<br>
+};<br>
+<br>
+LocalScope LocalScope::const_iterator::GuardScope;<br>
+<br>
+/// distance - Calculates distance from F to L. L must be reachable from F (with<br>
+/// use of ++ operator). Cost of calculating the distance is linear w.r.t.<br>
+/// number between F and L.<br>
+int distance(LocalScope::const_iterator F, LocalScope::const_iterator L) {<br>
+ return F.distance(L);<br>
+}<br>
+<br>
+int LocalScope::const_iterator::distance(LocalScope::const_iterator L) {<br>
+ int D = 0;<br>
+ const_iterator F = *this;<br>
+ while (F.Scope != L.Scope) {<br>
+ assert (F.Scope != &GuardScope<br>
+ && "L iterator is not reachable from F iterator.");<br>
+ D += F.Scope->Vars.rend() - F.VarIter;<br>
+ F = F.Scope->Prev;<br>
+ }<br>
+ D += L.VarIter - F.VarIter;<br>
+ return D;<br>
+}<br>
+<br>
+struct BlockScopePosPair {<br>
+ BlockScopePosPair() {}<br>
+ BlockScopePosPair(CFGBlock* B, LocalScope::const_iterator S)<br>
+ : Block(B), ScopePos(S) {}<br>
+<br>
+ CFGBlock* Block;<br>
+ LocalScope::const_iterator ScopePos;<br>
+};<br>
+<br>
/// CFGBuilder - This class implements CFG construction from an AST.<br>
/// The builder is stateful: an instance of the builder should be used to only<br>
/// construct a single CFG.<br>
@@ -67,41 +174,59 @@<br>
/// implicit fall-throughs without extra basic blocks.<br>
///<br>
class CFGBuilder {<br>
+ typedef BlockScopePosPair JumpTarget;<br>
+ typedef BlockScopePosPair JumpSource;<br>
+<br>
ASTContext *Context;<br>
llvm::OwningPtr<CFG> cfg;<br>
<br>
CFGBlock* Block;<br>
CFGBlock* Succ;<br>
- CFGBlock* ContinueTargetBlock;<br>
- CFGBlock* BreakTargetBlock;<br>
+ JumpTarget ContinueJumpTarget;<br>
+ JumpTarget BreakJumpTarget;<br>
CFGBlock* SwitchTerminatedBlock;<br>
CFGBlock* DefaultCaseBlock;<br>
CFGBlock* TryTerminatedBlock;<br>
<br>
- // LabelMap records the mapping from Label expressions to their blocks.<br>
- typedef llvm::DenseMap<LabelStmt*,CFGBlock*> LabelMapTy;<br>
+ // Current position in local scope.<br>
+ LocalScope::const_iterator ScopePos;<br>
+<br>
+ // LabelMap records the mapping from Label expressions to their jump targets.<br>
+ typedef llvm::DenseMap<LabelStmt*, JumpTarget> LabelMapTy;<br>
LabelMapTy LabelMap;<br>
<br>
// A list of blocks that end with a "goto" that must be backpatched to their<br>
// resolved targets upon completion of CFG construction.<br>
- typedef std::vector<CFGBlock*> BackpatchBlocksTy;<br>
+ typedef std::vector<JumpSource> BackpatchBlocksTy;<br>
BackpatchBlocksTy BackpatchBlocks;<br>
<br>
// A list of labels whose address has been taken (for indirect gotos).<br>
typedef llvm::SmallPtrSet<LabelStmt*,5> LabelSetTy;<br>
LabelSetTy AddressTakenLabels;<br>
<br>
+ // A list of local scopes, which must be destroyed at the end of build<br>
+ // process.<br>
+ typedef std::vector<LocalScope*> LocalScopesTy;<br>
+ LocalScopesTy LocalScopes;<br>
<br>
Consider making this a SmallVector to reduce malloc() traffic.<br>
<br>
+<br>
public:<br>
explicit CFGBuilder() : cfg(new CFG()), // crew a new CFG<br>
Block(NULL), Succ(NULL),<br>
- ContinueTargetBlock(NULL), BreakTargetBlock(NULL),<br>
+ ContinueJumpTarget(), BreakJumpTarget(),<br>
SwitchTerminatedBlock(NULL), DefaultCaseBlock(NULL),<br>
- TryTerminatedBlock(NULL) {}<br>
+ TryTerminatedBlock(NULL), ScopePos() {}<br>
<br>
+ ~CFGBuilder() {<br>
+ // Clean up local scopes.<br>
+ for (LocalScopesTy::iterator I = LocalScopes.begin(), E = LocalScopes.end()<br>
+ ; I != E; ++I) {<br>
+ delete *I;<br>
<br>
Another possibility is to make the LocalScopes be BumpVectors, and BumpPtrAllocate them. Then you don't need to free them explicitly, and it reduces malloc() traffic.<br>
<br></blockquote><div>BumpVector with BumpPtrAllocator handled by CFG or CFGBuilder? I would assume that the first case would be slightly faster, but would leave some unused data after build process.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
+ }<br>
+ }<br>
+<br>
// buildCFG - Used by external clients to construct the CFG.<br>
CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C,<br>
- bool pruneTriviallyFalseEdges, bool AddEHEdges,<br>
- bool AddScopes);<br>
+ CFG::BuildOptions BO);<br>
<br>
private:<br>
// Visitors to walk an AST and construct the CFG.<br>
@@ -150,37 +275,42 @@<br>
return Block;<br>
}<br>
<br>
void autoCreateBlock() { if (!Block) Block = createBlock(); }<br>
CFGBlock *createBlock(bool add_successor = true);<br>
bool FinishBlock(CFGBlock* B);<br>
+<br>
CFGBlock *addStmt(Stmt *S) {<br>
return Visit(S, AddStmtChoice::AlwaysAdd);<br>
}<br>
+ CFGBlock *addInitializer(CXXBaseOrMemberInitializer* I);<br>
+ CFGBlock *addAutomaticObjDtors(LocalScope::const_iterator B,<br>
+ LocalScope::const_iterator E, Stmt* S);<br>
<br>
+ // Local scopes creation.<br>
+ LocalScope* createOrReuseLocalScope(LocalScope* Scope);<br>
+<br>
+ LocalScope* addLocalScopeForStmt(Stmt* S, LocalScope* Scope = NULL);<br>
+ LocalScope* addLocalScopeForDeclStmt(DeclStmt* DS, LocalScope* Scope = NULL);<br>
+ LocalScope* addLocalScopeForVarDecl(VarDecl* VD, LocalScope* Scope = NULL);<br>
+<br>
+ LocalScope* addLocalScopeAndDtors(Stmt* S, LocalScope* Scope = NULL);<br>
+<br>
+ // Interface to CFGBlock - adding CFGElements.<br>
void AppendStmt(CFGBlock *B, Stmt *S,<br>
AddStmtChoice asc = AddStmtChoice::AlwaysAdd) {<br>
B->appendStmt(S, cfg->getBumpVectorContext(), asc.asLValue());<br>
}<br>
+ void appendInitializer(CFGBlock* B, CXXBaseOrMemberInitializer* I) {<br>
+ B->appendInitializer(I, cfg->getBumpVectorContext());<br>
+ }<br>
<br>
+ void insertAutomaticObjDtors(CFGBlock* Blk, CFGBlock::iterator I,<br>
+ LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt* S);<br>
+ void appendAutomaticObjDtors(CFGBlock* Blk, LocalScope::const_iterator B,<br>
+ LocalScope::const_iterator E, Stmt* S);<br>
+ void prependAutomaticObjDtorsWithTerminator(CFGBlock* Blk,<br>
+ LocalScope::const_iterator B, LocalScope::const_iterator E);<br>
+<br>
void AddSuccessor(CFGBlock *B, CFGBlock *S) {<br>
B->addSuccessor(S, cfg->getBumpVectorContext());<br>
}<br>
@@ -207,7 +337,7 @@<br>
/// TryEvaluateBool - Try and evaluate the Stmt and return 0 or 1<br>
/// if we can evaluate to a known value, otherwise return -1.<br>
TryResult TryEvaluateBool(Expr *S) {<br>
- if (!PruneTriviallyFalseEdges)<br>
+ if (!BuildOpts.PruneTriviallyFalseEdges)<br>
return TryResult();<br>
<br>
Expr::EvalResult Result;<br>
@@ -219,15 +349,7 @@<br>
}<br>
<br>
bool badCFG;<br>
};<br>
<br>
// FIXME: Add support for dependent-sized array types in C++?<br>
@@ -250,19 +372,17 @@<br>
/// transferred to the caller. If CFG construction fails, this method returns<br>
/// NULL.<br>
CFG* CFGBuilder::buildCFG(const Decl *D, Stmt* Statement, ASTContext* C,<br>
- bool pruneTriviallyFalseEdges,<br>
- bool addehedges, bool AddScopes) {<br>
+ CFG::BuildOptions BO) {<br>
<br>
- AddEHEdges = addehedges;<br>
- PruneTriviallyFalseEdges = pruneTriviallyFalseEdges;<br>
-<br>
Context = C;<br>
assert(cfg.get());<br>
if (!Statement)<br>
return NULL;<br>
<br>
- this->AddScopes = AddScopes;<br>
badCFG = false;<br>
+ BuildOpts = BO;<br>
+ if (!C->getLangOptions().CPlusPlus)<br>
+ BuildOpts.AddImplicitDtors = false;<br>
<br>
// Create an empty block that will serve as the exit block for the CFG. Since<br>
// this is the first block added to the CFG, it will be implicitly registered<br>
@@ -274,9 +394,14 @@<br>
// Visit the statements and create the CFG.<br>
CFGBlock* B = addStmt(Statement);<br>
<br>
+ // For C++ constructor add initializers to CFG.<br>
if (const CXXConstructorDecl *CD = dyn_cast_or_null<CXXConstructorDecl>(D)) {<br>
- // FIXME: Add code for base initializers and member initializers.<br>
- (void)CD;<br>
+ for (CXXConstructorDecl::init_const_reverse_iterator I = CD->init_rbegin()<br>
+ , E = CD->init_rend(); I != E; ++I) {<br>
+ B = addInitializer(*I);<br>
+ if (badCFG)<br>
+ return NULL;<br>
+ }<br>
}<br>
if (!B)<br>
B = Succ;<br>
@@ -291,15 +416,17 @@<br>
for (BackpatchBlocksTy::iterator I = BackpatchBlocks.begin(),<br>
E = BackpatchBlocks.end(); I != E; ++I ) {<br>
<br>
- CFGBlock* B = *I;<br>
+ CFGBlock* B = I->Block;<br>
GotoStmt* G = cast<GotoStmt>(B->getTerminator());<br>
LabelMapTy::iterator LI = LabelMap.find(G->getLabel());<br>
<br>
// If there is no target for the goto, then we are looking at an<br>
// incomplete AST. Handle this by not registering a successor.<br>
if (LI == LabelMap.end()) continue;<br>
+ JumpTarget JT = LI->second;<br>
<br>
- AddSuccessor(B, LI->second);<br>
+ prependAutomaticObjDtorsWithTerminator(B, I->ScopePos, JT.ScopePos);<br>
+ AddSuccessor(B, JT.Block);<br>
}<br>
<br>
// Add successors to the Indirect Goto Dispatch block (if we have one).<br>
@@ -314,7 +441,9 @@<br>
// at an incomplete AST. Handle this by not registering a successor.<br>
if (LI == LabelMap.end()) continue;<br>
<br>
- AddSuccessor(B, LI->second);<br>
+ // FIXME: Are indirect gotos supported in C++? If yes we have to add<br>
+ // additional block for each possible branch with destructors called.<br>
+ AddSuccessor(B, LI->second.Block);<br>
<br>
<br>
How horrible to consider. I don't know the answer to this question.<br>
<br>
<br>
}<br>
<br>
Succ = B;<br>
@@ -344,6 +473,148 @@<br>
return true;<br>
}<br>
<br>
+/// addInitializer - Add C++ base or member initializer element to CFG.<br>
+CFGBlock* CFGBuilder::addInitializer(CXXBaseOrMemberInitializer* I) {<br>
+ if (!BuildOpts.AddInitializers)<br>
+ return Block;<br>
+<br>
+ autoCreateBlock();<br>
+ appendInitializer(Block, I);<br>
+<br>
+ if (!I->getInit())<br>
+ // FIXME: Remove this check if is unnecessery.<br>
+ return Block;<br>
+<br>
+ return addStmt(I->getInit());<br>
+}<br>
<br>
Looks good.<br>
<br>
+<br>
+CFGBlock* CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B,<br>
+ LocalScope::const_iterator E, Stmt* S) {<br>
+ if (!BuildOpts.AddImplicitDtors)<br>
+ return Block;<br>
+ if (B == E)<br>
+ return Block;<br>
+<br>
+ autoCreateBlock();<br>
+ appendAutomaticObjDtors(Block, B, E, S);<br>
+ return Block;<br>
+}<br>
<br>
Looks good.<br>
<br>
+<br>
+LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) {<br>
+ if (!Scope) {<br>
+ Scope = new LocalScope(ScopePos);<br>
+ LocalScopes.push_back(Scope);<br>
+ }<br>
+ return Scope;<br>
+}<br>
<br>
Consider BumpPtrAllocating LocalScope, since we may create a lot of them.<br>
<br>
+<br>
+LocalScope* CFGBuilder::addLocalScopeForStmt(Stmt* S, LocalScope* Scope) {<br>
+ if (!BuildOpts.AddImplicitDtors)<br>
+ return Scope;<br>
+<br>
+ // For compound statement we will be creating explicit scope.<br>
+ if (CompoundStmt* CS = dyn_cast<CompoundStmt>(S)) {<br>
+ for (CompoundStmt::body_iterator BI = CS->body_begin(), BE = CS->body_end()<br>
+ ; BI != BE; ++BI) {<br>
+ if (DeclStmt* DS = dyn_cast<DeclStmt>(*BI))<br>
+ Scope = addLocalScopeForDeclStmt(DS, Scope);<br>
+ }<br>
+ // For any other statement scope will be implicit and as such will be<br>
+ // interesting only for DeclStmt.<br>
+ } else if (DeclStmt* DS = dyn_cast<DeclStmt>(S)) {<br>
+ Scope = addLocalScopeForDeclStmt(DS, Scope);<br>
+ }<br>
+ return Scope;<br>
+}<br>
<br>
Looks great.<br>
<br>
+<br>
+LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt* DS,<br>
+ LocalScope* Scope) {<br>
+ if (!BuildOpts.AddImplicitDtors)<br>
+ return Scope;<br>
+<br>
+ for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end()<br>
+ ; DI != DE; ++DI) {<br>
+ if (VarDecl* VD = dyn_cast<VarDecl>(*DI))<br>
+ Scope = addLocalScopeForVarDecl(VD, Scope);<br>
+ }<br>
+ return Scope;<br>
+}<br>
<br>
Looks great. Does each VarDecl get it's own scope?<br></blockquote><div>Scope variable passed and returned from addLocalScopeForVarDecl is for lazy creating new scope when needed. So when created it will be used for more variables, but we won't create scope if no variables will be added to it.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
+<br>
+LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl* VD,<br>
+ LocalScope* Scope) {<br>
+ if (!BuildOpts.AddImplicitDtors)<br>
+ return Scope;<br>
+<br>
+ // Check if variable is local.<br>
+ switch (VD->getStorageClass()) {<br>
+ case SC_None:<br>
+ case SC_Auto:<br>
+ case SC_Register:<br>
+ break;<br>
+ default: return Scope;<br>
+ }<br>
<br>
Does the 'default' case handle static variables?<br>
<br>
+<br>
+ // Check for const references bound to temporary. Set type to pointee.<br>
+ QualType QT = VD->getType();<br>
+ if (const ReferenceType* RT = QT.getTypePtr()->getAs<ReferenceType>()) {<br>
+ QT = RT->getPointeeType();<br>
+ if (!QT.isConstQualified() || !VD->getInit()->Classify(*Context).isRValue())<br>
+ return Scope;<br>
+ }<br>
+<br>
+ // Check if type is a C++ class with non-trivial destructor.<br>
+ const RecordType* RT = QT.getTypePtr()->getAs<RecordType>();<br>
+ if (!RT || cast<CXXRecordDecl>(RT->getDecl())->hasTrivialDestructor())<br>
+ return Scope;<br>
+<br>
+ // Add the variable to scope<br>
+ Scope = createOrReuseLocalScope(Scope);<br>
+ Scope->addVar(VD);<br>
+ ScopePos = Scope->begin();<br>
+ return Scope;<br>
+}<br>
<br>
Wow.<br>
<br>
+<br>
+/// addLocalScopeAndDtors - For given statement add local scope for it and<br>
+/// add destructors that will cleanup the scope.<br>
+LocalScope* CFGBuilder::addLocalScopeAndDtors(Stmt* S, LocalScope* Scope) {<br>
+ if (!BuildOpts.AddImplicitDtors)<br>
+ return Scope;<br>
+<br>
+ LocalScope::const_iterator scopeBeginPos = ScopePos;<br>
+ Scope = addLocalScopeForStmt(S, Scope);<br>
+ addAutomaticObjDtors(ScopePos, scopeBeginPos, S);<br>
+ return Scope;<br>
+}<br>
<br>
Awesome.<br>
<br>
+<br>
+/// insertAutomaticObjDtors - Insert destructor CFGElements for variables with<br>
+/// automatic storage duration to CFGBlock's elements vector. Insertion will be<br>
+/// performed in place specified with iterator.<br>
+void CFGBuilder::insertAutomaticObjDtors(CFGBlock* Blk, CFGBlock::iterator I,<br>
+ LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt* S) {<br>
+ BumpVectorContext& C = cfg->getBumpVectorContext();<br>
+ I = Blk->beginAutomaticObjDtorsInsert(I, distance(B, E), C);<br>
+ while (B != E)<br>
+ I = Blk->insertAutomaticObjDtor(I, *B++, S, C);<br>
+}<br>
+<br>
+/// appendAutomaticObjDtors - Append destructor CFGElements for variables with<br>
+/// automatic storage duration to CFGBlock's elements vector. Elements will be<br>
+/// appended to physical end of the vector which happens to be logical begining.<br>
+void CFGBuilder::appendAutomaticObjDtors(CFGBlock* Blk,<br>
+ LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt* S) {<br>
+ insertAutomaticObjDtors(Blk, Blk->begin(), B, E, S);<br>
+}<br>
+<br>
+/// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for<br>
+/// variables with automatic storage duration to CFGBlock's elements vector.<br>
+/// Elememnts will be prepended to physical begining of the vector which happens<br>
<br>
Typo: 'Elements'<br>
<br>
+/// to be logical end. Use blocks terminator as statement that specifies<br>
+/// destructors call site.<br>
+void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock* Blk,<br>
+ LocalScope::const_iterator B, LocalScope::const_iterator E) {<br>
+ insertAutomaticObjDtors(Blk, Blk->end(), B, E, Blk->getTerminator());<br>
+}<br>
+<br>
/// Visit - Walk the subtree of a statement and add extra<br>
/// blocks for ternary operators, &&, and ||. We also process "," and<br>
/// DeclStmts (which may contain nested control-flow).<br>
@@ -589,9 +860,10 @@<br>
<br>
// If there is no target for the break, then we are looking at an incomplete<br>
// AST. This means that the CFG cannot be constructed.<br>
- if (BreakTargetBlock)<br>
- AddSuccessor(Block, BreakTargetBlock);<br>
- else<br>
+ if (BreakJumpTarget.Block) {<br>
+ addAutomaticObjDtors(ScopePos, BreakJumpTarget.ScopePos, B);<br>
+ AddSuccessor(Block, BreakJumpTarget.Block);<br>
+ } else<br>
badCFG = true;<br>
<br>
<br>
@@ -625,7 +897,7 @@<br>
<br>
// Languages without exceptions are assumed to not throw.<br>
if (Context->getLangOptions().Exceptions) {<br>
- if (AddEHEdges)<br>
+ if (BuildOpts.AddEHEdges)<br>
AddEHEdge = true;<br>
}<br>
<br>
@@ -703,8 +975,7 @@<br>
<br>
<br>
CFGBlock* CFGBuilder::VisitCompoundStmt(CompoundStmt* C) {<br>
- EndScope(C);<br>
-<br>
+ addLocalScopeAndDtors(C);<br>
CFGBlock* LastBlock = Block;<br>
<br>
for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend();<br>
@@ -718,8 +989,6 @@<br>
return NULL;<br>
}<br>
<br>
- LastBlock = StartScope(C, LastBlock);<br>
-<br>
return LastBlock;<br>
}<br>
<br>
@@ -846,6 +1115,10 @@<br>
VA = FindVA(VA->getElementType().getTypePtr()))<br>
Block = addStmt(VA->getSizeExpr());<br>
<br>
+ // Remove variable from local scope.<br>
+ if (VD == *ScopePos)<br>
+ ++ScopePos;<br>
+<br>
return Block;<br>
}<br>
<br>
@@ -857,6 +1130,18 @@<br>
// middle of a block, we stop processing that block. That block is then the<br>
// implicit successor for the "then" and "else" clauses.<br>
<br>
+ // Save local scope position because in case of condition variable ScopePos<br>
+ // won't be restored when traversing AST.<br>
+ SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos);<br>
+<br>
+ // Create local scope for possible condition variable.<br>
+ // Store scope position. Add implicit destructor.<br>
+ if (VarDecl* VD = I->getConditionVariable()) {<br>
+ LocalScope::const_iterator BeginScopePos = ScopePos;<br>
+ addLocalScopeForVarDecl(VD);<br>
+ addAutomaticObjDtors(ScopePos, BeginScopePos, I);<br>
+ }<br>
+<br>
// The block we were proccessing is now finished. Make it the successor<br>
// block.<br>
if (Block) {<br>
@@ -874,6 +1159,12 @@<br>
// NULL out Block so that the recursive call to Visit will<br>
// create a new basic block.<br>
Block = NULL;<br>
+<br>
+ // If branch is not a compound statement create implicit scope<br>
+ // and add destructors.<br>
+ if (!isa<CompoundStmt>(Else))<br>
+ addLocalScopeAndDtors(Else);<br>
+<br>
ElseBlock = addStmt(Else);<br>
<br>
if (!ElseBlock) // Can occur when the Else body has all NullStmts.<br>
@@ -891,6 +1182,12 @@<br>
assert(Then);<br>
SaveAndRestore<CFGBlock*> sv(Succ);<br>
Block = NULL;<br>
+<br>
+ // If branch is not a compound statement create implicit scope<br>
+ // and add destructors.<br>
+ if (!isa<CompoundStmt>(Then))<br>
+ addLocalScopeAndDtors(Then);<br>
+<br>
ThenBlock = addStmt(Then);<br>
<br>
if (!ThenBlock) {<br>
@@ -949,6 +1246,7 @@<br>
<br>
// Create the new block.<br>
Block = createBlock(false);<br>
+ addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R);<br>
<br>
// The Exit block is the only successor.<br>
AddSuccessor(Block, &cfg->getExit());<br>
@@ -967,7 +1265,7 @@<br>
LabelBlock = createBlock(); // scopes that only contains NullStmts.<br>
<br>
assert(LabelMap.find(L) == LabelMap.end() && "label already in map");<br>
- LabelMap[ L ] = LabelBlock;<br>
+ LabelMap[ L ] = JumpTarget(LabelBlock, ScopePos);<br>
<br>
// Labels partition blocks, so this is the end of the basic block we were<br>
// processing (L is the block's label). Because this is label (and we have<br>
@@ -1000,9 +1298,12 @@<br>
<br>
if (I == LabelMap.end())<br>
// We will need to backpatch this block later.<br>
- BackpatchBlocks.push_back(Block);<br>
- else<br>
- AddSuccessor(Block, I->second);<br>
+ BackpatchBlocks.push_back(JumpSource(Block, ScopePos));<br>
+ else {<br>
+ JumpTarget JT = I->second;<br>
+ addAutomaticObjDtors(ScopePos, JT.ScopePos, G);<br>
+ AddSuccessor(Block, JT.Block);<br>
+ }<br>
<br>
return Block;<br>
}<br>
@@ -1010,6 +1311,22 @@<br>
CFGBlock* CFGBuilder::VisitForStmt(ForStmt* F) {<br>
CFGBlock* LoopSuccessor = NULL;<br>
<br>
+ // Save local scope position because in case of condition variable ScopePos<br>
+ // won't be restored when traversing AST.<br>
+ SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos);<br>
+<br>
+ // Create local scope for init statement and possible condition variable.<br>
+ // Add destructor for init statement.<br>
+ // Store scope position for continue statement.<br>
+ if (Stmt* Init = F->getInit()) {<br>
+ addLocalScopeForStmt(Init);<br>
+ addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F);<br>
+ }<br>
+<br>
+ LocalScope::const_iterator LoopBeginScopePos = ScopePos;<br>
+ if (VarDecl* VD = F->getConditionVariable())<br>
+ addLocalScopeForVarDecl(VD);<br>
+<br>
// "for" is a control-flow statement. Thus we stop processing the current<br>
// block.<br>
if (Block) {<br>
@@ -1021,8 +1338,8 @@<br>
<br>
// Save the current value for the break targets.<br>
// All breaks should go to the code following the loop.<br>
- SaveAndRestore<CFGBlock*> save_break(BreakTargetBlock);<br>
- BreakTargetBlock = LoopSuccessor;<br>
+ SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);<br>
+ BreakJumpTarget = JumpTarget(LoopSuccessor, LoopBeginScopePos);<br>
<br>
// Because of short-circuit evaluation, the condition of the loop can span<br>
// multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that<br>
@@ -1072,11 +1389,14 @@<br>
assert(F->getBody());<br>
<br>
// Save the current values for Block, Succ, and continue targets.<br>
- SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ),<br>
- save_continue(ContinueTargetBlock);<br>
+ SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);<br>
+ SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget);<br>
<br>
// Create a new block to contain the (bottom) of the loop body.<br>
Block = NULL;<br>
+<br>
+ // Loop body should end with destructor of Condition variable (if any).<br>
+ addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F);<br>
<br>
if (Stmt* I = F->getInc()) {<br>
// Generate increment code in its own basic block. This is the target of<br>
@@ -1086,7 +1406,7 @@<br>
// No increment code. Create a special, empty, block that is used as the<br>
// target block for "looping back" to the start of the loop.<br>
assert(Succ == EntryConditionBlock);<br>
- Succ = createBlock();<br>
+ Succ = Block ? Block : createBlock();<br>
}<br>
<br>
// Finish up the increment (or empty) block if it hasn't been already.<br>
@@ -1097,18 +1417,23 @@<br>
Block = 0;<br>
}<br>
<br>
- ContinueTargetBlock = Succ;<br>
+ ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos);<br>
<br>
// The starting block for the loop increment is the block that should<br>
// represent the 'loop target' for looping back to the start of the loop.<br>
- ContinueTargetBlock->setLoopTarget(F);<br>
+ ContinueJumpTarget.Block->setLoopTarget(F);<br>
<br>
+ // If body is not a compound statement create implicit scope<br>
+ // and add destructors.<br>
+ if (!isa<CompoundStmt>(F->getBody()))<br>
+ addLocalScopeAndDtors(F->getBody());<br>
+<br>
// Now populate the body block, and in the process create new blocks as we<br>
// walk the body of the loop.<br>
CFGBlock* BodyBlock = addStmt(F->getBody());<br>
<br>
if (!BodyBlock)<br>
- BodyBlock = ContinueTargetBlock; // can happen for "for (...;...;...) ;"<br>
+ BodyBlock = ContinueJumpTarget.Block;//can happen for "for (...;...;...);"<br>
else if (Block && !FinishBlock(BodyBlock))<br>
return 0;<br>
<br>
@@ -1217,11 +1542,12 @@<br>
// Now create the true branch.<br>
{<br>
// Save the current values for Succ, continue and break targets.<br>
- SaveAndRestore<CFGBlock*> save_Succ(Succ),<br>
- save_continue(ContinueTargetBlock), save_break(BreakTargetBlock);<br>
+ SaveAndRestore<CFGBlock*> save_Succ(Succ);<br>
+ SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget),<br>
+ save_break(BreakJumpTarget);<br>
<br>
- BreakTargetBlock = LoopSuccessor;<br>
- ContinueTargetBlock = EntryConditionBlock;<br>
+ BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);<br>
+ ContinueJumpTarget = JumpTarget(EntryConditionBlock, ScopePos);<br>
<br>
CFGBlock* BodyBlock = addStmt(S->getBody());<br>
<br>
@@ -1273,6 +1599,16 @@<br>
CFGBlock* CFGBuilder::VisitWhileStmt(WhileStmt* W) {<br>
CFGBlock* LoopSuccessor = NULL;<br>
<br>
+ // Save local scope position because in case of condition variable ScopePos<br>
+ // won't be restored when traversing AST.<br>
+ SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos);<br>
+<br>
+ // Create local scope for possible condition variable.<br>
+ // Store scope position for continue statement.<br>
+ LocalScope::const_iterator LoopBeginScopePos = ScopePos;<br>
+ if (VarDecl* VD = W->getConditionVariable())<br>
+ addLocalScopeForVarDecl(VD);<br>
+<br>
// "while" is a control-flow statement. Thus we stop processing the current<br>
// block.<br>
if (Block) {<br>
@@ -1328,9 +1664,9 @@<br>
assert(W->getBody());<br>
<br>
// Save the current values for Block, Succ, and continue and break targets<br>
- SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ),<br>
- save_continue(ContinueTargetBlock),<br>
- save_break(BreakTargetBlock);<br>
+ SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);<br>
+ SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget),<br>
+ save_break(BreakJumpTarget);<br>
<br>
// Create an empty block to represent the transition block for looping back<br>
// to the head of the loop.<br>
@@ -1338,19 +1674,27 @@<br>
assert(Succ == EntryConditionBlock);<br>
Succ = createBlock();<br>
Succ->setLoopTarget(W);<br>
- ContinueTargetBlock = Succ;<br>
+ ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos);<br>
<br>
// All breaks should go to the code following the loop.<br>
- BreakTargetBlock = LoopSuccessor;<br>
+ BreakJumpTarget = JumpTarget(LoopSuccessor, LoopBeginScopePos);<br>
<br>
// NULL out Block to force lazy instantiation of blocks for the body.<br>
Block = NULL;<br>
<br>
+ // Loop body should end with destructor of Condition variable (if any).<br>
+ addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);<br>
+<br>
+ // If body is not a compound statement create implicit scope<br>
+ // and add destructors.<br>
+ if (!isa<CompoundStmt>(W->getBody()))<br>
+ addLocalScopeAndDtors(W->getBody());<br>
+<br>
// Create the body. The returned block is the entry to the loop body.<br>
CFGBlock* BodyBlock = addStmt(W->getBody());<br>
<br>
if (!BodyBlock)<br>
- BodyBlock = ContinueTargetBlock; // can happen for "while(...) ;"<br>
+ BodyBlock = ContinueJumpTarget.Block; // can happen for "while(...) ;"<br>
else if (Block) {<br>
if (!FinishBlock(BodyBlock))<br>
return 0;<br>
@@ -1463,19 +1807,24 @@<br>
assert(D->getBody());<br>
<br>
// Save the current values for Block, Succ, and continue and break targets<br>
- SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ),<br>
- save_continue(ContinueTargetBlock),<br>
- save_break(BreakTargetBlock);<br>
+ SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);<br>
+ SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget),<br>
+ save_break(BreakJumpTarget);<br>
<br>
// All continues within this loop should go to the condition block<br>
- ContinueTargetBlock = EntryConditionBlock;<br>
+ ContinueJumpTarget = JumpTarget(EntryConditionBlock, ScopePos);<br>
<br>
// All breaks should go to the code following the loop.<br>
- BreakTargetBlock = LoopSuccessor;<br>
+ BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);<br>
<br>
// NULL out Block to force lazy instantiation of blocks for the body.<br>
Block = NULL;<br>
<br>
+ // If body is not a compound statement create implicit scope<br>
+ // and add destructors.<br>
+ if (!isa<CompoundStmt>(D->getBody()))<br>
+ addLocalScopeAndDtors(D->getBody());<br>
+<br>
// Create the body. The returned block is the entry to the loop body.<br>
BodyBlock = addStmt(D->getBody());<br>
<br>
@@ -1529,9 +1878,10 @@<br>
<br>
// If there is no target for the continue, then we are looking at an<br>
// incomplete AST. This means the CFG cannot be constructed.<br>
- if (ContinueTargetBlock)<br>
- AddSuccessor(Block, ContinueTargetBlock);<br>
- else<br>
+ if (ContinueJumpTarget.Block) {<br>
+ addAutomaticObjDtors(ScopePos, ContinueJumpTarget.ScopePos, C);<br>
+ AddSuccessor(Block, ContinueJumpTarget.Block);<br>
+ } else<br>
badCFG = true;<br>
<br>
return Block;<br>
@@ -1570,6 +1920,18 @@<br>
// block.<br>
CFGBlock* SwitchSuccessor = NULL;<br>
<br>
+ // Save local scope position because in case of condition variable ScopePos<br>
+ // won't be restored when traversing AST.<br>
+ SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos);<br>
+<br>
+ // Create local scope for possible condition variable.<br>
+ // Store scope position. Add implicit destructor.<br>
+ if (VarDecl* VD = Terminator->getConditionVariable()) {<br>
+ LocalScope::const_iterator SwitchBeginScopePos = ScopePos;<br>
+ addLocalScopeForVarDecl(VD);<br>
+ addAutomaticObjDtors(ScopePos, SwitchBeginScopePos, Terminator);<br>
+ }<br>
+<br>
if (Block) {<br>
if (!FinishBlock(Block))<br>
return 0;<br>
@@ -1578,8 +1940,8 @@<br>
<br>
// Save the current "switch" context.<br>
SaveAndRestore<CFGBlock*> save_switch(SwitchTerminatedBlock),<br>
- save_break(BreakTargetBlock),<br>
save_default(DefaultCaseBlock);<br>
+ SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);<br>
<br>
// Set the "default" case to be the block after the switch statement. If the<br>
// switch statement contains a "default:", this value will be overwritten with<br>
@@ -1592,13 +1954,19 @@<br>
// Now process the switch body. The code after the switch is the implicit<br>
// successor.<br>
Succ = SwitchSuccessor;<br>
- BreakTargetBlock = SwitchSuccessor;<br>
+ BreakJumpTarget = JumpTarget(SwitchSuccessor, ScopePos);<br>
<br>
// When visiting the body, the case statements should automatically get linked<br>
// up to the switch. We also don't keep a pointer to the body, since all<br>
// control-flow from the switch goes to case/default statements.<br>
assert(Terminator->getBody() && "switch must contain a non-NULL body");<br>
Block = NULL;<br>
+<br>
+ // If body is not a compound statement create implicit scope<br>
+ // and add destructors.<br>
+ if (!isa<CompoundStmt>(Terminator->getBody()))<br>
+ addLocalScopeAndDtors(Terminator->getBody());<br>
+<br>
CFGBlock *BodyBlock = addStmt(Terminator->getBody());<br>
if (Block) {<br>
if (!FinishBlock(BodyBlock))<br>
@@ -1776,6 +2144,18 @@<br>
// CXXCatchStmt are treated like labels, so they are the first statement in a<br>
// block.<br>
<br>
+ // Save local scope position because in case of exception variable ScopePos<br>
+ // won't be restored when traversing AST.<br>
+ SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos);<br>
+<br>
+ // Create local scope for possible exception variable.<br>
+ // Store scope position. Add implicit destructor.<br>
+ if (VarDecl* VD = CS->getExceptionDecl()) {<br>
+ LocalScope::const_iterator BeginScopePos = ScopePos;<br>
+ addLocalScopeForVarDecl(VD);<br>
+ addAutomaticObjDtors(ScopePos, BeginScopePos, CS);<br>
+ }<br>
+<br>
if (CS->getHandlerBlock())<br>
addStmt(CS->getHandlerBlock());<br>
<br>
@@ -1847,11 +2227,9 @@<br>
/// buildCFG - Constructs a CFG from an AST. Ownership of the returned<br>
/// CFG is returned to the caller.<br>
CFG* CFG::buildCFG(const Decl *D, Stmt* Statement, ASTContext *C,<br>
- bool PruneTriviallyFalse,<br>
- bool AddEHEdges, bool AddScopes) {<br>
+ CFG::BuildOptions BO) {<br>
CFGBuilder Builder;<br>
- return Builder.buildCFG(D, Statement, C, PruneTriviallyFalse,<br>
- AddEHEdges, AddScopes);<br>
+ return Builder.buildCFG(D, Statement, C, BO);<br>
}<br>
<br>
//===----------------------------------------------------------------------===//<br>
@@ -1889,16 +2267,26 @@<br>
llvm::SmallPtrSet<Expr*,50> SubExprAssignments;<br>
<br>
for (CFG::iterator I=cfg.begin(), E=cfg.end(); I != E; ++I)<br>
- for (CFGBlock::iterator BI=(*I)->begin(), EI=(*I)->end(); BI != EI; ++BI)<br>
- FindSubExprAssignments(*BI, SubExprAssignments);<br>
+ for (CFGBlock::iterator BI=(*I)->begin(), EI=(*I)->end(); BI != EI; ++BI) {<br>
+ if (CFGStmt S = BI->getAs<CFGStmt>()) {<br>
+ FindSubExprAssignments(S, SubExprAssignments);<br>
+ } else if (CFGInitializer I = BI->getAs<CFGInitializer>()) {<br>
+ Stmt* S = I.getInitializer()->getInit();<br>
+ FindSubExprAssignments(S, SubExprAssignments);<br>
+ }<br>
+ }<br>
<br>
for (CFG::iterator I=cfg.begin(), E=cfg.end(); I != E; ++I) {<br>
<br>
// Iterate over the statements again on identify the Expr* and Stmt* at the<br>
// block-level that are block-level expressions.<br>
<br>
- for (CFGBlock::iterator BI=(*I)->begin(), EI=(*I)->end(); BI != EI; ++BI)<br>
- if (Expr* Exp = dyn_cast<Expr>(*BI)) {<br>
+ for (CFGBlock::iterator BI=(*I)->begin(), EI=(*I)->end(); BI != EI; ++BI) {<br>
+ if (!BI->isStmt())<br>
+ continue;<br>
+<br>
+ CFGStmt& SE = static_cast<CFGStmt&>(*BI);<br>
+ if (Expr* Exp = dyn_cast<Expr>(SE.getStmt())) {<br>
<br>
if (BinaryOperator* B = dyn_cast<BinaryOperator>(Exp)) {<br>
// Assignment expressions that are not nested within another<br>
@@ -1919,6 +2307,7 @@<br>
unsigned x = M->size();<br>
(*M)[Exp] = x;<br>
}<br>
+ }<br>
<br>
// Look at terminators. The condition is a block-level expression.<br>
<br>
@@ -1969,7 +2358,9 @@<br>
<br>
class StmtPrinterHelper : public PrinterHelper {<br>
typedef llvm::DenseMap<Stmt*,std::pair<unsigned,unsigned> > StmtMapTy;<br>
+ typedef llvm::DenseMap<Decl*,std::pair<unsigned,unsigned> > DeclMapTy;<br>
StmtMapTy StmtMap;<br>
+ DeclMapTy DeclMap;<br>
signed CurrentBlock;<br>
unsigned CurrentStmt;<br>
const LangOptions &LangOpts;<br>
@@ -1981,7 +2372,34 @@<br>
unsigned j = 1;<br>
for (CFGBlock::const_iterator BI = (*I)->begin(), BEnd = (*I)->end() ;<br>
BI != BEnd; ++BI, ++j )<br>
- StmtMap[*BI] = std::make_pair((*I)->getBlockID(),j);<br>
+ if (CFGStmt SE = BI->getAs<CFGStmt>()) {<br>
+ std::pair<unsigned, unsigned> P((*I)->getBlockID(), j);<br>
+ StmtMap[SE.getStmt()] = P;<br>
+<br>
+ if (DeclStmt* DS = dyn_cast<DeclStmt>(SE.getStmt())) {<br>
+ DeclMap[DS->getSingleDecl()] = P;<br>
+<br>
+ } else if (IfStmt* IS = dyn_cast<IfStmt>(SE.getStmt())) {<br>
+ if (VarDecl* VD = IS->getConditionVariable())<br>
+ DeclMap[VD] = P;<br>
+<br>
+ } else if (ForStmt* FS = dyn_cast<ForStmt>(SE.getStmt())) {<br>
+ if (VarDecl* VD = FS->getConditionVariable())<br>
+ DeclMap[VD] = P;<br>
+<br>
+ } else if (WhileStmt* WS = dyn_cast<WhileStmt>(SE.getStmt())) {<br>
+ if (VarDecl* VD = WS->getConditionVariable())<br>
+ DeclMap[VD] = P;<br>
+<br>
+ } else if (SwitchStmt* SS = dyn_cast<SwitchStmt>(SE.getStmt())) {<br>
+ if (VarDecl* VD = SS->getConditionVariable())<br>
+ DeclMap[VD] = P;<br>
+<br>
+ } else if (CXXCatchStmt* CS = dyn_cast<CXXCatchStmt>(SE.getStmt())) {<br>
+ if (VarDecl* VD = CS->getExceptionDecl())<br>
+ DeclMap[VD] = P;<br>
+ }<br>
+ }<br>
}<br>
}<br>
<br>
@@ -1991,10 +2409,9 @@<br>
void setBlockID(signed i) { CurrentBlock = i; }<br>
void setStmtID(unsigned i) { CurrentStmt = i; }<br>
<br>
- virtual bool handledStmt(Stmt* Terminator, llvm::raw_ostream& OS) {<br>
+ virtual bool handledStmt(Stmt* S, llvm::raw_ostream& OS) {<br>
+ StmtMapTy::iterator I = StmtMap.find(S);<br>
<br>
- StmtMapTy::iterator I = StmtMap.find(Terminator);<br>
-<br>
if (I == StmtMap.end())<br>
return false;<br>
<br>
@@ -2006,6 +2423,21 @@<br>
OS << "[B" << I->second.first << "." << I->second.second << "]";<br>
return true;<br>
}<br>
+<br>
+ bool handleDecl(Decl* D, llvm::raw_ostream& OS) {<br>
+ DeclMapTy::iterator I = DeclMap.find(D);<br>
+<br>
+ if (I == DeclMap.end())<br>
+ return false;<br>
+<br>
+ if (CurrentBlock >= 0 && I->second.first == (unsigned) CurrentBlock<br>
+ && I->second.second == CurrentStmt) {<br>
+ return false;<br>
+ }<br>
+<br>
+ OS << "[B" << I->second.first << "." << I->second.second << "]";<br>
+ return true;<br>
+ }<br>
};<br>
} // end anonymous namespace<br>
<br>
@@ -2109,57 +2541,80 @@<br>
} // end anonymous namespace<br>
<br>
<br>
-static void print_stmt(llvm::raw_ostream &OS, StmtPrinterHelper* Helper,<br>
+static void print_elem(llvm::raw_ostream &OS, StmtPrinterHelper* Helper,<br>
const CFGElement &E) {<br>
<br>
- if (E.asStartScope()) {<br>
- OS << "start scope\n";<br>
- return;<br>
- }<br>
- if (E.asEndScope()) {<br>
- OS << "end scope\n";<br>
- return;<br>
- }<br>
+ if (CFGStmt CS = E.getAs<CFGStmt>()) {<br>
+ Stmt *S = CS;<br>
+<br>
+ if (Helper) {<br>
<br>
- Stmt *S = E;<br>
-<br>
- if (Helper) {<br>
- // special printing for statement-expressions.<br>
- if (StmtExpr* SE = dyn_cast<StmtExpr>(S)) {<br>
- CompoundStmt* Sub = SE->getSubStmt();<br>
+ // special printing for statement-expressions.<br>
+ if (StmtExpr* SE = dyn_cast<StmtExpr>(S)) {<br>
+ CompoundStmt* Sub = SE->getSubStmt();<br>
<br>
- if (Sub->child_begin() != Sub->child_end()) {<br>
- OS << "({ ... ; ";<br>
- Helper->handledStmt(*SE->getSubStmt()->body_rbegin(),OS);<br>
- OS << " })\n";<br>
- return;<br>
+ if (Sub->child_begin() != Sub->child_end()) {<br>
+ OS << "({ ... ; ";<br>
+ Helper->handledStmt(*SE->getSubStmt()->body_rbegin(),OS);<br>
+ OS << " })\n";<br>
+ return;<br>
+ }<br>
}<br>
- }<br>
<br>
- // special printing for comma expressions.<br>
- if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {<br>
- if (B->getOpcode() == BO_Comma) {<br>
- OS << "... , ";<br>
- Helper->handledStmt(B->getRHS(),OS);<br>
- OS << '\n';<br>
- return;<br>
+ // special printing for comma expressions.<br>
+ if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {<br>
+ if (B->getOpcode() == BO_Comma) {<br>
+ OS << "... , ";<br>
+ Helper->handledStmt(B->getRHS(),OS);<br>
+ OS << '\n';<br>
+ return;<br>
+ }<br>
}<br>
}<br>
- }<br>
<br>
- S->printPretty(OS, Helper, PrintingPolicy(Helper->getLangOpts()));<br>
-<br>
- if (isa<CXXOperatorCallExpr>(S)) {<br>
- OS << " (OperatorCall)";<br>
- }<br>
- else if (isa<CXXBindTemporaryExpr>(S)) {<br>
- OS << " (BindTemporary)";<br>
- }<br>
+ S->printPretty(OS, Helper, PrintingPolicy(Helper->getLangOpts()));<br>
<br>
+ if (isa<CXXOperatorCallExpr>(S)) {<br>
+ OS << " (OperatorCall)";<br>
+ }<br>
+ else if (isa<CXXBindTemporaryExpr>(S)) {<br>
+ OS << " (BindTemporary)";<br>
+ }<br>
<br>
- // Expressions need a newline.<br>
- if (isa<Expr>(S))<br>
- OS << '\n';<br>
+ // Expressions need a newline.<br>
+ if (isa<Expr>(S))<br>
+ OS << '\n';<br>
+<br>
+ } else if (CFGInitializer IE = E.getAs<CFGInitializer>()) {<br>
+ CXXBaseOrMemberInitializer* I = IE;<br>
+ if (I->isBaseInitializer())<br>
+ OS << I->getBaseClass()->getAsCXXRecordDecl()->getName();<br>
+ else OS << I->getMember()->getName();<br>
+<br>
+ OS << "(";<br>
+ if (Expr* IE = I->getInit())<br>
+ IE->printPretty(OS, Helper, PrintingPolicy(Helper->getLangOpts()));<br>
+ OS << ")";<br>
+<br>
+ if (I->isBaseInitializer())<br>
+ OS << " (Base initializer)\n";<br>
+ else OS << " (Member initializer)\n";<br>
+<br>
+ } else if (CFGAutomaticObjDtor DE = E.getAs<CFGAutomaticObjDtor>()){<br>
+ VarDecl* VD = DE.getVarDecl();<br>
+ Helper->handleDecl(VD, OS);<br>
+<br>
+ Type* T = VD->getType().getTypePtr();<br>
+ if (const ReferenceType* RT = T->getAs<ReferenceType>())<br>
+ T = RT->getPointeeType().getTypePtr();<br>
+<br>
+ OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";<br>
+ OS << " (Implicit destructor)\n";<br>
+<br>
+ } else if (CFGImplicitDtor DE = E.getAs<CFGImplicitDtor>()) {<br>
+ OS << "TODO: print implicit destructor desc\n";<br>
+ (void)DE;<br>
+ }<br>
}<br>
<br>
static void print_block(llvm::raw_ostream& OS, const CFG* cfg,<br>
@@ -2214,7 +2669,7 @@<br>
OS << ":\n";<br>
}<br>
<br>
- // Iterate through the statements in the block and print them.<br>
+ // Iterate through the elements in the block and print them.<br>
unsigned j = 1;<br>
<br>
for (CFGBlock::const_iterator I = B.begin(), E = B.end() ;<br>
@@ -2229,7 +2684,7 @@<br>
if (Helper)<br>
Helper->setStmtID(j);<br>
<br>
- print_stmt(OS,Helper,*I);<br>
+ print_elem(OS,Helper,*I);<br>
}<br>
<br>
// Print the terminator of this block.<br>
Index: lib/Analysis/ReachableCode.cpp<br>
===================================================================<br>
--- lib/Analysis/ReachableCode.cpp (revision 112851)<br>
+++ lib/Analysis/ReachableCode.cpp (working copy)<br>
@@ -31,9 +31,14 @@<br>
R1 = R2 = SourceRange();<br>
<br>
top:<br>
- if (sn < b.size())<br>
- S = b[sn].getStmt();<br>
- else if (b.getTerminator())<br>
+ if (sn < b.size()) {<br>
+ // FIXME: (CFGElement) Should handle other CFGElement kinds here also.<br>
+ if (!b[sn].isStmt()) {<br>
+ ++sn;<br>
+ goto top;<br>
+ }<br>
+ S = b[sn].getAs<CFGStmt>().getStmt();<br>
+ } else if (b.getTerminator())<br>
S = b.getTerminator();<br>
else<br>
return SourceLocation();<br>
@@ -43,7 +48,8 @@<br>
const BinaryOperator *BO = cast<BinaryOperator>(S);<br>
if (BO->getOpcode() == BO_Comma) {<br>
if (sn+1 < b.size())<br>
- return b[sn+1].getStmt()->getLocStart();<br>
+ // Here we can have only statement CFGElement.<br>
+ return b[sn+1].getAs<CFGStmt>().getStmt()->getLocStart();<br>
const CFGBlock *n = &b;<br>
while (1) {<br>
if (n->getTerminator())<br>
@@ -54,7 +60,8 @@<br>
if (n->pred_size() != 1)<br>
return SourceLocation();<br>
if (!n->empty())<br>
- return n[0][0].getStmt()->getLocStart();<br>
+ // Here we can have only statement CFGElement.<br>
+ return n[0][0].getAs<CFGStmt>().getStmt()->getLocStart();<br>
}<br>
}<br>
R1 = BO->getLHS()->getSourceRange();<br>
Index: lib/Analysis/CFGStmtMap.cpp<br>
===================================================================<br>
--- lib/Analysis/CFGStmtMap.cpp (revision 112851)<br>
+++ lib/Analysis/CFGStmtMap.cpp (working copy)<br>
@@ -49,8 +49,7 @@<br>
static void Accumulate(SMap &SM, CFGBlock *B) {<br>
// First walk the block-level expressions.<br>
for (CFGBlock::iterator I = B->begin(), E = B->end(); I != E; ++I) {<br>
- const CFGElement &CE = *I;<br>
- if (Stmt *S = CE.getStmt()) {<br>
+ if (CFGStmt S = I->getAs<CFGStmt>()) {<br>
CFGBlock *&Entry = SM[S];<br>
// If 'Entry' is already initialized (e.g., a terminator was already),<br>
// skip.<br>
Index: lib/Analysis/AnalysisContext.cpp<br>
===================================================================<br>
--- lib/Analysis/AnalysisContext.cpp (revision 112851)<br>
+++ lib/Analysis/AnalysisContext.cpp (working copy)<br>
@@ -59,7 +59,8 @@<br>
return getUnoptimizedCFG();<br>
<br>
if (!builtCFG) {<br>
- cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), true, AddEHEdges);<br>
+ cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(),<br>
+ CFG::BuildOptions().setAddEHEdges(AddEHEdges));<br>
// Even when the cfg is not successfully built, we don't<br>
// want to try building it again.<br>
builtCFG = true;<br>
@@ -70,7 +71,8 @@<br>
CFG *AnalysisContext::getUnoptimizedCFG() {<br>
if (!builtCompleteCFG) {<br>
completeCFG = CFG::buildCFG(D, getBody(), &D->getASTContext(),<br>
- false, AddEHEdges);<br>
+ CFG::BuildOptions().setAddEHEdges(AddEHEdges)<br>
+ .setPruneTriviallyFalseEdges(false));<br>
// Even when the cfg is not successfully built, we don't<br>
// want to try building it again.<br>
builtCompleteCFG = true;<br>
Index: lib/Analysis/LiveVariables.cpp<br>
===================================================================<br>
--- lib/Analysis/LiveVariables.cpp (revision 112851)<br>
+++ lib/Analysis/LiveVariables.cpp (working copy)<br>
@@ -86,7 +86,7 @@<br>
getAnalysisData().killAtAssign = killAtAssign;<br>
<br>
RegisterDecls R(getAnalysisData());<br>
- cfg.VisitBlockStmts(R);<br>
+ cfg.VisitCFGElements(R);<br>
<br>
// Register all parameters even if they didn't occur in the function body.<br>
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(AC.getDecl()))<br>
Index: lib/Analysis/UninitializedValues.cpp<br>
===================================================================<br>
--- lib/Analysis/UninitializedValues.cpp (revision 112851)<br>
+++ lib/Analysis/UninitializedValues.cpp (working copy)<br>
@@ -42,7 +42,7 @@<br>
<br>
void UninitializedValues::InitializeValues(const CFG& cfg) {<br>
RegisterDecls R(getAnalysisData());<br>
- cfg.VisitBlockStmts(R);<br>
+ cfg.VisitCFGElements(R);<br>
}<br>
<br>
//===----------------------------------------------------------------------===//<br>
Index: lib/Sema/AnalysisBasedWarnings.cpp<br>
===================================================================<br>
--- lib/Sema/AnalysisBasedWarnings.cpp (revision 112851)<br>
+++ lib/Sema/AnalysisBasedWarnings.cpp (working copy)<br>
@@ -116,6 +116,8 @@<br>
if (!live[B.getBlockID()])<br>
continue;<br>
if (B.size() == 0) {<br>
+ // FIXME: (CFGElement) This should be reimplemented when (or if) scopes<br>
+ // will be added to CFG.<br>
if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {<br>
HasAbnormalEdge = true;<br>
continue;<br>
@@ -125,66 +127,77 @@<br>
HasPlainEdge = true;<br>
continue;<br>
}<br>
- Stmt *S = B[B.size()-1];<br>
- if (isa<ReturnStmt>(S)) {<br>
- HasLiveReturn = true;<br>
- continue;<br>
- }<br>
- if (isa<ObjCAtThrowStmt>(S)) {<br>
- HasFakeEdge = true;<br>
- continue;<br>
- }<br>
- if (isa<CXXThrowExpr>(S)) {<br>
- HasFakeEdge = true;<br>
- continue;<br>
- }<br>
- if (const AsmStmt *AS = dyn_cast<AsmStmt>(S)) {<br>
- if (AS->isMSAsm()) {<br>
- HasFakeEdge = true;<br>
+ CFGElement E = B[B.size() - 1];<br>
+ if (CFGStmt SE = E.getAs<CFGStmt>()) {<br>
+ Stmt* S = SE;<br>
+ if (isa<ReturnStmt>(S)) {<br>
HasLiveReturn = true;<br>
continue;<br>
}<br>
- }<br>
- if (isa<CXXTryStmt>(S)) {<br>
- HasAbnormalEdge = true;<br>
- continue;<br>
- }<br>
-<br>
- bool NoReturnEdge = false;<br>
- if (CallExpr *C = dyn_cast<CallExpr>(S)) {<br>
- if (std::find(B.succ_begin(), B.succ_end(), &cfg->getExit())<br>
- == B.succ_end()) {<br>
- HasAbnormalEdge = true;<br>
+ if (isa<ObjCAtThrowStmt>(S)) {<br>
+ HasFakeEdge = true;<br>
continue;<br>
}<br>
- Expr *CEE = C->getCallee()->IgnoreParenCasts();<br>
- if (getFunctionExtInfo(CEE->getType()).getNoReturn()) {<br>
- NoReturnEdge = true;<br>
+ if (isa<CXXThrowExpr>(S)) {<br>
HasFakeEdge = true;<br>
- } else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CEE)) {<br>
- ValueDecl *VD = DRE->getDecl();<br>
- if (VD->hasAttr<NoReturnAttr>()) {<br>
- NoReturnEdge = true;<br>
+ continue;<br>
+ }<br>
+ if (const AsmStmt *AS = dyn_cast<AsmStmt>(S)) {<br>
+ if (AS->isMSAsm()) {<br>
HasFakeEdge = true;<br>
+ HasLiveReturn = true;<br>
+ continue;<br>
}<br>
}<br>
- }<br>
- // FIXME: Remove this hack once temporaries and their destructors are<br>
- // modeled correctly by the CFG.<br>
- if (CXXExprWithTemporaries *E = dyn_cast<CXXExprWithTemporaries>(S)) {<br>
- for (unsigned I = 0, N = E->getNumTemporaries(); I != N; ++I) {<br>
- const FunctionDecl *FD = E->getTemporary(I)->getDestructor();<br>
- if (FD->hasAttr<NoReturnAttr>() ||<br>
- FD->getType()->getAs<FunctionType>()->getNoReturnAttr()) {<br>
+ if (isa<CXXTryStmt>(S)) {<br>
+ HasAbnormalEdge = true;<br>
+ continue;<br>
+ }<br>
+<br>
+ bool NoReturnEdge = false;<br>
+ if (CallExpr *C = dyn_cast<CallExpr>(S)) {<br>
+ if (std::find(B.succ_begin(), B.succ_end(), &cfg->getExit())<br>
+ == B.succ_end()) {<br>
+ HasAbnormalEdge = true;<br>
+ continue;<br>
+ }<br>
+ Expr *CEE = C->getCallee()->IgnoreParenCasts();<br>
+ if (getFunctionExtInfo(CEE->getType()).getNoReturn()) {<br>
NoReturnEdge = true;<br>
HasFakeEdge = true;<br>
- break;<br>
+ } else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CEE)) {<br>
+ ValueDecl *VD = DRE->getDecl();<br>
+ if (VD->hasAttr<NoReturnAttr>()) {<br>
+ NoReturnEdge = true;<br>
+ HasFakeEdge = true;<br>
+ }<br>
}<br>
}<br>
+ // FIXME: Remove this hack once temporaries and their destructors are<br>
+ // modeled correctly by the CFG.<br>
+ if (CXXExprWithTemporaries *E = dyn_cast<CXXExprWithTemporaries>(S)) {<br>
+ for (unsigned I = 0, N = E->getNumTemporaries(); I != N; ++I) {<br>
+ const FunctionDecl *FD = E->getTemporary(I)->getDestructor();<br>
+ if (FD->hasAttr<NoReturnAttr>() ||<br>
+ FD->getType()->getAs<FunctionType>()->getNoReturnAttr()) {<br>
+ NoReturnEdge = true;<br>
+ HasFakeEdge = true;<br>
+ break;<br>
+ }<br>
+ }<br>
+ }<br>
+ // FIXME: Add noreturn message sends.<br>
+ if (NoReturnEdge == false)<br>
+ HasPlainEdge = true;<br>
+<br>
+ } else if (CFGInitializer IE = E.getAs<CFGInitializer>()) {<br>
+ // FIXME: (CFGElement) Handle initializer as last element.<br>
+ (void)IE;<br>
+<br>
+ } else if (CFGImplicitDtor DE = E.getAs<CFGImplicitDtor>()) {<br>
+ // FIXME: (CFGElement) Handle implicit destructor call as last element.<br>
+ (void)DE;<br>
}<br>
- // FIXME: Add noreturn message sends.<br>
- if (NoReturnEdge == false)<br>
- HasPlainEdge = true;<br>
}<br>
if (!HasPlainEdge) {<br>
if (HasLiveReturn)<br>
Index: lib/Checker/UnreachableCodeChecker.cpp<br>
===================================================================<br>
--- lib/Checker/UnreachableCodeChecker.cpp (revision 112851)<br>
+++ lib/Checker/UnreachableCodeChecker.cpp (working copy)<br>
@@ -116,10 +116,12 @@<br>
// FIXME: This should be extended to include other unreachable markers,<br>
// such as llvm_unreachable.<br>
if (!CB->empty()) {<br>
- const Stmt *First = CB->front();<br>
- if (const CallExpr *CE = dyn_cast<CallExpr>(First)) {<br>
- if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable)<br>
- continue;<br>
+ CFGElement First = CB->front();<br>
+ if (CFGStmt S = First.getAs<CFGStmt>()) {<br>
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S.getStmt())) {<br>
+ if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable)<br>
+ continue;<br>
+ }<br>
}<br>
}<br>
<br>
Looks good.<br>
<br>
<br>
@@ -173,12 +175,14 @@<br>
<br>
// Find the Stmt* in a CFGBlock for reporting a warning<br>
const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) {<br>
- if (CB->size() > 0)<br>
- return CB->front().getStmt();<br>
- else if (const Stmt *S = CB->getTerminator())<br>
+ // FIXME: (CFGElement) Should we handle other CFGElement kinds here as well?<br>
+ for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) {<br>
+ if (CFGStmt S = I->getAs<CFGStmt>())<br>
+ return S;<br>
+ }<br>
+ if (const Stmt *S = CB->getTerminator())<br>
return S;<br>
- else<br>
- return 0;<br>
+ return 0;<br>
}<br>
<br>
// Determines if the path to this CFGBlock contained an element that infers this<br>
Index: lib/Checker/CheckDeadStores.cpp<br>
===================================================================<br>
--- lib/Checker/CheckDeadStores.cpp (revision 112851)<br>
+++ lib/Checker/CheckDeadStores.cpp (working copy)<br>
@@ -283,7 +283,7 @@<br>
void clang::CheckDeadStores(CFG &cfg, LiveVariables &L, ParentMap &pmap,<br>
BugReporter& BR) {<br>
FindEscaped FS(&cfg);<br>
- FS.getCFG().VisitBlockStmts(FS);<br>
+ FS.getCFG().VisitCFGElements(FS);<br>
DeadStoreObs A(BR.getContext(), BR, pmap, FS.Escaped);<br>
L.runOnAllBlocks(cfg, &A);<br>
}<br>
<br>
Looks great.<br>
<br>
<br>
Index: lib/Checker/BugReporter.cpp<br>
===================================================================<br>
--- lib/Checker/BugReporter.cpp (revision 112851)<br>
+++ lib/Checker/BugReporter.cpp (working copy)<br>
@@ -1165,7 +1165,7 @@<br>
}<br>
<br>
if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) {<br>
- if (const Stmt* S = BE->getFirstStmt()) {<br>
+ if (CFGStmt S = BE->getFirstElement().getAs<CFGStmt>()) {<br>
if (IsControlFlowExpr(S)) {<br>
// Add the proper context for '&&', '||', and '?'.<br>
EB.addContext(S);<br>
<br>
Looks great.<br>
<br>
Index: lib/Checker/GRExprEngine.cpp<br>
===================================================================<br>
--- lib/Checker/GRExprEngine.cpp (revision 112851)<br>
+++ lib/Checker/GRExprEngine.cpp (working copy)<br>
@@ -633,7 +633,7 @@<br>
}<br>
<br>
void GRExprEngine::ProcessStmt(const CFGElement CE,GRStmtNodeBuilder& builder) {<br>
- CurrentStmt = CE.getStmt();<br>
+ CurrentStmt = CE.getAs<CFGStmt>();<br>
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),<br>
CurrentStmt->getLocStart(),<br>
"Error evaluating statement");<br>
@@ -720,7 +720,7 @@<br>
Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I));<br>
<br>
// Visit the statement.<br>
- if (CE.asLValue())<br>
+ if (CE.getAs<CFGStmt>().asLValue())<br>
<br>
Looks great.<br>
<br>
VisitLValue(cast<Expr>(CurrentStmt), *I, Dst);<br>
else<br>
Visit(CurrentStmt, *I, Dst);<br>
<br>
<br>
<br>
</blockquote></div><br>