diff options
-rw-r--r-- | include/clang/Analysis/CFG.h | 15 | ||||
-rw-r--r-- | lib/Analysis/CFG.cpp | 326 | ||||
-rw-r--r-- | test/Analysis/temp-obj-dtors-cfg-output.cpp | 581 |
3 files changed, 891 insertions, 31 deletions
diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h index 1e4692ea76..8fea3f2bb8 100644 --- a/include/clang/Analysis/CFG.h +++ b/include/clang/Analysis/CFG.h @@ -35,6 +35,7 @@ namespace clang { class VarDecl; class CXXBaseOrMemberInitializer; class CXXBaseSpecifier; + class CXXBindTemporaryExpr; class CFG; class PrinterHelper; class LangOptions; @@ -198,8 +199,18 @@ public: } }; +/// CFGTemporaryDtor - Represents C++ object destructor implicitly generated +/// at the end of full expression for temporary object. class CFGTemporaryDtor : public CFGImplicitDtor { public: + CFGTemporaryDtor() {} + CFGTemporaryDtor(CXXBindTemporaryExpr *E) + : CFGImplicitDtor(TemporaryDtor, E, NULL) {} + + CXXBindTemporaryExpr *getBindTemporaryExpr() const { + return static_cast<CXXBindTemporaryExpr *>(Data1.getPointer()); + } + static bool classof(const CFGElement *E) { return E->getKind() == Dtor && E->getDtorKind() == TemporaryDtor; } @@ -495,6 +506,10 @@ public: void appendMemberDtor(FieldDecl *FD, BumpVectorContext &C) { Elements.push_back(CFGMemberDtor(FD), C); } + + void appendTemporaryDtor(CXXBindTemporaryExpr *E, BumpVectorContext &C) { + Elements.push_back(CFGTemporaryDtor(E), C); + } // Destructors must be inserted in reversed order. So insertion is in two // steps. First we prepare space for some number of elements, then we insert diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index ed9899e3f9..5dcbe98c27 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -258,6 +258,8 @@ private: CFGBlock *VisitBlockExpr(BlockExpr* E, AddStmtChoice asc); CFGBlock *VisitBreakStmt(BreakStmt *B); CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S); + CFGBlock *VisitCXXExprWithTemporaries(CXXExprWithTemporaries *E, + AddStmtChoice asc); CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T); CFGBlock *VisitCXXTryStmt(CXXTryStmt *S); CFGBlock *VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, @@ -275,7 +277,7 @@ private: CFGBlock *VisitConditionalOperator(ConditionalOperator *C, AddStmtChoice asc); CFGBlock *VisitContinueStmt(ContinueStmt *C); CFGBlock *VisitDeclStmt(DeclStmt *DS); - CFGBlock *VisitDeclSubExpr(Decl* D); + CFGBlock *VisitDeclSubExpr(DeclStmt* DS); CFGBlock *VisitDefaultStmt(DefaultStmt *D); CFGBlock *VisitDoStmt(DoStmt *D); CFGBlock *VisitForStmt(ForStmt *F); @@ -300,6 +302,16 @@ private: CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc); CFGBlock *VisitChildren(Stmt* S); + // Visitors to walk an AST and generate destructors of temporaries in + // full expression. + CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary = false); + CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E); + CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E); + CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(CXXBindTemporaryExpr *E, + bool BindToTemporary); + CFGBlock *VisitConditionalOperatorForTemporaryDtors(ConditionalOperator *E, + bool BindToTemporary); + // NYS == Not Yet Supported CFGBlock* NYS() { badCFG = true; @@ -340,6 +352,9 @@ private: void appendMemberDtor(CFGBlock *B, FieldDecl *FD) { B->appendMemberDtor(FD, cfg->getBumpVectorContext()); } + void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E) { + B->appendTemporaryDtor(E, cfg->getBumpVectorContext()); + } void insertAutomaticObjDtors(CFGBlock* Blk, CFGBlock::iterator I, LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt* S); @@ -498,18 +513,38 @@ CFGBlock *CFGBuilder::addInitializer(CXXBaseOrMemberInitializer *I) { if (!BuildOpts.AddInitializers) return Block; - autoCreateBlock(); - appendInitializer(Block, I); + bool IsReference = false; + bool HasTemporaries = false; - if (Expr *Init = I->getInit()) { - AddStmtChoice::Kind K = AddStmtChoice::NotAlwaysAdd; + // Destructors of temporaries in initialization expression should be called + // after initialization finishes. + Expr *Init = I->getInit(); + if (Init) { if (FieldDecl *FD = I->getMember()) - if (FD->getType()->isReferenceType()) - K = AddStmtChoice::AsLValueNotAlwaysAdd; + IsReference = FD->getType()->isReferenceType(); + HasTemporaries = isa<CXXExprWithTemporaries>(Init); - return Visit(Init, AddStmtChoice(K)); + if (BuildOpts.AddImplicitDtors && HasTemporaries) { + // Generate destructors for temporaries in initialization expression. + VisitForTemporaryDtors(cast<CXXExprWithTemporaries>(Init)->getSubExpr(), + IsReference); + } } - + + autoCreateBlock(); + appendInitializer(Block, I); + + if (Init) { + AddStmtChoice asc = IsReference + ? AddStmtChoice::AsLValueNotAlwaysAdd + : AddStmtChoice::NotAlwaysAdd; + if (HasTemporaries) + // For expression with temporaries go directly to subexpression to omit + // generating destructors for the second time. + return Visit(cast<CXXExprWithTemporaries>(Init)->getSubExpr(), asc); + return Visit(Init, asc); + } + return Block; } @@ -763,11 +798,8 @@ tryAgain: case Stmt::CXXCatchStmtClass: return VisitCXXCatchStmt(cast<CXXCatchStmt>(S)); - case Stmt::CXXExprWithTemporariesClass: { - // FIXME: Handle temporaries. For now, just visit the subexpression - // so we don't artificially create extra blocks. - return Visit(cast<CXXExprWithTemporaries>(S)->getSubExpr(), asc); - } + case Stmt::CXXExprWithTemporariesClass: + return VisitCXXExprWithTemporaries(cast<CXXExprWithTemporaries>(S), asc); case Stmt::CXXBindTemporaryExprClass: return VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), asc); @@ -1184,12 +1216,8 @@ CFGBlock *CFGBuilder::VisitConditionalOperator(ConditionalOperator *C, } CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) { - autoCreateBlock(); - - if (DS->isSingleDecl()) { - AppendStmt(Block, DS); - return VisitDeclSubExpr(DS->getSingleDecl()); - } + if (DS->isSingleDecl()) + return VisitDeclSubExpr(DS); CFGBlock *B = 0; @@ -1210,30 +1238,55 @@ CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) { DeclStmt *DSNew = new (Mem) DeclStmt(DG, D->getLocation(), GetEndLoc(D)); // Append the fake DeclStmt to block. - AppendStmt(Block, DSNew); - B = VisitDeclSubExpr(D); + B = VisitDeclSubExpr(DSNew); } return B; } /// VisitDeclSubExpr - Utility method to add block-level expressions for -/// initializers in Decls. -CFGBlock *CFGBuilder::VisitDeclSubExpr(Decl* D) { - assert(Block); +/// DeclStmts and initializers in them. +CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt* DS) { + assert(DS->isSingleDecl() && "Can handle single declarations only."); - VarDecl *VD = dyn_cast<VarDecl>(D); + VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); - if (!VD) + if (!VD) { + autoCreateBlock(); + AppendStmt(Block, DS); return Block; + } + bool IsReference = false; + bool HasTemporaries = false; + + // Destructors of temporaries in initialization expression should be called + // after initialization finishes. Expr *Init = VD->getInit(); + if (Init) { + IsReference = VD->getType()->isReferenceType(); + HasTemporaries = isa<CXXExprWithTemporaries>(Init); + + if (BuildOpts.AddImplicitDtors && HasTemporaries) { + // Generate destructors for temporaries in initialization expression. + VisitForTemporaryDtors(cast<CXXExprWithTemporaries>(Init)->getSubExpr(), + IsReference); + } + } + + autoCreateBlock(); + AppendStmt(Block, DS); if (Init) { - AddStmtChoice::Kind k = - VD->getType()->isReferenceType() ? AddStmtChoice::AsLValueNotAlwaysAdd - : AddStmtChoice::NotAlwaysAdd; - Visit(Init, AddStmtChoice(k)); + AddStmtChoice asc = IsReference + ? AddStmtChoice::AsLValueNotAlwaysAdd + : AddStmtChoice::NotAlwaysAdd; + if (HasTemporaries) + // For expression with temporaries go directly to subexpression to omit + // generating destructors for the second time. + Visit(cast<CXXExprWithTemporaries>(Init)->getSubExpr(), asc); + else + Visit(Init, asc); } // If the type of VD is a VLA, then we must process its size expressions. @@ -2305,6 +2358,22 @@ CFGBlock* CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt* CS) { return CatchBlock; } +CFGBlock *CFGBuilder::VisitCXXExprWithTemporaries(CXXExprWithTemporaries *E, + AddStmtChoice asc) { + if (BuildOpts.AddImplicitDtors) { + // If adding implicit destructors visit the full expression for adding + // destructors of temporaries. + VisitForTemporaryDtors(E->getSubExpr()); + + // Full expression has to be added as CFGStmt so it will be sequenced + // before destructors of it's temporaries. + asc = asc.asLValue() + ? AddStmtChoice::AlwaysAddAsLValue + : AddStmtChoice::AlwaysAdd; + } + return Visit(E->getSubExpr(), asc); +} + CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, AddStmtChoice asc) { if (asc.alwaysAdd()) { @@ -2389,6 +2458,196 @@ CFGBlock* CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt* I) { return addStmt(I->getTarget()); } +CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary) { +tryAgain: + if (!E) { + badCFG = true; + return NULL; + } + switch (E->getStmtClass()) { + default: + return VisitChildrenForTemporaryDtors(E); + + case Stmt::BinaryOperatorClass: + return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E)); + + case Stmt::CXXBindTemporaryExprClass: + return VisitCXXBindTemporaryExprForTemporaryDtors( + cast<CXXBindTemporaryExpr>(E), BindToTemporary); + + case Stmt::ConditionalOperatorClass: + return VisitConditionalOperatorForTemporaryDtors( + cast<ConditionalOperator>(E), BindToTemporary); + + case Stmt::ImplicitCastExprClass: + // For implicit cast we want BindToTemporary to be passed further. + E = cast<CastExpr>(E)->getSubExpr(); + goto tryAgain; + + case Stmt::ParenExprClass: + E = cast<ParenExpr>(E)->getSubExpr(); + goto tryAgain; + } +} + +CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E) { + // When visiting children for destructors we want to visit them in reverse + // order. Because there's no reverse iterator for children must to reverse + // them in helper vector. + typedef llvm::SmallVector<Stmt *, 4> ChildrenVect; + ChildrenVect ChildrenRev; + for (Stmt::child_iterator I = E->child_begin(), L = E->child_end(); + I != L; ++I) { + if (*I) ChildrenRev.push_back(*I); + } + + CFGBlock *B = Block; + for (ChildrenVect::reverse_iterator I = ChildrenRev.rbegin(), + L = ChildrenRev.rend(); I != L; ++I) { + if (CFGBlock *R = VisitForTemporaryDtors(*I)) + B = R; + } + return B; +} + +CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E) { + if (E->isLogicalOp()) { + // Destructors for temporaries in LHS expression should be called after + // those for RHS expression. Even if this will unnecessarily create a block, + // this block will be used at least by the full expression. + autoCreateBlock(); + CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getLHS()); + if (badCFG) + return NULL; + + Succ = ConfluenceBlock; + Block = NULL; + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS()); + + if (RHSBlock) { + if (badCFG) + return NULL; + + // If RHS expression did produce destructors we need to connect created + // blocks to CFG in same manner as for binary operator itself. + CFGBlock *LHSBlock = createBlock(false); + LHSBlock->setTerminator(CFGTerminator(E, true)); + + // For binary operator LHS block is before RHS in list of predecessors + // of ConfluenceBlock. + std::reverse(ConfluenceBlock->pred_begin(), + ConfluenceBlock->pred_end()); + + // See if this is a known constant. + TryResult KnownVal = TryEvaluateBool(E->getLHS()); + if (KnownVal.isKnown() && (E->getOpcode() == BO_LOr)) + KnownVal.negate(); + + // Link LHSBlock with RHSBlock exactly the same way as for binary operator + // itself. + if (E->getOpcode() == BO_LOr) { + AddSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); + AddSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); + } else { + assert (E->getOpcode() == BO_LAnd); + AddSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); + AddSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); + } + + Block = LHSBlock; + return LHSBlock; + } + + Block = ConfluenceBlock; + return ConfluenceBlock; + } + + else if (E->isAssignmentOp()) { + // For assignment operator (=) LHS expression is visited + // before RHS expression. For destructors visit them in reverse order. + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS()); + CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS()); + return LHSBlock ? LHSBlock : RHSBlock; + } + + // For any other binary operator RHS expression is visited before + // LHS expression (order of children). For destructors visit them in reverse + // order. + CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS()); + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS()); + return RHSBlock ? RHSBlock : LHSBlock; +} + +CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors( + CXXBindTemporaryExpr *E, bool BindToTemporary) { + // First add destructors for temporaries in subexpression. + CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr()); + if (!BindToTemporary) { + // If lifetime of temporary is not prolonged (by assigning to constant + // reference) add destructor for it. + autoCreateBlock(); + appendTemporaryDtor(Block, E); + B = Block; + } + return B; +} + +CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors( + ConditionalOperator *E, bool BindToTemporary) { + // First add destructors for condition expression. Even if this will + // unnecessarily create a block, this block will be used at least by the full + // expression. + autoCreateBlock(); + CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getCond()); + if (badCFG) + return NULL; + + // Try to add block with destructors for LHS expression. + CFGBlock *LHSBlock = NULL; + if (E->getLHS()) { + Succ = ConfluenceBlock; + Block = NULL; + LHSBlock = VisitForTemporaryDtors(E->getLHS(), BindToTemporary); + if (badCFG) + return NULL; + } + + // Try to add block with destructors for RHS expression; + Succ = ConfluenceBlock; + Block = NULL; + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS(), BindToTemporary); + if (badCFG) + return NULL; + + if (!RHSBlock && !LHSBlock) { + // If neither LHS nor RHS expression had temporaries to destroy don't create + // more blocks. + Block = ConfluenceBlock; + return Block; + } + + Block = createBlock(false); + Block->setTerminator(CFGTerminator(E, true)); + + // See if this is a known constant. + const TryResult &KnownVal = TryEvaluateBool(E->getCond()); + + if (LHSBlock) { + AddSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock); + } else if (KnownVal.isFalse()) { + AddSuccessor(Block, NULL); + } else { + AddSuccessor(Block, ConfluenceBlock); + std::reverse(ConfluenceBlock->pred_begin(), ConfluenceBlock->pred_end()); + } + + if (!RHSBlock) + RHSBlock = ConfluenceBlock; + AddSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock); + + return Block; +} + } // end anonymous namespace /// createBlock - Constructs and adds a new CFGBlock to the CFG. The block has @@ -2828,6 +3087,11 @@ static void print_elem(llvm::raw_ostream &OS, StmtPrinterHelper* Helper, OS << "this->" << FD->getName(); OS << ".~" << T->getAsCXXRecordDecl()->getName() << "()"; OS << " (Member object destructor)\n"; + + } else if (CFGTemporaryDtor TE = E.getAs<CFGTemporaryDtor>()) { + CXXBindTemporaryExpr *BT = TE.getBindTemporaryExpr(); + OS << "~" << BT->getType()->getAsCXXRecordDecl()->getName() << "()"; + OS << " (Temporary object destructor)\n"; } } diff --git a/test/Analysis/temp-obj-dtors-cfg-output.cpp b/test/Analysis/temp-obj-dtors-cfg-output.cpp new file mode 100644 index 0000000000..488a54e691 --- /dev/null +++ b/test/Analysis/temp-obj-dtors-cfg-output.cpp @@ -0,0 +1,581 @@ +// RUN: %clang_cc1 -analyze -cfg-dump -cfg-add-implicit-dtors -cfg-add-initializers %s 2>&1 | FileCheck %s +// XPASS: * + +class A { +public: + A() {} + ~A() {} + + static A make() { return A(); } + + operator bool() { return false; } + operator int() { return 0; } +}; + +class B { +public: + B() {} + ~B() {} + + operator bool() { return true; } + operator int() { return 1; } + operator A() { return A(); } +}; + +void foo(int); +void foo(bool); +void foo(const A&); + +void test_binary() { + int a = int(A()) + int(B()); + foo(int(A()) + int(B())); + int b; +} + +void test_and() { + bool a = A() && B(); + foo(A() && B()); + int b; +} + +void test_or() { + bool a = A() || B(); + foo(A() || B()); + int b; +} + +void test_cond() { + A a = B() ? A() : A(B()); + if (B()) { foo(0); } else { foo(0); } + int b; +} + +void test_cond_cref() { + const A& a = B() ? A() : A(B()); + foo(B() ? A() : A(B())); + int b; +} + +void test_cond_implicit() { + A a = A() ?: A(); + int b; +} + +void test_cond_implicit_cref() { + const A& a = A() ?: A(); + foo(A() ?: A()); + int b; +} + +void test_copy_init() { + A a = A(); + int b; +} + +void test_cref_init() { + const A& a = A(); + foo(A()); + int b; +} + +void test_call_copy_init() { + A a = A::make(); + int b; +} + +void test_call_cref_init() { + const A& a = A::make(); + foo(A::make()); + int b; +} + +void test_assign() { + int a; + a = A(); + int b; +} + +class TestCtorInits { + int a; + int b; +public: + TestCtorInits(); +}; + +TestCtorInits::TestCtorInits() + : a(int(A()) + int(B())) + , b() {} + +// CHECK: [ B2 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B1 +// CHECK: [ B1 ] +// CHECK: 1: A() +// CHECK: 2: [B1.1].operator int() +// CHECK: 3: B() +// CHECK: 4: [B1.3].operator int() +// CHECK: 5: int a = int(A().operator int()) + int(B().operator int()); +// CHECK: 6: ~B() (Temporary object destructor) +// CHECK: 7: ~A() (Temporary object destructor) +// CHECK: 8: A() +// CHECK: 9: [B1.8].operator int() +// CHECK: 10: B() +// CHECK: 11: [B1.10].operator int() +// CHECK: 12: foo(int([B1.9]) + int([B1.11])) +// CHECK: 13: ~B() (Temporary object destructor) +// CHECK: 14: ~A() (Temporary object destructor) +// CHECK: 15: int b; +// CHECK: Predecessors (1): B2 +// CHECK: Successors (1): B0 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B10 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B8 +// CHECK: [ B1 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: int b; +// CHECK: Predecessors (2): B2 B3 +// CHECK: Successors (1): B0 +// CHECK: [ B2 ] +// CHECK: 1: ~B() (Temporary object destructor) +// CHECK: Predecessors (1): B3 +// CHECK: Successors (1): B1 +// CHECK: [ B3 ] +// CHECK: 1: [B4.3] && [B5.2] +// CHECK: 2: foo([B3.1]) +// CHECK: T: [B4.3] && ... +// CHECK: Predecessors (2): B5 B4 +// CHECK: Successors (2): B2 B1 +// CHECK: [ B4 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: A() +// CHECK: 3: [B4.2].operator _Bool() +// CHECK: T: [B4.3] && ... +// CHECK: Predecessors (2): B6 B7 +// CHECK: Successors (2): B5 B3 +// CHECK: [ B5 ] +// CHECK: 1: B() +// CHECK: 2: [B5.1].operator _Bool() +// CHECK: Predecessors (1): B4 +// CHECK: Successors (1): B3 +// CHECK: [ B6 ] +// CHECK: 1: ~B() (Temporary object destructor) +// CHECK: Predecessors (1): B7 +// CHECK: Successors (1): B4 +// CHECK: [ B7 ] +// CHECK: 1: [B8.2] && [B9.2] +// CHECK: 2: bool a = A().operator _Bool() && B().operator _Bool(); +// CHECK: T: [B8.2] && ... +// CHECK: Predecessors (2): B9 B8 +// CHECK: Successors (2): B6 B4 +// CHECK: [ B8 ] +// CHECK: 1: A() +// CHECK: 2: [B8.1].operator _Bool() +// CHECK: T: [B8.2] && ... +// CHECK: Predecessors (1): B10 +// CHECK: Successors (2): B9 B7 +// CHECK: [ B9 ] +// CHECK: 1: B() +// CHECK: 2: [B9.1].operator _Bool() +// CHECK: Predecessors (1): B8 +// CHECK: Successors (1): B7 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B10 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B8 +// CHECK: [ B1 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: int b; +// CHECK: Predecessors (2): B2 B3 +// CHECK: Successors (1): B0 +// CHECK: [ B2 ] +// CHECK: 1: ~B() (Temporary object destructor) +// CHECK: Predecessors (1): B3 +// CHECK: Successors (1): B1 +// CHECK: [ B3 ] +// CHECK: 1: [B4.3] || [B5.2] +// CHECK: 2: foo([B3.1]) +// CHECK: T: [B4.3] || ... +// CHECK: Predecessors (2): B5 B4 +// CHECK: Successors (2): B1 B2 +// CHECK: [ B4 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: A() +// CHECK: 3: [B4.2].operator _Bool() +// CHECK: T: [B4.3] || ... +// CHECK: Predecessors (2): B6 B7 +// CHECK: Successors (2): B3 B5 +// CHECK: [ B5 ] +// CHECK: 1: B() +// CHECK: 2: [B5.1].operator _Bool() +// CHECK: Predecessors (1): B4 +// CHECK: Successors (1): B3 +// CHECK: [ B6 ] +// CHECK: 1: ~B() (Temporary object destructor) +// CHECK: Predecessors (1): B7 +// CHECK: Successors (1): B4 +// CHECK: [ B7 ] +// CHECK: 1: [B8.2] || [B9.2] +// CHECK: 2: bool a = A().operator _Bool() || B().operator _Bool(); +// CHECK: T: [B8.2] || ... +// CHECK: Predecessors (2): B9 B8 +// CHECK: Successors (2): B4 B6 +// CHECK: [ B8 ] +// CHECK: 1: A() +// CHECK: 2: [B8.1].operator _Bool() +// CHECK: T: [B8.2] || ... +// CHECK: Predecessors (1): B10 +// CHECK: Successors (2): B7 B9 +// CHECK: [ B9 ] +// CHECK: 1: B() +// CHECK: 2: [B9.1].operator _Bool() +// CHECK: Predecessors (1): B8 +// CHECK: Successors (1): B7 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B11 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B10 +// CHECK: [ B1 ] +// CHECK: 1: int b; +// CHECK: 2: [B7.3].~A() (Implicit destructor) +// CHECK: Predecessors (2): B2 B3 +// CHECK: Successors (1): B0 +// CHECK: [ B2 ] +// CHECK: 1: foo(0) +// CHECK: Predecessors (1): B4 +// CHECK: Successors (1): B1 +// CHECK: [ B3 ] +// CHECK: 1: foo(0) +// CHECK: Predecessors (1): B4 +// CHECK: Successors (1): B1 +// CHECK: [ B4 ] +// CHECK: 1: ~B() (Temporary object destructor) +// CHECK: 2: B() +// CHECK: 3: [B4.2].operator _Bool() +// CHECK: 4: ~B() (Temporary object destructor) +// CHECK: T: if [B4.3] +// CHECK: Predecessors (2): B5 B6 +// CHECK: Successors (2): B3 B2 +// CHECK: [ B5 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: ~A() (Temporary object destructor) +// CHECK: Predecessors (1): B7 +// CHECK: Successors (1): B4 +// CHECK: [ B6 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: ~A() (Temporary object destructor) +// CHECK: 3: ~A() (Temporary object destructor) +// CHECK: 4: ~B() (Temporary object destructor) +// CHECK: Predecessors (1): B7 +// CHECK: Successors (1): B4 +// CHECK: [ B7 ] +// CHECK: 1: [B10.2] ? [B8.3] : [B9.5] +// CHECK: 2: [B7.1] +// CHECK: 3: A a = B().operator _Bool() ? A() : A(B().operator A()); +// CHECK: T: [B10.2] ? ... : ... +// CHECK: Predecessors (2): B8 B9 +// CHECK: Successors (2): B5 B6 +// CHECK: [ B8 ] +// CHECK: 1: A() +// CHECK: 2: [B8.1] +// CHECK: 3: [B8.2] (BindTemporary) +// CHECK: Predecessors (1): B10 +// CHECK: Successors (1): B7 +// CHECK: [ B9 ] +// CHECK: 1: B() +// CHECK: 2: [B9.1].operator A() +// CHECK: 3: [B9.2] +// CHECK: 4: A([B9.3]) +// CHECK: 5: [B9.4] (BindTemporary) +// CHECK: Predecessors (1): B10 +// CHECK: Successors (1): B7 +// CHECK: [ B10 ] +// CHECK: 1: B() +// CHECK: 2: [B10.1].operator _Bool() +// CHECK: T: [B10.2] ? ... : ... +// CHECK: Predecessors (1): B11 +// CHECK: Successors (2): B8 B9 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B14 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B13 +// CHECK: [ B1 ] +// CHECK: 1: ~B() (Temporary object destructor) +// CHECK: 2: int b; +// CHECK: 3: [B10.2].~A() (Implicit destructor) +// CHECK: Predecessors (2): B2 B3 +// CHECK: Successors (1): B0 +// CHECK: [ B2 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: ~A() (Temporary object destructor) +// CHECK: Predecessors (1): B4 +// CHECK: Successors (1): B1 +// CHECK: [ B3 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: ~A() (Temporary object destructor) +// CHECK: 3: ~A() (Temporary object destructor) +// CHECK: 4: ~B() (Temporary object destructor) +// CHECK: Predecessors (1): B4 +// CHECK: Successors (1): B1 +// CHECK: [ B4 ] +// CHECK: 1: [B7.3] ? [B5.3] : [B6.5] +// CHECK: 2: foo([B4.1]) +// CHECK: T: [B7.3] ? ... : ... +// CHECK: Predecessors (2): B5 B6 +// CHECK: Successors (2): B2 B3 +// CHECK: [ B5 ] +// CHECK: 1: A() +// CHECK: 2: [B5.1] +// CHECK: 3: [B5.2] (BindTemporary) +// CHECK: Predecessors (1): B7 +// CHECK: Successors (1): B4 +// CHECK: [ B6 ] +// CHECK: 1: B() +// CHECK: 2: [B6.1].operator A() +// CHECK: 3: [B6.2] +// CHECK: 4: A([B6.3]) +// CHECK: 5: [B6.4] (BindTemporary) +// CHECK: Predecessors (1): B7 +// CHECK: Successors (1): B4 +// CHECK: [ B7 ] +// CHECK: 1: ~B() (Temporary object destructor) +// CHECK: 2: B() +// CHECK: 3: [B7.2].operator _Bool() +// CHECK: T: [B7.3] ? ... : ... +// CHECK: Predecessors (2): B8 B9 +// CHECK: Successors (2): B5 B6 +// CHECK: [ B8 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: Predecessors (1): B10 +// CHECK: Successors (1): B7 +// CHECK: [ B9 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: ~A() (Temporary object destructor) +// CHECK: 3: ~B() (Temporary object destructor) +// CHECK: Predecessors (1): B10 +// CHECK: Successors (1): B7 +// CHECK: [ B10 ] +// CHECK: 1: [B13.2] ? [B11.3] : [B12.5] +// CHECK: 2: const A &a = B().operator _Bool() ? A() : A(B().operator A()); +// CHECK: T: [B13.2] ? ... : ... +// CHECK: Predecessors (2): B11 B12 +// CHECK: Successors (2): B8 B9 +// CHECK: [ B11 ] +// CHECK: 1: A() +// CHECK: 2: [B11.1] +// CHECK: 3: [B11.2] (BindTemporary) +// CHECK: Predecessors (1): B13 +// CHECK: Successors (1): B10 +// CHECK: [ B12 ] +// CHECK: 1: B() +// CHECK: 2: [B12.1].operator A() +// CHECK: 3: [B12.2] +// CHECK: 4: A([B12.3]) +// CHECK: 5: [B12.4] (BindTemporary) +// CHECK: Predecessors (1): B13 +// CHECK: Successors (1): B10 +// CHECK: [ B13 ] +// CHECK: 1: B() +// CHECK: 2: [B13.1].operator _Bool() +// CHECK: T: [B13.2] ? ... : ... +// CHECK: Predecessors (1): B14 +// CHECK: Successors (2): B11 B12 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B6 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B5 +// CHECK: [ B1 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: int b; +// CHECK: 3: [B3.3].~A() (Implicit destructor) +// CHECK: Predecessors (2): B3 B2 +// CHECK: Successors (1): B0 +// CHECK: [ B2 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: ~A() (Temporary object destructor) +// CHECK: Predecessors (1): B3 +// CHECK: Successors (1): B1 +// CHECK: [ B3 ] +// CHECK: 1: [B5.2] ?: [B4.3] +// CHECK: 2: [B3.1] +// CHECK: 3: A a = A().operator _Bool() ?: A(); +// CHECK: T: [B5.2] ? ... : ... +// CHECK: Predecessors (2): B5 B4 +// CHECK: Successors (2): B1 B2 +// CHECK: [ B4 ] +// CHECK: 1: A() +// CHECK: 2: [B4.1] +// CHECK: 3: [B4.2] (BindTemporary) +// CHECK: Predecessors (1): B5 +// CHECK: Successors (1): B3 +// CHECK: [ B5 ] +// CHECK: 1: A() +// CHECK: 2: [B5.1].operator _Bool() +// CHECK: T: [B5.2] ? ... : ... +// CHECK: Predecessors (1): B6 +// CHECK: Successors (2): B3 B4 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B10 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B9 +// CHECK: [ B1 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: int b; +// CHECK: 3: [B7.2].~A() (Implicit destructor) +// CHECK: Predecessors (2): B3 B2 +// CHECK: Successors (1): B0 +// CHECK: [ B2 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: ~A() (Temporary object destructor) +// CHECK: Predecessors (1): B3 +// CHECK: Successors (1): B1 +// CHECK: [ B3 ] +// CHECK: 1: [B5.3] ?: [B4.3] +// CHECK: 2: foo([B3.1]) +// CHECK: T: [B5.3] ? ... : ... +// CHECK: Predecessors (2): B5 B4 +// CHECK: Successors (2): B1 B2 +// CHECK: [ B4 ] +// CHECK: 1: A() +// CHECK: 2: [B4.1] +// CHECK: 3: [B4.2] (BindTemporary) +// CHECK: Predecessors (1): B5 +// CHECK: Successors (1): B3 +// CHECK: [ B5 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: 2: A() +// CHECK: 3: [B5.2].operator _Bool() +// CHECK: T: [B5.3] ? ... : ... +// CHECK: Predecessors (2): B7 B6 +// CHECK: Successors (2): B3 B4 +// CHECK: [ B6 ] +// CHECK: 1: ~A() (Temporary object destructor) +// CHECK: Predecessors (1): B7 +// CHECK: Successors (1): B5 +// CHECK: [ B7 ] +// CHECK: 1: [B9.2] ?: [B8.3] +// CHECK: 2: const A &a = A().operator _Bool() ?: A(); +// CHECK: T: [B9.2] ? ... : ... +// CHECK: Predecessors (2): B9 B8 +// CHECK: Successors (2): B5 B6 +// CHECK: [ B8 ] +// CHECK: 1: A() +// CHECK: 2: [B8.1] +// CHECK: 3: [B8.2] (BindTemporary) +// CHECK: Predecessors (1): B9 +// CHECK: Successors (1): B7 +// CHECK: [ B9 ] +// CHECK: 1: A() +// CHECK: 2: [B9.1].operator _Bool() +// CHECK: T: [B9.2] ? ... : ... +// CHECK: Predecessors (1): B10 +// CHECK: Successors (2): B7 B8 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B2 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B1 +// CHECK: [ B1 ] +// CHECK: 1: A() +// CHECK: 2: [B1.1] +// CHECK: 3: A a = A(); +// CHECK: 4: ~A() (Temporary object destructor) +// CHECK: 5: int b; +// CHECK: 6: [B1.3].~A() (Implicit destructor) +// CHECK: Predecessors (1): B2 +// CHECK: Successors (1): B0 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B2 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B1 +// CHECK: [ B1 ] +// CHECK: 1: A() +// CHECK: 2: const A &a = A(); +// CHECK: 3: A() +// CHECK: 4: foo([B1.3]) +// CHECK: 5: ~A() (Temporary object destructor) +// CHECK: 6: int b; +// CHECK: 7: [B1.2].~A() (Implicit destructor) +// CHECK: Predecessors (1): B2 +// CHECK: Successors (1): B0 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B2 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B1 +// CHECK: [ B1 ] +// CHECK: 1: A::make() +// CHECK: 2: [B1.1] +// CHECK: 3: A a = A::make(); +// CHECK: 4: ~A() (Temporary object destructor) +// CHECK: 5: int b; +// CHECK: 6: [B1.3].~A() (Implicit destructor) +// CHECK: Predecessors (1): B2 +// CHECK: Successors (1): B0 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B2 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B1 +// CHECK: [ B1 ] +// CHECK: 1: A::make() +// CHECK: 2: const A &a = A::make(); +// CHECK: 3: A::make() +// CHECK: 4: foo([B1.3]) +// CHECK: 5: ~A() (Temporary object destructor) +// CHECK: 6: int b; +// CHECK: 7: [B1.2].~A() (Implicit destructor) +// CHECK: Predecessors (1): B2 +// CHECK: Successors (1): B0 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B2 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B1 +// CHECK: [ B1 ] +// CHECK: 1: int a; +// CHECK: 2: A() +// CHECK: 3: [B1.2].operator int() +// CHECK: 4: a = [B1.3] +// CHECK: 5: ~A() (Temporary object destructor) +// CHECK: 6: int b; +// CHECK: Predecessors (1): B2 +// CHECK: Successors (1): B0 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): +// CHECK: [ B2 (ENTRY) ] +// CHECK: Predecessors (0): +// CHECK: Successors (1): B1 +// CHECK: [ B1 ] +// CHECK: 1: A() +// CHECK: 2: [B1.1].operator int() +// CHECK: 3: B() +// CHECK: 4: [B1.3].operator int() +// CHECK: 5: a(int([B1.2]) + int([B1.4])) (Member initializer) +// CHECK: 6: ~B() (Temporary object destructor) +// CHECK: 7: ~A() (Temporary object destructor) +// CHECK: 8: b(/*implicit*/int()) (Member initializer) +// CHECK: Predecessors (1): B2 +// CHECK: Successors (1): B0 +// CHECK: [ B0 (EXIT) ] +// CHECK: Predecessors (1): B1 +// CHECK: Successors (0): |