diff options
-rw-r--r-- | clang/include/clang/Analysis/CFG.h | 3 | ||||
-rw-r--r-- | clang/include/clang/Analysis/PathSensitive/AnalysisContext.h | 11 | ||||
-rw-r--r-- | clang/lib/Analysis/AnalysisContext.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Analysis/CFG.cpp | 44 | ||||
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 38 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 4 | ||||
-rw-r--r-- | clang/test/SemaCXX/warn-unreachable.cpp | 35 |
7 files changed, 121 insertions, 16 deletions
diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index fcfa6b7c957..e87784fd85e 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -133,7 +133,7 @@ class CFGBlock { /// Label - An (optional) label that prefixes the executable /// statements in the block. When this variable is non-NULL, it is - /// either an instance of LabelStmt or SwitchCase. + /// either an instance of LabelStmt, SwitchCase or CXXCatchStmt. Stmt *Label; /// Terminator - The terminator for a basic block that @@ -287,6 +287,7 @@ public: /// buildCFG - Builds a CFG from an AST. The responsibility to free the /// constructed CFG belongs to the caller. static CFG* buildCFG(const Decl *D, Stmt* AST, ASTContext *C, + bool AddEHEdges = false, bool AddScopes = false); /// createBlock - Create a new block in the CFG. The CFG owns the block; diff --git a/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h b/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h index 63ba558e3d5..c82bb962fd1 100644 --- a/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h +++ b/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h @@ -46,14 +46,21 @@ class AnalysisContext { ParentMap *PM; llvm::DenseMap<const BlockDecl*,void*> *ReferencedBlockVars; llvm::BumpPtrAllocator A; + bool AddEHEdges; public: - AnalysisContext(const Decl *d) : D(d), cfg(0), liveness(0), PM(0), - ReferencedBlockVars(0) {} + AnalysisContext(const Decl *d, bool addehedges = false) + : D(d), cfg(0), liveness(0), PM(0), ReferencedBlockVars(0), + AddEHEdges(addehedges) {} ~AnalysisContext(); ASTContext &getASTContext() { return D->getASTContext(); } const Decl *getDecl() { return D; } + /// getAddEHEdges - Return true iff we are adding exceptional edges from + /// callExprs. If this is false, then try/catch statements and blocks + /// reachable from them can appear to be dead in the CFG, analysis passes must + /// cope with that. + bool getAddEHEdges() const { return AddEHEdges; } Stmt *getBody(); CFG *getCFG(); ParentMap &getParentMap(); diff --git a/clang/lib/Analysis/AnalysisContext.cpp b/clang/lib/Analysis/AnalysisContext.cpp index 1d5b4a18e98..ad9f6dd1941 100644 --- a/clang/lib/Analysis/AnalysisContext.cpp +++ b/clang/lib/Analysis/AnalysisContext.cpp @@ -55,7 +55,7 @@ const ImplicitParamDecl *AnalysisContext::getSelfDecl() const { CFG *AnalysisContext::getCFG() { if (!cfg) - cfg = CFG::buildCFG(D, getBody(), &D->getASTContext()); + cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), AddEHEdges); return cfg; } diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 57053b16589..ef3cdd88abe 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -94,7 +94,8 @@ public: TryTerminatedBlock(NULL) {} // buildCFG - Used by external clients to construct the CFG. - CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, bool AddScopes); + CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, bool AddEHEdges, + bool AddScopes); private: // Visitors to walk an AST and construct the CFG. @@ -208,6 +209,11 @@ private: } bool badCFG; + + // True iff EH edges on CallExprs should be added to the CFG. + bool AddEHEdges; + + // True iff scope start and scope end notes should be added to the CFG. bool AddScopes; }; @@ -231,7 +237,7 @@ static VariableArrayType* FindVA(Type* t) { /// transferred to the caller. If CFG construction fails, this method returns /// NULL. CFG* CFGBuilder::buildCFG(const Decl *D, Stmt* Statement, ASTContext* C, - bool AddScopes) { + bool AddEHEdges, bool AddScopes) { Context = C; assert(cfg.get()); if (!Statement) @@ -540,6 +546,22 @@ CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) { return Block; } +static bool CanThrow(Expr *E) { + QualType Ty = E->getType(); + if (Ty->isFunctionPointerType()) + Ty = Ty->getAs<PointerType>()->getPointeeType(); + else if (Ty->isBlockPointerType()) + Ty = Ty->getAs<BlockPointerType>()->getPointeeType(); + + const FunctionType *FT = Ty->getAs<FunctionType>(); + if (FT) { + if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT)) + if (Proto->hasEmptyExceptionSpec()) + return false; + } + return true; +} + CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { // If this is a call to a no-return function, this stops the block here. bool NoReturn = false; @@ -547,21 +569,25 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { NoReturn = true; } - bool CanThrow = false; + bool AddEHEdge = false; // Languages without exceptions are assumed to not throw. if (Context->getLangOptions().Exceptions) { - CanThrow = true; + if (AddEHEdges) + AddEHEdge = true; } if (FunctionDecl *FD = C->getDirectCallee()) { if (FD->hasAttr<NoReturnAttr>()) NoReturn = true; if (FD->hasAttr<NoThrowAttr>()) - CanThrow = false; + AddEHEdge = false; } - if (!NoReturn && !CanThrow) + if (!CanThrow(C->getCallee())) + AddEHEdge = false; + + if (!NoReturn && !AddEHEdge) return VisitStmt(C, asc); if (Block) { @@ -577,7 +603,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { // Wire this to the exit block directly. AddSuccessor(Block, &cfg->getExit()); } - if (CanThrow) { + if (AddEHEdge) { // Add exceptional edges. if (TryTerminatedBlock) AddSuccessor(Block, TryTerminatedBlock); @@ -1714,9 +1740,9 @@ CFGBlock* CFG::createBlock() { /// buildCFG - Constructs a CFG from an AST. Ownership of the returned /// CFG is returned to the caller. CFG* CFG::buildCFG(const Decl *D, Stmt* Statement, ASTContext *C, - bool AddScopes) { + bool AddEHEdges, bool AddScopes) { CFGBuilder Builder; - return Builder.buildCFG(D, Statement, C, AddScopes); + return Builder.buildCFG(D, Statement, C, AddEHEdges, AddScopes); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index e61110f195f..9a6f950c245 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2091,6 +2091,9 @@ static SourceLocation GetUnreachableLoc(CFGBlock &b) { } return b[1].getStmt()->getLocStart(); } + case Stmt::CXXTryStmtClass: { + return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc(); + } default: ; } return S->getLocStart(); @@ -2167,12 +2170,20 @@ void Sema::CheckUnreachable(AnalysisContext &AC) { return; llvm::SmallVector<SourceLocation, 24> lines; + bool AddEHEdges = AC.getAddEHEdges(); // First, give warnings for blocks with no predecessors, as they // can't be part of a loop. for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { CFGBlock &b = **I; if (!live[b.getBlockID()]) { if (b.pred_begin() == b.pred_end()) { + if (!AddEHEdges && b.getTerminator() + && isa<CXXTryStmt>(b.getTerminator())) { + // When not adding EH edges from calls, catch clauses + // can otherwise seem dead. Avoid noting them as dead. + count += MarkLive(&b, live); + continue; + } SourceLocation c = GetUnreachableLoc(b); if (!c.isValid()) { // Blocks without a location can't produce a warning, so don't mark @@ -2222,11 +2233,29 @@ Sema::ControlFlowKind Sema::CheckFallThrough(AnalysisContext &AC) { // FIXME: This should be NeverFallThrough return NeverFallThroughOrReturn; - // The CFG leaves in dead things, and we don't want to dead code paths to + // The CFG leaves in dead things, and we don't want the dead code paths to // confuse us, so we mark all live things first. std::queue<CFGBlock*> workq; llvm::BitVector live(cfg->getNumBlockIDs()); - MarkLive(&cfg->getEntry(), live); + unsigned count = MarkLive(&cfg->getEntry(), live); + + bool AddEHEdges = AC.getAddEHEdges(); + if (!AddEHEdges && count != cfg->getNumBlockIDs()) + // When there are things remaining dead, and we didn't add EH edges + // from CallExprs to the catch clauses, we have to go back and + // mark them as live. + for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { + CFGBlock &b = **I; + if (!live[b.getBlockID()]) { + if (b.pred_begin() == b.pred_end()) { + if (b.getTerminator() && isa<CXXTryStmt>(b.getTerminator())) + // When not adding EH edges from calls, catch clauses + // can otherwise seem dead. Avoid noting them as dead. + count += MarkLive(&b, live); + continue; + } + } + } // Now we know what is live, we check the live precessors of the exit block // and look for fall through paths, being careful to ignore normal returns, @@ -2243,6 +2272,11 @@ Sema::ControlFlowKind Sema::CheckFallThrough(AnalysisContext &AC) { if (!live[B.getBlockID()]) continue; if (B.size() == 0) { + if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) { + HasAbnormalEdge = true; + continue; + } + // A labeled empty statement, or the entry block... HasPlainEdge = true; continue; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 1e1df069ea2..b6b3b5bee16 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4102,7 +4102,9 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg, Decl *dcl = D.getAs<Decl>(); Stmt *Body = BodyArg.takeAs<Stmt>(); - AnalysisContext AC(dcl); + // Don't generate EH edges for CallExprs as we'd like to avoid the n^2 + // explosion for destrutors that can result and the compile time hit. + AnalysisContext AC(dcl, false); FunctionDecl *FD = 0; FunctionTemplateDecl *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(dcl); if (FunTmpl) diff --git a/clang/test/SemaCXX/warn-unreachable.cpp b/clang/test/SemaCXX/warn-unreachable.cpp new file mode 100644 index 00000000000..13a82f4f183 --- /dev/null +++ b/clang/test/SemaCXX/warn-unreachable.cpp @@ -0,0 +1,35 @@ +// RUN: %clang %s -fsyntax-only -Xclang -verify -fblocks -Wunreachable-code -Wno-unused-value + +int live(); +int dead(); +int liveti() throw(int); +int (*livetip)() throw(int); + +int test1() { + try { + live(); + } catch (int i) { + live(); + } + return 1; +} + +void test2() { + try { + live(); + } catch (int i) { + live(); + } + try { + liveti(); + } catch (int i) { + live(); + } + try { + livetip(); + } catch (int i) { + live(); + } + throw 1; + dead(); // expected-warning {{will never be executed}} +} |