NoQ created this revision.
NoQ added reviewers: rsmith, dcoughlin, xazax.hun, a.sidorin, george.karpenkov, 
szepet.
Herald added subscribers: cfe-commits, rnkovacs.

https://reviews.llvm.org/D42672 has added extra context to construction calls 
in the CFG so that the users, such as the analyzer, didn't have to peek ahead 
in order to figure out what sort of constructor is being called. The same seems 
to be necessary for arbitrary functions that return C++ objects by value (as 
opposed to by reference).

One of the use cases for such extra context at the call site would be to 
perform any sort of inter-procedural analysis that involves functions returning 
objects by value. In this case the elidable constructor at the return site 
would construct the object explained by the context at the call site, and its 
lifetime would also be managed by the caller, not the callee.

The extra context would also be useful for properly handling the return-value 
temporary at the call site, even if the callee is not being analyzed 
inter-procedurally.


Repository:
  rC Clang

https://reviews.llvm.org/D44120

Files:
  include/clang/Analysis/CFG.h
  lib/Analysis/CFG.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/PathDiagnostic.cpp
  test/Analysis/cfg-rich-constructors.cpp
  test/Analysis/temp-obj-dtors-cfg-output.cpp

Index: test/Analysis/temp-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -1111,7 +1111,8 @@
 // CHECK:   [B1]
 // CHECK:     1: A::make
 // CHECK:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
-// CHECK:     3: [B1.2]()
+// WARNINGS:     3: [B1.2]()
+// ANALYZER:     3: [B1.2]() (ValueTypedCall, [B1.4], [B1.6])
 // CHECK:     4: [B1.3] (BindTemporary)
 // CHECK:     5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     6: [B1.5]
@@ -1130,16 +1131,18 @@
 // CHECK:   [B1]
 // CHECK:     1: A::make
 // CHECK:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
-// CHECK:     3: [B1.2]()
+// WARNINGS:     3: [B1.2]()
+// ANALYZER:     3: [B1.2]() (ValueTypedCall, [B1.4], [B1.6])
 // CHECK:     4: [B1.3] (BindTemporary)
 // CHECK:     5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     6: [B1.5]
 // CHECK:     7: const A &a = A::make();
 // CHECK:     8: foo
 // CHECK:     9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
 // CHECK:    10: A::make
 // CHECK:    11: [B1.10] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
-// CHECK:    12: [B1.11]()
+// WARNINGS:    12: [B1.11]()
+// ANALYZER:    12: [B1.11]() (ValueTypedCall, [B1.13], [B1.15])
 // CHECK:    13: [B1.12] (BindTemporary)
 // CHECK:    14: [B1.13] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:    15: [B1.14]
Index: test/Analysis/cfg-rich-constructors.cpp
===================================================================
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -97,7 +97,7 @@
 // CHECK: void simpleVariableInitializedByValue()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (ValueTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CHECK-NEXT:     6: C c = C::get();
@@ -114,7 +114,7 @@
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B2.2]()
+// CHECK-NEXT:     3: [B2.2]() (ValueTypedCall, [B2.4])
 // CHECK-NEXT:     4: [B2.3]
 // CHECK-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
 // CHECK:        [B3]
@@ -172,7 +172,7 @@
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B2.2]()
+// CHECK-NEXT:     3: [B2.2]() (ValueTypedCall, [B2.4])
 // CHECK-NEXT:     4: [B2.3]
 // CHECK-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.3], class C)
 // CHECK:        [B3]
@@ -217,14 +217,14 @@
 // CHECK: D(double)
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (ValueTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, C([B1.4]) (Base initializer), class C)
 // CHECK-NEXT:     6: C([B1.5]) (Base initializer)
 // CHECK-NEXT:     7: CFGNewAllocator(C *)
 // CHECK-NEXT:     8: C::get
 // CHECK-NEXT:     9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:    10: [B1.9]()
+// CHECK-NEXT:    10: [B1.9]() (ValueTypedCall, [B1.11])
 // CHECK-NEXT:    11: [B1.10]
 // CHECK-NEXT:    12: [B1.11] (CXXConstructExpr, [B1.13], class C)
 // CHECK-NEXT:    13: new C([B1.12])
@@ -299,7 +299,7 @@
 // CHECK: C returnTemporaryConstructedByFunction()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (ValueTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CHECK-NEXT:     6: return [B1.5];
@@ -310,7 +310,7 @@
 // CHECK: C returnChainOfCopies()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (ValueTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class C)
 // CHECK-NEXT:     6: C([B1.5]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
@@ -435,7 +435,7 @@
 // CHECK:        [B5]
 // CHECK-NEXT:     1: D::get
 // CHECK-NEXT:     2: [B5.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void))
-// CHECK-NEXT:     3: [B5.2]()
+// CHECK-NEXT:     3: [B5.2]() (ValueTypedCall, [B5.4], [B5.6])
 // CHECK-NEXT:     4: [B5.3] (BindTemporary)
 // CHECK-NEXT:     5: [B5.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
 // CHECK-NEXT:     6: [B5.5]
@@ -518,7 +518,7 @@
 // CHECK: void implicitConstructionConversionFromFunctionValue()
 // CHECK:          1: get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conversion::A (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (ValueTypedCall, [B1.5])
 // CHECK-NEXT:     4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
 // CHECK-NEXT:     5: [B1.4]
 // CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B)
@@ -552,7 +552,7 @@
 // CHECK: void implicitConstructionConversionFromFunctionValueWithLifetimeExtension()
 // CHECK:          1: get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conver
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (ValueTypedCall, [B1.5])
 // CHECK-NEXT:     4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
 // CHECK-NEXT:     5: [B1.4]
 // CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.9], class implicit_constructor_conversion::B)
Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp
===================================================================
--- lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -567,6 +567,7 @@
   switch (Source.getKind()) {
   case CFGElement::Statement:
   case CFGElement::Constructor:
+  case CFGElement::ValueTypedCall:
     return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),
                                   SM, CallerCtx);
   case CFGElement::Initializer: {
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -612,6 +612,7 @@
   switch (E.getKind()) {
     case CFGElement::Statement:
     case CFGElement::Constructor:
+    case CFGElement::ValueTypedCall:
       ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred);
       return;
     case CFGElement::Initializer:
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -475,8 +475,8 @@
 
   // Information about the currently visited C++ object construction site.
   // This is set in the construction trigger and read when the constructor
-  // itself is being visited.
-  llvm::DenseMap<CXXConstructExpr *, const ConstructionContextLayer *>
+  // or a function that returns an object by value is being visited.
+  llvm::DenseMap<Expr *, const ConstructionContextLayer *>
       ConstructionContextMap;
 
   bool badCFG = false;
@@ -656,7 +656,7 @@
   // Remember to apply the construction context based on the current \p Layer
   // when constructing the CFG element for \p CE.
   void consumeConstructionContext(const ConstructionContextLayer *Layer,
-                                  CXXConstructExpr *CE);
+                                  Expr *E);
 
   // Scan \p Child statement to find constructors in it, while keeping in mind
   // that its parent statement is providing a partial construction context
@@ -667,9 +667,9 @@
                                 Stmt *Child);
 
   // Unset the construction context after consuming it. This is done immediately
-  // after adding the CFGConstructor element, so there's no need to
-  // do this manually in every Visit... function.
-  void cleanupConstructionContext(CXXConstructExpr *CE);
+  // after adding the CFGConstructor or CFGValueTypedCall element, so there's
+  // no need to do this manually in every Visit... function.
+  void cleanupConstructionContext(Expr *E);
 
   void autoCreateBlock() { if (!Block) Block = createBlock(); }
   CFGBlock *createBlock(bool add_successor = true);
@@ -727,6 +727,27 @@
     B->appendStmt(CE, cfg->getBumpVectorContext());
   }
 
+  void appendCall(CFGBlock *B, CallExpr *CE) {
+    if (BuildOpts.AddRichCXXConstructors) {
+      if (CFGValueTypedCall::isValueTypedCall(CE)) {
+        if (const ConstructionContextLayer *Layer =
+                ConstructionContextMap.lookup(CE)) {
+          const ConstructionContext *CC =
+              ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
+                                                    Layer);
+          B->appendValueTypedCall(CE,
+                                  cast<TemporaryObjectConstructionContext>(CC),
+                                  cfg->getBumpVectorContext());
+          cleanupConstructionContext(CE);
+          return;
+        }
+      }
+    }
+
+    // No valid construction context found. Fall back to statement.
+    B->appendStmt(CE, cfg->getBumpVectorContext());
+  }
+
   void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) {
     B->appendInitializer(I, cfg->getBumpVectorContext());
   }
@@ -1162,15 +1183,15 @@
 }
 
 void CFGBuilder::consumeConstructionContext(
-    const ConstructionContextLayer *Layer, CXXConstructExpr *CE) {
+    const ConstructionContextLayer *Layer, Expr *E) {
   if (const ConstructionContextLayer *PreviouslyStoredLayer =
-          ConstructionContextMap.lookup(CE)) {
+          ConstructionContextMap.lookup(E)) {
     // We might have visited this child when we were finding construction
     // contexts within its parents.
     assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) &&
            "Already within a different construction context!");
   } else {
-    ConstructionContextMap[CE] = Layer;
+    ConstructionContextMap[E] = Layer;
   }
 }
 
@@ -1188,6 +1209,18 @@
     consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child));
     break;
   }
+  // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr.
+  // FIXME: An isa<> would look much better but this whole switch is a
+  // workaround for an internal compiler error in MSVC 2015 (see r326021).
+  case Stmt::CallExprClass:
+  case Stmt::CXXMemberCallExprClass:
+  case Stmt::CXXOperatorCallExprClass:
+  case Stmt::UserDefinedLiteralClass: {
+    auto *CE = cast<CallExpr>(Child);
+    if (CFGValueTypedCall::isValueTypedCall(CE))
+      consumeConstructionContext(Layer, CE);
+    break;
+  }
   case Stmt::ExprWithCleanupsClass: {
     auto *Cleanups = cast<ExprWithCleanups>(Child);
     findConstructionContexts(Layer, Cleanups->getSubExpr());
@@ -1229,12 +1262,12 @@
   }
 }
 
-void CFGBuilder::cleanupConstructionContext(CXXConstructExpr *CE) {
+void CFGBuilder::cleanupConstructionContext(Expr *E) {
   assert(BuildOpts.AddRichCXXConstructors &&
          "We should not be managing construction contexts!");
-  assert(ConstructionContextMap.count(CE) &&
+  assert(ConstructionContextMap.count(E) &&
          "Cannot exit construction context without the context!");
-  ConstructionContextMap.erase(CE);
+  ConstructionContextMap.erase(E);
 }
 
 
@@ -2232,7 +2265,10 @@
   }
 
   if (!NoReturn && !AddEHEdge) {
-    return VisitStmt(C, asc.withAlwaysAdd(true));
+    autoCreateBlock();
+    appendCall(Block, C);
+
+    return VisitChildren(C);
   }
 
   if (Block) {
@@ -2246,7 +2282,7 @@
   else
     Block = createBlock();
 
-  appendStmt(Block, C);
+  appendCall(Block, C);
 
   if (AddEHEdge) {
     // Add exceptional edges.
@@ -4380,6 +4416,7 @@
     case CFGElement::LifetimeEnds:
     case CFGElement::Statement:
     case CFGElement::Constructor:
+    case CFGElement::ValueTypedCall:
       llvm_unreachable("getDestructorDecl should only be used with "
                        "ImplicitDtors");
     case CFGElement::AutomaticObjectDtor: {
@@ -4730,6 +4767,49 @@
     OS << " (Member initializer)";
 }
 
+static void print_construction_context(raw_ostream &OS,
+                                       StmtPrinterHelper &Helper,
+                                       const ConstructionContext *CC) {
+  const Stmt *S1 = nullptr, *S2 = nullptr;
+  switch (CC->getKind()) {
+  case ConstructionContext::ConstructorInitializerKind: {
+    OS << ", ";
+    const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+    print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
+    break;
+  }
+  case ConstructionContext::SimpleVariableKind: {
+    const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+    S1 = DSCC->getDeclStmt();
+    break;
+  }
+  case ConstructionContext::NewAllocatedObjectKind: {
+    const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+    S1 = NECC->getCXXNewExpr();
+    break;
+  }
+  case ConstructionContext::ReturnedValueKind: {
+    const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
+    S1 = RSCC->getReturnStmt();
+    break;
+  }
+  case ConstructionContext::TemporaryObjectKind: {
+    const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
+    S1 = TOCC->getCXXBindTemporaryExpr();
+    S2 = TOCC->getMaterializedTemporaryExpr();
+    break;
+  }
+  }
+  if (S1) {
+    OS << ", ";
+    Helper.handledStmt(const_cast<Stmt *>(S1), OS);
+  }
+  if (S2) {
+    OS << ", ";
+    Helper.handledStmt(const_cast<Stmt *>(S2), OS);
+  }
+}
+
 static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
                        const CFGElement &E) {
   if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) {
@@ -4759,54 +4839,22 @@
     }
     S->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts()));
 
-    if (isa<CXXOperatorCallExpr>(S)) {
+    if (auto VTC = E.getAs<CFGValueTypedCall>()) {
+      if (isa<CXXOperatorCallExpr>(S))
+        OS << " (OperatorCall)";
+      OS << " (ValueTypedCall";
+      print_construction_context(OS, Helper, VTC->getConstructionContext());
+      OS << ")";
+    } else if (isa<CXXOperatorCallExpr>(S)) {
       OS << " (OperatorCall)";
     } else if (isa<CXXBindTemporaryExpr>(S)) {
       OS << " (BindTemporary)";
     } else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) {
-      OS << " (CXXConstructExpr, ";
+      OS << " (CXXConstructExpr";
       if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
-        const ConstructionContext *CC = CE->getConstructionContext();
-        const Stmt *S1 = nullptr, *S2 = nullptr;
-        switch (CC->getKind()) {
-        case ConstructionContext::ConstructorInitializerKind: {
-          const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
-          print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
-          OS << ", ";
-          break;
-        }
-        case ConstructionContext::SimpleVariableKind: {
-          const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
-          S1 = DSCC->getDeclStmt();
-          break;
-        }
-        case ConstructionContext::NewAllocatedObjectKind: {
-          const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
-          S1 = NECC->getCXXNewExpr();
-          break;
-        }
-        case ConstructionContext::ReturnedValueKind: {
-          const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
-          S1 = RSCC->getReturnStmt();
-          break;
-        }
-        case ConstructionContext::TemporaryObjectKind: {
-          const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
-          S1 = TOCC->getCXXBindTemporaryExpr();
-          S2 = TOCC->getMaterializedTemporaryExpr();
-          break;
-        }
-        }
-        if (S1) {
-          Helper.handledStmt(const_cast<Stmt *>(S1), OS);
-          OS << ", ";
-        }
-        if (S2) {
-          Helper.handledStmt(const_cast<Stmt *>(S2), OS);
-          OS << ", ";
-        }
+        print_construction_context(OS, Helper, CE->getConstructionContext());
       }
-      OS << CCE->getType().getAsString() << ")";
+      OS << ", " << CCE->getType().getAsString() << ")";
     } else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) {
       OS << " (" << CE->getStmtClassName() << ", "
          << CE->getCastKindName()
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -39,6 +39,7 @@
 class BinaryOperator;
 class CFG;
 class ConstructionContext;
+class TemporaryObjectConstructionContext;
 class CXXBaseSpecifier;
 class CXXBindTemporaryExpr;
 class CXXCtorInitializer;
@@ -63,8 +64,9 @@
     // stmt kind
     Statement,
     Constructor,
+    ValueTypedCall,
     STMT_BEGIN = Statement,
-    STMT_END = Constructor,
+    STMT_END = ValueTypedCall,
     // dtor kind
     AutomaticObjectDtor,
     DeleteDtor,
@@ -156,10 +158,6 @@
     return static_cast<ConstructionContext *>(Data2.getPointer());
   }
 
-  QualType getType() const {
-    return cast<CXXConstructExpr>(getStmt())->getType();
-  }
-
 private:
   friend class CFGElement;
 
@@ -170,6 +168,46 @@
   }
 };
 
+/// CFGValueTypedCall - Represents a function call that returns a C++ object by
+/// value. This, like constructor, requires a construction context, which would
+/// always be that of a temporary object - usually consumed by an elidable
+/// constructor. For such value-typed calls the ReturnedValueConstructionContext
+/// of their return value is naturally complemented by the
+/// TemporaryObjectConstructionContext at the call site (here). In C such
+/// tracking is not necessary because no additional effort is required for
+/// destroying the object or modeling copy elision. Like CFGConstructor, this is
+/// for now only used by the analyzer's CFG.
+class CFGValueTypedCall : public CFGStmt {
+public:
+  /// Returns true when call expression \p CE needs to be represented
+  /// by CFGValueTypedCall, as opposed to a regular CFGStmt.
+  static bool isValueTypedCall(CallExpr *CE) {
+    return CE->getType().getCanonicalType()->getAsCXXRecordDecl();
+  }
+
+  explicit CFGValueTypedCall(CallExpr *CE,
+                             const TemporaryObjectConstructionContext *C)
+      : CFGStmt(CE, ValueTypedCall) {
+    assert(isValueTypedCall(CE));
+    assert(C);
+    Data2.setPointer(const_cast<TemporaryObjectConstructionContext *>(C));
+  }
+
+  const TemporaryObjectConstructionContext *getConstructionContext() const {
+    return static_cast<TemporaryObjectConstructionContext *>(
+        Data2.getPointer());
+  }
+
+private:
+  friend class CFGElement;
+
+  CFGValueTypedCall() = default;
+
+  static bool isKind(const CFGElement &E) {
+    return E.getKind() == ValueTypedCall;
+  }
+};
+
 /// CFGInitializer - Represents C++ base or member initializer from
 /// constructor's initialization list.
 class CFGInitializer : public CFGElement {
@@ -789,6 +827,12 @@
     Elements.push_back(CFGConstructor(CE, CC), C);
   }
 
+  void appendValueTypedCall(CallExpr *CE,
+                            const TemporaryObjectConstructionContext *CC,
+                            BumpVectorContext &C) {
+    Elements.push_back(CFGValueTypedCall(CE, CC), C);
+  }
+
   void appendInitializer(CXXCtorInitializer *initializer,
                         BumpVectorContext &C) {
     Elements.push_back(CFGInitializer(initializer), C);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to