[cfe-commits] r118158 - in /cfe/trunk: include/clang/Analysis/CFG.h lib/Analysis/CFG.cpp test/Analysis/temp-obj-dtors-cfg-output.cpp

Marcin Swiderski marcin.sfider at gmail.com
Tue Nov 2 23:19:36 PDT 2010


Author: sfider
Date: Wed Nov  3 01:19:35 2010
New Revision: 118158

URL: http://llvm.org/viewvc/llvm-project?rev=118158&view=rev
Log:
Added generating destructors for temporary objects. Two cases I know of, that are not handled properly:
1. For statement: const C& c = C(0) ?: C(1) destructors generated for condition will not differ from those generated for case without prolonged lifetime of temporary,
2. There will be no destructor for constant reference member bound to temporary at the exit from constructor.

Added:
    cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp
Modified:
    cfe/trunk/include/clang/Analysis/CFG.h
    cfe/trunk/lib/Analysis/CFG.cpp

Modified: cfe/trunk/include/clang/Analysis/CFG.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CFG.h?rev=118158&r1=118157&r2=118158&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/CFG.h (original)
+++ cfe/trunk/include/clang/Analysis/CFG.h Wed Nov  3 01:19:35 2010
@@ -35,6 +35,7 @@
   class VarDecl;
   class CXXBaseOrMemberInitializer;
   class CXXBaseSpecifier;
+  class CXXBindTemporaryExpr;
   class CFG;
   class PrinterHelper;
   class LangOptions;
@@ -198,8 +199,18 @@
   }
 };
 
+/// 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 @@
   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

Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=118158&r1=118157&r2=118158&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Wed Nov  3 01:19:35 2010
@@ -258,6 +258,8 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
     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::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 @@
     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 @@
   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 @@
   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 @@
     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";
   }
 }
 

Added: cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp?rev=118158&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp (added)
+++ cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp Wed Nov  3 01:19:35 2010
@@ -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):





More information about the cfe-commits mailing list