This revision was automatically updated to reflect the committed changes.
Closed by commit rL335795: [CFG] [analyzer] Add construction contexts that 
explain pre-C++17 copy elision. (authored by dergachev, committed by ).
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D47616?vs=151277&id=153212#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D47616

Files:
  cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
  cfe/trunk/include/clang/Analysis/CFG.h
  cfe/trunk/include/clang/Analysis/ConstructionContext.h
  cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
  cfe/trunk/lib/Analysis/CFG.cpp
  cfe/trunk/lib/Analysis/ConstructionContext.cpp
  cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp
  cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  cfe/trunk/test/Analysis/analyzer-config.c
  cfe/trunk/test/Analysis/analyzer-config.cpp
  cfe/trunk/test/Analysis/cfg-rich-constructors.cpp
  cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp

Index: cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -327,6 +327,9 @@
   /// \sa naiveCTUEnabled
   Optional<bool> NaiveCTU;
 
+  /// \sa shouldElideConstructors
+  Optional<bool> ElideConstructors;
+
 
   /// A helper function that retrieves option for a given full-qualified
   /// checker name.
@@ -703,6 +706,13 @@
   /// This is an experimental feature to inline functions from another
   /// translation units.
   bool naiveCTUEnabled();
+
+  /// Returns true if elidable C++ copy-constructors and move-constructors
+  /// should be actually elided during analysis. Both behaviors are allowed
+  /// by the C++ standard, and the analyzer, like CodeGen, defaults to eliding.
+  /// Starting with C++17 some elisions become mandatory, and in these cases
+  /// the option will be ignored.
+  bool shouldElideConstructors();
 };
   
 using AnalyzerOptionsRef = IntrusiveRefCntPtr<AnalyzerOptions>;
Index: cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
===================================================================
--- cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
+++ cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
@@ -451,6 +451,7 @@
                              bool addStaticInitBranches = false,
                              bool addCXXNewAllocator = true,
                              bool addRichCXXConstructors = true,
+                             bool markElidedCXXConstructors = true,
                              CodeInjector *injector = nullptr);
 
   AnalysisDeclContext *getContext(const Decl *D);
Index: cfe/trunk/include/clang/Analysis/ConstructionContext.h
===================================================================
--- cfe/trunk/include/clang/Analysis/ConstructionContext.h
+++ cfe/trunk/include/clang/Analysis/ConstructionContext.h
@@ -107,7 +107,10 @@
     INITIALIZER_BEGIN = SimpleConstructorInitializerKind,
     INITIALIZER_END = CXX17ElidedCopyConstructorInitializerKind,
     NewAllocatedObjectKind,
-    TemporaryObjectKind,
+    SimpleTemporaryObjectKind,
+    ElidedTemporaryObjectKind,
+    TEMPORARY_BEGIN = SimpleTemporaryObjectKind,
+    TEMPORARY_END = ElidedTemporaryObjectKind,
     SimpleReturnedValueKind,
     CXX17ElidedCopyReturnedValueKind,
     RETURNED_VALUE_BEGIN = SimpleReturnedValueKind,
@@ -305,16 +308,15 @@
   const CXXBindTemporaryExpr *BTE;
   const MaterializeTemporaryExpr *MTE;
 
-  friend class ConstructionContext; // Allows to create<>() itself.
-
+protected:
   explicit TemporaryObjectConstructionContext(
-      const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
-      : ConstructionContext(ConstructionContext::TemporaryObjectKind),
-        BTE(BTE), MTE(MTE) {
+      ConstructionContext::Kind K, const CXXBindTemporaryExpr *BTE,
+      const MaterializeTemporaryExpr *MTE)
+      : ConstructionContext(K), BTE(BTE), MTE(MTE) {
     // Both BTE and MTE can be null here, all combinations possible.
     // Even though for now at least one should be non-null, we simply haven't
-    // implemented this case yet (this would be a temporary in the middle of
-    // nowhere that doesn't have a non-trivial destructor).
+    // implemented the other case yet (this would be a temporary in the middle
+    // of nowhere that doesn't have a non-trivial destructor).
   }
 
 public:
@@ -334,7 +336,67 @@
   }
 
   static bool classof(const ConstructionContext *CC) {
-    return CC->getKind() == TemporaryObjectKind;
+    return CC->getKind() >= TEMPORARY_BEGIN && CC->getKind() <= TEMPORARY_END;
+  }
+};
+
+/// Represents a temporary object that is not constructed for the purpose of
+/// being immediately copied/moved by an elidable copy/move-constructor.
+/// This includes temporary objects "in the middle of nowhere" like T(123) and
+/// lifetime-extended temporaries.
+class SimpleTemporaryObjectConstructionContext
+    : public TemporaryObjectConstructionContext {
+  friend class ConstructionContext; // Allows to create<>() itself.
+
+  explicit SimpleTemporaryObjectConstructionContext(
+      const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
+      : TemporaryObjectConstructionContext(
+            ConstructionContext::SimpleTemporaryObjectKind, BTE, MTE) {}
+
+public:
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == SimpleTemporaryObjectKind;
+  }
+};
+
+/// Represents a temporary object that is constructed for the sole purpose
+/// of being immediately copied by an elidable copy/move constructor.
+/// For example, T t = T(123); includes a temporary T(123) that is immediately
+/// copied to variable t. In such cases the elidable copy can (but not
+/// necessarily should) be omitted ("elided") accodring to the rules of the
+/// language; the constructor would then construct variable t directly.
+/// This construction context contains information of the elidable constructor
+/// and its respective construction context.
+class ElidedTemporaryObjectConstructionContext
+    : public TemporaryObjectConstructionContext {
+  const CXXConstructExpr *ElidedCE;
+  const ConstructionContext *ElidedCC;
+
+  friend class ConstructionContext; // Allows to create<>() itself.
+
+  explicit ElidedTemporaryObjectConstructionContext(
+      const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE,
+      const CXXConstructExpr *ElidedCE, const ConstructionContext *ElidedCC)
+      : TemporaryObjectConstructionContext(
+            ConstructionContext::ElidedTemporaryObjectKind, BTE, MTE),
+        ElidedCE(ElidedCE), ElidedCC(ElidedCC) {
+    // Elided constructor and its context should be either both specified
+    // or both unspecified. In the former case, the constructor must be
+    // elidable.
+    assert(ElidedCE && ElidedCE->isElidable() && ElidedCC);
+  }
+
+public:
+  const CXXConstructExpr *getConstructorAfterElision() const {
+    return ElidedCE;
+  }
+
+  const ConstructionContext *getConstructionContextAfterElision() const {
+    return ElidedCC;
+  }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == ElidedTemporaryObjectKind;
   }
 };
 
Index: cfe/trunk/include/clang/Analysis/CFG.h
===================================================================
--- cfe/trunk/include/clang/Analysis/CFG.h
+++ cfe/trunk/include/clang/Analysis/CFG.h
@@ -1025,6 +1025,7 @@
     bool AddCXXNewAllocator = false;
     bool AddCXXDefaultInitExprInCtors = false;
     bool AddRichCXXConstructors = false;
+    bool MarkElidedCXXConstructors = false;
 
     BuildOptions() = default;
 
Index: cfe/trunk/test/Analysis/cfg-rich-constructors.cpp
===================================================================
--- cfe/trunk/test/Analysis/cfg-rich-constructors.cpp
+++ cfe/trunk/test/Analysis/cfg-rich-constructors.cpp
@@ -1,7 +1,11 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 -w %s > %t 2>&1
-// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11 %s
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,ELIDE,CXX11-ELIDE %s
 // RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++17 -w %s > %t 2>&1
-// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17 %s
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,ELIDE,CXX17-ELIDE %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 -w -analyzer-config elide-constructors=false %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,NOELIDE,CXX11-NOELIDE %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++17 -w -analyzer-config elide-constructors=false %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,NOELIDE,CXX17-NOELIDE %s
 
 class C {
 public:
@@ -99,10 +103,12 @@
 // CHECK: void simpleVariableInitializedByValue()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX11-NEXT:     4: [B1.3]
 // CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CXX11-NEXT:     6: C c = C::get();
+// CXX17-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX17-NEXT:     4: C c = C::get();
 void simpleVariableInitializedByValue() {
   C c = C::get();
@@ -122,17 +128,21 @@
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
+// CXX11-ELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4], [B2.5])
+// CXX11-NOELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
 // CXX11-NEXT:     4: [B2.3]
-// CXX11-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
+// CXX11-ELIDE-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], [B1.3], class C)
+// CXX11-NOELIDE-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
 // CXX17-NEXT:     3: [B2.2]()
 // CHECK:        [B3]
 // CHECK-NEXT:     1: 0
 // CHECK-NEXT:     2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CXX11-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
+// CXX11-ELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], [B3.6], class C)
+// CXX11-NOELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
 // CXX11-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B3.4]
-// CXX11-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.2], class C)
+// CXX11-ELIDE-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.2], [B1.3], class C)
+// CXX11-NOELIDE-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.2], class C)
 // CXX17-NEXT:     3: [B3.2] (CXXConstructExpr, class C)
 // CXX17-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CHECK:        [B4]
@@ -146,7 +156,9 @@
 // CHECK: void simpleVariableWithElidableCopy()
 // CHECK:          1: 0
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX11-ELIDE-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], [B1.6], class C)
+// CXX11-NOELIDE-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX17-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
 // CHECK-NEXT:     4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B1.4]
 // CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
@@ -185,14 +197,16 @@
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
+// CXX11-ELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4], [B2.5])
+// CXX11-NOELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
 // CXX11-NEXT:     4: [B2.3]
 // CXX11-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.3], class C)
 // CXX17-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B1.3])
 // CHECK:        [B3]
 // CHECK-NEXT:     1: 0
 // CHECK-NEXT:     2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CXX11-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
+// CXX11-ELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], [B3.6], class C)
+// CXX11-NOELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
 // CXX11-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B3.4]
 // CXX11-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.3], class C)
@@ -242,7 +256,8 @@
 // CHECK-NEXT:     7: CFGNewAllocator(C *)
 // CHECK-NEXT:     8: C::get
 // CHECK-NEXT:     9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:    10: [B1.9]() (CXXRecordTypedCall, [B1.11])
+// CXX11-ELIDE-NEXT:    10: [B1.9]() (CXXRecordTypedCall, [B1.11], [B1.12])
+// CXX11-NOELIDE-NEXT:    10: [B1.9]() (CXXRecordTypedCall, [B1.11])
 // CXX11-NEXT:    11: [B1.10]
 // CXX11-NEXT:    12: [B1.11] (CXXConstructExpr, [B1.13], class C)
 // CXX11-NEXT:    13: new C([B1.12])
@@ -270,7 +285,8 @@
 // CHECK: F()
 // CHECK:          1: E::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class ctor_initializers::E (*)(
-// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
 // CXX11-NEXT:     4: [B1.3] (BindTemporary)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class ctor_initializers::E)
 // CXX11-NEXT:     6: [B1.5]
@@ -326,19 +342,23 @@
 }
 
 // CHECK: C returnTemporary()
-// CHECK:          1: C() (CXXConstructExpr, [B1.2], class C)
+// CXX11-ELIDE:    1: C() (CXXConstructExpr, [B1.2], [B1.3], class C)
+// CXX11-NOELIDE:  1: C() (CXXConstructExpr, [B1.2], class C)
 // CXX11-NEXT:     2: [B1.1]
 // CXX11-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.4], class C)
 // CXX11-NEXT:     4: return [B1.3];
+// CXX17:          1: C() (CXXConstructExpr, [B1.2], class C)
 // CXX17-NEXT:     2: return [B1.1];
 C returnTemporary() {
   return C();
 }
 
 // CHECK: C returnTemporaryWithArgument()
 // CHECK:          1: nullptr
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX11-ELIDE-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], [B1.6], class C)
+// CXX11-NOELIDE-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX17-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
 // CHECK-NEXT:     4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B1.4]
 // CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
@@ -352,21 +372,25 @@
 // CHECK: C returnTemporaryConstructedByFunction()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX11-NEXT:     4: [B1.3]
 // CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CXX11-NEXT:     6: return [B1.5];
+// CXX17-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX17-NEXT:     4: return [B1.3];
 C returnTemporaryConstructedByFunction() {
   return C::get();
 }
 
 // CHECK: C returnChainOfCopies()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX11-NEXT:     4: [B1.3]
-// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class C)
+// CXX11-ELIDE-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], [B1.8], class C)
+// CXX11-NOELIDE-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class C)
 // CXX11-NEXT:     6: C([B1.5]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     7: [B1.6]
 // CXX11-NEXT:     8: [B1.7] (CXXConstructExpr, [B1.9], class C)
@@ -390,7 +414,8 @@
 
 // FIXME: There should be no temporary destructor in C++17.
 // CHECK:  return_stmt_with_dtor::D returnTemporary()
-// CXX11:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D)
+// CXX11-ELIDE:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class return_stmt_with_dtor::D)
+// CXX11-NOELIDE:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D)
 // CXX11-NEXT:     2: [B1.1] (BindTemporary)
 // CXX11-NEXT:     3: [B1.2] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D)
 // CXX11-NEXT:     4: [B1.3]
@@ -409,7 +434,8 @@
 // CHECK: void returnByValueIntoVariable()
 // CHECK:          1: returnTemporary
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class return_stmt_with_dtor::D (*)(void))
-// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
 // CXX11-NEXT:     4: [B1.3] (BindTemporary)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D)
 // CXX11-NEXT:     6: [B1.5]
@@ -451,7 +477,8 @@
 }
 
 // CHECK: void temporaryInConditionVariable()
-// CHECK:          1: C() (CXXConstructExpr, [B2.2], class C)
+// CXX11-ELIDE:    1: C() (CXXConstructExpr, [B2.2], [B2.3], class C)
+// CXX11-NOELIDE:  1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX11-NEXT:     2: [B2.1]
 // CXX11-NEXT:     3: [B2.2] (CXXConstructExpr, [B2.4], class C)
 // CXX11-NEXT:     4: C c = C();
@@ -461,6 +488,7 @@
 // CXX11-NEXT:     8: [B2.6]
 // CXX11-NEXT:     9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
 // CXX11-NEXT:     T: if [B2.9]
+// CXX17:          1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX17-NEXT:     2: C c = C();
 // CXX17-NEXT:     3: c
 // CXX17-NEXT:     4: [B2.3] (ImplicitCastExpr, NoOp, const class C)
@@ -475,7 +503,8 @@
 
 // CHECK: void temporaryInForLoopConditionVariable()
 // CHECK:        [B2]
-// CXX11-NEXT:     1: C() (CXXConstructExpr, [B2.2], class C)
+// CXX11-ELIDE-NEXT:     1: C() (CXXConstructExpr, [B2.2], [B2.3], class C)
+// CXX11-NOELIDE-NEXT:     1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX11-NEXT:     2: [B2.1]
 // CXX11-NEXT:     3: [B2.2] (CXXConstructExpr, [B2.4], class C)
 // CXX11-NEXT:     4: C c2 = C();
@@ -494,7 +523,8 @@
 // CXX17-NEXT:     7: [B2.6] (ImplicitCastExpr, UserDefinedConversion, _Bool)
 // CXX17-NEXT:     T: for (...; [B2.7]; )
 // CHECK:        [B3]
-// CXX11-NEXT:     1: C() (CXXConstructExpr, [B3.2], class C)
+// CXX11-ELIDE-NEXT:     1: C() (CXXConstructExpr, [B3.2], [B3.3], class C)
+// CXX11-NOELIDE-NEXT:     1: C() (CXXConstructExpr, [B3.2], class C)
 // CXX11-NEXT:     2: [B3.1]
 // CXX11-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.4], class C)
 // CXX11-NEXT:     4: C c1 = C();
@@ -505,9 +535,9 @@
 }
 
 
-// FIXME: Find construction context for the loop condition variable.
 // CHECK: void temporaryInWhileLoopConditionVariable()
-// CXX11:          1: C() (CXXConstructExpr, [B2.2], class C)
+// CXX11-ELIDE:          1: C() (CXXConstructExpr, [B2.2], [B2.3], class C)
+// CXX11-NOELIDE:          1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX11-NEXT:     2: [B2.1]
 // CXX11-NEXT:     3: [B2.2] (CXXConstructExpr, [B2.4], class C)
 // CXX11-NEXT:     4: C c = C();
@@ -603,15 +633,17 @@
 // CXX11:        [B5]
 // CXX11-NEXT:     1: D::get
 // CXX11-NEXT:     2: [B5.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void))
-// CXX11-NEXT:     3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6])
+// CXX11-ELIDE-NEXT:     3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6], [B5.7])
+// CXX11-NOELIDE-NEXT:     3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6])
 // CXX11-NEXT:     4: [B5.3] (BindTemporary)
 // CXX11-NEXT:     5: [B5.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
 // CXX11-NEXT:     6: [B5.5]
 // CXX11-NEXT:     7: [B5.6] (CXXConstructExpr, [B5.8], [B4.3], class temporary_object_expr_with_dtors::D)
 // CXX11-NEXT:     8: [B5.7] (BindTemporary)
 // CXX11:        [B6]
 // CXX11-NEXT:     1: 0
-// CXX11-NEXT:     2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], class temporary_object_expr_with_dtors::D)
+// CXX11-ELIDE-NEXT:     2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], [B6.7], class temporary_object_expr_with_dtors::D)
+// CXX11-NOELIDE-NEXT:     2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], class temporary_object_expr_with_dtors::D)
 // CXX11-NEXT:     3: [B6.2] (BindTemporary)
 // CXX11-NEXT:     4: temporary_object_expr_with_dtors::D([B6.3]) (CXXFunctionalCastExpr, ConstructorConversion, class temporary_object_expr_with_dtors::D)
 // CXX11-NEXT:     5: [B6.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
@@ -699,7 +731,8 @@
 // CHECK:          1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A)
 // CXX11-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
 // CXX11-NEXT:     3: [B1.2]
-// CXX11-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B)
+// CXX11-ELIDE-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], [B1.9], class implicit_constructor_conversion::B)
+// CXX11-NOELIDE-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
 // CXX11-NEXT:     6: [B1.5] (BindTemporary)
 // CXX11-NEXT:     7: [B1.6] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
@@ -724,7 +757,8 @@
 // CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.5])
 // CHECK-NEXT:     4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
 // CHECK-NEXT:     5: [B1.4]
-// CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B)
+// CXX11-ELIDE-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], [B1.11], class implicit_constructor_conversion::B)
+// CXX11-NOELIDE-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B)
 // CXX11-NEXT:     7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
 // CXX11-NEXT:     8: [B1.7] (BindTemporary)
 // CXX11-NEXT:     9: [B1.8] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
Index: cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp
===================================================================
--- cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -235,7 +235,7 @@
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A)
 // CHECK:     2: [B1.1] (BindTemporary)
 // CHECK:     3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B1.3]
@@ -295,7 +295,7 @@
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A)
 // CHECK:     2: [B1.1] (BindTemporary)
 // CHECK:     3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B1.3]
@@ -550,12 +550,12 @@
 // CHECK:     Succs (2): B6 B5
 // CHECK:   [B8]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B8.2], [B8.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B8.2], [B8.4], [B8.5], class A)
 // CHECK:     2: [B8.1] (BindTemporary)
 // CHECK:     3: [B8.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B8.3]
 // WARNINGS:     5: [B8.4] (CXXConstructExpr, class A)
-// ANALYZER:     5: [B8.4] (CXXConstructExpr, [B8.6], [B7.3], class A)
+// ANALYZER:     5: [B8.4] (CXXConstructExpr, [B8.6], [B7.3], [B7.4], class A)
 // CHECK:     6: [B8.5] (BindTemporary)
 // CHECK:     Preds (1): B10
 // CHECK:     Succs (1): B7
@@ -570,13 +570,13 @@
 // CHECK:     7: [B9.6] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     8: [B9.7]
 // WARNINGS:     9: [B9.8] (CXXConstructExpr, class A)
-// ANALYZER:     9: [B9.8] (CXXConstructExpr, [B9.10], [B9.13], class A)
+// ANALYZER:     9: [B9.8] (CXXConstructExpr, [B9.10], [B9.13], [B9.14], class A)
 // CHECK:    10: [B9.9] (BindTemporary)
 // CHECK:    11: A([B9.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A)
 // CHECK:    12: [B9.11] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:    13: [B9.12]
 // WARNINGS:    14: [B9.13] (CXXConstructExpr, class A)
-// ANALYZER:    14: [B9.13] (CXXConstructExpr, [B9.15], [B7.3], class A)
+// ANALYZER:    14: [B9.13] (CXXConstructExpr, [B9.15], [B7.3], [B7.4], class A)
 // CHECK:    15: [B9.14] (BindTemporary)
 // CHECK:     Preds (1): B10
 // CHECK:     Succs (1): B7
@@ -680,7 +680,7 @@
 // CHECK:     Succs (1): B0
 // CHECK:   [B4]
 // WARNINGS:     1: C() (CXXConstructExpr, struct C)
-// ANALYZER:     1: C() (CXXConstructExpr, [B4.2], [B4.4], struct C)
+// ANALYZER:     1: C() (CXXConstructExpr, [B4.2], [B4.4], [B4.5], struct C)
 // CHECK:     2: [B4.1] (BindTemporary)
 // CHECK:     3: [B4.2] (ImplicitCastExpr, NoOp, const struct C)
 // CHECK:     4: [B4.3]
@@ -733,7 +733,7 @@
 // CHECK:     Succs (1): B0
 // CHECK:   [B3]
 // CXX98-WARNINGS:     1: D() (CXXConstructExpr, struct D)
-// CXX98-ANALYZER:     1: D() (CXXConstructExpr, [B3.3], struct D)
+// CXX98-ANALYZER:     1: D() (CXXConstructExpr, [B3.3], [B3.4], struct D)
 // CXX98:     2: [B3.1] (ImplicitCastExpr, NoOp, const struct D)
 // CXX98:     3: [B3.2]
 // CXX98-WARNINGS:     4: [B3.3] (CXXConstructExpr, struct D)
@@ -745,7 +745,7 @@
 // CXX98:     9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
 // CXX98:     T: if [B3.9]
 // CXX11-WARNINGS:     1: D() (CXXConstructExpr, struct D)
-// CXX11-ANALYZER:     1: D() (CXXConstructExpr, [B3.2], struct D)
+// CXX11-ANALYZER:     1: D() (CXXConstructExpr, [B3.2], [B3.3], struct D)
 // CXX11:     2: [B3.1]
 // CXX11-WARNINGS:     3: [B3.2] (CXXConstructExpr, struct D)
 // CXX11-ANALYZER:     3: [B3.2] (CXXConstructExpr, [B3.4], struct D)
@@ -789,7 +789,7 @@
 // CHECK:     Succs (2): B3 B2
 // CHECK:   [B5]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B5.2], [B5.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B5.2], [B5.4], [B5.5], class A)
 // CHECK:     2: [B5.1] (BindTemporary)
 // CHECK:     3: [B5.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B5.3]
@@ -809,7 +809,7 @@
 // CHECK:     7: [B6.6] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     8: [B6.7]
 // WARNINGS:     9: [B6.8] (CXXConstructExpr, class A)
-// ANALYZER:     9: [B6.8] (CXXConstructExpr, [B6.10], [B6.13], class A)
+// ANALYZER:     9: [B6.8] (CXXConstructExpr, [B6.10], [B6.13], [B6.14], class A)
 // CHECK:    10: [B6.9] (BindTemporary)
 // CHECK:    11: A([B6.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A)
 // CHECK:    12: [B6.11] (ImplicitCastExpr, NoOp, const class A)
@@ -852,7 +852,7 @@
 // CHECK:     Succs (2): B9 B8
 // CHECK:   [B11]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B11.2], [B11.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B11.2], [B11.4], [B11.5], class A)
 // CHECK:     2: [B11.1] (BindTemporary)
 // CHECK:     3: [B11.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B11.3]
@@ -872,7 +872,7 @@
 // CHECK:     7: [B12.6] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     8: [B12.7]
 // WARNINGS:     9: [B12.8] (CXXConstructExpr, class A)
-// ANALYZER:     9: [B12.8] (CXXConstructExpr, [B12.10], [B12.13], class A)
+// ANALYZER:     9: [B12.8] (CXXConstructExpr, [B12.10], [B12.13], [B12.14], class A)
 // CHECK:    10: [B12.9] (BindTemporary)
 // CHECK:    11: A([B12.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A)
 // CHECK:    12: [B12.11] (ImplicitCastExpr, NoOp, const class A)
@@ -935,7 +935,7 @@
 // CHECK:     Succs (1): B4
 // CHECK:   [B6]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], [B6.5], class A)
 // CHECK:     2: [B6.1] (BindTemporary)
 // CHECK:     3: [B6.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B6.3]
@@ -1001,7 +1001,7 @@
 // CHECK:     Succs (1): B4
 // CHECK:   [B6]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], [B6.5], class A)
 // CHECK:     2: [B6.1] (BindTemporary)
 // CHECK:     3: [B6.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B6.3]
@@ -1086,7 +1086,7 @@
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A)
 // CHECK:     2: [B1.1] (BindTemporary)
 // CHECK:     3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B1.3]
@@ -1130,7 +1130,7 @@
 // CHECK:     1: A::make
 // CHECK:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
 // WARNINGS:     3: [B1.2]()
-// ANALYZER:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
+// ANALYZER:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7])
 // CHECK:     4: [B1.3] (BindTemporary)
 // CHECK:     5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     6: [B1.5]
Index: cfe/trunk/test/Analysis/analyzer-config.cpp
===================================================================
--- cfe/trunk/test/Analysis/analyzer-config.cpp
+++ cfe/trunk/test/Analysis/analyzer-config.cpp
@@ -32,6 +32,7 @@
 // CHECK-NEXT: cfg-rich-constructors = true
 // CHECK-NEXT: cfg-scopes = false
 // CHECK-NEXT: cfg-temporary-dtors = true
+// CHECK-NEXT: elide-constructors = true
 // CHECK-NEXT: experimental-enable-naive-ctu-analysis = false
 // CHECK-NEXT: exploration_strategy = unexplored_first_queue
 // CHECK-NEXT: faux-bodies = true
@@ -50,4 +51,4 @@
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 30
+// CHECK-NEXT: num-entries = 31
Index: cfe/trunk/test/Analysis/analyzer-config.c
===================================================================
--- cfe/trunk/test/Analysis/analyzer-config.c
+++ cfe/trunk/test/Analysis/analyzer-config.c
@@ -18,6 +18,7 @@
 // CHECK-NEXT: cfg-rich-constructors = true
 // CHECK-NEXT: cfg-scopes = false
 // CHECK-NEXT: cfg-temporary-dtors = true
+// CHECK-NEXT: elide-constructors = true
 // CHECK-NEXT: exploration_strategy = unexplored_first_queue
 // CHECK-NEXT: faux-bodies = true
 // CHECK-NEXT: graph-trim-interval = 1000
@@ -35,4 +36,4 @@
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 23
+// CHECK-NEXT: num-entries = 24
Index: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -209,7 +209,11 @@
       }
       llvm_unreachable("Unhandled return value construction context!");
     }
-    case ConstructionContext::TemporaryObjectKind: {
+    case ConstructionContext::ElidedTemporaryObjectKind:
+      assert(AMgr.getAnalyzerOptions().shouldElideConstructors());
+      // FALL-THROUGH
+    case ConstructionContext::SimpleTemporaryObjectKind: {
+      // TODO: Copy elision implementation goes here.
       const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
       const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
       const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
Index: cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp
===================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -31,6 +31,7 @@
                 Options.shouldConditionalizeStaticInitializers(),
                 /*addCXXNewAllocator=*/true,
                 Options.includeRichConstructorsInCFG(),
+                Options.shouldElideConstructors(),
                 injector),
       Ctx(ASTCtx), Diags(diags), LangOpts(lang), PathConsumers(PDC),
       CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
Index: cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -321,6 +321,12 @@
                           /* Default = */ false);
 }
 
+bool AnalyzerOptions::shouldElideConstructors() {
+  return getBooleanOption(ElideConstructors,
+                          "elide-constructors",
+                          /* Default = */ true);
+}
+
 int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal,
                                         const CheckerBase *C,
                                         bool SearchInParents) {
Index: cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
===================================================================
--- cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
+++ cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
@@ -71,7 +71,8 @@
     bool addInitializers, bool addTemporaryDtors, bool addLifetime,
     bool addLoopExit, bool addScopes, bool synthesizeBodies,
     bool addStaticInitBranch, bool addCXXNewAllocator,
-    bool addRichCXXConstructors, CodeInjector *injector)
+    bool addRichCXXConstructors, bool markElidedCXXConstructors,
+    CodeInjector *injector)
     : Injector(injector), FunctionBodyFarm(ASTCtx, injector),
       SynthesizeBodies(synthesizeBodies) {
   cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
@@ -84,6 +85,7 @@
   cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch;
   cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
   cfgBuildOptions.AddRichCXXConstructors = addRichCXXConstructors;
+  cfgBuildOptions.MarkElidedCXXConstructors = markElidedCXXConstructors;
 }
 
 void AnalysisDeclContextManager::clear() { Contexts.clear(); }
Index: cfe/trunk/lib/Analysis/ConstructionContext.cpp
===================================================================
--- cfe/trunk/lib/Analysis/ConstructionContext.cpp
+++ cfe/trunk/lib/Analysis/ConstructionContext.cpp
@@ -61,7 +61,6 @@
       // For temporaries with destructors, there may or may not be
       // lifetime extension on the parent layer.
       if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
-        assert(ParentLayer->isLast());
         // C++17 *requires* elision of the constructor at the return site
         // and at variable/member initialization site, while previous standards
         // were allowing an optional elidable constructor.
@@ -77,8 +76,33 @@
         // both destruction and materialization info attached to it in the AST.
         if ((MTE = dyn_cast<MaterializeTemporaryExpr>(
                  ParentLayer->getTriggerStmt()))) {
-          return create<TemporaryObjectConstructionContext>(C, BTE, MTE);
+          // Handle pre-C++17 copy and move elision.
+          const CXXConstructExpr *ElidedCE = nullptr;
+          const ConstructionContext *ElidedCC = nullptr;
+          if (const ConstructionContextLayer *ElidedLayer =
+                  ParentLayer->getParent()) {
+            ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
+            assert(ElidedCE->isElidable());
+            // We're creating a construction context that might have already
+            // been created elsewhere. Maybe we should unique our construction
+            // contexts. That's what we often do, but in this case it's unlikely
+            // to bring any benefits.
+            ElidedCC = createFromLayers(C, ElidedLayer->getParent());
+            if (!ElidedCC) {
+              // We may fail to create the elided construction context.
+              // In this case, skip copy elision entirely.
+              return create<SimpleTemporaryObjectConstructionContext>(C, BTE,
+                                                                      MTE);
+            } else {
+              return create<ElidedTemporaryObjectConstructionContext>(
+                  C, BTE, MTE, ElidedCE, ElidedCC);
+            }
+          }
+          assert(ParentLayer->isLast());
+          return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);
         }
+        assert(ParentLayer->isLast());
+
         // This is a constructor into a function argument. Not implemented yet.
         if (isa<CallExpr>(ParentLayer->getTriggerStmt()))
           return nullptr;
@@ -99,7 +123,11 @@
         llvm_unreachable("Unexpected construction context with destructor!");
       }
       // A temporary object that doesn't require materialization.
-      return create<TemporaryObjectConstructionContext>(C, BTE, /*MTE=*/nullptr);
+      // In particular, it shouldn't require copy elision, because
+      // copy/move constructors take a reference, which requires
+      // materialization to obtain the glvalue.
+      return create<SimpleTemporaryObjectConstructionContext>(C, BTE,
+                                                              /*MTE=*/nullptr);
     }
     if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
       // If the object requires destruction and is not lifetime-extended,
@@ -110,8 +138,28 @@
              MTE->getStorageDuration() != SD_FullExpression))
         return nullptr;
 
+      // Handle pre-C++17 copy and move elision.
+      const CXXConstructExpr *ElidedCE = nullptr;
+      const ConstructionContext *ElidedCC = nullptr;
+      if (const ConstructionContextLayer *ElidedLayer = TopLayer->getParent()) {
+        ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
+        assert(ElidedCE->isElidable());
+        // We're creating a construction context that might have already
+        // been created elsewhere. Maybe we should unique our construction
+        // contexts. That's what we often do, but in this case it's unlikely
+        // to bring any benefits.
+        ElidedCC = createFromLayers(C, ElidedLayer->getParent());
+        if (!ElidedCC) {
+          // We may fail to create the elided construction context.
+          // In this case, skip copy elision entirely.
+          return create<SimpleTemporaryObjectConstructionContext>(C, nullptr,
+                                                                  MTE);
+        }
+        return create<ElidedTemporaryObjectConstructionContext>(
+            C, nullptr, MTE, ElidedCE, ElidedCC);
+      }
       assert(TopLayer->isLast());
-      return create<TemporaryObjectConstructionContext>(C, nullptr, MTE);
+      return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, MTE);
     }
     if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
       assert(TopLayer->isLast());
Index: cfe/trunk/lib/Analysis/CFG.cpp
===================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp
+++ cfe/trunk/lib/Analysis/CFG.cpp
@@ -1252,10 +1252,22 @@
   if (!Child)
     return;
 
+  auto withExtraLayer = [this, Layer](Stmt *S) {
+    return ConstructionContextLayer::create(cfg->getBumpVectorContext(), S,
+                                            Layer);
+  };
+
   switch(Child->getStmtClass()) {
   case Stmt::CXXConstructExprClass:
   case Stmt::CXXTemporaryObjectExprClass: {
-    consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child));
+    // Support pre-C++17 copy elision AST.
+    auto *CE = cast<CXXConstructExpr>(Child);
+    if (BuildOpts.MarkElidedCXXConstructors && CE->isElidable()) {
+      assert(CE->getNumArgs() == 1);
+      findConstructionContexts(withExtraLayer(CE), CE->getArg(0));
+    }
+
+    consumeConstructionContext(Layer, CE);
     break;
   }
   // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr.
@@ -1294,10 +1306,21 @@
   }
   case Stmt::CXXBindTemporaryExprClass: {
     auto *BTE = cast<CXXBindTemporaryExpr>(Child);
-    findConstructionContexts(
-        ConstructionContextLayer::create(cfg->getBumpVectorContext(),
-                                         BTE, Layer),
-        BTE->getSubExpr());
+    findConstructionContexts(withExtraLayer(BTE), BTE->getSubExpr());
+    break;
+  }
+  case Stmt::MaterializeTemporaryExprClass: {
+    // Normally we don't want to search in MaterializeTemporaryExpr because
+    // it indicates the beginning of a temporary object construction context,
+    // so it shouldn't be found in the middle. However, if it is the beginning
+    // of an elidable copy or move construction context, we need to include it.
+    if (const auto *CE =
+            dyn_cast_or_null<CXXConstructExpr>(Layer->getTriggerStmt())) {
+      if (CE->isElidable()) {
+        auto *MTE = cast<MaterializeTemporaryExpr>(Child);
+        findConstructionContexts(withExtraLayer(MTE), MTE->GetTemporaryExpr());
+      }
+    }
     break;
   }
   case Stmt::ConditionalOperatorClass: {
@@ -4931,7 +4954,7 @@
 static void print_construction_context(raw_ostream &OS,
                                        StmtPrinterHelper &Helper,
                                        const ConstructionContext *CC) {
-  const Stmt *S1 = nullptr, *S2 = nullptr;
+  SmallVector<const Stmt *, 3> Stmts;
   switch (CC->getKind()) {
   case ConstructionContext::SimpleConstructorInitializerKind: {
     OS << ", ";
@@ -4944,52 +4967,56 @@
     const auto *CICC =
         cast<CXX17ElidedCopyConstructorInitializerConstructionContext>(CC);
     print_initializer(OS, Helper, CICC->getCXXCtorInitializer());
-    S2 = CICC->getCXXBindTemporaryExpr();
+    Stmts.push_back(CICC->getCXXBindTemporaryExpr());
     break;
   }
   case ConstructionContext::SimpleVariableKind: {
     const auto *SDSCC = cast<SimpleVariableConstructionContext>(CC);
-    S1 = SDSCC->getDeclStmt();
+    Stmts.push_back(SDSCC->getDeclStmt());
     break;
   }
   case ConstructionContext::CXX17ElidedCopyVariableKind: {
     const auto *CDSCC = cast<CXX17ElidedCopyVariableConstructionContext>(CC);
-    S1 = CDSCC->getDeclStmt();
-    S2 = CDSCC->getCXXBindTemporaryExpr();
+    Stmts.push_back(CDSCC->getDeclStmt());
+    Stmts.push_back(CDSCC->getCXXBindTemporaryExpr());
     break;
   }
   case ConstructionContext::NewAllocatedObjectKind: {
     const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
-    S1 = NECC->getCXXNewExpr();
+    Stmts.push_back(NECC->getCXXNewExpr());
     break;
   }
   case ConstructionContext::SimpleReturnedValueKind: {
     const auto *RSCC = cast<SimpleReturnedValueConstructionContext>(CC);
-    S1 = RSCC->getReturnStmt();
+    Stmts.push_back(RSCC->getReturnStmt());
     break;
   }
   case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
     const auto *RSCC =
         cast<CXX17ElidedCopyReturnedValueConstructionContext>(CC);
-    S1 = RSCC->getReturnStmt();
-    S2 = RSCC->getCXXBindTemporaryExpr();
+    Stmts.push_back(RSCC->getReturnStmt());
+    Stmts.push_back(RSCC->getCXXBindTemporaryExpr());
     break;
   }
-  case ConstructionContext::TemporaryObjectKind: {
-    const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
-    S1 = TOCC->getCXXBindTemporaryExpr();
-    S2 = TOCC->getMaterializedTemporaryExpr();
+  case ConstructionContext::SimpleTemporaryObjectKind: {
+    const auto *TOCC = cast<SimpleTemporaryObjectConstructionContext>(CC);
+    Stmts.push_back(TOCC->getCXXBindTemporaryExpr());
+    Stmts.push_back(TOCC->getMaterializedTemporaryExpr());
     break;
   }
+  case ConstructionContext::ElidedTemporaryObjectKind: {
+    const auto *TOCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
+    Stmts.push_back(TOCC->getCXXBindTemporaryExpr());
+    Stmts.push_back(TOCC->getMaterializedTemporaryExpr());
+    Stmts.push_back(TOCC->getConstructorAfterElision());
+    break;
   }
-  if (S1) {
-    OS << ", ";
-    Helper.handledStmt(const_cast<Stmt *>(S1), OS);
-  }
-  if (S2) {
-    OS << ", ";
-    Helper.handledStmt(const_cast<Stmt *>(S2), OS);
   }
+  for (auto I: Stmts)
+    if (I) {
+      OS << ", ";
+      Helper.handledStmt(const_cast<Stmt *>(I), OS);
+    }
 }
 
 static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to