Index: include/clang/Analysis/AnalysisContext.h =================================================================== --- include/clang/Analysis/AnalysisContext.h +++ include/clang/Analysis/AnalysisContext.h @@ -425,6 +425,7 @@ bool addImplicitDtors = false, bool addInitializers = false, bool addTemporaryDtors = false, + bool addLifetime = false, bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true, Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -57,6 +57,7 @@ Statement, Initializer, NewAllocator, + LifetimeEnds, // dtor kind AutomaticObjectDtor, DeleteDtor, @@ -166,6 +167,28 @@ } }; +/// Represents the point where the lifetime of an automatic object ends +class CFGLifetimeEnds : public CFGElement { +public: + explicit CFGLifetimeEnds(const VarDecl *var, const Stmt *stmt) + : CFGElement(LifetimeEnds, var, stmt) {} + + const VarDecl *getVarDecl() const { + return static_cast(Data1.getPointer()); + } + + const Stmt *getTriggerStmt() const { + return static_cast(Data2.getPointer()); + } + +private: + friend class CFGElement; + CFGLifetimeEnds() {} + static bool isKind(const CFGElement &elem) { + return elem.getKind() == LifetimeEnds; + } +}; + /// CFGImplicitDtor - Represents C++ object destructor implicitly generated /// by compiler on various occasions. class CFGImplicitDtor : public CFGElement { @@ -682,6 +705,10 @@ Elements.push_back(CFGAutomaticObjDtor(VD, S), C); } + void appendLifetimeEnds(VarDecl *VD, Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGLifetimeEnds(VD, S), C); + } + void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) { Elements.push_back(CFGDeleteDtor(RD, DE), C); } @@ -698,6 +725,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 beginLifetimeEndsInsert(iterator I, size_t Cnt, + BumpVectorContext &C) { + return iterator(Elements.insert(I.base(), Cnt, + CFGLifetimeEnds(nullptr, nullptr), C)); + } + iterator insertLifetimeEnds(iterator I, VarDecl *VD, Stmt *S) { + *I = CFGLifetimeEnds(VD, S); + return ++I; + } }; /// \brief CFGCallback defines methods that should be called when a logical @@ -734,6 +774,7 @@ bool AddEHEdges; bool AddInitializers; bool AddImplicitDtors; + bool AddLifetime; bool AddTemporaryDtors; bool AddStaticInitBranches; bool AddCXXNewAllocator; @@ -755,8 +796,10 @@ BuildOptions() : forcedBlkExprs(nullptr), Observer(nullptr), - PruneTriviallyFalseEdges(true), AddEHEdges(false), + PruneTriviallyFalseEdges(true), + AddEHEdges(false), AddInitializers(false), AddImplicitDtors(false), + AddLifetime(false), AddTemporaryDtors(false), AddStaticInitBranches(false), AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {} }; Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -201,9 +201,15 @@ /// Controls which C++ member functions will be considered for inlining. CXXInlineableMemberKind CXXMemberInliningMode; + /// \sa includeImplicitDtorsInCFG + Optional IncludeImplicitDtorsInCFG; + /// \sa includeTemporaryDtorsInCFG Optional IncludeTemporaryDtorsInCFG; + /// \sa IncludeLifetimeInCFG + Optional IncludeLifetimeInCFG; + /// \sa mayInlineCXXStandardLibrary Optional InlineCXXStandardLibrary; @@ -388,6 +394,19 @@ /// accepts the values "true" and "false". bool includeTemporaryDtorsInCFG(); + /// Returns whether or not implicit destructors for C++ objects should + /// be included in the CFG. + /// + /// This is controlled by the 'cfg-implicit-dtors' config option, which + /// accepts the values "true" and "false". + bool includeImplicitDtorsInCFG(); + + /// Returns whether or not end-of-lifetime information should be included in the CFG. + /// + /// This is controlled by the 'cfg-lifetime' config option, which accepts + /// the values "true" and "false". + bool includeLifetimeInCFG(); + /// Returns whether or not C++ standard library functions may be considered /// for inlining. /// Index: lib/Analysis/AnalysisDeclContext.cpp =================================================================== --- lib/Analysis/AnalysisDeclContext.cpp +++ lib/Analysis/AnalysisDeclContext.cpp @@ -67,6 +67,7 @@ bool addImplicitDtors, bool addInitializers, bool addTemporaryDtors, + bool addLifetime, bool synthesizeBodies, bool addStaticInitBranch, bool addCXXNewAllocator, @@ -77,6 +78,7 @@ cfgBuildOptions.AddImplicitDtors = addImplicitDtors; cfgBuildOptions.AddInitializers = addInitializers; cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; + cfgBuildOptions.AddLifetime = addLifetime; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; } Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -233,6 +233,7 @@ } int distance(const_iterator L); + const_iterator shared_parent(const_iterator L); }; friend class const_iterator; @@ -275,6 +276,27 @@ return D; } +/// Calculates the closest parent of this iterator +/// that is in a scope reachable through the parents of L. +/// I.e. when using 'goto' from this to L, the lifetime of all variables +/// between this and shared_parent(L) end. +LocalScope::const_iterator LocalScope::const_iterator::shared_parent(LocalScope::const_iterator L) { + const_iterator F = *this; + while(true) { + const_iterator LL = L; + while (true) { + if(F.Scope == LL.Scope) + return F; + if (LL == const_iterator()) + break; + LL = LL.Scope->Prev; + } + assert (F != const_iterator() + && "L iterator is not reachable from F iterator."); + F = F.Scope->Prev; + } +} + /// Structure for specifying position in CFG during its build process. It /// consists of CFGBlock that specifies position in CFG and /// LocalScope::const_iterator that specifies position in LocalScope graph. @@ -579,6 +601,10 @@ CFGBlock *addInitializer(CXXCtorInitializer *I); void addAutomaticObjDtors(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); + void addLifetimeEnds(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. @@ -619,6 +645,10 @@ B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext()); } + void appendLifetimeEnds(CFGBlock *B, VarDecl *VD, Stmt *S) { + B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext()); + } + void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) { B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext()); } @@ -957,7 +987,8 @@ return TryResult(); } - + + bool hasTrivialDestructor(VarDecl *VD); }; inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder, @@ -1031,6 +1062,8 @@ assert(Succ == &cfg->getExit()); Block = nullptr; // the EXIT block is empty. Create all other blocks lazily. + assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && "AddImplicitDtors and AddLifetime cannot be used at the same time"); + if (BuildOpts.AddImplicitDtors) if (const CXXDestructorDecl *DD = dyn_cast_or_null(D)) addImplicitDtorsForDestructor(DD); @@ -1206,7 +1239,58 @@ 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.AddLifetime) + addLifetimeEnds(B, E, S); +} + +/// Add to current block automatic objects that leave the scope. +void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + if (!BuildOpts.AddLifetime) + return; + + if (B == E) + return; + + // To from B to E, one first goes up the scopes from B to P + // then sideways in one scope from P to P' and then down + // the scopes from P' to E. + // The lifetime of all objects between B and P end. + LocalScope::const_iterator P = B.shared_parent(E); + int dist = B.distance(P); + if (dist <= 0) + return; + + // We need to perform the scope leaving in reverse order + SmallVector DeclsTrivial; + SmallVector DeclsNonTrivial; + DeclsTrivial.reserve(dist); + DeclsNonTrivial.reserve(dist); + + for (LocalScope::const_iterator I = B; I != P; ++I) + if(hasTrivialDestructor(*I)) + DeclsTrivial.push_back(*I); + else + DeclsNonTrivial.push_back(*I); + + autoCreateBlock(); + // object with trivial destructor end their lifetime last (when storage duration ends) + for (SmallVectorImpl::reverse_iterator I = DeclsTrivial.rbegin(), + E = DeclsTrivial.rend(); + I != E; ++I) + appendLifetimeEnds(Block, *I, S); + + for (SmallVectorImpl::reverse_iterator I = DeclsNonTrivial.rbegin(), + E = DeclsNonTrivial.rend(); + I != E; ++I) + appendLifetimeEnds(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. @@ -1306,7 +1390,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.AddLifetime) return; LocalScope *Scope = nullptr; @@ -1331,7 +1415,7 @@ /// reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, LocalScope* Scope) { - if (!BuildOpts.AddImplicitDtors) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) return Scope; for (auto *DI : DS->decls()) @@ -1340,12 +1424,50 @@ return Scope; } +bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) +{ + // 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 true; + if (const ExprWithCleanups *EWC = dyn_cast(Init)) + Init = EWC->getSubExpr(); + if (!isa(Init)) + return true; + + // 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 true; + QT = AT->getElementType(); + } + + // Check if type is a C++ class with non-trivial destructor. + if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) + return CD->hasTrivialDestructor(); + return true; +} + /// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will /// create add scope for automatic objects and temporary objects bound to /// const reference. Will reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope) { - if (!BuildOpts.AddImplicitDtors) + assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && "AddImplicitDtors and AddLifetime cannot be used at the same time"); + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) return Scope; // Check if variable is local. @@ -1357,55 +1479,30 @@ 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; - - // 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 if type is a C++ class with non-trivial destructor. - if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) - if (!CD->hasTrivialDestructor()) { + if (BuildOpts.AddImplicitDtors) { + if (!hasTrivialDestructor(VD)) { // Add the variable to scope Scope = createOrReuseLocalScope(Scope); Scope->addVar(VD); ScopePos = Scope->begin(); } + return Scope; + } + + assert(BuildOpts.AddLifetime); + // 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 @@ -1417,6 +1514,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); @@ -1809,7 +1908,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; @@ -1941,12 +2040,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; @@ -2173,7 +2271,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 @@ -2290,7 +2388,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. @@ -2371,7 +2469,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); } @@ -2396,7 +2494,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. @@ -2448,7 +2546,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. @@ -2735,7 +2833,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 @@ -2770,7 +2868,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. @@ -3013,7 +3111,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; @@ -3064,7 +3162,7 @@ if (VarDecl *VD = Terminator->getConditionVariable()) { LocalScope::const_iterator SwitchBeginScopePos = ScopePos; addLocalScopeForVarDecl(VD); - addAutomaticObjDtors(ScopePos, SwitchBeginScopePos, Terminator); + addAutomaticObjHandling(ScopePos, SwitchBeginScopePos, Terminator); } if (Block) { @@ -3347,7 +3445,7 @@ if (VarDecl *VD = CS->getExceptionDecl()) { LocalScope::const_iterator BeginScopePos = ScopePos; addLocalScopeForVarDecl(VD); - addAutomaticObjDtors(ScopePos, BeginScopePos, CS); + addAutomaticObjHandling(ScopePos, BeginScopePos, CS); } if (CS->getHandlerBlock()) @@ -3401,7 +3499,7 @@ addLocalScopeForStmt(Begin); if (Stmt *End = S->getEndStmt()) addLocalScopeForStmt(End); - addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S); + addAutomaticObjHandling(ScopePos, save_scope_pos.get(), S); LocalScope::const_iterator ContinueScopePos = ScopePos; @@ -3868,6 +3966,7 @@ case CFGElement::Statement: case CFGElement::Initializer: case CFGElement::NewAllocator: + case CFGElement::LifetimeEnds: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4268,6 +4367,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 << " (Lifetime ends)\n"; + } else if (Optional NE = E.getAs()) { OS << "CFGNewAllocator("; if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) Index: lib/StaticAnalyzer/Core/AnalysisManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -23,9 +23,10 @@ AnalyzerOptions &Options, CodeInjector *injector) : AnaCtxMgr(Options.UnoptimizedCFG, - /*AddImplicitDtors=*/true, + Options.includeImplicitDtorsInCFG(), /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), + Options.includeLifetimeInCFG(), Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -153,6 +153,18 @@ /* Default = */ false); } +bool AnalyzerOptions::includeImplicitDtorsInCFG() { + return getBooleanOption(IncludeImplicitDtorsInCFG, + "cfg-implicit-dtors", + /* Default = */ true); +} + +bool AnalyzerOptions::includeLifetimeInCFG() { + return getBooleanOption(IncludeLifetimeInCFG, + "cfg-lifetime", + /* Default = */ false); +} + bool AnalyzerOptions::mayInlineCXXStandardLibrary() { return getBooleanOption(InlineCXXStandardLibrary, "c++-stdlib-inlining", Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -310,6 +310,8 @@ case CFGElement::TemporaryDtor: ProcessImplicitDtor(E.castAs(), Pred); return; + case CFGElement::LifetimeEnds: + return; } } Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -570,6 +570,7 @@ } case CFGElement::TemporaryDtor: case CFGElement::NewAllocator: + case CFGElement::LifetimeEnds: llvm_unreachable("not yet implemented!"); } Index: test/Analysis/analyzer-config.c =================================================================== --- test/Analysis/analyzer-config.c +++ test/Analysis/analyzer-config.c @@ -12,6 +12,8 @@ // CHECK: [config] // CHECK-NEXT: cfg-conditional-static-initializers = true +// CHECK-NEXT: cfg-implicit-dtors = true +// CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -27,5 +29,5 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 15 +// CHECK-NEXT: num-entries = 17 Index: test/Analysis/analyzer-config.cpp =================================================================== --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -23,6 +23,8 @@ // CHECK-NEXT: c++-stdlib-inlining = true // CHECK-NEXT: c++-template-inlining = true // CHECK-NEXT: cfg-conditional-static-initializers = true +// CHECK-NEXT: cfg-implicit-dtors = true +// CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -38,4 +40,4 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 20 +// CHECK-NEXT: num-entries = 22 Index: test/Analysis/lifetime-cfg-output.cpp =================================================================== --- /dev/null +++ test/Analysis/lifetime-cfg-output.cpp @@ -0,0 +1,735 @@ +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-lifetime=true -analyzer-config cfg-implicit-dtors=false %s > %t 2>&1 +// RUN: FileCheck --input-file=%t %s + +extern bool UV; +class A { +public: +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: true +// CHECK-NEXT: 2: UV +// CHECK-NEXT: 3: [B1.2] = [B1.1] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + A() { + UV = true; + } +// CHECK: [B3 (ENTRY)] +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B1] +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: this +// CHECK-NEXT: 3: [B1.2]->p +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, int *) +// CHECK-NEXT: 5: *[B1.4] +// CHECK-NEXT: 6: [B1.5] = [B1.1] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: this +// CHECK-NEXT: 2: [B2.1]->p +// CHECK-NEXT: 3: [B2.2] (ImplicitCastExpr, LValueToRValue, int *) +// CHECK-NEXT: 4: [B2.3] (ImplicitCastExpr, PointerToBoolean, _Bool) +// CHECK-NEXT: T: if [B2.4] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (2): B1 B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B2 + ~A() { + if(p) + *p = 0; + } +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: return [B1.1]; +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + operator int() const { return 1; } + int* p; +}; + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 5: const A &b = a; +// CHECK-NEXT: 6: A() (CXXConstructExpr, class A) +// CHECK-NEXT: 7: [B1.6] (BindTemporary) +// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 9: [B1.8] +// CHECK-NEXT: 10: const A &c = A(); +// CHECK-NEXT: 11: [B1.10] (Lifetime ends) +// CHECK-NEXT: 12: [B1.2] (Lifetime ends) +// CHECK-NEXT: 13: [B1.5] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_const_ref() { + A a; + const A& b = a; + const A& c = A(); +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A [2]) +// CHECK-NEXT: 2: A a[2]; +// CHECK-NEXT: 3: (CXXConstructExpr, class A [0]) +// CHECK-NEXT: 4: A b[0]; +// lifetime of a ends when its destructors are run +// CHECK-NEXT: 5: [B1.2] (Lifetime ends) +// lifetime of b ends when its storage duration ends +// CHECK-NEXT: 6: [B1.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_array() { + A a[2]; + A b[0]; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A c; +// CHECK-NEXT: 5: (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A d; +// CHECK-NEXT: 7: [B1.6] (Lifetime ends) +// CHECK-NEXT: 8: [B1.4] (Lifetime ends) +// CHECK-NEXT: 9: (CXXConstructExpr, class A) +// CHECK-NEXT: 10: A b; +// CHECK-NEXT: 11: [B1.10] (Lifetime ends) +// CHECK-NEXT: 12: [B1.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_scope() { + A a; + { A c; + A d; + } + A b; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B1.2] (Lifetime ends) +// CHECK-NEXT: 4: [B3.4] (Lifetime ends) +// CHECK-NEXT: 5: [B3.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B3.4] (Lifetime ends) +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b; +// CHECK-NEXT: 5: UV +// CHECK-NEXT: 6: [B3.5] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B3.6] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (2): B2 B1 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B2 +void test_return() { + A a; + A b; + if (UV) return; + A c; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.6] (Lifetime ends) +// CHECK-NEXT: 2: [B4.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B2.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B4] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 5: [B4.4] (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A b = a; +// CHECK-NEXT: 7: b +// CHECK-NEXT: 8: [B4.7] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 9: [B4.8].operator int +// CHECK-NEXT: 10: [B4.8] +// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 12: [B4.11] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B4.12] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_if_implicit_scope() { + A a; + if (A b = a) + A c; + else A c; +} + +// CHECK: [B9 (ENTRY)] +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B1] +// CHECK-NEXT: 1: [B8.6] (Lifetime ends) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: [B1.3] (Lifetime ends) +// CHECK-NEXT: 5: [B8.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B2.2] (Lifetime ends) +// CHECK-NEXT: 4: [B4.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B4.2] (Lifetime ends) +// CHECK-NEXT: 3: [B8.6] (Lifetime ends) +// CHECK-NEXT: 4: [B8.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B4] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B4.4] +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B5] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B5.2] (Lifetime ends) +// CHECK-NEXT: 4: [B7.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B6] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B7.2] (Lifetime ends) +// CHECK-NEXT: 3: [B8.6] (Lifetime ends) +// CHECK-NEXT: 4: [B8.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B7] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B7.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.4] +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B8.3] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 5: [B8.4] (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A b = a; +// CHECK-NEXT: 7: b +// CHECK-NEXT: 8: [B8.7] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 9: [B8.8].operator int +// CHECK-NEXT: 10: [B8.8] +// CHECK-NEXT: 11: [B8.10] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 12: [B8.11] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B8.12] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B7 B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (3): B1 B3 B6 +void test_if_jumps() { + A a; + if (A b = a) { + A c; + if (UV) return; + A d; + } else { + A c; + if (UV) return; + A d; + } + A e; +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.4] (Lifetime ends) +// CHECK-NEXT: 2: [B5.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B4.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6].operator int +// CHECK-NEXT: 8: [B4.6] +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B4.10] +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_while_implicit_scope() { + A a; + while (A b = a) + A c; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: [B10.4] (Lifetime ends) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: [B1.3] (Lifetime ends) +// CHECK-NEXT: 5: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B9.2] (Lifetime ends) +// CHECK-NEXT: 5: [B10.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B9.2] (Lifetime ends) +// CHECK-NEXT: 3: [B10.4] (Lifetime ends) +// CHECK-NEXT: 4: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: 2: [B10.4] (Lifetime ends) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B10.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B10.6].operator int +// CHECK-NEXT: 8: [B10.6] +// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B10.10] +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_while_jumps() { + A a; + while (A b = a) { + A c; + if (UV) break; + if (UV) continue; + if (UV) return; + A d; + } + A e; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B1.2] (Lifetime ends) +// CHECK-NEXT: 4: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B8 B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: do ... while [B2.2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (2): B10 B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B9.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B9.2] (Lifetime ends) +// CHECK-NEXT: 3: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (2): B10 B11 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B11] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_do_jumps() { + A a; + do { + A b; + if (UV) break; + if (UV) continue; + if (UV) return; + A c; + } while (UV); + A d; +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.4] (Lifetime ends) +// CHECK-NEXT: 2: [B5.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B4.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6].operator int +// CHECK-NEXT: 8: [B4.6] +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B4.10]; ) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_for_implicit_scope() { + for (A a; A b = a; ) + A c; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: [B10.4] (Lifetime ends) +// CHECK-NEXT: 2: [B11.4] (Lifetime ends) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A f; +// CHECK-NEXT: 5: [B1.4] (Lifetime ends) +// CHECK-NEXT: 6: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A e; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B9.2] (Lifetime ends) +// CHECK-NEXT: 5: [B10.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B9.2] (Lifetime ends) +// CHECK-NEXT: 3: [B10.4] (Lifetime ends) +// CHECK-NEXT: 4: [B11.4] (Lifetime ends) +// CHECK-NEXT: 5: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B10.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A c = b; +// CHECK-NEXT: 5: c +// CHECK-NEXT: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B10.6].operator int +// CHECK-NEXT: 8: [B10.6] +// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B10.10]; ) +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_for_jumps() { + A a; + for (A b; A c = b; ) { + A d; + if (UV) break; + if (UV) continue; + if (UV) return; + A e; + } + A f; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: int n; +// CHECK-NEXT: 4: n +// CHECK-NEXT: 5: &[B1.4] +// CHECK-NEXT: 6: a +// CHECK-NEXT: 7: [B1.6].p +// CHECK-NEXT: 8: [B1.7] = [B1.5] +// CHECK-NEXT: 9: [B1.2] (Lifetime ends) +// CHECK-NEXT: 10: [B1.3] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_trivial_vs_non_trivial_order() { + A a; + int n; + a.p = &n; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: a: +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: i +// CHECK-NEXT: 3: [B1.2] = [B1.1] +// CHECK-NEXT: 4: [B2.1] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: int i; +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: T: goto a; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void goto_past_declaration() { + goto a; + int i; + a: + i = 1; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: a: +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: k +// CHECK-NEXT: 3: [B1.2] = [B1.1] +// CHECK-NEXT: 4: [B2.4] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: int j; +// CHECK-NEXT: 2: [B2.1] (Lifetime ends) +// CHECK-NEXT: 3: [B3.1] (Lifetime ends) +// CHECK-NEXT: 4: int k; +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: int i; +// CHECK-NEXT: 2: [B3.1] (Lifetime ends) +// CHECK-NEXT: T: goto a; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void goto_past_declaration2() { + { + int i; + goto a; + int j; + } + { + int k; + a: + k = 1; + } +} \ No newline at end of file