r307759 - CFG: Add CFGElement for automatic variables that leave the scope

Matthias Gehre via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 12 00:04:19 PDT 2017


Author: mgehre
Date: Wed Jul 12 00:04:19 2017
New Revision: 307759

URL: http://llvm.org/viewvc/llvm-project?rev=307759&view=rev
Log:
CFG: Add CFGElement for automatic variables that leave the scope

Summary:
This mimics the implementation for the implicit destructors. The
generation of this scope leaving elements is hidden behind
a flag to the CFGBuilder, thus it should not affect existing code.

Currently, I'm missing a test (it's implicitly tested by the clang-tidy
lifetime checker that I'm proposing).
I though about a test using debug.DumpCFG, but then I would
have to add an option to StaticAnalyzer/Core/AnalyzerOptions
to enable the scope leaving CFGElement,
which would only be useful to that particular test.

Any other ideas how I could make a test for this feature?

Reviewers: krememek, jordan_rose

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D15031

Added:
    cfe/trunk/test/Analysis/lifetime-cfg-output.cpp
Modified:
    cfe/trunk/include/clang/Analysis/AnalysisContext.h
    cfe/trunk/include/clang/Analysis/CFG.h
    cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
    cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
    cfe/trunk/lib/Analysis/CFG.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
    cfe/trunk/test/Analysis/analyzer-config.c
    cfe/trunk/test/Analysis/analyzer-config.cpp

Modified: cfe/trunk/include/clang/Analysis/AnalysisContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/AnalysisContext.h?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/AnalysisContext.h (original)
+++ cfe/trunk/include/clang/Analysis/AnalysisContext.h Wed Jul 12 00:04:19 2017
@@ -426,6 +426,7 @@ public:
                              bool addImplicitDtors = false,
                              bool addInitializers = false,
                              bool addTemporaryDtors = false,
+                             bool addLifetime = false,
                              bool synthesizeBodies = false,
                              bool addStaticInitBranches = false,
                              bool addCXXNewAllocator = true,

Modified: cfe/trunk/include/clang/Analysis/CFG.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CFG.h?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/CFG.h (original)
+++ cfe/trunk/include/clang/Analysis/CFG.h Wed Jul 12 00:04:19 2017
@@ -58,6 +58,7 @@ public:
     Statement,
     Initializer,
     NewAllocator,
+    LifetimeEnds,
     // dtor kind
     AutomaticObjectDtor,
     DeleteDtor,
@@ -167,6 +168,28 @@ private:
   }
 };
 
+/// Represents the point where the lifetime of an automatic object ends
+class CFGLifetimeEnds : public CFGElement {
+public:
+  explicit CFGLifetimeEnds(const VarDecl *var, const Stmt *stmt)
+      : CFGElement(LifetimeEnds, var, stmt) {}
+
+  const VarDecl *getVarDecl() const {
+    return static_cast<VarDecl *>(Data1.getPointer());
+  }
+
+  const Stmt *getTriggerStmt() const {
+    return static_cast<Stmt *>(Data2.getPointer());
+  }
+
+private:
+  friend class CFGElement;
+  CFGLifetimeEnds() {}
+  static bool isKind(const CFGElement &elem) {
+    return elem.getKind() == LifetimeEnds;
+  }
+};
+
 /// CFGImplicitDtor - Represents C++ object destructor implicitly generated
 /// by compiler on various occasions.
 class CFGImplicitDtor : public CFGElement {
@@ -701,6 +724,10 @@ public:
     Elements.push_back(CFGAutomaticObjDtor(VD, S), C);
   }
 
+  void appendLifetimeEnds(VarDecl *VD, Stmt *S, BumpVectorContext &C) {
+    Elements.push_back(CFGLifetimeEnds(VD, S), C);
+  }
+
   void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) {
     Elements.push_back(CFGDeleteDtor(RD, DE), C);
   }
@@ -717,6 +744,19 @@ public:
     *I = CFGAutomaticObjDtor(VD, S);
     return ++I;
   }
+
+  // Scope leaving must be performed in reversed order. So insertion is in two
+  // steps. First we prepare space for some number of elements, then we insert
+  // the elements beginning at the last position in prepared space.
+  iterator beginLifetimeEndsInsert(iterator I, size_t Cnt,
+                                   BumpVectorContext &C) {
+    return iterator(
+        Elements.insert(I.base(), Cnt, CFGLifetimeEnds(nullptr, nullptr), C));
+  }
+  iterator insertLifetimeEnds(iterator I, VarDecl *VD, Stmt *S) {
+    *I = CFGLifetimeEnds(VD, S);
+    return ++I;
+  }
 };
 
 /// \brief CFGCallback defines methods that should be called when a logical
@@ -753,6 +793,7 @@ public:
     bool AddEHEdges;
     bool AddInitializers;
     bool AddImplicitDtors;
+    bool AddLifetime;
     bool AddTemporaryDtors;
     bool AddStaticInitBranches;
     bool AddCXXNewAllocator;
@@ -774,8 +815,10 @@ public:
 
     BuildOptions()
       : forcedBlkExprs(nullptr), Observer(nullptr),
-        PruneTriviallyFalseEdges(true), AddEHEdges(false),
+        PruneTriviallyFalseEdges(true),
+        AddEHEdges(false),
         AddInitializers(false), AddImplicitDtors(false),
+        AddLifetime(false),
         AddTemporaryDtors(false), AddStaticInitBranches(false),
         AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {}
   };

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h Wed Jul 12 00:04:19 2017
@@ -205,9 +205,15 @@ private:
   /// Controls which C++ member functions will be considered for inlining.
   CXXInlineableMemberKind CXXMemberInliningMode;
   
+  /// \sa includeImplicitDtorsInCFG
+  Optional<bool> IncludeImplicitDtorsInCFG;
+
   /// \sa includeTemporaryDtorsInCFG
   Optional<bool> IncludeTemporaryDtorsInCFG;
-  
+
+  /// \sa IncludeLifetimeInCFG
+  Optional<bool> IncludeLifetimeInCFG;
+
   /// \sa mayInlineCXXStandardLibrary
   Optional<bool> InlineCXXStandardLibrary;
   
@@ -395,6 +401,20 @@ public:
   /// accepts the values "true" and "false".
   bool includeTemporaryDtorsInCFG();
 
+  /// Returns whether or not implicit destructors for C++ objects should
+  /// be included in the CFG.
+  ///
+  /// This is controlled by the 'cfg-implicit-dtors' config option, which
+  /// accepts the values "true" and "false".
+  bool includeImplicitDtorsInCFG();
+
+  /// Returns whether or not end-of-lifetime information should be included in
+  /// the CFG.
+  ///
+  /// This is controlled by the 'cfg-lifetime' config option, which accepts
+  /// the values "true" and "false".
+  bool includeLifetimeInCFG();
+
   /// Returns whether or not C++ standard library functions may be considered
   /// for inlining.
   ///

Modified: cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp (original)
+++ cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp Wed Jul 12 00:04:19 2017
@@ -67,6 +67,7 @@ AnalysisDeclContextManager::AnalysisDecl
                                                        bool addImplicitDtors,
                                                        bool addInitializers,
                                                        bool addTemporaryDtors,
+                                                       bool addLifetime,
                                                        bool synthesizeBodies,
                                                        bool addStaticInitBranch,
                                                        bool addCXXNewAllocator,
@@ -77,6 +78,7 @@ AnalysisDeclContextManager::AnalysisDecl
   cfgBuildOptions.AddImplicitDtors = addImplicitDtors;
   cfgBuildOptions.AddInitializers = addInitializers;
   cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors;
+  cfgBuildOptions.AddLifetime = addLifetime;
   cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch;
   cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
 }

Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Wed Jul 12 00:04:19 2017
@@ -233,6 +233,7 @@ public:
     }
 
     int distance(const_iterator L);
+    const_iterator shared_parent(const_iterator L);
   };
 
   friend class const_iterator;
@@ -275,6 +276,30 @@ int LocalScope::const_iterator::distance
   return D;
 }
 
+/// Calculates the closest parent of this iterator
+/// that is in a scope reachable through the parents of L.
+/// I.e. when using 'goto' from this to L, the lifetime of all variables
+/// between this and shared_parent(L) end.
+LocalScope::const_iterator
+LocalScope::const_iterator::shared_parent(LocalScope::const_iterator L) {
+  llvm::SmallPtrSet<const LocalScope *, 4> ScopesOfL;
+  while (true) {
+    ScopesOfL.insert(L.Scope);
+    if (L == const_iterator())
+      break;
+    L = L.Scope->Prev;
+  }
+
+  const_iterator F = *this;
+  while (true) {
+    if (ScopesOfL.count(F.Scope))
+      return F;
+    assert(F != const_iterator() &&
+           "L iterator is not reachable from F iterator.");
+    F = F.Scope->Prev;
+  }
+}
+
 /// Structure for specifying position in CFG during its build process. It
 /// consists of CFGBlock that specifies position in CFG and
 /// LocalScope::const_iterator that specifies position in LocalScope graph.
@@ -579,6 +604,10 @@ private:
   CFGBlock *addInitializer(CXXCtorInitializer *I);
   void addAutomaticObjDtors(LocalScope::const_iterator B,
                             LocalScope::const_iterator E, Stmt *S);
+  void addLifetimeEnds(LocalScope::const_iterator B,
+                       LocalScope::const_iterator E, Stmt *S);
+  void addAutomaticObjHandling(LocalScope::const_iterator B,
+                               LocalScope::const_iterator E, Stmt *S);
   void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD);
 
   // Local scopes creation.
@@ -619,6 +648,10 @@ private:
     B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());
   }
 
+  void appendLifetimeEnds(CFGBlock *B, VarDecl *VD, Stmt *S) {
+    B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext());
+  }
+
   void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) {
     B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext());
   }
@@ -626,6 +659,10 @@ private:
   void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,
       LocalScope::const_iterator B, LocalScope::const_iterator E);
 
+  void prependAutomaticObjLifetimeWithTerminator(CFGBlock *Blk,
+                                                 LocalScope::const_iterator B,
+                                                 LocalScope::const_iterator E);
+
   void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) {
     B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable),
                     cfg->getBumpVectorContext());
@@ -957,7 +994,8 @@ private:
 
     return TryResult();
   }
-  
+
+  bool hasTrivialDestructor(VarDecl *VD);
 };
 
 inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder,
@@ -1031,6 +1069,9 @@ std::unique_ptr<CFG> CFGBuilder::buildCF
   assert(Succ == &cfg->getExit());
   Block = nullptr;  // the EXIT block is empty.  Create all other blocks lazily.
 
+  assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) &&
+         "AddImplicitDtors and AddLifetime cannot be used at the same time");
+
   if (BuildOpts.AddImplicitDtors)
     if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D))
       addImplicitDtorsForDestructor(DD);
@@ -1067,6 +1108,8 @@ std::unique_ptr<CFG> CFGBuilder::buildCF
     if (LI == LabelMap.end()) continue;
 
     JumpTarget JT = LI->second;
+    prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition,
+                                              JT.scopePosition);
     prependAutomaticObjDtorsWithTerminator(B, I->scopePosition,
                                            JT.scopePosition);
     addSuccessor(B, JT.block);
@@ -1209,7 +1252,61 @@ static QualType getReferenceInitTemporar
 
   return Init->getType();
 }
-  
+
+void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B,
+                                         LocalScope::const_iterator E,
+                                         Stmt *S) {
+  if (BuildOpts.AddImplicitDtors)
+    addAutomaticObjDtors(B, E, S);
+  if (BuildOpts.AddLifetime)
+    addLifetimeEnds(B, E, S);
+}
+
+/// Add to current block automatic objects that leave the scope.
+void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B,
+                                 LocalScope::const_iterator E, Stmt *S) {
+  if (!BuildOpts.AddLifetime)
+    return;
+
+  if (B == E)
+    return;
+
+  // To go from B to E, one first goes up the scopes from B to P
+  // then sideways in one scope from P to P' and then down
+  // the scopes from P' to E.
+  // The lifetime of all objects between B and P end.
+  LocalScope::const_iterator P = B.shared_parent(E);
+  int dist = B.distance(P);
+  if (dist <= 0)
+    return;
+
+  // We need to perform the scope leaving in reverse order
+  SmallVector<VarDecl *, 10> DeclsTrivial;
+  SmallVector<VarDecl *, 10> DeclsNonTrivial;
+  DeclsTrivial.reserve(dist);
+  DeclsNonTrivial.reserve(dist);
+
+  for (LocalScope::const_iterator I = B; I != P; ++I)
+    if (hasTrivialDestructor(*I))
+      DeclsTrivial.push_back(*I);
+    else
+      DeclsNonTrivial.push_back(*I);
+
+  autoCreateBlock();
+  // object with trivial destructor end their lifetime last (when storage
+  // duration ends)
+  for (SmallVectorImpl<VarDecl *>::reverse_iterator I = DeclsTrivial.rbegin(),
+                                                    E = DeclsTrivial.rend();
+       I != E; ++I)
+    appendLifetimeEnds(Block, *I, S);
+
+  for (SmallVectorImpl<VarDecl *>::reverse_iterator
+           I = DeclsNonTrivial.rbegin(),
+           E = DeclsNonTrivial.rend();
+       I != E; ++I)
+    appendLifetimeEnds(Block, *I, S);
+}
+
 /// addAutomaticObjDtors - Add to current block automatic objects destructors
 /// for objects in range of local scope positions. Use S as trigger statement
 /// for destructors.
@@ -1309,7 +1406,7 @@ LocalScope* CFGBuilder::createOrReuseLoc
 /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement
 /// that should create implicit scope (e.g. if/else substatements). 
 void CFGBuilder::addLocalScopeForStmt(Stmt *S) {
-  if (!BuildOpts.AddImplicitDtors)
+  if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime)
     return;
 
   LocalScope *Scope = nullptr;
@@ -1334,7 +1431,7 @@ void CFGBuilder::addLocalScopeForStmt(St
 /// reuse Scope if not NULL.
 LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS,
                                                  LocalScope* Scope) {
-  if (!BuildOpts.AddImplicitDtors)
+  if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime)
     return Scope;
 
   for (auto *DI : DS->decls())
@@ -1343,23 +1440,7 @@ LocalScope* CFGBuilder::addLocalScopeFor
   return Scope;
 }
 
-/// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will
-/// create add scope for automatic objects and temporary objects bound to
-/// const reference. Will reuse Scope if not NULL.
-LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD,
-                                                LocalScope* Scope) {
-  if (!BuildOpts.AddImplicitDtors)
-    return Scope;
-
-  // Check if variable is local.
-  switch (VD->getStorageClass()) {
-  case SC_None:
-  case SC_Auto:
-  case SC_Register:
-    break;
-  default: return Scope;
-  }
-
+bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) {
   // Check for const references bound to temporary. Set type to pointee.
   QualType QT = VD->getType();
   if (QT.getTypePtr()->isReferenceType()) {
@@ -1370,44 +1451,74 @@ LocalScope* CFGBuilder::addLocalScopeFor
     // temporaries, and a single declaration can extend multiple temporaries.
     // We should look at the storage duration on each nested
     // MaterializeTemporaryExpr instead.
+
     const Expr *Init = VD->getInit();
     if (!Init)
-      return Scope;
+      return true;
 
     // Lifetime-extending a temporary.
     bool FoundMTE = false;
     QT = getReferenceInitTemporaryType(*Context, Init, &FoundMTE);
     if (!FoundMTE)
-      return Scope;
+      return true;
   }
 
   // Check for constant size array. Set type to array element type.
   while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
     if (AT->getSize() == 0)
-      return Scope;
+      return true;
     QT = AT->getElementType();
   }
 
   // Check if type is a C++ class with non-trivial destructor.
   if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
-    if (CD->hasDefinition() && !CD->hasTrivialDestructor()) {
+    return !CD->hasDefinition() || CD->hasTrivialDestructor();
+  return true;
+}
+
+/// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will
+/// create add scope for automatic objects and temporary objects bound to
+/// const reference. Will reuse Scope if not NULL.
+LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD,
+                                                LocalScope* Scope) {
+  assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) &&
+         "AddImplicitDtors and AddLifetime cannot be used at the same time");
+  if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime)
+    return Scope;
+
+  // Check if variable is local.
+  switch (VD->getStorageClass()) {
+  case SC_None:
+  case SC_Auto:
+  case SC_Register:
+    break;
+  default: return Scope;
+  }
+
+  if (BuildOpts.AddImplicitDtors) {
+    if (!hasTrivialDestructor(VD)) {
       // Add the variable to scope
       Scope = createOrReuseLocalScope(Scope);
       Scope->addVar(VD);
       ScopePos = Scope->begin();
     }
+    return Scope;
+  }
+
+  assert(BuildOpts.AddLifetime);
+  // Add the variable to scope
+  Scope = createOrReuseLocalScope(Scope);
+  Scope->addVar(VD);
+  ScopePos = Scope->begin();
   return Scope;
 }
 
 /// addLocalScopeAndDtors - For given statement add local scope for it and
 /// add destructors that will cleanup the scope. Will reuse Scope if not NULL.
 void CFGBuilder::addLocalScopeAndDtors(Stmt *S) {
-  if (!BuildOpts.AddImplicitDtors)
-    return;
-
   LocalScope::const_iterator scopeBeginPos = ScopePos;
   addLocalScopeForStmt(S);
-  addAutomaticObjDtors(ScopePos, scopeBeginPos, S);
+  addAutomaticObjHandling(ScopePos, scopeBeginPos, S);
 }
 
 /// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for
@@ -1419,6 +1530,8 @@ void CFGBuilder::addLocalScopeAndDtors(S
 /// no-return destructors properly.
 void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,
     LocalScope::const_iterator B, LocalScope::const_iterator E) {
+  if (!BuildOpts.AddImplicitDtors)
+    return;
   BumpVectorContext &C = cfg->getBumpVectorContext();
   CFGBlock::iterator InsertPos
     = Blk->beginAutomaticObjDtorsInsert(Blk->end(), B.distance(E), C);
@@ -1427,6 +1540,21 @@ void CFGBuilder::prependAutomaticObjDtor
                                             Blk->getTerminator());
 }
 
+/// prependAutomaticObjLifetimeWithTerminator - Prepend lifetime CFGElements for
+/// variables with automatic storage duration to CFGBlock's elements vector.
+/// Elements will be prepended to physical beginning of the vector which
+/// happens to be logical end. Use blocks terminator as statement that specifies
+/// where lifetime ends.
+void CFGBuilder::prependAutomaticObjLifetimeWithTerminator(
+    CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E) {
+  if (!BuildOpts.AddLifetime)
+    return;
+  BumpVectorContext &C = cfg->getBumpVectorContext();
+  CFGBlock::iterator InsertPos =
+      Blk->beginLifetimeEndsInsert(Blk->end(), B.distance(E), C);
+  for (LocalScope::const_iterator I = B; I != E; ++I)
+    InsertPos = Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminator());
+}
 /// Visit - Walk the subtree of a statement and add extra
 ///   blocks for ternary operators, &&, and ||.  We also process "," and
 ///   DeclStmts (which may contain nested control-flow).
@@ -1815,7 +1943,7 @@ CFGBlock *CFGBuilder::VisitBreakStmt(Bre
   // If there is no target for the break, then we are looking at an incomplete
   // AST.  This means that the CFG cannot be constructed.
   if (BreakJumpTarget.block) {
-    addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B);
+    addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B);
     addSuccessor(Block, BreakJumpTarget.block);
   } else
     badCFG = true;
@@ -1947,13 +2075,12 @@ CFGBlock *CFGBuilder::VisitChooseExpr(Ch
 
 CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
   LocalScope::const_iterator scopeBeginPos = ScopePos;
-  if (BuildOpts.AddImplicitDtors) {
-    addLocalScopeForStmt(C);
-  }
+  addLocalScopeForStmt(C);
+
   if (!C->body_empty() && !isa<ReturnStmt>(*C->body_rbegin())) {
     // If the body ends with a ReturnStmt, the dtors will be added in
     // VisitReturnStmt.
-    addAutomaticObjDtors(ScopePos, scopeBeginPos, C);
+    addAutomaticObjHandling(ScopePos, scopeBeginPos, C);
   }
 
   CFGBlock *LastBlock = Block;
@@ -2183,7 +2310,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt
   if (VarDecl *VD = I->getConditionVariable())
     addLocalScopeForVarDecl(VD);
 
-  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), I);
+  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), I);
 
   // The block we were processing is now finished.  Make it the successor
   // block.
@@ -2308,7 +2435,7 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Re
   // Create the new block.
   Block = createBlock(false);
 
-  addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R);
+  addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R);
 
   // If the one of the destructors does not return, we already have the Exit
   // block as a successor.
@@ -2389,7 +2516,7 @@ CFGBlock *CFGBuilder::VisitGotoStmt(Goto
     BackpatchBlocks.push_back(JumpSource(Block, ScopePos));
   else {
     JumpTarget JT = I->second;
-    addAutomaticObjDtors(ScopePos, JT.scopePosition, G);
+    addAutomaticObjHandling(ScopePos, JT.scopePosition, G);
     addSuccessor(Block, JT.block);
   }
 
@@ -2414,7 +2541,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForSt
     addLocalScopeForVarDecl(VD);
   LocalScope::const_iterator ContinueScopePos = ScopePos;
 
-  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F);
+  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), F);
 
   // "for" is a control-flow statement.  Thus we stop processing the current
   // block.
@@ -2466,7 +2593,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForSt
    ContinueJumpTarget.block->setLoopTarget(F);
 
     // Loop body should end with destructor of Condition variable (if any).
-    addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F);
+   addAutomaticObjHandling(ScopePos, LoopBeginScopePos, F);
 
     // If body is not a compound statement create implicit scope
     // and add destructors.
@@ -2753,7 +2880,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(Whi
   LocalScope::const_iterator LoopBeginScopePos = ScopePos;
   if (VarDecl *VD = W->getConditionVariable()) {
     addLocalScopeForVarDecl(VD);
-    addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);
+    addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W);
   }
 
   // "while" is a control-flow statement.  Thus we stop processing the current
@@ -2788,7 +2915,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(Whi
     BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);
 
     // Loop body should end with destructor of Condition variable (if any).
-    addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);
+    addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W);
 
     // If body is not a compound statement create implicit scope
     // and add destructors.
@@ -3030,7 +3157,7 @@ CFGBlock *CFGBuilder::VisitContinueStmt(
   // If there is no target for the continue, then we are looking at an
   // incomplete AST.  This means the CFG cannot be constructed.
   if (ContinueJumpTarget.block) {
-    addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C);
+    addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C);
     addSuccessor(Block, ContinueJumpTarget.block);
   } else
     badCFG = true;
@@ -3085,7 +3212,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(Sw
   if (VarDecl *VD = Terminator->getConditionVariable())
     addLocalScopeForVarDecl(VD);
 
-  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), Terminator);
+  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), Terminator);
 
   if (Block) {
     if (badCFG)
@@ -3373,7 +3500,7 @@ CFGBlock *CFGBuilder::VisitCXXCatchStmt(
   if (VarDecl *VD = CS->getExceptionDecl()) {
     LocalScope::const_iterator BeginScopePos = ScopePos;
     addLocalScopeForVarDecl(VD);
-    addAutomaticObjDtors(ScopePos, BeginScopePos, CS);
+    addAutomaticObjHandling(ScopePos, BeginScopePos, CS);
   }
 
   if (CS->getHandlerBlock())
@@ -3427,7 +3554,7 @@ CFGBlock *CFGBuilder::VisitCXXForRangeSt
     addLocalScopeForStmt(Begin);
   if (Stmt *End = S->getEndStmt())
     addLocalScopeForStmt(End);
-  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S);
+  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), S);
 
   LocalScope::const_iterator ContinueScopePos = ScopePos;
 
@@ -3898,6 +4025,7 @@ CFGImplicitDtor::getDestructorDecl(ASTCo
     case CFGElement::Statement:
     case CFGElement::Initializer:
     case CFGElement::NewAllocator:
+    case CFGElement::LifetimeEnds:
       llvm_unreachable("getDestructorDecl should only be used with "
                        "ImplicitDtors");
     case CFGElement::AutomaticObjectDtor: {
@@ -4308,6 +4436,12 @@ static void print_elem(raw_ostream &OS,
     OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
     OS << " (Implicit destructor)\n";
 
+  } else if (Optional<CFGLifetimeEnds> DE = E.getAs<CFGLifetimeEnds>()) {
+    const VarDecl *VD = DE->getVarDecl();
+    Helper.handleDecl(VD, OS);
+
+    OS << " (Lifetime ends)\n";
+
   } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) {
     OS << "CFGNewAllocator(";
     if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr())

Modified: cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp Wed Jul 12 00:04:19 2017
@@ -23,9 +23,10 @@ AnalysisManager::AnalysisManager(ASTCont
                                  AnalyzerOptions &Options,
                                  CodeInjector *injector)
   : AnaCtxMgr(Options.UnoptimizedCFG,
-              /*AddImplicitDtors=*/true,
+              Options.includeImplicitDtorsInCFG(),
               /*AddInitializers=*/true,
               Options.includeTemporaryDtorsInCFG(),
+	      Options.includeLifetimeInCFG(),
               Options.shouldSynthesizeBodies(),
               Options.shouldConditionalizeStaticInitializers(),
               /*addCXXNewAllocator=*/true,

Modified: cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp Wed Jul 12 00:04:19 2017
@@ -172,6 +172,17 @@ bool AnalyzerOptions::includeTemporaryDt
                           /* Default = */ false);
 }
 
+bool AnalyzerOptions::includeImplicitDtorsInCFG() {
+  return getBooleanOption(IncludeImplicitDtorsInCFG,
+                          "cfg-implicit-dtors",
+                          /* Default = */ true);
+}
+
+bool AnalyzerOptions::includeLifetimeInCFG() {
+  return getBooleanOption(IncludeLifetimeInCFG, "cfg-lifetime",
+                          /* Default = */ false);
+}
+
 bool AnalyzerOptions::mayInlineCXXStandardLibrary() {
   return getBooleanOption(InlineCXXStandardLibrary,
                           "c++-stdlib-inlining",

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Wed Jul 12 00:04:19 2017
@@ -362,6 +362,8 @@ void ExprEngine::processCFGElement(const
     case CFGElement::TemporaryDtor:
       ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred);
       return;
+    case CFGElement::LifetimeEnds:
+      return;
   }
 }
 

Modified: cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp Wed Jul 12 00:04:19 2017
@@ -578,6 +578,7 @@ getLocationForCaller(const StackFrameCon
   }
   case CFGElement::TemporaryDtor:
   case CFGElement::NewAllocator:
+  case CFGElement::LifetimeEnds:
     llvm_unreachable("not yet implemented!");
   }
 

Modified: cfe/trunk/test/Analysis/analyzer-config.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/analyzer-config.c?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/analyzer-config.c (original)
+++ cfe/trunk/test/Analysis/analyzer-config.c Wed Jul 12 00:04:19 2017
@@ -12,6 +12,8 @@ void foo() {
 
 // CHECK: [config]
 // CHECK-NEXT: cfg-conditional-static-initializers = true
+// CHECK-NEXT: cfg-implicit-dtors = true
+// CHECK-NEXT: cfg-lifetime = false
 // CHECK-NEXT: cfg-temporary-dtors = false
 // CHECK-NEXT: faux-bodies = true
 // CHECK-NEXT: graph-trim-interval = 1000
@@ -27,5 +29,4 @@ void foo() {
 // CHECK-NEXT: region-store-small-struct-limit = 2
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 15
-
+// CHECK-NEXT: num-entries = 17

Modified: cfe/trunk/test/Analysis/analyzer-config.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/analyzer-config.cpp?rev=307759&r1=307758&r2=307759&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/analyzer-config.cpp (original)
+++ cfe/trunk/test/Analysis/analyzer-config.cpp Wed Jul 12 00:04:19 2017
@@ -23,6 +23,8 @@ public:
 // CHECK-NEXT: c++-stdlib-inlining = true
 // CHECK-NEXT: c++-template-inlining = true
 // CHECK-NEXT: cfg-conditional-static-initializers = true
+// CHECK-NEXT: cfg-implicit-dtors = true
+// CHECK-NEXT: cfg-lifetime = false
 // CHECK-NEXT: cfg-temporary-dtors = false
 // CHECK-NEXT: faux-bodies = true
 // CHECK-NEXT: graph-trim-interval = 1000
@@ -38,4 +40,4 @@ public:
 // CHECK-NEXT: region-store-small-struct-limit = 2
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 20
+// CHECK-NEXT: num-entries = 22

Added: cfe/trunk/test/Analysis/lifetime-cfg-output.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lifetime-cfg-output.cpp?rev=307759&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/lifetime-cfg-output.cpp (added)
+++ cfe/trunk/test/Analysis/lifetime-cfg-output.cpp Wed Jul 12 00:04:19 2017
@@ -0,0 +1,783 @@
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-lifetime=true -analyzer-config cfg-implicit-dtors=false %s > %t 2>&1
+// RUN: FileCheck --input-file=%t %s
+
+extern bool UV;
+class A {
+public:
+  // CHECK:       [B2 (ENTRY)]
+  // CHECK-NEXT:    Succs (1): B1
+  // CHECK:       [B1]
+  // CHECK-NEXT:    1: true
+  // CHECK-NEXT:    2: UV
+  // CHECK-NEXT:    3: [B1.2] = [B1.1]
+  // CHECK-NEXT:    Preds (1): B2
+  // CHECK-NEXT:    Succs (1): B0
+  // CHECK:       [B0 (EXIT)]
+  // CHECK-NEXT:    Preds (1): B1
+  A() {
+    UV = true;
+  }
+  // CHECK:       [B3 (ENTRY)]
+  // CHECK-NEXT:    Succs (1): B2
+  // CHECK:       [B1]
+  // CHECK-NEXT:    1: 0
+  // CHECK-NEXT:    2: this
+  // CHECK-NEXT:    3: [B1.2]->p
+  // CHECK-NEXT:    4: [B1.3] (ImplicitCastExpr, LValueToRValue, int *)
+  // CHECK-NEXT:    5: *[B1.4]
+  // CHECK-NEXT:    6: [B1.5] = [B1.1]
+  // CHECK-NEXT:    Preds (1): B2
+  // CHECK-NEXT:    Succs (1): B0
+  // CHECK:       [B2]
+  // CHECK-NEXT:    1: this
+  // CHECK-NEXT:    2: [B2.1]->p
+  // CHECK-NEXT:    3: [B2.2] (ImplicitCastExpr, LValueToRValue, int *)
+  // CHECK-NEXT:    4: [B2.3] (ImplicitCastExpr, PointerToBoolean, _Bool)
+  // CHECK-NEXT:    T: if [B2.4]
+  // CHECK-NEXT:    Preds (1): B3
+  // CHECK-NEXT:    Succs (2): B1 B0
+  // CHECK:       [B0 (EXIT)]
+  // CHECK-NEXT:    Preds (2): B1 B2
+  ~A() {
+    if (p)
+      *p = 0;
+  }
+  // CHECK:       [B2 (ENTRY)]
+  // CHECK-NEXT:    Succs (1): B1
+  // CHECK:       [B1]
+  // CHECK-NEXT:    1: 1
+  // CHECK-NEXT:    2: return [B1.1];
+  // CHECK-NEXT:    Preds (1): B2
+  // CHECK-NEXT:    Succs (1): B0
+  // CHECK:       [B0 (EXIT)]
+  // CHECK-NEXT:    Preds (1): B1
+  operator int() const { return 1; }
+  int *p;
+};
+
+// CHECK:       [B2 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B1]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    3: a
+// CHECK-NEXT:    4: [B1.3] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    5: const A &b = a;
+// CHECK-NEXT:    6: A() (CXXConstructExpr, class A)
+// CHECK-NEXT:    7: [B1.6] (BindTemporary)
+// CHECK-NEXT:    8: [B1.7] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    9: [B1.8]
+// CHECK-NEXT:   10: const A &c = A();
+// CHECK-NEXT:   11: [B1.10] (Lifetime ends)
+// CHECK-NEXT:   12: [B1.2] (Lifetime ends)
+// CHECK-NEXT:   13: [B1.5] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B2
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+void test_const_ref() {
+  A a;
+  const A &b = a;
+  const A &c = A();
+}
+
+// CHECK:      [B2 (ENTRY)]
+// CHECK-NEXT:   Succs (1): B1
+// CHECK:       [B1]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A [2])
+// CHECK-NEXT:    2: A a[2];
+// CHECK-NEXT:    3:  (CXXConstructExpr, class A [0])
+// CHECK-NEXT:    4: A b[0];
+// lifetime of a ends when its destructors are run
+// CHECK-NEXT:    5: [B1.2] (Lifetime ends)
+// lifetime of b ends when its storage duration ends
+// CHECK-NEXT:    6: [B1.4] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B2
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:      [B0 (EXIT)]
+// CHECK-NEXT:   Preds (1): B1
+void test_array() {
+  A a[2];
+  A b[0];
+}
+
+// CHECK:      [B2 (ENTRY)]
+// CHECK-NEXT:   Succs (1): B1
+// CHECK:       [B1]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    3:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A c;
+// CHECK-NEXT:    5:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    6: A d;
+// CHECK-NEXT:    7: [B1.6] (Lifetime ends)
+// CHECK-NEXT:    8: [B1.4] (Lifetime ends)
+// CHECK-NEXT:    9:  (CXXConstructExpr, class A)
+// CHECK-NEXT:   10: A b;
+// CHECK-NEXT:   11: [B1.10] (Lifetime ends)
+// CHECK-NEXT:   12: [B1.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B2
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:      [B0 (EXIT)]
+// CHECK-NEXT:   Preds (1): B1
+void test_scope() {
+  A a;
+  {
+    A c;
+    A d;
+  }
+  A b;
+}
+
+// CHECK:      [B4 (ENTRY)]
+// CHECK-NEXT:   Succs (1): B3
+// CHECK:       [B1]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: [B1.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B3.4] (Lifetime ends)
+// CHECK-NEXT:    5: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B3
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    1: return;
+// CHECK-NEXT:    2: [B3.4] (Lifetime ends)
+// CHECK-NEXT:    3: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B3
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B3]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    3:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A b;
+// CHECK-NEXT:    5: UV
+// CHECK-NEXT:    6: [B3.5] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B3.6]
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (2): B2 B1
+
+// CHECK:      [B0 (EXIT)]
+// CHECK-NEXT:   Preds (2): B1 B2
+void test_return() {
+  A a;
+  A b;
+  if (UV)
+    return;
+  A c;
+}
+
+// CHECK:       [B5 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B4
+// CHECK:       [B1]
+// CHECK-NEXT:    1: [B4.6] (Lifetime ends)
+// CHECK-NEXT:    2: [B4.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (2): B2 B3
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: [B2.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B3]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B4]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    3: a
+// CHECK-NEXT:    4: [B4.3] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    5: [B4.4] (CXXConstructExpr, class A)
+// CHECK-NEXT:    6: A b = a;
+// CHECK-NEXT:    7: b
+// CHECK-NEXT:    8: [B4.7] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    9: [B4.8].operator int
+// CHECK-NEXT:   10: [B4.8]
+// CHECK-NEXT:   11: [B4.10] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT:   12: [B4.11] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT:    T: if [B4.12]
+// CHECK-NEXT:    Preds (1): B5
+// CHECK-NEXT:    Succs (2): B3 B2
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+void test_if_implicit_scope() {
+  A a;
+  if (A b = a)
+    A c;
+  else
+    A c;
+}
+
+// CHECK:       [B9 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B8
+// CHECK:       [B1]
+// CHECK-NEXT:    1: [B8.6] (Lifetime ends)
+// CHECK-NEXT:    2:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    3: A e;
+// CHECK-NEXT:    4: [B1.3] (Lifetime ends)
+// CHECK-NEXT:    5: [B8.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (2): B2 B5
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A d;
+// CHECK-NEXT:    3: [B2.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B4.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B3]
+// CHECK-NEXT:    1: return;
+// CHECK-NEXT:    2: [B4.2] (Lifetime ends)
+// CHECK-NEXT:    3: [B8.6] (Lifetime ends)
+// CHECK-NEXT:    4: [B8.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B4]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: UV
+// CHECK-NEXT:    4: [B4.3] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B4.4]
+// CHECK-NEXT:    Preds (1): B8
+// CHECK-NEXT:    Succs (2): B3 B2
+// CHECK:       [B5]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A d;
+// CHECK-NEXT:    3: [B5.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B7.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B6]
+// CHECK-NEXT:    1: return;
+// CHECK-NEXT:    2: [B7.2] (Lifetime ends)
+// CHECK-NEXT:    3: [B8.6] (Lifetime ends)
+// CHECK-NEXT:    4: [B8.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B7]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: UV
+// CHECK-NEXT:    4: [B7.3] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B7.4]
+// CHECK-NEXT:    Preds (1): B8
+// CHECK-NEXT:    Succs (2): B6 B5
+// CHECK:       [B8]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    3: a
+// CHECK-NEXT:    4: [B8.3] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    5: [B8.4] (CXXConstructExpr, class A)
+// CHECK-NEXT:    6: A b = a;
+// CHECK-NEXT:    7: b
+// CHECK-NEXT:    8: [B8.7] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    9: [B8.8].operator int
+// CHECK-NEXT:   10: [B8.8]
+// CHECK-NEXT:   11: [B8.10] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT:   12: [B8.11] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT:    T: if [B8.12]
+// CHECK-NEXT:    Preds (1): B9
+// CHECK-NEXT:    Succs (2): B7 B4
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (3): B1 B3 B6
+void test_if_jumps() {
+  A a;
+  if (A b = a) {
+    A c;
+    if (UV)
+      return;
+    A d;
+  } else {
+    A c;
+    if (UV)
+      return;
+    A d;
+  }
+  A e;
+}
+
+// CHECK:       [B6 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B5
+// CHECK:       [B1]
+// CHECK-NEXT:    1: [B4.4] (Lifetime ends)
+// CHECK-NEXT:    2: [B5.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    Preds (1): B3
+// CHECK-NEXT:    Succs (1): B4
+// CHECK:       [B3]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B4.4] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B4]
+// CHECK-NEXT:    1: a
+// CHECK-NEXT:    2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    3: [B4.2] (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A b = a;
+// CHECK-NEXT:    5: b
+// CHECK-NEXT:    6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    7: [B4.6].operator int
+// CHECK-NEXT:    8: [B4.6]
+// CHECK-NEXT:    9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT:   10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT:    T: while [B4.10]
+// CHECK-NEXT:    Preds (2): B2 B5
+// CHECK-NEXT:    Succs (2): B3 B1
+// CHECK:       [B5]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    Preds (1): B6
+// CHECK-NEXT:    Succs (1): B4
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+void test_while_implicit_scope() {
+  A a;
+  while (A b = a)
+    A c;
+}
+
+// CHECK:       [B12 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B11
+// CHECK:       [B1]
+// CHECK-NEXT:    1: [B10.4] (Lifetime ends)
+// CHECK-NEXT:    2:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    3: A e;
+// CHECK-NEXT:    4: [B1.3] (Lifetime ends)
+// CHECK-NEXT:    5: [B11.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (2): B8 B10
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    Preds (2): B3 B6
+// CHECK-NEXT:    Succs (1): B10
+// CHECK:       [B3]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A d;
+// CHECK-NEXT:    3: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    5: [B10.4] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B5
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B4]
+// CHECK-NEXT:    1: return;
+// CHECK-NEXT:    2: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    3: [B10.4] (Lifetime ends)
+// CHECK-NEXT:    4: [B11.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B5
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B5]
+// CHECK-NEXT:    1: UV
+// CHECK-NEXT:    2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B5.2]
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (2): B4 B3
+// CHECK:       [B6]
+// CHECK-NEXT:    1: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    2: [B10.4] (Lifetime ends)
+// CHECK-NEXT:    T: continue;
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B7]
+// CHECK-NEXT:    1: UV
+// CHECK-NEXT:    2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B7.2]
+// CHECK-NEXT:    Preds (1): B9
+// CHECK-NEXT:    Succs (2): B6 B5
+// CHECK:       [B8]
+// CHECK-NEXT:    1: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    T: break;
+// CHECK-NEXT:    Preds (1): B9
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B9]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: UV
+// CHECK-NEXT:    4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B9.4]
+// CHECK-NEXT:    Preds (1): B10
+// CHECK-NEXT:    Succs (2): B8 B7
+// CHECK:       [B10]
+// CHECK-NEXT:    1: a
+// CHECK-NEXT:    2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    3: [B10.2] (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A b = a;
+// CHECK-NEXT:    5: b
+// CHECK-NEXT:    6: [B10.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    7: [B10.6].operator int
+// CHECK-NEXT:    8: [B10.6]
+// CHECK-NEXT:    9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT:   10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT:    T: while [B10.10]
+// CHECK-NEXT:    Preds (2): B2 B11
+// CHECK-NEXT:    Succs (2): B9 B1
+// CHECK:       [B11]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    Preds (1): B12
+// CHECK-NEXT:    Succs (1): B10
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (2): B1 B4
+void test_while_jumps() {
+  A a;
+  while (A b = a) {
+    A c;
+    if (UV)
+      break;
+    if (UV)
+      continue;
+    if (UV)
+      return;
+    A d;
+  }
+  A e;
+}
+
+// CHECK:       [B12 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B11
+// CHECK:       [B1]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A d;
+// CHECK-NEXT:    3: [B1.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B11.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (2): B8 B2
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    1: UV
+// CHECK-NEXT:    2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: do ... while [B2.2]
+// CHECK-NEXT:    Preds (2): B3 B6
+// CHECK-NEXT:    Succs (2): B10 B1
+// CHECK:       [B3]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B5
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B4]
+// CHECK-NEXT:    1: return;
+// CHECK-NEXT:    2: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    3: [B11.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B5
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B5]
+// CHECK-NEXT:    1: UV
+// CHECK-NEXT:    2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B5.2]
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (2): B4 B3
+// CHECK:       [B6]
+// CHECK-NEXT:    1: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    T: continue;
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B7]
+// CHECK-NEXT:    1: UV
+// CHECK-NEXT:    2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B7.2]
+// CHECK-NEXT:    Preds (1): B9
+// CHECK-NEXT:    Succs (2): B6 B5
+// CHECK:       [B8]
+// CHECK-NEXT:    1: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    T: break;
+// CHECK-NEXT:    Preds (1): B9
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B9]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A b;
+// CHECK-NEXT:    3: UV
+// CHECK-NEXT:    4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B9.4]
+// CHECK-NEXT:    Preds (2): B10 B11
+// CHECK-NEXT:    Succs (2): B8 B7
+// CHECK:       [B10]
+// CHECK-NEXT:    Preds (1): B2
+// CHECK-NEXT:    Succs (1): B9
+// CHECK:       [B11]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    Preds (1): B12
+// CHECK-NEXT:    Succs (1): B9
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (2): B1 B4
+void test_do_jumps() {
+  A a;
+  do {
+    A b;
+    if (UV)
+      break;
+    if (UV)
+      continue;
+    if (UV)
+      return;
+    A c;
+  } while (UV);
+  A d;
+}
+
+// CHECK:       [B6 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B5
+// CHECK:       [B1]
+// CHECK-NEXT:    1: [B4.4] (Lifetime ends)
+// CHECK-NEXT:    2: [B5.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    Preds (1): B3
+// CHECK-NEXT:    Succs (1): B4
+// CHECK:       [B3]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A c;
+// CHECK-NEXT:    3: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B4.4] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B4]
+// CHECK-NEXT:    1: a
+// CHECK-NEXT:    2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    3: [B4.2] (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A b = a;
+// CHECK-NEXT:    5: b
+// CHECK-NEXT:    6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    7: [B4.6].operator int
+// CHECK-NEXT:    8: [B4.6]
+// CHECK-NEXT:    9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT:   10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT:    T: for (...; [B4.10]; )
+// CHECK-NEXT:    Preds (2): B2 B5
+// CHECK-NEXT:    Succs (2): B3 B1
+// CHECK:       [B5]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    Preds (1): B6
+// CHECK-NEXT:    Succs (1): B4
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+void test_for_implicit_scope() {
+  for (A a; A b = a;)
+    A c;
+}
+
+// CHECK:       [B12 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B11
+// CHECK:       [B1]
+// CHECK-NEXT:    1: [B10.4] (Lifetime ends)
+// CHECK-NEXT:    2: [B11.4] (Lifetime ends)
+// CHECK-NEXT:    3:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A f;
+// CHECK-NEXT:    5: [B1.4] (Lifetime ends)
+// CHECK-NEXT:    6: [B11.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (2): B8 B10
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    Preds (2): B3 B6
+// CHECK-NEXT:    Succs (1): B10
+// CHECK:       [B3]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A e;
+// CHECK-NEXT:    3: [B3.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    5: [B10.4] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B5
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B4]
+// CHECK-NEXT:    1: return;
+// CHECK-NEXT:    2: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    3: [B10.4] (Lifetime ends)
+// CHECK-NEXT:    4: [B11.4] (Lifetime ends)
+// CHECK-NEXT:    5: [B11.2] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B5
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B5]
+// CHECK-NEXT:    1: UV
+// CHECK-NEXT:    2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B5.2]
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (2): B4 B3
+// CHECK:       [B6]
+// CHECK-NEXT:    1: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    T: continue;
+// CHECK-NEXT:    Preds (1): B7
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B7]
+// CHECK-NEXT:    1: UV
+// CHECK-NEXT:    2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B7.2]
+// CHECK-NEXT:    Preds (1): B9
+// CHECK-NEXT:    Succs (2): B6 B5
+// CHECK:       [B8]
+// CHECK-NEXT:    1: [B9.2] (Lifetime ends)
+// CHECK-NEXT:    T: break;
+// CHECK-NEXT:    Preds (1): B9
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B9]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A d;
+// CHECK-NEXT:    3: UV
+// CHECK-NEXT:    4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:    T: if [B9.4]
+// CHECK-NEXT:    Preds (1): B10
+// CHECK-NEXT:    Succs (2): B8 B7
+// CHECK:       [B10]
+// CHECK-NEXT:    1: b
+// CHECK-NEXT:    2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    3: [B10.2] (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A c = b;
+// CHECK-NEXT:    5: c
+// CHECK-NEXT:    6: [B10.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT:    7: [B10.6].operator int
+// CHECK-NEXT:    8: [B10.6]
+// CHECK-NEXT:    9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT:   10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT:    T: for (...; [B10.10]; )
+// CHECK-NEXT:    Preds (2): B2 B11
+// CHECK-NEXT:    Succs (2): B9 B1
+// CHECK:       [B11]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    3:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    4: A b;
+// CHECK-NEXT:    Preds (1): B12
+// CHECK-NEXT:    Succs (1): B10
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (2): B1 B4
+void test_for_jumps() {
+  A a;
+  for (A b; A c = b;) {
+    A d;
+    if (UV)
+      break;
+    if (UV)
+      continue;
+    if (UV)
+      return;
+    A e;
+  }
+  A f;
+}
+
+// CHECK:       [B2 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B1]
+// CHECK-NEXT:    1:  (CXXConstructExpr, class A)
+// CHECK-NEXT:    2: A a;
+// CHECK-NEXT:    3: int n;
+// CHECK-NEXT:    4: n
+// CHECK-NEXT:    5: &[B1.4]
+// CHECK-NEXT:    6: a
+// CHECK-NEXT:    7: [B1.6].p
+// CHECK-NEXT:    8: [B1.7] = [B1.5]
+// CHECK-NEXT:    9: [B1.2] (Lifetime ends)
+// CHECK-NEXT:   10: [B1.3] (Lifetime ends)
+// CHECK-NEXT:    Preds (1): B2
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+void test_trivial_vs_non_trivial_order() {
+  A a;
+  int n;
+  a.p = &n;
+}
+
+// CHECK:       [B4 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B3
+// CHECK:       [B1]
+// CHECK-NEXT:   a:
+// CHECK-NEXT:    1: 1
+// CHECK-NEXT:    2: i
+// CHECK-NEXT:    3: [B1.2] = [B1.1]
+// CHECK-NEXT:    4: [B2.1] (Lifetime ends)
+// CHECK-NEXT:    Preds (2): B2 B3
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    1: int i;
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B3]
+// CHECK-NEXT:    T: goto a;
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+void goto_past_declaration() {
+  goto a;
+  int i;
+a:
+  i = 1;
+}
+
+// CHECK:       [B4 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B3
+// CHECK:       [B1]
+// CHECK-NEXT:   a:
+// CHECK-NEXT:    1: 1
+// CHECK-NEXT:    2: k
+// CHECK-NEXT:    3: [B1.2] = [B1.1]
+// CHECK-NEXT:    4: [B2.4] (Lifetime ends)
+// CHECK-NEXT:    Preds (2): B2 B3
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:    1: int j;
+// CHECK-NEXT:    2: [B2.1] (Lifetime ends)
+// CHECK-NEXT:    3: [B3.1] (Lifetime ends)
+// CHECK-NEXT:    4: int k;
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B3]
+// CHECK-NEXT:    1: int i;
+// CHECK-NEXT:    2: [B3.1] (Lifetime ends)
+// CHECK-NEXT:    T: goto a;
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B1
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+void goto_past_declaration2() {
+  {
+    int i;
+    goto a;
+    int j;
+  }
+  {
+    int k;
+  a:
+    k = 1;
+  }
+}
+
+struct B {
+  ~B();
+};
+
+// CHECK:       [B4 (ENTRY)]
+// CHECK-NEXT:    Succs (1): B3
+// CHECK:       [B1]
+// CHECK-NEXT:    1: i
+// CHECK-NEXT:    2: [B1.1]++
+// CHECK-NEXT:    3: [B2.2] (Lifetime ends)
+// CHECK-NEXT:    4: [B3.1] (Lifetime ends)
+// CHECK-NEXT:    Succs (1): B0
+// CHECK:       [B2]
+// CHECK-NEXT:   label:
+// CHECK-NEXT:    1:  (CXXConstructExpr, struct B)
+// CHECK-NEXT:    2: B b;
+// CHECK-NEXT:    3: [B2.2] (Lifetime ends)
+// CHECK-NEXT:    T: goto label;
+// CHECK-NEXT:    Preds (2): B3 B2
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B3]
+// CHECK-NEXT:    1: int i;
+// CHECK-NEXT:    Preds (1): B4
+// CHECK-NEXT:    Succs (1): B2
+// CHECK:       [B0 (EXIT)]
+// CHECK-NEXT:    Preds (1): B1
+int backpatched_goto() {
+  int i;
+label:
+  B b;
+  goto label;
+  i++;
+}




More information about the cfe-commits mailing list