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

In C++17 copy elision is mandatory for variable and return value constructors 
(as long as it doesn't involve type conversion) which results in AST that does 
not contain elidable constructors in their usual places. In order to provide 
construction contexts in this scenario we need to cover more AST patterns.

This patch makes the CFG prepared for these scenarios by:

- Adding two new construction context classes: 
`CXX17ElidedCopyVariableConstructionContext` and 
`CXX17ElidedCopyReturnedValueConstructionContext`. These are forks of 
`SimpleVariableConstructionContext` and `ReturnedValueConstructionContext` 
which contain the additional `CXXBindTemporaryExpr` when it's found in the AST. 
Additionally, `ReturnedValueConstructionContext` is renamed into 
`SimpleReturnedValueConstructionContext`. Finally, common abstract base classes 
are introduced: `VariableConstructionContext` and 
`ReturnedValueConstructionContext`.
- Allowing `CFGCXXRecordTypedCall` element to accept 
`VariableConstructionContext` and `ReturnedValueConstructionContext` as its 
context.

`CXXBindTemporaryExpr` is assumed to be present iff the object has non-trivial 
destructor. If the object has trivial destructor then `CXXBindTemporaryExpr` is 
not there, and the AST is pretty much indistinguishable from the simple case so 
we re-use  `SimpleVariableConstructionContext` and 
`SimpleReturnedValueConstructionContext`. I'm not entirely sure that this is 
indeed the same case, but for the purposes of the analyzer, which is the 
primary user of construction contexts, this seems fine because when the object 
has trivial destructor it is not scary to inline the constructor. When the 
object requires non-trivial destruction, the new construction context is 
provided, and the analyzer bails out (with the hope of falling back to the 
pre-temporary-support behavior) for now - but more work seems to be necessary 
on the analyzer side to prevent it from crashing during actual analysis.


Repository:
  rC Clang

https://reviews.llvm.org/D44597

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

Index: test/Analysis/cfg-rich-constructors.cpp
===================================================================
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -1,5 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 -w %s > %t 2>&1
-// RUN: FileCheck --input-file=%t %s
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11 %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++17 -w %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17 %s
 
 class C {
 public:
@@ -98,32 +100,38 @@
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
 // CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
-// CHECK-NEXT:     4: [B1.3]
-// CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
-// CHECK-NEXT:     6: C c = C::get();
+// CXX11-NEXT:     4: [B1.3]
+// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
+// CXX11-NEXT:     6: C c = C::get();
+// CXX17-NEXT:     4: C c = C::get();
 void simpleVariableInitializedByValue() {
   C c = C::get();
 }
 
 // CHECK: void simpleVariableWithTernaryOperator(bool coin)
 // CHECK:        [B1]
-// CHECK-NEXT:     1: [B4.2] ? [B2.5] : [B3.6]
-// CHECK-NEXT:     2: [B1.1]
-// CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.4], class C)
-// CHECK-NEXT:     4: C c = coin ? C::get() : C(0);
+// CXX11-NEXT:     1: [B4.2] ? [B2.5] : [B3.6]
+// CXX11-NEXT:     2: [B1.1]
+// CXX11-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.4], class C)
+// CXX11-NEXT:     4: C c = coin ? C::get() : C(0);
+// CXX17-NEXT:     1: [B4.2] ? [B2.3] : [B3.4]
+// CXX17-NEXT:     2: C c = coin ? C::get() : C(0);
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
-// CHECK-NEXT:     4: [B2.3]
-// CHECK-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
+// CXX11-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
+// CXX11-NEXT:     4: [B2.3]
+// CXX11-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
+// CXX17-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B1.2])
 // CHECK:        [B3]
 // CHECK-NEXT:     1: 0
 // CHECK-NEXT:     2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CHECK-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
-// CHECK-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
-// CHECK-NEXT:     5: [B3.4]
-// CHECK-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.2], class C)
+// CXX11-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)
+// CXX17-NEXT:     3: [B3.2] (CXXConstructExpr, [B1.2], class C)
+// CXX17-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CHECK:        [B4]
 // CHECK-NEXT:     1: coin
 // CHECK-NEXT:     2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
@@ -137,9 +145,10 @@
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *)
 // CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
 // CHECK-NEXT:     4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
-// CHECK-NEXT:     5: [B1.4]
-// CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
-// CHECK-NEXT:     7: C c = C(0);
+// CXX11-NEXT:     5: [B1.4]
+// CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
+// CXX11-NEXT:     7: C c = C(0);
+// CXX17-NEXT:     5: C c = C(0);
 void simpleVariableWithElidableCopy() {
   C c = C(0);
 }
@@ -165,23 +174,27 @@
 
 // CHECK: void referenceVariableWithTernaryOperator(bool coin)
 // CHECK:        [B1]
-// CHECK-NEXT:     1: [B4.2] ? [B2.5] : [B3.6]
+// CXX11-NEXT:     1: [B4.2] ? [B2.5] : [B3.6]
+// CXX17-NEXT:     1: [B4.2] ? [B2.3] : [B3.4]
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class C)
 // CHECK-NEXT:     3: [B1.2]
 // CHECK-NEXT:     4: const C &c = coin ? C::get() : C(0);
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
-// CHECK-NEXT:     4: [B2.3]
-// CHECK-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.3], class C)
+// CXX11-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 *)
-// CHECK-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
-// CHECK-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
-// CHECK-NEXT:     5: [B3.4]
-// CHECK-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.3], class C)
+// CXX11-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)
+// CXX17-NEXT:     3: [B3.2] (CXXConstructExpr, [B1.3], class C)
+// CXX17-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CHECK:        [B4]
 // CHECK-NEXT:     1: coin
 // CHECK-NEXT:     2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
@@ -214,6 +227,8 @@
 // CHECK-NEXT:     2: D([B1.1]) (Delegating initializer)
   D(int): D() {}
 
+// FIXME: Why is CXXRecordTypedCall not present in C++17? Note that once it gets
+// detected the test would not fail, because FileCheck allows partial matches.
 // CHECK: D(double)
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
@@ -224,12 +239,16 @@
 // 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]() (CXXRecordTypedCall, [B1.11])
-// CHECK-NEXT:    11: [B1.10]
-// CHECK-NEXT:    12: [B1.11] (CXXConstructExpr, [B1.13], class C)
-// CHECK-NEXT:    13: new C([B1.12])
-// CHECK-NEXT:    14: [B1.13] (CXXConstructExpr, c1([B1.13]) (Member initializer), class C)
-// CHECK-NEXT:    15: c1([B1.14]) (Member initializer)
+// CXX11-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])
+// CXX11-NEXT:    14: [B1.13] (CXXConstructExpr, c1([B1.13]) (Member initializer), class C)
+// CXX11-NEXT:    15: c1([B1.14]) (Member initializer)
+// CXX17-NEXT:    10: [B1.9]()
+// CXX17-NEXT:    11: new C([B1.10])
+// CXX17-NEXT:    12: [B1.11] (CXXConstructExpr, c1([B1.11]) (Member initializer), class C)
+// CXX17-NEXT:    13: c1([B1.12]) (Member initializer)
   D(double): C(C::get()), c1(new C(C::get())) {}
 };
 
@@ -277,9 +296,10 @@
 
 // CHECK: C returnTemporary()
 // CHECK:          1: C() (CXXConstructExpr, [B1.2], class C)
-// CHECK-NEXT:     2: [B1.1]
-// CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.4], class C)
-// CHECK-NEXT:     4: return [B1.3];
+// CXX11-NEXT:     2: [B1.1]
+// CXX11-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.4], class C)
+// CXX11-NEXT:     4: return [B1.3];
+// CXX17-NEXT:     2: return [B1.1];
 C returnTemporary() {
   return C();
 }
@@ -289,34 +309,40 @@
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *)
 // CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
 // CHECK-NEXT:     4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
-// CHECK-NEXT:     5: [B1.4]
-// CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
-// CHECK-NEXT:     7: return [B1.6];
+// CXX11-NEXT:     5: [B1.4]
+// CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
+// CXX11-NEXT:     7: return [B1.6];
+// CXX17-NEXT:     5: return [B1.4];
+
 C returnTemporaryWithArgument() {
   return C(nullptr);
 }
 
 // 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])
-// CHECK-NEXT:     4: [B1.3]
-// CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
-// CHECK-NEXT:     6: return [B1.5];
+// CXX11-NEXT:     4: [B1.3]
+// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
+// CXX11-NEXT:     6: return [B1.5];
+// 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))
-// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [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)
-// CHECK-NEXT:     7: [B1.6]
-// CHECK-NEXT:     8: [B1.7] (CXXConstructExpr, [B1.9], class C)
-// CHECK-NEXT:     9: return [B1.8];
+// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
+// CXX11-NEXT:     4: [B1.3]
+// CXX11-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)
+// CXX11-NEXT:     9: return [B1.8];
+// CXX17-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.5])
+// CXX17-NEXT:     4: C([B1.3]) (CXXFunctionalCastExpr, NoOp, class C)
+// CXX17-NEXT:     5: return [B1.4];
 C returnChainOfCopies() {
   return C(C::get());
 }
@@ -331,18 +357,44 @@
   ~D();
 };
 
+// FIXME: There should be no temporary destructor in C++17.
 // CHECK:  return_stmt_with_dtor::D returnTemporary()
-// CHECK:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D)
-// CHECK-NEXT:     2: [B1.1] (BindTemporary)
-// CHECK-NEXT:     3: [B1.2] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D)
-// CHECK-NEXT:     4: [B1.3]
-// CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class return_stmt_with_dtor::D)
-// CHECK-NEXT:     6: ~return_stmt_with_dtor::D() (Temporary object destructor)
-// CHECK-NEXT:     7: return [B1.5];
+// CXX11:          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]
+// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class return_stmt_with_dtor::D)
+// CXX11-NEXT:     6: ~return_stmt_with_dtor::D() (Temporary object destructor)
+// CXX11-NEXT:     7: return [B1.5];
+// CXX17:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.4], [B1.2], class return_stmt_w
+// CXX17-NEXT:     2: [B1.1] (BindTemporary)
+// CXX17-NEXT:     3: ~return_stmt_with_dtor::D() (Temporary object destructor)
+// CXX17-NEXT:     4: return [B1.2];
 D returnTemporary() {
   return D();
 }
 
+// FIXME: There should be no temporary destructor in C++17.
+// 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-NEXT:     4: [B1.3] (BindTemporary)
+// CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D)
+// CXX11-NEXT:     6: [B1.5]
+// CXX11-NEXT:     7: [B1.6] (CXXConstructExpr, [B1.8], class return_stmt_with_dtor::D)
+// CXX11-NEXT:     8: return_stmt_with_dtor::D d = returnTemporary();
+// CXX11-NEXT:     9: ~return_stmt_with_dtor::D() (Temporary object destructor)
+// CXX11-NEXT:    10: [B1.8].~D() (Implicit destructor)
+// CXX17-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.5], [B1.4])
+// CXX17-NEXT:     4: [B1.3] (BindTemporary)
+// CXX17-NEXT:     5: return_stmt_with_dtor::D d = returnTemporary();
+// CXX17-NEXT:     6: ~return_stmt_with_dtor::D() (Temporary object destructor)
+// CXX17-NEXT:     7: [B1.5].~D() (Implicit destructor)
+void returnByValueIntoVariable() {
+  D d = returnTemporary();
+}
+
 } // end namespace return_stmt_with_dtor
 
 namespace temporary_object_expr_without_dtors {
@@ -426,34 +478,60 @@
 }
 
 // CHECK: void referenceVariableWithTernaryOperator(bool coin)
-// CHECK:        [B4]
-// CHECK-NEXT:     1: [B7.2] ? [B5.8] : [B6.8]
-// CHECK-NEXT:     2: [B4.1] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     3: [B4.2]
-// CHECK-NEXT:     4: const temporary_object_expr_with_dtors::D &d = coin ? D::get() : temporary_object_expr_with_dtors::D(0);
-// CHECK-NEXT:     T: (Temp Dtor) [B6.3]
-// 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]() (CXXRecordTypedCall, [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]
-// CHECK-NEXT:     7: [B5.6] (CXXConstructExpr, [B5.8], [B4.3], class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     8: [B5.7] (BindTemporary)
-// CHECK:        [B6]
-// CHECK-NEXT:     1: 0
-// CHECK-NEXT:     2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     3: [B6.2] (BindTemporary)
-// CHECK-NEXT:     4: temporary_object_expr_with_dtors::D([B6.3]) (CXXFunctionalCastExpr, ConstructorConversion, class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     5: [B6.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     6: [B6.5]
-// CHECK-NEXT:     7: [B6.6] (CXXConstructExpr, [B6.8], [B4.3], class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     8: [B6.7] (BindTemporary)
-// CHECK:        [B7]
-// CHECK-NEXT:     1: coin
-// CHECK-NEXT:     2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
-// CHECK-NEXT:     T: [B7.2] ? ... : ...
+// CXX11:        [B1]
+// CXX11-NEXT:     1: [B4.4].~D() (Implicit destructor)
+// CXX11:        [B2]
+// CXX11-NEXT:     1: ~temporary_object_expr_with_dtors::D() (Temporary object destructor)
+// CXX11:        [B3]
+// CXX11-NEXT:     1: ~temporary_object_expr_with_dtors::D() (Temporary object destructor)
+// CXX11:        [B4]
+// CXX11-NEXT:     1: [B7.2] ? [B5.8] : [B6.8]
+// CXX11-NEXT:     2: [B4.1] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
+// CXX11-NEXT:     3: [B4.2]
+// CXX11-NEXT:     4: const temporary_object_expr_with_dtors::D &d = coin ? D::get() : temporary_object_expr_with_dtors::D(0);
+// CXX11-NEXT:     T: (Temp Dtor) [B6.3]
+// 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-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-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)
+// CXX11-NEXT:     6: [B6.5]
+// CXX11-NEXT:     7: [B6.6] (CXXConstructExpr, [B6.8], [B4.3], class temporary_object_expr_with_dtors::D)
+// CXX11-NEXT:     8: [B6.7] (BindTemporary)
+// CXX11:        [B7]
+// CXX11-NEXT:     1: coin
+// CXX11-NEXT:     2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CXX11-NEXT:     T: [B7.2] ? ... : ...
+// CXX17:        [B1]
+// CXX17-NEXT:     1: [B4.2] ? [B2.4] : [B3.4]
+// CXX17-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
+// CXX17-NEXT:     3: [B1.2]
+// CXX17-NEXT:     4: const temporary_object_expr_with_dtors::D &d = coin ? D::get() : temporary_object_expr_with_dtors::D(0);
+// CXX17-NEXT:     5: [B1.4].~D() (Implicit destructor)
+// CXX17:        [B2]
+// CXX17-NEXT:     1: D::get
+// CXX17-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void))
+// CXX17-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4], [B1.3])
+// CXX17-NEXT:     4: [B2.3] (BindTemporary)
+// CXX17:        [B3]
+// CXX17-NEXT:     1: 0
+// CXX17-NEXT:     2: [B3.1] (CXXConstructExpr, [B3.3], [B1.3], class temporary_object_expr_with_dtors::D)
+// CXX17-NEXT:     3: [B3.2] (BindTemporary)
+// CXX17-NEXT:     4: temporary_object_expr_with_dtors::D([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class temporary_object_expr_with_dtors::D)
+// CXX17:        [B4]
+// CXX17-NEXT:     1: coin
+// CXX17-NEXT:     2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CXX17-NEXT:     T: [B4.2] ? ... : ...
 void referenceVariableWithTernaryOperator(bool coin) {
   const D &d = coin ? D::get() : D(0);
 }
@@ -472,15 +550,24 @@
 
 // Test the condition constructor, we don't care about branch constructors here.
 // CHECK: void constructorInTernaryCondition()
-// CHECK:          1: 1
-// CHECK-NEXT:     2: [B7.1] (CXXConstructExpr, [B7.3], class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     3: [B7.2] (BindTemporary)
-// CHECK-NEXT:     4: temporary_object_expr_with_dtors::D([B7.3]) (CXXFunctionalCastExpr, ConstructorConv
-// CHECK-NEXT:     5: [B7.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
-// CHECK-NEXT:     6: [B7.5].operator bool
-// CHECK-NEXT:     7: [B7.5]
-// CHECK-NEXT:     8: [B7.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK-NEXT:     T: [B7.8] ? ... : ...
+// CXX11:          1: 1
+// CXX11-NEXT:     2: [B7.1] (CXXConstructExpr, [B7.3], class temporary_object_expr_with_dtors::D)
+// CXX11-NEXT:     3: [B7.2] (BindTemporary)
+// CXX11-NEXT:     4: temporary_object_expr_with_dtors::D([B7.3]) (CXXFunctionalCastExpr, ConstructorConversion, class temporary_object_expr_with_dtors::D)
+// CXX11-NEXT:     5: [B7.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
+// CXX11-NEXT:     6: [B7.5].operator bool
+// CXX11-NEXT:     7: [B7.5]
+// CXX11-NEXT:     8: [B7.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CXX11-NEXT:     T: [B7.8] ? ... : ...
+// CXX17:          1: 1
+// CXX17-NEXT:     2: [B4.1] (CXXConstructExpr, [B4.3], class temporary_object_expr_with_dtors::D)
+// CXX17-NEXT:     3: [B4.2] (BindTemporary)
+// CXX17-NEXT:     4: temporary_object_expr_with_dtors::D([B4.3]) (CXXFunctionalCastExpr, ConstructorConversion, class temporary_object_expr_with_dtors::D)
+// CXX17-NEXT:     5: [B4.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
+// CXX17-NEXT:     6: [B4.5].operator bool
+// CXX17-NEXT:     7: [B4.5]
+// CXX17-NEXT:     8: [B4.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CXX17-NEXT:     T: [B4.8] ? ... : ...
 void constructorInTernaryCondition() {
   const D &d = D(1) ? D(2) : D(3);
 }
@@ -500,17 +587,23 @@
 
 // CHECK: void implicitConstructionConversionFromTemporary()
 // CHECK:          1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A)
-// CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
-// CHECK-NEXT:     3: [B1.2]
-// CHECK-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B)
-// CHECK-NEXT:     5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
-// CHECK-NEXT:     6: [B1.5] (BindTemporary)
-// CHECK-NEXT:     7: [B1.6] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
-// CHECK-NEXT:     8: [B1.7]
-// CHECK-NEXT:     9: [B1.8] (CXXConstructExpr, [B1.10], class implicit_constructor_conversion::B)
-// CHECK-NEXT:    10: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
-// CHECK-NEXT:    11: ~implicit_constructor_conversion::B() (Temporary object destructor)
-// CHECK-NEXT:    12: [B1.10].~B() (Implicit destructor)
+// 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-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)
+// CXX11-NEXT:     8: [B1.7]
+// CXX11-NEXT:     9: [B1.8] (CXXConstructExpr, [B1.10], class implicit_constructor_conversion::B)
+// CXX11-NEXT:    10: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
+// CXX11-NEXT:    11: ~implicit_constructor_conversion::B() (Temporary object destructor)
+// CXX11-NEXT:    12: [B1.10].~B() (Implicit destructor)
+// CXX17-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
+// CXX17-NEXT:     3: [B1.2]
+// CXX17-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], class implicit_constructor_conversion::B)
+// CXX17-NEXT:     5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
+// CXX17-NEXT:     6: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
+// CXX17-NEXT:     7: [B1.6].~B() (Implicit destructor)
 void implicitConstructionConversionFromTemporary() {
   B b = A();
 }
@@ -521,15 +614,19 @@
 // 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]
-// CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B)
-// CHECK-NEXT:     7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
-// CHECK-NEXT:     8: [B1.7] (BindTemporary)
-// CHECK-NEXT:     9: [B1.8] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
-// CHECK-NEXT:    10: [B1.9]
-// CHECK-NEXT:    11: [B1.10] (CXXConstructExpr, [B1.12], class implicit_constructor_conversion::B)
-// CHECK-NEXT:    12: implicit_constructor_conversion::B b = get();
-// CHECK-NEXT:    13: ~implicit_constructor_conversion::B() (Temporary object destructor)
-// CHECK-NEXT:    14: [B1.12].~B() (Implicit destructor)
+// CXX11-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)
+// CXX11-NEXT:    10: [B1.9]
+// CXX11-NEXT:    11: [B1.10] (CXXConstructExpr, [B1.12], class implicit_constructor_conversion::B)
+// CXX11-NEXT:    12: implicit_constructor_conversion::B b = get();
+// CXX11-NEXT:    13: ~implicit_constructor_conversion::B() (Temporary object destructor)
+// CXX11-NEXT:    14: [B1.12].~B() (Implicit destructor)
+// CXX17-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], class implicit_constructor_conversion::B)
+// CXX17-NEXT:     7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
+// CXX17-NEXT:     8: implicit_constructor_conversion::B b = get();
+// CXX17-NEXT:     9: [B1.8].~B() (Implicit destructor)
 void implicitConstructionConversionFromFunctionValue() {
   B b = get();
 }
@@ -548,7 +645,6 @@
   const B &b = A();
 }
 
-// FIXME: Find construction context for the implicit constructor conversion.
 // CHECK: void implicitConstructionConversionFromFunctionValueWithLifetimeExtension()
 // CHECK:          1: get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conver
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -184,7 +184,7 @@
       CallOpts.IsTemporaryCtorOrDtor = true;
       return MRMgr.getCXXTempObjectRegion(CE, LCtx);
     }
-    case ConstructionContext::ReturnedValueKind: {
+    case ConstructionContext::SimpleReturnedValueKind: {
       // The temporary is to be managed by the parent stack frame.
       // So build it in the parent stack frame if we're not in the
       // top frame of the analysis.
@@ -199,6 +199,10 @@
       CallOpts.IsTemporaryCtorOrDtor = true;
       return MRMgr.getCXXTempObjectRegion(CE, TempLCtx);
     }
+    case ConstructionContext::CXX17ElidedCopyVariableKind:
+    case ConstructionContext::CXX17ElidedCopyReturnedValueKind:
+      // 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
@@ -64,11 +64,44 @@
       // lifetime extension on the parent layer.
       if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
         assert(ParentLayer->isLast());
-        MTE = cast<MaterializeTemporaryExpr>(ParentLayer->getTriggerStmt());
+        if ((MTE = dyn_cast<MaterializeTemporaryExpr>(
+                 ParentLayer->getTriggerStmt()))) {
+          // A temporary object which has both destruction and
+          // materialization info.
+          auto *CC =
+              C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
+          return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
+        } else {
+          // C++17 *requires* elision of the constructor at the return site
+          // and at variable initialization site, while previous standards
+          // were allowing an optional elidable constructor.
+          if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
+            assert(!RS->getRetValue()->getType().getCanonicalType()
+                      ->getAsCXXRecordDecl()->hasTrivialDestructor());
+            auto *CC =
+                C.getAllocator()
+                    .Allocate<
+                        CXX17ElidedCopyReturnedValueConstructionContext>();
+            return new (CC)
+                CXX17ElidedCopyReturnedValueConstructionContext(RS, BTE);
+          } else {
+            auto *DS = cast<DeclStmt>(ParentLayer->getTriggerStmt());
+            assert(!cast<VarDecl>(DS->getSingleDecl())->getType()
+                        .getCanonicalType()->getAsCXXRecordDecl()
+                        ->hasTrivialDestructor());
+            auto *CC =
+                C.getAllocator()
+                    .Allocate<CXX17ElidedCopyVariableConstructionContext>();
+            return new (CC) CXX17ElidedCopyVariableConstructionContext(DS, BTE);
+          }
+        }
+      } else {
+        // A temporary object that doesn't require materialization.
+        auto *CC =
+            C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
+        return new (CC)
+            TemporaryObjectConstructionContext(BTE, /*MTE=*/nullptr);
       }
-      auto *CC =
-          C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
-      return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
     } else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
       // If the object requires destruction and is not lifetime-extended,
       // then it must have a BTE within its MTE.
@@ -82,8 +115,8 @@
     } else if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
       assert(TopLayer->isLast());
       auto *CC =
-          C.getAllocator().Allocate<ReturnedValueConstructionContext>();
-      return new (CC) ReturnedValueConstructionContext(RS);
+          C.getAllocator().Allocate<SimpleReturnedValueConstructionContext>();
+      return new (CC) SimpleReturnedValueConstructionContext(RS);
     }
   } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
     assert(TopLayer->isLast());
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -735,9 +735,7 @@
           const ConstructionContext *CC =
               ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
                                                     Layer);
-          B->appendCXXRecordTypedCall(
-              CE, cast<TemporaryObjectConstructionContext>(CC),
-              cfg->getBumpVectorContext());
+          B->appendCXXRecordTypedCall(CE, CC, cfg->getBumpVectorContext());
           cleanupConstructionContext(CE);
           return;
         }
@@ -4778,18 +4776,31 @@
     break;
   }
   case ConstructionContext::SimpleVariableKind: {
-    const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
-    S1 = DSCC->getDeclStmt();
+    const auto *SDSCC = cast<SimpleVariableConstructionContext>(CC);
+    S1 = SDSCC->getDeclStmt();
+    break;
+  }
+  case ConstructionContext::CXX17ElidedCopyVariableKind: {
+    const auto *CDSCC = cast<CXX17ElidedCopyVariableConstructionContext>(CC);
+    S1 = CDSCC->getDeclStmt();
+    S2 = CDSCC->getCXXBindTemporaryExpr();
     break;
   }
   case ConstructionContext::NewAllocatedObjectKind: {
     const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
     S1 = NECC->getCXXNewExpr();
     break;
   }
-  case ConstructionContext::ReturnedValueKind: {
-    const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
+  case ConstructionContext::SimpleReturnedValueKind: {
+    const auto *RSCC = cast<SimpleReturnedValueConstructionContext>(CC);
+    S1 = RSCC->getReturnStmt();
+    break;
+  }
+  case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
+    const auto *RSCC =
+        cast<CXX17ElidedCopyReturnedValueConstructionContext>(CC);
     S1 = RSCC->getReturnStmt();
+    S2 = RSCC->getCXXBindTemporaryExpr();
     break;
   }
   case ConstructionContext::TemporaryObjectKind: {
Index: include/clang/Analysis/ConstructionContext.h
===================================================================
--- include/clang/Analysis/ConstructionContext.h
+++ include/clang/Analysis/ConstructionContext.h
@@ -99,22 +99,26 @@
 public:
   enum Kind {
     SimpleVariableKind,
+    CXX17ElidedCopyVariableKind,
+    VARIABLE_BEGIN = SimpleVariableKind,
+    VARIABLE_END = CXX17ElidedCopyVariableKind,
     ConstructorInitializerKind,
     NewAllocatedObjectKind,
     TemporaryObjectKind,
-    ReturnedValueKind
+    SimpleReturnedValueKind,
+    CXX17ElidedCopyReturnedValueKind,
+    RETURNED_VALUE_BEGIN = SimpleReturnedValueKind,
+    RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind
   };
 
 protected:
   Kind K;
 
-protected:
   // Do not make public! These need to only be constructed
   // via createFromLayers().
   explicit ConstructionContext(Kind K) : K(K) {}
 
 public:
-
   /// Consume the construction context layer, together with its parent layers,
   /// and wrap it up into a complete construction context.
   static const ConstructionContext *
@@ -124,23 +128,68 @@
   Kind getKind() const { return K; }
 };
 
-/// Represents construction into a simple local variable, eg. T var(123);.
-class SimpleVariableConstructionContext : public ConstructionContext {
+/// An abstract base class for local variable constructors.
+class VariableConstructionContext : public ConstructionContext {
   const DeclStmt *DS;
 
-public:
-  explicit SimpleVariableConstructionContext(const DeclStmt *DS)
-      : ConstructionContext(ConstructionContext::SimpleVariableKind), DS(DS) {
+protected:
+  VariableConstructionContext(ConstructionContext::Kind K, const DeclStmt *DS)
+      : ConstructionContext(K), DS(DS) {
+    assert(classof(this));
     assert(DS);
   }
 
+public:
   const DeclStmt *getDeclStmt() const { return DS; }
 
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() >= VARIABLE_BEGIN &&
+           CC->getKind() <= VARIABLE_END;
+  }
+};
+
+/// Represents construction into a simple local variable, eg. T var(123);.
+/// If a variable has an initializer, eg. T var = makeT();, then the final
+/// elidable copy-constructor from makeT() into var would also be a simple
+/// variable constructor handled by this class.
+class SimpleVariableConstructionContext : public VariableConstructionContext {
+public:
+  explicit SimpleVariableConstructionContext(const DeclStmt *DS)
+      : VariableConstructionContext(ConstructionContext::SimpleVariableKind,
+                                    DS) {}
+
   static bool classof(const ConstructionContext *CC) {
     return CC->getKind() == SimpleVariableKind;
   }
 };
 
+/// Represents construction into a simple variable with an initializer syntax,
+/// with a single constructor, eg. T var = makeT();. Such construction context
+/// may only appear in C++17 because previously it was split into a temporary
+/// object constructor and an elidable simple variable copy-constructor and
+/// we were producing separate construction contexts for these constructors.
+/// In C++17 we have a single construction context that combines both.
+/// Note that if the object has trivial destructor, then this code is
+/// indistinguishable from a simple variable constructor on the AST level;
+/// in this case we provide a simple variable construction context.
+class CXX17ElidedCopyVariableConstructionContext
+    : public VariableConstructionContext {
+  const CXXBindTemporaryExpr *BTE;
+
+public:
+  explicit CXX17ElidedCopyVariableConstructionContext(
+      const DeclStmt *DS, const CXXBindTemporaryExpr *BTE)
+      : VariableConstructionContext(CXX17ElidedCopyVariableKind, DS), BTE(BTE) {
+    assert(BTE);
+  }
+
+  const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == CXX17ElidedCopyVariableKind;
+  }
+};
+
 /// Represents construction into a field or a base class within a bigger object
 /// via a constructor initializer, eg. T(): field(123) { ... }.
 class ConstructorInitializerConstructionContext : public ConstructionContext {
@@ -219,24 +268,68 @@
   }
 };
 
+class ReturnedValueConstructionContext : public ConstructionContext {
+  const ReturnStmt *RS;
+
+protected:
+  explicit ReturnedValueConstructionContext(ConstructionContext::Kind K,
+                                            const ReturnStmt *RS)
+      : ConstructionContext(K), RS(RS) {
+    assert(classof(this));
+    assert(RS);
+  }
+
+public:
+  const ReturnStmt *getReturnStmt() const { return RS; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() >= RETURNED_VALUE_BEGIN &&
+           CC->getKind() <= RETURNED_VALUE_END;
+  }
+};
+
 /// Represents a temporary object that is being immediately returned from a
 /// function by value, eg. return t; or return T(123);. In this case there is
 /// always going to be a constructor at the return site. However, the usual
 /// temporary-related bureaucracy (CXXBindTemporaryExpr,
 /// MaterializeTemporaryExpr) is normally located in the caller function's AST.
-class ReturnedValueConstructionContext : public ConstructionContext {
-  const ReturnStmt *RS;
+class SimpleReturnedValueConstructionContext
+    : public ReturnedValueConstructionContext {
+public:
+  explicit SimpleReturnedValueConstructionContext(const ReturnStmt *RS)
+      : ReturnedValueConstructionContext(
+            ConstructionContext::SimpleReturnedValueKind, RS) {}
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == SimpleReturnedValueKind;
+  }
+};
+
+/// Represents a temporary object that is being immediately returned from a
+/// function by value, eg. return t; or return T(123); in C++17.
+/// In C++17 there is not going to be an elidable copy constructor at the
+/// return site.  However, the usual temporary-related bureaucracy (CXXBindTemporaryExpr,
+/// MaterializeTemporaryExpr) is normally located in the caller function's AST.
+/// Note that if the object has trivial destructor, then this code is
+/// indistinguishable from a simple returned value constructor on the AST level;
+/// in this case we provide a simple returned value construction context.
+class CXX17ElidedCopyReturnedValueConstructionContext
+    : public ReturnedValueConstructionContext {
+  const CXXBindTemporaryExpr *BTE;
 
 public:
-  explicit ReturnedValueConstructionContext(const ReturnStmt *RS)
-      : ConstructionContext(ConstructionContext::ReturnedValueKind), RS(RS) {
-    assert(RS);
+  explicit CXX17ElidedCopyReturnedValueConstructionContext(
+      const ReturnStmt *RS, const CXXBindTemporaryExpr *BTE)
+      : ReturnedValueConstructionContext(
+            ConstructionContext::CXX17ElidedCopyReturnedValueKind, RS),
+        BTE(BTE) {
+    assert(BTE);
   }
 
-  const ReturnStmt *getReturnStmt() const { return RS; }
+  const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; }
 
   static bool classof(const ConstructionContext *CC) {
-    return CC->getKind() == ReturnedValueKind;
+    return CC->getKind() == CXX17ElidedCopyReturnedValueKind;
   }
 };
 
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -17,6 +17,7 @@
 
 #include "clang/AST/ExprCXX.h"
 #include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Analysis/ConstructionContext.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/GraphTraits.h"
@@ -38,8 +39,6 @@
 class ASTContext;
 class BinaryOperator;
 class CFG;
-class ConstructionContext;
-class TemporaryObjectConstructionContext;
 class CXXBaseSpecifier;
 class CXXBindTemporaryExpr;
 class CXXCtorInitializer;
@@ -170,34 +169,36 @@
 
 /// CFGCXXRecordTypedCall - Represents a function call that returns a C++ object
 /// by value. This, like constructor, requires a construction context, which
-/// will 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
+/// will usually be that of a temporary object - usually consumed by an elidable
+/// constructor. In C++17 it may also be a construction for a caller function's
+/// return value or of a variable because in these cases copy elision is
+/// required, unlike C++14. For value-typed calls the
+/// ReturnedValueConstructionContext of their return value is naturally
+/// complemented by the ConstructionContext 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.
+/// destroying the object or modeling copy elision. Like CFGConstructor,
+/// this element is for now only used by the analyzer's CFG.
 class CFGCXXRecordTypedCall : public CFGStmt {
 public:
   /// Returns true when call expression \p CE needs to be represented
   /// by CFGCXXRecordTypedCall, as opposed to a regular CFGStmt.
   static bool isCXXRecordTypedCall(CallExpr *CE, const ASTContext &ACtx) {
     return CE->getCallReturnType(ACtx).getCanonicalType()->getAsCXXRecordDecl();
   }
 
-  explicit CFGCXXRecordTypedCall(CallExpr *CE,
-                                 const TemporaryObjectConstructionContext *C)
+  explicit CFGCXXRecordTypedCall(CallExpr *CE, const ConstructionContext *C)
       : CFGStmt(CE, CXXRecordTypedCall) {
     // FIXME: This is not protected against squeezing a non-record-typed-call
     // into the constructor. An assertion would require passing an ASTContext
     // which would mean paying for something we don't use.
-    assert(C);
-    Data2.setPointer(const_cast<TemporaryObjectConstructionContext *>(C));
+    assert(C && (isa<TemporaryObjectConstructionContext>(C) ||
+                 isa<ReturnedValueConstructionContext>(C) ||
+                 isa<VariableConstructionContext>(C)));
+    Data2.setPointer(const_cast<ConstructionContext *>(C));
   }
 
-  const TemporaryObjectConstructionContext *getConstructionContext() const {
-    return static_cast<TemporaryObjectConstructionContext *>(
-        Data2.getPointer());
+  const ConstructionContext *getConstructionContext() const {
+    return static_cast<ConstructionContext *>(Data2.getPointer());
   }
 
 private:
@@ -830,7 +831,7 @@
   }
 
   void appendCXXRecordTypedCall(CallExpr *CE,
-                                const TemporaryObjectConstructionContext *CC,
+                                const ConstructionContext *CC,
                                 BumpVectorContext &C) {
     Elements.push_back(CFGCXXRecordTypedCall(CE, CC), C);
   }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to