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

Replace stubs we had to discriminate between temporaries and function arguments 
with actual construction contexts for arguments. Make them visible in the CFG. 
Which means that when there's an argument constructor in the CFG, it'll now be 
a `CFGConstructor` element (or a `CFGCXXRecordTypedCall` element if it's a 
temporary produced by a function call in C++17 where copy elision is mandatory) 
that will augment the construct-expression with the information about the 
constructed object  - that it's argument #N of a certain function call.

On the analyzer side, the patch interacts with the recently implemented 
pre-C++17 copy elision support (https://reviews.llvm.org/D47616, 
https://reviews.llvm.org/D47671) in an interesting manner. If on the CFG side 
we didn't find a construction context for the elidable constructor, we build 
the CFG as if the elidable constructor is not elided, and the non-elided 
constructor within it is a simple temporary. But the same problem may occur in 
the analyzer: if the elidable constructor has a construction context but the 
analyzer doesn't implement such context yet, the analyzer should also try to 
skip copy elision and still inline the non-elided temporary constructor. This 
was implemented by adding a "roll back" mechanism: when elision fails, roll 
back the changes and proceed as if it's a simple temporary. The approach is 
wonky, but i'm fine with that as long as it's merely a defensive mechanism that 
should eventually go away once all construction contexts become supported.


Repository:
  rC Clang

https://reviews.llvm.org/D48681

Files:
  include/clang/Analysis/CFG.h
  include/clang/Analysis/ConstructionContext.h
  lib/Analysis/CFG.cpp
  lib/Analysis/ConstructionContext.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/cfg-rich-constructors.cpp
  test/Analysis/cfg-rich-constructors.mm

Index: test/Analysis/cfg-rich-constructors.mm
===================================================================
--- test/Analysis/cfg-rich-constructors.mm
+++ test/Analysis/cfg-rich-constructors.mm
@@ -18,21 +18,21 @@
 -(D) bar;
 @end
 
-// FIXME: Find construction context for the argument.
 // CHECK: void passArgumentIntoMessage(E *e)
 // CHECK:          1: e
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, LValueToRValue, E *)
-// CXX11-NEXT:     3: D() (CXXConstructExpr, [B1.4], [B1.6], class D)
+// CXX11-ELIDE-NEXT:     3: D() (CXXConstructExpr, [B1.4], [B1.6], [B1.7], class D)
+// CXX11-NOELIDE-NEXT:     3: D() (CXXConstructExpr, [B1.4], [B1.6], class D)
 // CXX11-NEXT:     4: [B1.3] (BindTemporary)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class D)
 // CXX11-NEXT:     6: [B1.5]
-// CXX11-NEXT:     7: [B1.6] (CXXConstructExpr, class D)
+// CXX11-NEXT:     7: [B1.6] (CXXConstructExpr, [B1.8], [B1.9]+0, class D)
 // CXX11-NEXT:     8: [B1.7] (BindTemporary)
 // Double brackets trigger FileCheck variables, escape.
 // CXX11-NEXT:     9: {{\[}}[B1.2] foo:[B1.8]]
 // CXX11-NEXT:    10: ~D() (Temporary object destructor)
 // CXX11-NEXT:    11: ~D() (Temporary object destructor)
-// CXX17-NEXT:     3: D() (CXXConstructExpr, class D)
+// CXX17-NEXT:     3: D() (CXXConstructExpr, [B1.4], [B1.5]+0, class D)
 // CXX17-NEXT:     4: [B1.3] (BindTemporary)
 // Double brackets trigger FileCheck variables, escape.
 // CXX17-NEXT:     5: {{\[}}[B1.2] foo:[B1.4]]
Index: test/Analysis/cfg-rich-constructors.cpp
===================================================================
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -823,21 +823,49 @@
 void useCByReference(const C &c);
 void useD(D d);
 void useDByReference(const D &d);
+void useCAndD(C c, D d);
 
-// FIXME: Find construction context for the argument.
 // CHECK: void passArgument()
 // CHECK:          1: useC
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(class C))
-// CXX11-NEXT:     3: C() (CXXConstructExpr, [B1.4], class C)
+// CXX11-ELIDE-NEXT:     3: C() (CXXConstructExpr, [B1.4], [B1.5], class C)
+// CXX11-NOELIDE-NEXT:     3: C() (CXXConstructExpr, [B1.4], class C)
 // CXX11-NEXT:     4: [B1.3]
-// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, class C)
+// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6]+0, class C)
 // CXX11-NEXT:     6: [B1.2]([B1.5])
-// CXX17-NEXT:     3: C() (CXXConstructExpr, class C)
+// CXX17-NEXT:     3: C() (CXXConstructExpr, [B1.4]+0, class C)
 // CXX17-NEXT:     4: [B1.2]([B1.3])
 void passArgument() {
   useC(C());
 }
 
+// CHECK: void passTwoArguments()
+// CHECK:        [B1]
+// CHECK-NEXT:     1: useCAndD
+// CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(class C, class argument_constructors::D))
+// CXX11-ELIDE-NEXT:     3: C() (CXXConstructExpr, [B1.4], [B1.5], class C)
+// CXX11-NOELIDE-NEXT:     3: C() (CXXConstructExpr, [B1.4], class C)
+// CXX11-NEXT:     4: [B1.3]
+// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.12]+0, class C)
+// CXX11-ELIDE-NEXT:     6: argument_constructors::D() (CXXConstructExpr, [B1.7], [B1.9], [B1.10], class argument_constructors::D)
+// CXX11-NOELIDE-NEXT:     6: argument_constructors::D() (CXXConstructExpr, [B1.7], [B1.9], class argument_constructors::D)
+// CXX11-NEXT:     7: [B1.6] (BindTemporary)
+// CXX11-NEXT:     8: [B1.7] (ImplicitCastExpr, NoOp, const class argument_constructors::D)
+// CXX11-NEXT:     9: [B1.8]
+// CXX11-NEXT:    10: [B1.9] (CXXConstructExpr, [B1.11], [B1.12]+1, class argument_constructors::D)
+// CXX11-NEXT:    11: [B1.10] (BindTemporary)
+// CXX11-NEXT:    12: [B1.2]([B1.5], [B1.11])
+// CXX11-NEXT:    13: ~argument_constructors::D() (Temporary object destructor)
+// CXX11-NEXT:    14: ~argument_constructors::D() (Temporary object destructor)
+// CXX17-NEXT:     3: C() (CXXConstructExpr, [B1.6]+0, class C)
+// CXX17-NEXT:     4: argument_constructors::D() (CXXConstructExpr, [B1.5], [B1.6]+1, class argument_co
+// CXX17-NEXT:     5: [B1.4] (BindTemporary)
+// CXX17-NEXT:     6: [B1.2]([B1.3], [B1.5])
+// CXX17-NEXT:     7: ~argument_constructors::D() (Temporary object destructor)
+void passTwoArguments() {
+  useCAndD(C(), D());
+}
+
 // CHECK: void passArgumentByReference()
 // CHECK:          1: useCByReference
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class C &))
@@ -849,20 +877,20 @@
   useCByReference(C());
 }
 
-// FIXME: Find construction context for the argument.
 // CHECK: void passArgumentWithDestructor()
 // CHECK:          1: useD
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(class argument_constructors::D))
-// CXX11-NEXT:     3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.6], class argument_constructors::D)
+// CXX11-ELIDE-NEXT:     3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.6], [B1.7], class argument_constructors::D)
+// CXX11-NOELIDE-NEXT:     3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.6], class argument_constructors::D)
 // CXX11-NEXT:     4: [B1.3] (BindTemporary)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class argument_constructors::D)
 // CXX11-NEXT:     6: [B1.5]
-// CXX11-NEXT:     7: [B1.6] (CXXConstructExpr, class argument_constructors::D)
+// CXX11-NEXT:     7: [B1.6] (CXXConstructExpr, [B1.8], [B1.9]+0, class argument_constructors::D)
 // CXX11-NEXT:     8: [B1.7] (BindTemporary)
 // CXX11-NEXT:     9: [B1.2]([B1.8])
 // CXX11-NEXT:    10: ~argument_constructors::D() (Temporary object destructor)
 // CXX11-NEXT:    11: ~argument_constructors::D() (Temporary object destructor)
-// CXX17-NEXT:     3: argument_constructors::D() (CXXConstructExpr, class argument_constructors::D)
+// CXX17-NEXT:     3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.5]+0, class argument_constructors::D)
 // CXX17-NEXT:     4: [B1.3] (BindTemporary)
 // CXX17-NEXT:     5: [B1.2]([B1.4])
 // CXX17-NEXT:     6: ~argument_constructors::D() (Temporary object destructor)
@@ -883,13 +911,13 @@
   useDByReference(D());
 }
 
-// FIXME: Find construction context for the argument.
 // CHECK: void passArgumentIntoAnotherConstructor()
-// CXX11:          1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], class argument_constructors::D)
+// CXX11-ELIDE:          1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class argument_constructors::D)
+// CXX11-NOELIDE:          1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], class argument_constructors::D)
 // CXX11-NEXT:     2: [B1.1] (BindTemporary)
 // CXX11-NEXT:     3: [B1.2] (ImplicitCastExpr, NoOp, const class argument_constructors::D)
 // CXX11-NEXT:     4: [B1.3]
-// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, class argument_constructors::D)
+// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], [B1.7]+0, class argument_constructors::D)
 // CXX11-NEXT:     6: [B1.5] (BindTemporary)
 // CXX11-ELIDE-NEXT:     7: [B1.6] (CXXConstructExpr, [B1.9], [B1.10], class argument_constructors::E)
 // CXX11-NOELIDE-NEXT:     7: [B1.6] (CXXConstructExpr, [B1.9], class argument_constructors::E)
@@ -899,7 +927,7 @@
 // CXX11-NEXT:    11: argument_constructors::E e = argument_constructors::E(argument_constructors::D());
 // CXX11-NEXT:    12: ~argument_constructors::D() (Temporary object destructor)
 // CXX11-NEXT:    13: ~argument_constructors::D() (Temporary object destructor)
-// CXX17:          1: argument_constructors::D() (CXXConstructExpr, class argument_constructors::D)
+// CXX17:          1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.3]+0, class argument_constructors::D)
 // CXX17-NEXT:     2: [B1.1] (BindTemporary)
 // CXX17-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class argument_constructors::E)
 // CXX17-NEXT:     4: argument_constructors::E([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class argument_constructors::E)
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -221,25 +221,42 @@
       // and construct directly into the final object. This call
       // also sets the CallOpts flags for us.
       SVal V;
+      // If the elided copy/move constructor is not supported, there's still
+      // benefit in trying to model the non-elided constructor.
+      // Stash our state before trying to elide, as it'll get overwritten.
+      ProgramStateRef PreElideState = State;
+      EvalCallOptions PreElideCallOpts = CallOpts;
+
       std::tie(State, V) = prepareForObjectConstruction(
           CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
 
-      // Remember that we've elided the constructor.
-      State = addObjectUnderConstruction(State, CE, LCtx, V);
+      // FIXME: This definition of "copy elision has not failed" is unreliable.
+      // It doesn't indicate that the constructor will actually be inlined
+      // later; it is still up to evalCall() to decide.
+      if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
+        // Remember that we've elided the constructor.
+        State = addObjectUnderConstruction(State, CE, LCtx, V);
 
-      // Remember that we've elided the destructor.
-      if (BTE)
-        State = elideDestructor(State, BTE, LCtx);
+        // Remember that we've elided the destructor.
+        if (BTE)
+          State = elideDestructor(State, BTE, LCtx);
 
-      // Instead of materialization, shamelessly return
-      // the final object destination.
-      if (MTE)
-        State = addObjectUnderConstruction(State, MTE, LCtx, V);
+        // Instead of materialization, shamelessly return
+        // the final object destination.
+        if (MTE)
+          State = addObjectUnderConstruction(State, MTE, LCtx, V);
 
-      return std::make_pair(State, V);
+        return std::make_pair(State, V);
+      } else {
+        // Copy elision failed. Revert the changes and proceed as if we have
+        // a simple temporary.
+        State = PreElideState;
+        CallOpts = PreElideCallOpts;
+      }
+      // FALL-THROUGH
     }
     case ConstructionContext::SimpleTemporaryObjectKind: {
-      const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC);
+      const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
       const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
       const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
       SVal V = UnknownVal();
@@ -274,6 +291,10 @@
       CallOpts.IsTemporaryCtorOrDtor = true;
       return std::make_pair(State, V);
     }
+    case ConstructionContext::ArgumentKind: {
+      // Function argument constructors. Not implemented yet.
+      break;
+    }
     }
   }
   // If we couldn't find an existing region to construct into, assume we're
Index: lib/Analysis/ConstructionContext.cpp
===================================================================
--- lib/Analysis/ConstructionContext.cpp
+++ lib/Analysis/ConstructionContext.cpp
@@ -21,10 +21,11 @@
 
 const ConstructionContextLayer *
 ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger,
+                                 unsigned Index,
                                  const ConstructionContextLayer *Parent) {
   ConstructionContextLayer *CC =
       C.getAllocator().Allocate<ConstructionContextLayer>();
-  return new (CC) ConstructionContextLayer(Trigger, Parent);
+  return new (CC) ConstructionContextLayer(Trigger, Index, Parent);
 }
 
 bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
@@ -111,11 +112,14 @@
         }
         assert(ParentLayer->isLast());
 
-        // This is a constructor into a function argument. Not implemented yet.
+        // This is a constructor into a function argument.
         if (isa<CallExpr>(ParentLayer->getTriggerStmt()) ||
             isa<CXXConstructExpr>(ParentLayer->getTriggerStmt()) ||
-            isa<ObjCMessageExpr>(ParentLayer->getTriggerStmt()))
-          return nullptr;
+            isa<ObjCMessageExpr>(ParentLayer->getTriggerStmt())) {
+          return create<ArgumentConstructionContext>(
+              C, cast<Expr>(ParentLayer->getTriggerStmt()),
+              ParentLayer->getIndex(), BTE);
+        }
         // This is C++17 copy-elided construction into return statement.
         if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
           assert(!RS->getRetValue()->getType().getCanonicalType()
@@ -175,11 +179,14 @@
       assert(TopLayer->isLast());
       return create<SimpleReturnedValueConstructionContext>(C, RS);
     }
-    // This is a constructor into a function argument. Not implemented yet.
+    // This is a constructor into a function argument.
     if (isa<CallExpr>(TopLayer->getTriggerStmt()) ||
         isa<CXXConstructExpr>(TopLayer->getTriggerStmt()) ||
-        isa<ObjCMessageExpr>(TopLayer->getTriggerStmt()))
-      return nullptr;
+        isa<ObjCMessageExpr>(TopLayer->getTriggerStmt())) {
+      return create<ArgumentConstructionContext>(
+          C, cast<Expr>(TopLayer->getTriggerStmt()), TopLayer->getIndex(),
+          /*BTE=*/nullptr);
+    }
     llvm_unreachable("Unexpected construction context with statement!");
   } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
     assert(TopLayer->isLast());
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -693,16 +693,13 @@
                 std::is_same<CallLikeExpr, CXXConstructExpr>::value ||
                 std::is_same<CallLikeExpr, ObjCMessageExpr>::value>>
   void findConstructionContextsForArguments(CallLikeExpr *E) {
-    // A stub for the code that'll eventually be used for finding construction
-    // contexts for constructors of C++ object-type arguments passed into
-    // call-like expression E.
-    // FIXME: Once actually implemented, this construction context layer should
-    // include the index of the argument as well.
-    for (auto Arg : E->arguments())
+    for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) {
+      Expr *Arg = E->getArg(i);
       if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue())
         findConstructionContexts(
-            ConstructionContextLayer::create(cfg->getBumpVectorContext(), E),
+            ConstructionContextLayer::create(cfg->getBumpVectorContext(), E, i),
             Arg);
+    }
   }
 
   // Unset the construction context after consuming it. This is done immediately
@@ -1289,9 +1286,9 @@
   if (!Child)
     return;
 
-  auto withExtraLayer = [this, Layer](Stmt *S) {
+  auto withExtraLayer = [this, Layer](Stmt *S, unsigned Index = 0) {
     return ConstructionContextLayer::create(cfg->getBumpVectorContext(), S,
-                                            Layer);
+                                            Index, Layer);
   };
 
   switch(Child->getStmtClass()) {
@@ -5011,7 +5008,7 @@
     OS << ", ";
     const auto *SICC = cast<SimpleConstructorInitializerConstructionContext>(CC);
     print_initializer(OS, Helper, SICC->getCXXCtorInitializer());
-    break;
+    return;
   }
   case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: {
     OS << ", ";
@@ -5062,6 +5059,17 @@
     Stmts.push_back(TOCC->getConstructorAfterElision());
     break;
   }
+  case ConstructionContext::ArgumentKind: {
+    const auto *ACC = cast<ArgumentConstructionContext>(CC);
+    if (const Stmt *BTE = ACC->getCXXBindTemporaryExpr()) {
+      OS << ", ";
+      Helper.handledStmt(const_cast<Stmt *>(BTE), OS);
+    }
+    OS << ", ";
+    Helper.handledStmt(const_cast<Expr *>(ACC->getCallLikeExpr()), OS);
+    OS << "+" << ACC->getIndex();
+    return;
+  }
   }
   for (auto I: Stmts)
     if (I) {
Index: include/clang/Analysis/ConstructionContext.h
===================================================================
--- include/clang/Analysis/ConstructionContext.h
+++ include/clang/Analysis/ConstructionContext.h
@@ -41,6 +41,12 @@
   /// new-expression triggers construction of the newly allocated object(s).
   TriggerTy Trigger;
 
+  /// If a single trigger statement triggers multiple constructors, they are
+  /// usually being enumerated. This covers function argument constructors
+  /// triggered by a call-expression and items in an initializer list triggered
+  /// by an init-list-expression.
+  unsigned Index;
+
   /// Sometimes a single trigger is not enough to describe the construction
   /// site. In this case we'd have a chain of "partial" construction context
   /// layers.
@@ -55,13 +61,13 @@
   /// Not all of these are currently supported.
   const ConstructionContextLayer *Parent = nullptr;
 
-  ConstructionContextLayer(TriggerTy Trigger,
-                          const ConstructionContextLayer *Parent)
-      : Trigger(Trigger), Parent(Parent) {}
+  ConstructionContextLayer(TriggerTy Trigger, unsigned Index,
+                           const ConstructionContextLayer *Parent)
+      : Trigger(Trigger), Index(Index), Parent(Parent) {}
 
 public:
   static const ConstructionContextLayer *
-  create(BumpVectorContext &C, TriggerTy Trigger,
+  create(BumpVectorContext &C, TriggerTy Trigger, unsigned Index = 0,
          const ConstructionContextLayer *Parent = nullptr);
 
   const ConstructionContextLayer *getParent() const { return Parent; }
@@ -75,6 +81,8 @@
     return Trigger.dyn_cast<CXXCtorInitializer *>();
   }
 
+  unsigned getIndex() const { return Index; }
+
   /// Returns true if these layers are equal as individual layers, even if
   /// their parents are different.
   bool isSameLayer(const ConstructionContextLayer *Other) const {
@@ -114,7 +122,8 @@
     SimpleReturnedValueKind,
     CXX17ElidedCopyReturnedValueKind,
     RETURNED_VALUE_BEGIN = SimpleReturnedValueKind,
-    RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind
+    RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind,
+    ArgumentKind
   };
 
 protected:
@@ -469,6 +478,32 @@
   }
 };
 
+class ArgumentConstructionContext : public ConstructionContext {
+  const Expr *CE; // The call of which the context is an argument.
+  unsigned Index; // Which argument we're constructing.
+  const CXXBindTemporaryExpr *BTE; // Whether the object needs to be destroyed.
+
+  friend class ConstructionContext; // Allows to create<>() itself.
+
+  explicit ArgumentConstructionContext(const Expr *CE, unsigned Index,
+                                       const CXXBindTemporaryExpr *BTE)
+      : ConstructionContext(ArgumentKind), CE(CE),
+        Index(Index), BTE(BTE) {
+    assert(isa<CallExpr>(CE) || isa<CXXConstructExpr>(CE) ||
+           isa<ObjCMessageExpr>(CE));
+    // BTE is optional.
+  }
+
+public:
+  const Expr *getCallLikeExpr() const { return CE; }
+  unsigned getIndex() const { return Index; }
+  const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == ArgumentKind;
+  }
+};
+
 } // end namespace clang
 
 #endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -196,7 +196,8 @@
                  // These are possible in C++17 due to mandatory copy elision.
                  isa<ReturnedValueConstructionContext>(C) ||
                  isa<VariableConstructionContext>(C) ||
-                 isa<ConstructorInitializerConstructionContext>(C)));
+                 isa<ConstructorInitializerConstructionContext>(C) ||
+                 isa<ArgumentConstructionContext>(C)));
     Data2.setPointer(const_cast<ConstructionContext *>(C));
   }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to