diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index 38d4bdfcfced..d85330b7d648 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -20,6 +20,7 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/SourceLocation.h" #include namespace llvm { @@ -33,15 +34,39 @@ namespace clang { class LangOptions; class ASTContext; +namespace { +// An element of the CFG for implicit descructor calls implied by the language +// rules. +class Dtor { + // Statement that introduces the variable. + Stmt *S; + // A token which ends the scope, return, goto, throw, }. + SourceLocation Loc; +public: + Dtor(Stmt *s, SourceLocation l) : S(s), Loc(l) { + } + SourceLocation getLoc() { return Loc; } + Stmt *getStmt() { return S; } +}; +} + /// CFGElement - Represents a top-level expression in a basic block. class CFGElement { - llvm::PointerIntPair Data; + llvm::PointerIntPair Data; public: + enum Type { StartScope, EndScope }; explicit CFGElement() {} CFGElement(Stmt *S, bool lvalue) : Data(S, lvalue ? 1 : 0) {} + CFGElement(Stmt *S, Type t) : Data(S, t == StartScope ? 2 : 3) {} + // CFGElement(Dtor *S, Type t) : Data(reinterpret_cast(S), 4) {} Stmt *getStmt() const { return Data.getPointer(); } bool asLValue() const { return Data.getInt() == 1; } + bool asStartScope() const { return Data.getInt() == 2; } + bool asEndScope() const { return Data.getInt() == 3; } + bool asDtor() const { return Data.getInt() == 4; } operator Stmt*() const { return getStmt(); } + operator bool() const { return getStmt() != 0; } + operator Dtor*() const { return reinterpret_cast(getStmt()); } }; /// CFGBlock - Represents a single basic block in a source-level CFG. @@ -236,6 +261,12 @@ public: void appendStmt(Stmt* Statement, BumpVectorContext &C, bool asLValue) { Stmts.push_back(CFGElement(Statement, asLValue), C); } + void StartScope(Stmt* S, BumpVectorContext &C) { + Stmts.push_back(CFGElement(S, CFGElement::StartScope), C); + } + void EndScope(Stmt* S, BumpVectorContext &C) { + Stmts.push_back(CFGElement(S, CFGElement::EndScope), C); + } }; @@ -254,7 +285,7 @@ public: /// buildCFG - Builds a CFG from an AST. The responsibility to free the /// constructed CFG belongs to the caller. - static CFG* buildCFG(Stmt* AST, ASTContext *C); + static CFG* buildCFG(Stmt* AST, ASTContext *C, bool AddScopes = false); /// createBlock - Create a new block in the CFG. The CFG owns the block; /// the caller should not directly free it. diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 21c241848a57..6508c351f0e7 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -93,7 +93,7 @@ public: TryTerminatedBlock(NULL) {} // buildCFG - Used by external clients to construct the CFG. - CFG* buildCFG(Stmt *Statement, ASTContext *C); + CFG* buildCFG(Stmt *Statement, ASTContext *C, bool AddScopes); private: // Visitors to walk an AST and construct the CFG. @@ -141,6 +141,25 @@ private: return Block; } + CFGBlock *StartScope(Stmt *S, CFGBlock *B) { + if (!AddScopes) + return B; + + if (B == 0) + B = createBlock(); + B->StartScope(S, cfg->getBumpVectorContext()); + return B; + } + + void EndScope(Stmt *S) { + if (!AddScopes) + return; + + if (Block == 0) + Block = createBlock(); + Block->EndScope(S, cfg->getBumpVectorContext()); + } + void autoCreateBlock() { if (!Block) Block = createBlock(); } CFGBlock *createBlock(bool add_successor = true); bool FinishBlock(CFGBlock* B); @@ -188,6 +207,7 @@ private: } bool badCFG; + bool AddScopes; }; // FIXME: Add support for dependent-sized array types in C++? @@ -209,12 +229,13 @@ static VariableArrayType* FindVA(Type* t) { /// body (compound statement). The ownership of the returned CFG is /// transferred to the caller. If CFG construction fails, this method returns /// NULL. -CFG* CFGBuilder::buildCFG(Stmt* Statement, ASTContext* C) { +CFG* CFGBuilder::buildCFG(Stmt* Statement, ASTContext* C, bool AddScopes) { Context = C; assert(cfg.get()); if (!Statement) return NULL; + this->AddScopes = AddScopes; badCFG = false; // Create an empty block that will serve as the exit block for the CFG. Since @@ -519,22 +540,43 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { NoReturn = true; } - if (FunctionDecl *FD = C->getDirectCallee()) + bool CanThrow = false; + + // Languages without exceptions are assumed to not throw. + if (Context->getLangOptions().Exceptions) { + CanThrow = true; + } + + if (FunctionDecl *FD = C->getDirectCallee()) { if (FD->hasAttr()) NoReturn = true; + if (FD->hasAttr()) + CanThrow = false; + } - if (!NoReturn) + if (!NoReturn && !CanThrow) return VisitStmt(C, asc); - if (Block && !FinishBlock(Block)) - return 0; + if (Block) { + Succ = Block; + if (!FinishBlock(Block)) + return 0; + } - // Create new block with no successor for the remaining pieces. - Block = createBlock(false); + Block = createBlock(!NoReturn); AppendStmt(Block, C, asc); - // Wire this to the exit block directly. - AddSuccessor(Block, &cfg->getExit()); + if (NoReturn) { + // Wire this to the exit block directly. + AddSuccessor(Block, &cfg->getExit()); + } + if (CanThrow) { + // Add exceptional edges. + if (TryTerminatedBlock) + AddSuccessor(Block, TryTerminatedBlock); + else + AddSuccessor(Block, &cfg->getExit()); + } return VisitChildren(C); } @@ -569,6 +611,8 @@ CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C, CFGBlock* CFGBuilder::VisitCompoundStmt(CompoundStmt* C) { + EndScope(C); + CFGBlock* LastBlock = Block; for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend(); @@ -578,6 +622,9 @@ CFGBlock* CFGBuilder::VisitCompoundStmt(CompoundStmt* C) { if (badCFG) return NULL; } + + LastBlock = StartScope(C, LastBlock); + return LastBlock; } @@ -1648,9 +1695,9 @@ CFGBlock* CFG::createBlock() { /// buildCFG - Constructs a CFG from an AST. Ownership of the returned /// CFG is returned to the caller. -CFG* CFG::buildCFG(Stmt* Statement, ASTContext *C) { +CFG* CFG::buildCFG(Stmt* Statement, ASTContext *C, bool AddScopes) { CFGBuilder Builder; - return Builder.buildCFG(Statement, C); + return Builder.buildCFG(Statement, C, AddScopes); } //===----------------------------------------------------------------------===// @@ -1909,7 +1956,18 @@ public: static void print_stmt(llvm::raw_ostream &OS, StmtPrinterHelper* Helper, - Stmt* Terminator) { + const CFGElement &E) { + Stmt *Terminator = E; + + if (E.asStartScope()) { + OS << "start scope\n"; + return; + } + if (E.asEndScope()) { + OS << "end scope\n"; + return; + } + if (Helper) { // special printing for statement-expressions. if (StmtExpr* SE = dyn_cast(Terminator)) { @@ -1959,14 +2017,14 @@ static void print_block(llvm::raw_ostream& OS, const CFG* cfg, OS << " ]\n"; // Print the label of this block. - if (Stmt* Terminator = const_cast(B.getLabel())) { + if (Stmt* Label = const_cast(B.getLabel())) { if (print_edges) OS << " "; - if (LabelStmt* L = dyn_cast(Terminator)) + if (LabelStmt* L = dyn_cast(Label)) OS << L->getName(); - else if (CaseStmt* C = dyn_cast(Terminator)) { + else if (CaseStmt* C = dyn_cast(Label)) { OS << "case "; C->getLHS()->printPretty(OS, Helper, PrintingPolicy(Helper->getLangOpts())); @@ -1975,9 +2033,9 @@ static void print_block(llvm::raw_ostream& OS, const CFG* cfg, C->getRHS()->printPretty(OS, Helper, PrintingPolicy(Helper->getLangOpts())); } - } else if (isa(Terminator)) + } else if (isa(Label)) OS << "default"; - else if (CXXCatchStmt *CS = dyn_cast(Terminator)) { + else if (CXXCatchStmt *CS = dyn_cast(Label)) { OS << "catch ("; CS->getExceptionDecl()->print(OS, PrintingPolicy(Helper->getLangOpts()), 0);