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; @@ -171,14 +170,11 @@ }; /// 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 @@ 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(C)); + assert(C && (isa(C) || + isa(C) || + isa(C))); + Data2.setPointer(const_cast(C)); } - const TemporaryObjectConstructionContext *getConstructionContext() const { - return static_cast( - Data2.getPointer()); + const ConstructionContext *getConstructionContext() const { + return static_cast(Data2.getPointer()); } private: @@ -881,7 +877,7 @@ } void appendCXXRecordTypedCall(CallExpr *CE, - const TemporaryObjectConstructionContext *CC, + const ConstructionContext *CC, BumpVectorContext &C) { Elements.push_back(CFGCXXRecordTypedCall(CE, CC), C); } 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: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -760,9 +760,7 @@ const ConstructionContext *CC = ConstructionContext::createFromLayers(cfg->getBumpVectorContext(), Layer); - B->appendCXXRecordTypedCall( - CE, cast(CC), - cfg->getBumpVectorContext()); + B->appendCXXRecordTypedCall(CE, CC, cfg->getBumpVectorContext()); cleanupConstructionContext(CE); return; } @@ -4920,8 +4918,14 @@ break; } case ConstructionContext::SimpleVariableKind: { - const auto *DSCC = cast(CC); - S1 = DSCC->getDeclStmt(); + const auto *SDSCC = cast(CC); + S1 = SDSCC->getDeclStmt(); + break; + } + case ConstructionContext::CXX17ElidedCopyVariableKind: { + const auto *CDSCC = cast(CC); + S1 = CDSCC->getDeclStmt(); + S2 = CDSCC->getCXXBindTemporaryExpr(); break; } case ConstructionContext::NewAllocatedObjectKind: { @@ -4929,9 +4933,16 @@ S1 = NECC->getCXXNewExpr(); break; } - case ConstructionContext::ReturnedValueKind: { - const auto *RSCC = cast(CC); + case ConstructionContext::SimpleReturnedValueKind: { + const auto *RSCC = cast(CC); + S1 = RSCC->getReturnStmt(); + break; + } + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + const auto *RSCC = + cast(CC); S1 = RSCC->getReturnStmt(); + S2 = RSCC->getCXXBindTemporaryExpr(); break; } case ConstructionContext::TemporaryObjectKind: { Index: lib/Analysis/ConstructionContext.cpp =================================================================== --- lib/Analysis/ConstructionContext.cpp +++ lib/Analysis/ConstructionContext.cpp @@ -64,11 +64,43 @@ // lifetime extension on the parent layer. if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) { assert(ParentLayer->isLast()); - MTE = cast(ParentLayer->getTriggerStmt()); + if ((MTE = dyn_cast( + ParentLayer->getTriggerStmt()))) { + // A temporary object which has both destruction and + // materialization info. + auto *CC = + C.getAllocator().Allocate(); + 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(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(ParentLayer->getTriggerStmt())) { + assert(!cast(DS->getSingleDecl())->getType() + .getCanonicalType()->getAsCXXRecordDecl() + ->hasTrivialDestructor()); + auto *CC = + C.getAllocator() + .Allocate(); + 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(); - return new (CC) TemporaryObjectConstructionContext(BTE, MTE); + return new (CC) + TemporaryObjectConstructionContext(BTE, /*MTE=*/nullptr); } else if (const auto *MTE = dyn_cast(S)) { // If the object requires destruction and is not lifetime-extended, // then it must have a BTE within its MTE. @@ -82,8 +114,8 @@ } else if (const auto *RS = dyn_cast(S)) { assert(TopLayer->isLast()); auto *CC = - C.getAllocator().Allocate(); - return new (CC) ReturnedValueConstructionContext(RS); + C.getAllocator().Allocate(); + return new (CC) SimpleReturnedValueConstructionContext(RS); } } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) { assert(TopLayer->isLast()); Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -196,7 +196,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. @@ -211,6 +211,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: 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,9 +309,11 @@ // 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 @@ // 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 @@ // 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