]> granicus.if.org Git - clang/commitdiff
[CFG] Add branch to skip vbase inits when they're handled by superclass.
authorArtem Dergachev <artem.dergachev@gmail.com>
Fri, 24 May 2019 23:37:08 +0000 (23:37 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Fri, 24 May 2019 23:37:08 +0000 (23:37 +0000)
This patch adds the run-time CFG branch that would skip initialization of
virtual base classes depending on whether the constructor is called from a
superclass constructor or not. Previously the Static Analyzer was already
skipping virtual base-class initializers in such constructors, but it wasn't
skipping their arguments and their potential side effects, which was causing
pr41300 (and was generally incorrect). The previous skipping behavior is
now replaced with a hard assertion that we're not even getting there due
to how our CFG works.

The new CFG element is under a CFG build option so that not to break other
consumers of the CFG by this change. Static Analyzer support for this change
is implemented.

Differential Revision: https://reviews.llvm.org/D61816

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@361681 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/AnalysisDeclContext.h
include/clang/Analysis/CFG.h
include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
lib/Analysis/AnalysisDeclContext.cpp
lib/Analysis/CFG.cpp
lib/StaticAnalyzer/Core/AnalysisManager.cpp
lib/StaticAnalyzer/Core/CoreEngine.cpp
lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
test/Analysis/initializer.cpp
test/Analysis/initializers-cfg-output.cpp

index d42432a28d06aed03ea96275f26df2ce65eb0f61..86f331d26a9bb02447579573f09d00f64b0c2f17 100644 (file)
@@ -459,6 +459,7 @@ public:
                              bool addCXXNewAllocator = true,
                              bool addRichCXXConstructors = true,
                              bool markElidedCXXConstructors = true,
+                             bool addVirtualBaseBranches = true,
                              CodeInjector *injector = nullptr);
 
   AnalysisDeclContext *getContext(const Decl *D);
index 212fd1baef5d6462cd77f82bf4585898ad46e78c..734c49881e3e5d543a11768afcb7b21178ab4b90 100644 (file)
@@ -504,15 +504,19 @@ public:
     /// terminator statement is the same statement that branches control flow
     /// in evaluation of matching full expression.
     TemporaryDtorsBranch,
+    /// A shortcut around virtual base initializers. It gets taken when
+    /// virtual base classes have already been initialized by the constructor
+    /// of the most derived class while we're in the base class.
+    VirtualBaseBranch,
 
     /// Number of different kinds, for sanity checks. We subtract 1 so that
     /// to keep receiving compiler warnings when we don't cover all enum values
     /// in a switch.
-    NumKindsMinusOne = TemporaryDtorsBranch
+    NumKindsMinusOne = VirtualBaseBranch
   };
 
 private:
-  static constexpr int KindBits = 1;
+  static constexpr int KindBits = 2;
   static_assert((1 << KindBits) > NumKindsMinusOne,
                 "Not enough room for kind!");
   llvm::PointerIntPair<Stmt *, KindBits> Data;
@@ -532,6 +536,9 @@ public:
   bool isTemporaryDtorsBranch() const {
     return getKind() == TemporaryDtorsBranch;
   }
+  bool isVirtualBaseBranch() const {
+    return getKind() == VirtualBaseBranch;
+  }
 };
 
 /// Represents a single basic block in a source-level CFG.
@@ -552,11 +559,12 @@ public:
 /// Successors: the order in the set of successors is NOT arbitrary.  We
 ///  currently have the following orderings based on the terminator:
 ///
-///     Terminator       Successor Ordering
-///  -----------------------------------------------------
-///       if            Then Block;  Else Block
-///     ? operator      LHS expression;  RHS expression
-///     &&, ||          expression that uses result of && or ||, RHS
+///     Terminator     |   Successor Ordering
+///  ------------------|------------------------------------
+///       if           |  Then Block;  Else Block
+///     ? operator     |  LHS expression;  RHS expression
+///     logical and/or |  expression that consumes the op, RHS
+///     vbase inits    |  already handled by the most derived class; not yet
 ///
 /// But note that any of that may be NULL in case of optimized-out edges.
 class CFGBlock {
@@ -1039,6 +1047,7 @@ public:
     bool AddCXXDefaultInitExprInCtors = false;
     bool AddRichCXXConstructors = false;
     bool MarkElidedCXXConstructors = false;
+    bool AddVirtualBaseBranches = false;
 
     BuildOptions() = default;
 
index 310c2a43aa412a59bb1c38a02ac610d9ad8a8ece..019acc0b7d9f86887b41bc7d8f7c47eae3c4690f 100644 (file)
@@ -116,6 +116,8 @@ private:
   void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
                         ExplodedNode *Pred);
 
+  void HandleVirtualBaseBranch(const CFGBlock *B, ExplodedNode *Pred);
+
 private:
   ExplodedNode *generateCallExitBeginNode(ExplodedNode *N,
                                           const ReturnStmt *RS);
index f32c9f903f4a16585e38108144887ec45e9cfc95..750d9bb1202ae4e7be58bb92949bcfee42dfa457 100644 (file)
@@ -70,7 +70,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(
     bool addLoopExit, bool addScopes, bool synthesizeBodies,
     bool addStaticInitBranch, bool addCXXNewAllocator,
     bool addRichCXXConstructors, bool markElidedCXXConstructors,
-    CodeInjector *injector)
+    bool addVirtualBaseBranches, CodeInjector *injector)
     : Injector(injector), FunctionBodyFarm(ASTCtx, injector),
       SynthesizeBodies(synthesizeBodies) {
   cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
@@ -84,6 +84,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(
   cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
   cfgBuildOptions.AddRichCXXConstructors = addRichCXXConstructors;
   cfgBuildOptions.MarkElidedCXXConstructors = markElidedCXXConstructors;
+  cfgBuildOptions.AddVirtualBaseBranches = addVirtualBaseBranches;
 }
 
 void AnalysisDeclContextManager::clear() { Contexts.clear(); }
index 915e5cc222f5b276e8a8510dab40d70a6845ed9e..5d50cfb474e197762ebabe8b864130502820af72 100644 (file)
@@ -1431,13 +1431,41 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {
   if (badCFG)
     return nullptr;
 
-  // For C++ constructor add initializers to CFG.
-  if (const CXXConstructorDecl *CD = dyn_cast_or_null<CXXConstructorDecl>(D)) {
+  // For C++ constructor add initializers to CFG. Constructors of virtual bases
+  // are ignored unless the object is of the most derived class.
+  //   class VBase { VBase() = default; VBase(int) {} };
+  //   class A : virtual public VBase { A() : VBase(0) {} };
+  //   class B : public A {};
+  //   B b; // Constructor calls in order: VBase(), A(), B().
+  //        // VBase(0) is ignored because A isn't the most derived class.
+  // This may result in the virtual base(s) being already initialized at this
+  // point, in which case we should jump right onto non-virtual bases and
+  // fields. To handle this, make a CFG branch. We only need to add one such
+  // branch per constructor, since the Standard states that all virtual bases
+  // shall be initialized before non-virtual bases and direct data members.
+  if (const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(D)) {
+    CFGBlock *VBaseSucc = nullptr;
     for (auto *I : llvm::reverse(CD->inits())) {
+      if (BuildOpts.AddVirtualBaseBranches && !VBaseSucc &&
+          I->isBaseInitializer() && I->isBaseVirtual()) {
+        // We've reached the first virtual base init while iterating in reverse
+        // order. Make a new block for virtual base initializers so that we
+        // could skip them.
+        VBaseSucc = Succ = B ? B : &cfg->getExit();
+        Block = createBlock();
+      }
       B = addInitializer(I);
       if (badCFG)
         return nullptr;
     }
+    if (VBaseSucc) {
+      // Make a branch block for potentially skipping virtual base initializers.
+      Succ = VBaseSucc;
+      B = createBlock();
+      B->setTerminator(
+          CFGTerminator(nullptr, CFGTerminator::VirtualBaseBranch));
+      addSuccessor(B, Block, true);
+    }
   }
 
   if (B)
@@ -1769,6 +1797,9 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) {
 
   // At the end destroy virtual base objects.
   for (const auto &VI : RD->vbases()) {
+    // TODO: Add a VirtualBaseBranch to see if the most derived class
+    // (which is different from the current class) is responsible for
+    // destroying them.
     const CXXRecordDecl *CD = VI.getType()->getAsCXXRecordDecl();
     if (!CD->hasTrivialDestructor()) {
       autoCreateBlock();
@@ -5066,6 +5097,9 @@ public:
       OS << "(Temp Dtor) ";
       Visit(T.getStmt());
       break;
+    case CFGTerminator::VirtualBaseBranch:
+      OS << "(See if most derived ctor has already initialized vbases)";
+      break;
     }
   }
 };
index 2e69c2c43b62504a384c5d0451f6afbc14ee625e..95f2b703cdd65b79692814e0a59cfcda6dd70ba6 100644 (file)
@@ -35,7 +35,9 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags,
           Options.ShouldConditionalizeStaticInitializers,
           /*addCXXNewAllocator=*/true,
           Options.ShouldIncludeRichConstructorsInCFG,
-          Options.ShouldElideConstructors, injector),
+          Options.ShouldElideConstructors,
+          /*addVirtualBaseBranches=*/true,
+          injector),
       Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()),
       PathConsumers(PDC), CreateStoreMgr(storemgr),
       CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
index ca9a48ef9808c838a408cd02d3ef7cf358169d8e..500995b053ef9b0f766ce053b9e726db8a193a0a 100644 (file)
@@ -380,6 +380,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
     }
   }
 
+  if (B->getTerminator().isVirtualBaseBranch()) {
+    HandleVirtualBaseBranch(B, Pred);
+    return;
+  }
+
   assert(B->succ_size() == 1 &&
          "Blocks with no terminator should have at most 1 successor.");
 
@@ -439,6 +444,29 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
   }
 }
 
+void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B,
+                                         ExplodedNode *Pred) {
+  const LocationContext *LCtx = Pred->getLocationContext();
+  if (const auto *CallerCtor = dyn_cast_or_null<CXXConstructExpr>(
+          LCtx->getStackFrame()->getCallSite())) {
+    switch (CallerCtor->getConstructionKind()) {
+    case CXXConstructExpr::CK_NonVirtualBase:
+    case CXXConstructExpr::CK_VirtualBase: {
+      BlockEdge Loc(B, *B->succ_begin(), LCtx);
+      HandleBlockEdge(Loc, Pred);
+      return;
+    }
+    default:
+      break;
+    }
+  }
+
+  // We either don't see a parent stack frame because we're in the top frame,
+  // or the parent stack frame doesn't initialize our virtual bases.
+  BlockEdge Loc(B, *(B->succ_begin() + 1), LCtx);
+  HandleBlockEdge(Loc, Pred);
+}
+
 /// generateNode - Utility method to generate nodes, hook up successors,
 ///  and add nodes to the worklist.
 void CoreEngine::generateNode(const ProgramPoint &Loc,
index 62699fb3186b5a873b8ed86716d07d6f71190155..1cbd09ea57932927725ce2d4f5988270092ece64 100644 (file)
@@ -428,25 +428,20 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
         prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
     break;
   }
-  case CXXConstructExpr::CK_VirtualBase:
+  case CXXConstructExpr::CK_VirtualBase: {
     // Make sure we are not calling virtual base class initializers twice.
     // Only the most-derived object should initialize virtual base classes.
-    if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) {
-      const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer);
-      if (OuterCtor) {
-        switch (OuterCtor->getConstructionKind()) {
-        case CXXConstructExpr::CK_NonVirtualBase:
-        case CXXConstructExpr::CK_VirtualBase:
-          // Bail out!
-          destNodes.Add(Pred);
-          return;
-        case CXXConstructExpr::CK_Complete:
-        case CXXConstructExpr::CK_Delegating:
-          break;
-        }
-      }
-    }
+    const auto *OuterCtor = dyn_cast_or_null<CXXConstructExpr>(
+        LCtx->getStackFrame()->getCallSite());
+    assert(
+        (!OuterCtor ||
+         OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Complete ||
+         OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Delegating) &&
+        ("This virtual base should have already been initialized by "
+         "the most derived class!"));
+    (void)OuterCtor;
     LLVM_FALLTHROUGH;
+  }
   case CXXConstructExpr::CK_NonVirtualBase:
     // In C++17, classes with non-virtual bases may be aggregates, so they would
     // be initialized as aggregates without a constructor call, so we may have
index 56b0a09d47455ac9b88b7eb9007d93197502bf13..16d7a348fdfb6d57fd722a8639a6c2c7222dc337 100644 (file)
@@ -275,3 +275,94 @@ B foo_recursive() {
   B b { foo_recursive() };
 }
 } // namespace CXX17_transparent_init_list_exprs
+
+namespace skip_vbase_initializer_side_effects {
+int glob;
+struct S {
+  S() { ++glob; }
+};
+
+struct A {
+  A() {}
+  A(S s) {}
+};
+
+struct B : virtual A {
+  B() : A(S()) {}
+};
+
+struct C : B {
+  C() {}
+};
+
+void foo() {
+  glob = 0;
+  B b;
+  clang_analyzer_eval(glob == 1); // expected-warning{{TRUE}}
+  C c; // no-crash
+  clang_analyzer_eval(glob == 1); // expected-warning{{TRUE}}
+}
+} // namespace skip_vbase_initializer_side_effects
+
+namespace dont_skip_vbase_initializers_in_most_derived_class {
+struct A {
+  static int a;
+  A() { a = 0; }
+  A(int x) { a = x; }
+};
+
+struct B {
+  static int b;
+  B() { b = 0; }
+  B(int y) { b = y; }
+};
+
+struct C : virtual A {
+  C() : A(1) {}
+};
+struct D : C, virtual B {
+  D() : B(2) {}
+};
+
+void testD() {
+  D d;
+  clang_analyzer_eval(A::a == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(B::b == 2); // expected-warning{{TRUE}}
+}
+
+struct E : virtual B, C {
+  E() : B(2) {}
+};
+
+void testE() {
+  E e;
+  clang_analyzer_eval(A::a == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(B::b == 2); // expected-warning{{TRUE}}
+}
+
+struct F : virtual A, virtual B {
+  F() : A(1) {}
+};
+struct G : F {
+  G(): B(2) {}
+};
+
+void testG() {
+  G g;
+  clang_analyzer_eval(A::a == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(B::b == 2); // expected-warning{{TRUE}}
+}
+
+struct H : virtual B, virtual A {
+  H(): A(1) {}
+};
+struct I : H {
+  I(): B(2) {}
+};
+
+void testI() {
+  I i;
+  clang_analyzer_eval(A::a == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(B::b == 2); // expected-warning{{TRUE}}
+}
+} // namespace dont_skip_vbase_initializers_in_most_derived_class
index a69e78faeda08557565c0224f1b3e2ca268a54b9..f83386492656a11161cac57f937bd255cdd3005e 100644 (file)
@@ -30,21 +30,25 @@ public:
 class B : public virtual A {
 public:
   // CHECK:       B()
-  // CHECK:        [B2 (ENTRY)]
-  // CHECK-NEXT:     Succs (1): B1
+  // CHECK:        [B3 (ENTRY)]
+  // CHECK-NEXT:     Succs (1): B2
   // CHECK:        [B1]
   // WARNINGS-NEXT:     1:  (CXXConstructExpr, class A)
   // ANALYZER-NEXT:     1:  (CXXConstructExpr, A() (Base initializer), class A)
   // CHECK-NEXT:     2: A([B1.1]) (Base initializer)
   // CHECK-NEXT:     Preds (1): B2
   // CHECK-NEXT:     Succs (1): B0
+  // CHECK:        [B2]
+  // CHECK-NEXT:     T: (See if most derived ctor has already initialized vbases)
+  // CHECK-NEXT:     Preds (1): B3
+  // CHECK-NEXT:     Succs (2): B0 B1
   // CHECK:        [B0 (EXIT)]
-  // CHECK-NEXT:     Preds (1): B1
+  // CHECK-NEXT:     Preds (2): B1 B2
   B() {}
 
   // CHECK:       B(int i)
-  // CHECK:        [B2 (ENTRY)]
-  // CHECK-NEXT:     Succs (1): B1
+  // CHECK:        [B3 (ENTRY)]
+  // CHECK-NEXT:     Succs (1): B2
   // CHECK:        [B1]
   // CHECK-NEXT:     1: i
   // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, LValueToRValue, int)
@@ -53,29 +57,37 @@ public:
   // CHECK-NEXT:     4: A([B1.3]) (Base initializer)
   // CHECK-NEXT:     Preds (1): B2
   // CHECK-NEXT:     Succs (1): B0
+  // CHECK:        [B2]
+  // CHECK-NEXT:     T: (See if most derived ctor has already initialized vbases)
+  // CHECK-NEXT:     Preds (1): B3
+  // CHECK-NEXT:     Succs (2): B0 B1
   // CHECK:        [B0 (EXIT)]
-  // CHECK-NEXT:     Preds (1): B1
+  // CHECK-NEXT:     Preds (2): B1 B2
   B(int i) : A(i) {}
 };
 
 class C : public virtual A {
 public:
   // CHECK:       C()
-  // CHECK:        [B2 (ENTRY)]
-  // CHECK-NEXT:     Succs (1): B1
+  // CHECK:        [B3 (ENTRY)]
+  // CHECK-NEXT:     Succs (1): B2
   // CHECK:        [B1]
   // WARNINGS-NEXT:     1:  (CXXConstructExpr, class A)
   // ANALYZER-NEXT:     1:  (CXXConstructExpr, A() (Base initializer), class A)
   // CHECK-NEXT:     2: A([B1.1]) (Base initializer)
   // CHECK-NEXT:     Preds (1): B2
   // CHECK-NEXT:     Succs (1): B0
+  // CHECK:        [B2]
+  // CHECK-NEXT:     T: (See if most derived ctor has already initialized vbases)
+  // CHECK-NEXT:     Preds (1): B3
+  // CHECK-NEXT:     Succs (2): B0 B1
   // CHECK:        [B0 (EXIT)]
-  // CHECK-NEXT:     Preds (1): B1
+  // CHECK-NEXT:     Preds (2): B1 B2
   C() {}
 
   // CHECK:       C(int i)
-  // CHECK:        [B2 (ENTRY)]
-  // CHECK-NEXT:     Succs (1): B1
+  // CHECK:        [B3 (ENTRY)]
+  // CHECK-NEXT:     Succs (1): B2
   // CHECK:        [B1]
   // CHECK-NEXT:     1: i
   // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, LValueToRValue, int)
@@ -84,8 +96,12 @@ public:
   // CHECK-NEXT:     4: A([B1.3]) (Base initializer)
   // CHECK-NEXT:     Preds (1): B2
   // CHECK-NEXT:     Succs (1): B0
+  // CHECK:        [B2]
+  // CHECK-NEXT:     T: (See if most derived ctor has already initialized vbases)
+  // CHECK-NEXT:     Preds (1): B3
+  // CHECK-NEXT:     Succs (2): B0 B1
   // CHECK:        [B0 (EXIT)]
-  // CHECK-NEXT:     Preds (1): B1
+  // CHECK-NEXT:     Preds (2): B1 B2
   C(int i) : A(i) {}
 };
 
@@ -98,31 +114,38 @@ public:
 };
 
 // CHECK:       TestOrder::TestOrder()
-// CHECK:        [B2 (ENTRY)]
-// CHECK-NEXT:     Succs (1): B1
+// CHECK:        [B4 (ENTRY)]
+// CHECK-NEXT:     Succs (1): B3
 // CHECK:        [B1]
+// WARNINGS-NEXT:     1:  (CXXConstructExpr, class C)
+// ANALYZER-NEXT:     1:  (CXXConstructExpr, C() (Base initializer), class C)
+// CHECK-NEXT:     2: C([B1.1]) (Base initializer)
+// WARNINGS-NEXT:     3:  (CXXConstructExpr, class B)
+// ANALYZER-NEXT:     3:  (CXXConstructExpr, B() (Base initializer), class B)
+// CHECK-NEXT:     4: B([B1.3]) (Base initializer)
+// WARNINGS-NEXT:     5:  (CXXConstructExpr, class A)
+// ANALYZER-NEXT:     5:  (CXXConstructExpr, A() (Base initializer), class A)
+// CHECK-NEXT:     6: A([B1.5]) (Base initializer)
+// CHECK-NEXT:     7: /*implicit*/(int)0
+// CHECK-NEXT:     8: i([B1.7]) (Member initializer)
+// CHECK-NEXT:     9: this
+// CHECK-NEXT:    10: [B1.9]->i
+// CHECK-NEXT:    11: r([B1.10]) (Member initializer)
+// WARNINGS-NEXT:    12:  (CXXConstructExpr, class A)
+// ANALYZER-NEXT:    12:  (CXXConstructExpr, [B1.13], class A)
+// CHECK-NEXT:    13: A a;
+// CHECK-NEXT:     Preds (2): B2 B3
+// CHECK-NEXT:     Succs (1): B0
+// CHECK:        [B2]
 // WARNINGS-NEXT:     1:  (CXXConstructExpr, class A)
 // ANALYZER-NEXT:     1:  (CXXConstructExpr, A() (Base initializer), class A)
-// CHECK-NEXT:     2: A([B1.1]) (Base initializer)
-// WARNINGS-NEXT:     3:  (CXXConstructExpr, class C)
-// ANALYZER-NEXT:     3:  (CXXConstructExpr, C() (Base initializer), class C)
-// CHECK-NEXT:     4: C([B1.3]) (Base initializer)
-// WARNINGS-NEXT:     5:  (CXXConstructExpr, class B)
-// ANALYZER-NEXT:     5:  (CXXConstructExpr, B() (Base initializer), class B)
-// CHECK-NEXT:     6: B([B1.5]) (Base initializer)
-// WARNINGS-NEXT:     7:  (CXXConstructExpr, class A)
-// ANALYZER-NEXT:     7:  (CXXConstructExpr, A() (Base initializer), class A)
-// CHECK-NEXT:     8: A([B1.7]) (Base initializer)
-// CHECK-NEXT:     9: /*implicit*/(int)0
-// CHECK-NEXT:    10: i([B1.9]) (Member initializer)
-// CHECK-NEXT:    11: this
-// CHECK-NEXT:    12: [B1.11]->i
-// CHECK-NEXT:    13: r([B1.12]) (Member initializer)
-// WARNINGS-NEXT:    14:  (CXXConstructExpr, class A)
-// ANALYZER-NEXT:    14:  (CXXConstructExpr, [B1.15], class A)
-// CHECK-NEXT:    15: A a;
-// CHECK-NEXT:     Preds (1): B2
-// CHECK-NEXT:     Succs (1): B0
+// CHECK-NEXT:     2: A([B2.1]) (Base initializer)
+// CHECK-NEXT:     Preds (1): B3
+// CHECK-NEXT:     Succs (1): B1
+// CHECK:        [B3]
+// CHECK-NEXT:     T: (See if most derived ctor has already initialized vbases)
+// CHECK-NEXT:     Preds (1): B4
+// CHECK-NEXT:     Succs (2): B1 B2
 // CHECK:        [B0 (EXIT)]
 // CHECK-NEXT:     Preds (1): B1
 TestOrder::TestOrder()
@@ -209,3 +232,64 @@ public:
   // CHECK-NEXT:     Preds (1): B1
   TestDelegating(int x, int z) : x(x), z(z) {}
 };
+
+class TestMoreControlFlow : public virtual A {
+  A a;
+
+public:
+  TestMoreControlFlow(bool coin);
+};
+
+// CHECK:       TestMoreControlFlow::TestMoreControlFlow(bool coin)
+// CHECK:        [B10 (ENTRY)]
+// CHECK-NEXT:     Succs (1): B9
+// CHECK:        [B1]
+// CHECK-NEXT:     1: [B4.2] ? [B2.1] : [B3.1]
+// WARNINGS-NEXT:     2: [B1.1] (CXXConstructExpr, class A)
+// ANALYZER-NEXT:     2: [B1.1] (CXXConstructExpr, a([B1.1]) (Member initializer), class A)
+// CHECK-NEXT:     3: a([B1.2]) (Member initializer)
+// CHECK-NEXT:     Preds (2): B2 B3
+// CHECK-NEXT:     Succs (1): B0
+// CHECK:        [B2]
+// CHECK-NEXT:     1: 3
+// CHECK-NEXT:     Preds (1): B4
+// CHECK-NEXT:     Succs (1): B1
+// CHECK:        [B3]
+// CHECK-NEXT:     1: 4
+// CHECK-NEXT:     Preds (1): B4
+// CHECK-NEXT:     Succs (1): B1
+// CHECK:        [B4]
+// CHECK-NEXT:     1: coin
+// CHECK-NEXT:     2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:     T: [B4.2] ? ... : ...
+// CHECK-NEXT:     Preds (2): B5 B9
+// CHECK-NEXT:     Succs (2): B2 B3
+// CHECK:        [B5]
+// CHECK-NEXT:     1: [B8.2] ? [B6.1] : [B7.1]
+// WARNINGS-NEXT:     2: [B5.1] (CXXConstructExpr, class A)
+// ANALYZER-NEXT:     2: [B5.1] (CXXConstructExpr, A([B5.1]) (Base initializer), class A)
+// CHECK-NEXT:     3: A([B5.2]) (Base initializer)
+// CHECK-NEXT:     Preds (2): B6 B7
+// CHECK-NEXT:     Succs (1): B4
+// CHECK:        [B6]
+// CHECK-NEXT:     1: 1
+// CHECK-NEXT:     Preds (1): B8
+// CHECK-NEXT:     Succs (1): B5
+// CHECK:        [B7]
+// CHECK-NEXT:     1: 2
+// CHECK-NEXT:     Preds (1): B8
+// CHECK-NEXT:     Succs (1): B5
+// CHECK:        [B8]
+// CHECK-NEXT:     1: coin
+// CHECK-NEXT:     2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT:     T: [B8.2] ? ... : ...
+// CHECK-NEXT:     Preds (1): B9
+// CHECK-NEXT:     Succs (2): B6 B7
+// CHECK:        [B9]
+// CHECK-NEXT:     T: (See if most derived ctor has already initialized vbases)
+// CHECK-NEXT:     Preds (1): B10
+// CHECK-NEXT:     Succs (2): B4 B8
+// CHECK:        [B0 (EXIT)]
+// CHECK-NEXT:     Preds (1): B1
+TestMoreControlFlow::TestMoreControlFlow(bool coin)
+    : A(coin ? 1 : 2), a(coin ? 3 : 4) {}