Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -57,6 +57,7 @@ Statement, Initializer, NewAllocator, + AutomaticObjLeavesScope, // dtor kind AutomaticObjectDtor, DeleteDtor, @@ -166,6 +167,27 @@ } }; +class CFGAutomaticObjLeavesScope : public CFGElement { +public: + explicit CFGAutomaticObjLeavesScope(const VarDecl *var, const Stmt *stmt) + : CFGElement(AutomaticObjLeavesScope, var, stmt) {} + + const VarDecl *getVarDecl() const { + return static_cast(Data1.getPointer()); + } + + const Stmt *getTriggerStmt() const { + return static_cast(Data2.getPointer()); + } + +private: + friend class CFGElement; + CFGAutomaticObjLeavesScope() {} + static bool isKind(const CFGElement &elem) { + return elem.getKind() == AutomaticObjLeavesScope; + } +}; + /// CFGImplicitDtor - Represents C++ object destructor implicitly generated /// by compiler on various occasions. class CFGImplicitDtor : public CFGElement { @@ -682,6 +704,10 @@ Elements.push_back(CFGAutomaticObjDtor(VD, S), C); } + void appendAutomaticObjLeavesScope(VarDecl *VD, Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGAutomaticObjLeavesScope(VD, S), C); + } + void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) { Elements.push_back(CFGDeleteDtor(RD, DE), C); } @@ -698,6 +724,19 @@ *I = CFGAutomaticObjDtor(VD, S); return ++I; } + + // Scope leaving must be performed in reversed order. So insertion is in two + // steps. First we prepare space for some number of elements, then we insert + // the elements beginning at the last position in prepared space. + iterator beginAutomaticObjLeavesScopeInsert(iterator I, size_t Cnt, + BumpVectorContext &C) { + return iterator(Elements.insert(I.base(), Cnt, + CFGAutomaticObjLeavesScope(nullptr, nullptr), C)); + } + iterator insertAutomaticObjLeavesScope(iterator I, VarDecl *VD, Stmt *S) { + *I = CFGAutomaticObjLeavesScope(VD, S); + return ++I; + } }; /// \brief CFGCallback defines methods that should be called when a logical @@ -731,6 +770,7 @@ ForcedBlkExprs **forcedBlkExprs; CFGCallback *Observer; bool PruneTriviallyFalseEdges; + bool AddAutomaticObjLeavesScope; bool AddEHEdges; bool AddInitializers; bool AddImplicitDtors; @@ -755,7 +795,9 @@ BuildOptions() : forcedBlkExprs(nullptr), Observer(nullptr), - PruneTriviallyFalseEdges(true), AddEHEdges(false), + PruneTriviallyFalseEdges(true), + AddAutomaticObjLeavesScope(false), + AddEHEdges(false), AddInitializers(false), AddImplicitDtors(false), AddTemporaryDtors(false), AddStaticInitBranches(false), AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {} Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -578,6 +578,10 @@ CFGBlock *addInitializer(CXXCtorInitializer *I); void addAutomaticObjDtors(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); + void addAutomaticObjLeavesScope(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S); + void addAutomaticObjHandling(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S); void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); // Local scopes creation. @@ -618,6 +622,10 @@ B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext()); } + void appendAutomaticObjLeavesScope(CFGBlock *B, VarDecl *VD, Stmt *S) { + B->appendAutomaticObjLeavesScope(VD, S, cfg->getBumpVectorContext()); + } + void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) { B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext()); } @@ -625,6 +633,9 @@ void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E); + void prependAutomaticObjLeavesScopeWithTerminator(CFGBlock *Blk, + LocalScope::const_iterator B, LocalScope::const_iterator E); + void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) { B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable), cfg->getBumpVectorContext()); @@ -1033,6 +1044,8 @@ assert(Succ == &cfg->getExit()); Block = nullptr; // the EXIT block is empty. Create all other blocks lazily. + assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddAutomaticObjLeavesScope) && "AddImplicitDtors and AddScopeLeavers cannot be used at the same time"); + if (BuildOpts.AddImplicitDtors) if (const CXXDestructorDecl *DD = dyn_cast_or_null(D)) addImplicitDtorsForDestructor(DD); @@ -1208,7 +1221,41 @@ return Init->getType(); } - + +void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + if (BuildOpts.AddImplicitDtors) + addAutomaticObjDtors(B, E, S); + if (BuildOpts.AddAutomaticObjLeavesScope) + addAutomaticObjLeavesScope(B, E, S); +} + +/// addAutomaticObjLeavesScope - Add to current block automatic objects thats leave the scope. +void CFGBuilder::addAutomaticObjLeavesScope(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + if (!BuildOpts.AddAutomaticObjLeavesScope) + return; + + if (B == E) + return; + + int dist = B.distance(E); + if (dist <= 0) + return; + + // We need to perform the scope leaving in reverse order + SmallVector Decls; + Decls.reserve(dist); + for (LocalScope::const_iterator I = B; I != E; ++I) + Decls.push_back(*I); + + autoCreateBlock(); + for (SmallVectorImpl::reverse_iterator I = Decls.rbegin(), + E = Decls.rend(); + I != E; ++I) + appendAutomaticObjLeavesScope(Block, *I, S); +} + /// addAutomaticObjDtors - Add to current block automatic objects destructors /// for objects in range of local scope positions. Use S as trigger statement /// for destructors. @@ -1308,7 +1355,7 @@ /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement /// that should create implicit scope (e.g. if/else substatements). void CFGBuilder::addLocalScopeForStmt(Stmt *S) { - if (!BuildOpts.AddImplicitDtors) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddAutomaticObjLeavesScope) return; LocalScope *Scope = nullptr; @@ -1333,7 +1380,7 @@ /// reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, LocalScope* Scope) { - if (!BuildOpts.AddImplicitDtors) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddAutomaticObjLeavesScope) return Scope; for (auto *DI : DS->decls()) @@ -1347,7 +1394,7 @@ /// const reference. Will reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope) { - if (!BuildOpts.AddImplicitDtors) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddAutomaticObjLeavesScope) return Scope; // Check if variable is local. @@ -1359,55 +1406,61 @@ default: return Scope; } - // Check for const references bound to temporary. Set type to pointee. - QualType QT = VD->getType(); - if (QT.getTypePtr()->isReferenceType()) { - // Attempt to determine whether this declaration lifetime-extends a - // temporary. - // - // FIXME: This is incorrect. Non-reference declarations can lifetime-extend - // temporaries, and a single declaration can extend multiple temporaries. - // We should look at the storage duration on each nested - // MaterializeTemporaryExpr instead. - const Expr *Init = VD->getInit(); - if (!Init) - return Scope; - if (const ExprWithCleanups *EWC = dyn_cast(Init)) - Init = EWC->getSubExpr(); - if (!isa(Init)) - return Scope; + if (BuildOpts.AddImplicitDtors) { + // Check for const references bound to temporary. Set type to pointee. + QualType QT = VD->getType(); + if (QT.getTypePtr()->isReferenceType()) { + // Attempt to determine whether this declaration lifetime-extends a + // temporary. + // + // FIXME: This is incorrect. Non-reference declarations can lifetime-extend + // temporaries, and a single declaration can extend multiple temporaries. + // We should look at the storage duration on each nested + // MaterializeTemporaryExpr instead. + const Expr *Init = VD->getInit(); + if (!Init) + return Scope; + if (const ExprWithCleanups *EWC = dyn_cast(Init)) + Init = EWC->getSubExpr(); + if (!isa(Init)) + return Scope; + + // Lifetime-extending a temporary. + QT = getReferenceInitTemporaryType(*Context, Init); + } - // Lifetime-extending a temporary. - QT = getReferenceInitTemporaryType(*Context, Init); - } + // Check for constant size array. Set type to array element type. + while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { + if (AT->getSize() == 0) + return Scope; + QT = AT->getElementType(); + } - // Check for constant size array. Set type to array element type. - while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { - if (AT->getSize() == 0) - return Scope; - QT = AT->getElementType(); + // Check if type is a C++ class with non-trivial destructor. + if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) + if (!CD->hasTrivialDestructor()) { + // Add the variable to scope + Scope = createOrReuseLocalScope(Scope); + Scope->addVar(VD); + ScopePos = Scope->begin(); + } + return Scope; } - // Check if type is a C++ class with non-trivial destructor. - if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) - if (!CD->hasTrivialDestructor()) { - // Add the variable to scope - Scope = createOrReuseLocalScope(Scope); - Scope->addVar(VD); - ScopePos = Scope->begin(); - } + assert(BuildOpts.AddAutomaticObjLeavesScope); + // Add the variable to scope + Scope = createOrReuseLocalScope(Scope); + Scope->addVar(VD); + ScopePos = Scope->begin(); return Scope; } /// addLocalScopeAndDtors - For given statement add local scope for it and /// add destructors that will cleanup the scope. Will reuse Scope if not NULL. void CFGBuilder::addLocalScopeAndDtors(Stmt *S) { - if (!BuildOpts.AddImplicitDtors) - return; - LocalScope::const_iterator scopeBeginPos = ScopePos; addLocalScopeForStmt(S); - addAutomaticObjDtors(ScopePos, scopeBeginPos, S); + addAutomaticObjHandling(ScopePos, scopeBeginPos, S); } /// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for @@ -1419,6 +1472,8 @@ /// no-return destructors properly. void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E) { + if (!BuildOpts.AddImplicitDtors) + return; BumpVectorContext &C = cfg->getBumpVectorContext(); CFGBlock::iterator InsertPos = Blk->beginAutomaticObjDtorsInsert(Blk->end(), B.distance(E), C); @@ -1427,6 +1482,25 @@ Blk->getTerminator()); } +/// prependAutomaticObjLeavesScopeWithTerminator - Prepend destructor CFGElements for +/// variables with automatic storage duration to CFGBlock's elements vector. +/// Elements will be prepended to physical beginning of the vector which +/// happens to be logical end. Use blocks terminator as statement that specifies +/// destructors call site. +/// FIXME: This mechanism for adding automatic destructors doesn't handle +/// no-return destructors properly. +void CFGBuilder::prependAutomaticObjLeavesScopeWithTerminator(CFGBlock *Blk, + LocalScope::const_iterator B, LocalScope::const_iterator E) { + if (!BuildOpts.AddAutomaticObjLeavesScope) + return; + BumpVectorContext &C = cfg->getBumpVectorContext(); + CFGBlock::iterator InsertPos + = Blk->beginAutomaticObjLeavesScopeInsert(Blk->end(), B.distance(E), C); + for (LocalScope::const_iterator I = B; I != E; ++I) + InsertPos = Blk->insertAutomaticObjLeavesScope(InsertPos, *I, + Blk->getTerminator()); +} + /// Visit - Walk the subtree of a statement and add extra /// blocks for ternary operators, &&, and ||. We also process "," and /// DeclStmts (which may contain nested control-flow). @@ -1811,7 +1885,7 @@ // If there is no target for the break, then we are looking at an incomplete // AST. This means that the CFG cannot be constructed. if (BreakJumpTarget.block) { - addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B); + addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B); addSuccessor(Block, BreakJumpTarget.block); } else badCFG = true; @@ -1943,12 +2017,11 @@ CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { LocalScope::const_iterator scopeBeginPos = ScopePos; - if (BuildOpts.AddImplicitDtors) { - addLocalScopeForStmt(C); - } + addLocalScopeForStmt(C); + if (!C->body_empty() && !isa(*C->body_rbegin())) { // If the body ends with a ReturnStmt, the dtors will be added in VisitReturnStmt - addAutomaticObjDtors(ScopePos, scopeBeginPos, C); + addAutomaticObjHandling(ScopePos, scopeBeginPos, C); } CFGBlock *LastBlock = Block; @@ -2175,7 +2248,7 @@ if (VarDecl *VD = I->getConditionVariable()) { LocalScope::const_iterator BeginScopePos = ScopePos; addLocalScopeForVarDecl(VD); - addAutomaticObjDtors(ScopePos, BeginScopePos, I); + addAutomaticObjHandling(ScopePos, BeginScopePos, I); } // The block we were processing is now finished. Make it the successor @@ -2292,7 +2365,7 @@ // Create the new block. Block = createBlock(false); - addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R); + addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R); // If the one of the destructors does not return, we already have the Exit // block as a successor. @@ -2361,7 +2434,7 @@ BackpatchBlocks.push_back(JumpSource(Block, ScopePos)); else { JumpTarget JT = I->second; - addAutomaticObjDtors(ScopePos, JT.scopePosition, G); + addAutomaticObjHandling(ScopePos, JT.scopePosition, G); addSuccessor(Block, JT.block); } @@ -2386,7 +2459,7 @@ addLocalScopeForVarDecl(VD); LocalScope::const_iterator ContinueScopePos = ScopePos; - addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F); + addAutomaticObjHandling(ScopePos, save_scope_pos.get(), F); // "for" is a control-flow statement. Thus we stop processing the current // block. @@ -2438,7 +2511,7 @@ ContinueJumpTarget.block->setLoopTarget(F); // Loop body should end with destructor of Condition variable (if any). - addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F); + addAutomaticObjHandling(ScopePos, LoopBeginScopePos, F); // If body is not a compound statement create implicit scope // and add destructors. @@ -2725,7 +2798,7 @@ LocalScope::const_iterator LoopBeginScopePos = ScopePos; if (VarDecl *VD = W->getConditionVariable()) { addLocalScopeForVarDecl(VD); - addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); + addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); } // "while" is a control-flow statement. Thus we stop processing the current @@ -2760,7 +2833,7 @@ BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); // Loop body should end with destructor of Condition variable (if any). - addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); + addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); // If body is not a compound statement create implicit scope // and add destructors. @@ -3003,7 +3076,7 @@ // If there is no target for the continue, then we are looking at an // incomplete AST. This means the CFG cannot be constructed. if (ContinueJumpTarget.block) { - addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C); + addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C); addSuccessor(Block, ContinueJumpTarget.block); } else badCFG = true; @@ -3054,7 +3127,7 @@ if (VarDecl *VD = Terminator->getConditionVariable()) { LocalScope::const_iterator SwitchBeginScopePos = ScopePos; addLocalScopeForVarDecl(VD); - addAutomaticObjDtors(ScopePos, SwitchBeginScopePos, Terminator); + addAutomaticObjHandling(ScopePos, SwitchBeginScopePos, Terminator); } if (Block) { @@ -3337,7 +3410,7 @@ if (VarDecl *VD = CS->getExceptionDecl()) { LocalScope::const_iterator BeginScopePos = ScopePos; addLocalScopeForVarDecl(VD); - addAutomaticObjDtors(ScopePos, BeginScopePos, CS); + addAutomaticObjHandling(ScopePos, BeginScopePos, CS); } if (CS->getHandlerBlock()) @@ -3389,7 +3462,7 @@ addLocalScopeForStmt(Range); if (Stmt *BeginEnd = S->getBeginEndStmt()) addLocalScopeForStmt(BeginEnd); - addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S); + addAutomaticObjHandling(ScopePos, save_scope_pos.get(), S); LocalScope::const_iterator ContinueScopePos = ScopePos; @@ -3855,6 +3928,7 @@ case CFGElement::Statement: case CFGElement::Initializer: case CFGElement::NewAllocator: + case CFGElement::AutomaticObjLeavesScope: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4255,6 +4329,13 @@ OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n"; + } else if (Optional DE = + E.getAs()) { + const VarDecl *VD = DE->getVarDecl(); + Helper.handleDecl(VD, OS); + + OS << " (leaves scope)\n"; + } else if (Optional NE = E.getAs()) { OS << "CFGNewAllocator("; if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr())