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,
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);
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;
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);
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;
}
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);
}
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;
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.
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()) {
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
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";
}
}
--- /dev/null
+// 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):