]> granicus.if.org Git - clang/commitdiff
[CFG] [analyzer] Add C++17-specific variable and return construction contexts.
authorArtem Dergachev <artem.dergachev@gmail.com>
Thu, 22 Mar 2018 21:37:39 +0000 (21:37 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Thu, 22 Mar 2018 21:37:39 +0000 (21:37 +0000)
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:

- Fork VariableConstructionContext and ReturnedValueConstructionContext into
  two different sub-classes (each) one of which indicates the C++17 case and
  contains a reference to an extra CXXBindTemporaryExpr.
- Allow CFGCXXRecordTypedCall element to accept VariableConstructionContext and
  ReturnedValueConstructionContext as its context.

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

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

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 bb66aac3f6dcea80fd58653c1efdbee3329e6b87..3f98e6be9cda2896464148f3a5449205b36321f7 100644 (file)
@@ -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 @@ namespace clang {
 class ASTContext;
 class BinaryOperator;
 class CFG;
-class ConstructionContext;
-class TemporaryObjectConstructionContext;
 class CXXBaseSpecifier;
 class CXXBindTemporaryExpr;
 class CXXCtorInitializer;
@@ -171,14 +170,11 @@ private:
 };
 
 /// 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
-/// 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.
+/// by value. This, like constructor, requires a construction context in order
+/// to understand the storage of the returned object . In C such tracking is not
+/// necessary because no additional effort is required for 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
@@ -187,19 +183,19 @@ public:
     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:
@@ -881,7 +877,7 @@ public:
   }
 
   void appendCXXRecordTypedCall(CallExpr *CE,
-                                const TemporaryObjectConstructionContext *CC,
+                                const ConstructionContext *CC,
                                 BumpVectorContext &C) {
     Elements.push_back(CFGCXXRecordTypedCall(CE, CC), C);
   }
index 770f1c929e862282a810a2e66bceb20416d29cdd..67657c93f626fd0950a553e9a4dbe8884e252c4e 100644 (file)
@@ -99,22 +99,26 @@ class ConstructionContext {
 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 @@ public:
   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 @@ public:
   }
 };
 
+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 0672b3d9256c4632e5217794a2338fd8d8195c28..77eb1d37a4067253868b9a1085c3d2acc130bed2 100644 (file)
@@ -760,9 +760,7 @@ private:
           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;
         }
@@ -1284,7 +1282,7 @@ void CFGBuilder::findConstructionContexts(
   }
   case Stmt::ImplicitCastExprClass: {
     auto *Cast = cast<ImplicitCastExpr>(Child);
-    // TODO: We need to support CK_ConstructorConversion, maybe other kinds?
+    // Should we support other implicit cast kinds?
     switch (Cast->getCastKind()) {
     case CK_NoOp:
     case CK_ConstructorConversion:
@@ -4920,8 +4918,14 @@ static void print_construction_context(raw_ostream &OS,
     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: {
@@ -4929,9 +4933,16 @@ static void print_construction_context(raw_ostream &OS,
     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 1a1bc7db8fbcd6570c400d81d1df865642014e90..68e1cc047463e3e3a3d3a4e486141d7d6c2a492d 100644 (file)
@@ -64,11 +64,43 @@ const ConstructionContext *ConstructionContext::createFromLayers(
       // 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);
+        }
+        // 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);
+        }
+        if (auto *DS = dyn_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);
+        }
+        llvm_unreachable("Unexpected construction context with destructor!");
       }
+      // A temporary object that doesn't require materialization.
       auto *CC =
           C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
-      return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
+      return new (CC)
+          TemporaryObjectConstructionContext(BTE, /*MTE=*/nullptr);
     } 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 +114,8 @@ const ConstructionContext *ConstructionContext::createFromLayers(
     } 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 7cf6896aa8bf1ad785ae389a32c19498baf40901..07515b364707ce489c148de79f8b1e5549ab8ed9 100644 (file)
@@ -196,7 +196,7 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
       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.
@@ -211,6 +211,10 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
       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 c22f96dd42a26a2a530d5a18fa7da0eb3c88940c..9070b003def54777de1dbf8ee3f872ca57caa90e 100644 (file)
@@ -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 @@ void simpleVariableWithOperatorNewInBraces() {
 // 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 @@ void simpleVariableWithTernaryOperator(bool coin) {
 // 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 @@ void referenceVariableWithInitializer() {
 
 // 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 @@ public:
 // 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 @@ public:
 // 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 @@ C returnBracesWithMultipleItems() {
 
 // 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,9 +309,11 @@ C returnTemporary() {
 // 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);
 }
@@ -300,9 +322,10 @@ C returnTemporaryWithArgument() {
 // 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();
 }
@@ -310,13 +333,16 @@ C returnTemporaryConstructedByFunction() {
 // 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 @@ public:
   ~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 @@ void referenceVariableWithInitializer() {
 }
 
 // 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 @@ void referenceWithFunctionalCast() {
 
 // 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 @@ public:
 
 // 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 @@ void implicitConstructionConversionFromTemporary() {
 // 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 @@ void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
   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