NoQ created this revision. NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, rnkovacs. Herald added subscribers: cfe-commits, baloghadamsoftware.
Copy elision occurs when 1. a temporary object (with its corresponding temporary object construction context) is constructed and 2. it is immediately copied or moved into any other object (with its own construction context, which can be any construction context). Constructor on step 2 would then be called "elidable". Copy elision consists in skipping ("eliding") the constructor on step 2 and performing the constructor on step 1 as if it was constructing the object of step 2. Additionally, destructors are skipped for the temporary object constructed on step 1. The current patch modifies the temporary object construction context for constructor on step 1 to include information about the upcoming step 2. Only temporary object construction contexts would need to be updated because step 1 always has a temporary object construction context. The updated context will include both a reference to the elidable construct-expression of step 2 and the construction context of step 2. This will allow the analyzer to both skip the elidable constructor itself when it's encountered and compute the correct target region associated with step 2 while modeling step 1. TOTHINK: whether "temporary object construction context with elision" should be a separate construction context sub-class. Repository: rC Clang https://reviews.llvm.org/D47616 Files: include/clang/Analysis/ConstructionContext.h lib/Analysis/CFG.cpp lib/Analysis/ConstructionContext.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/temp-obj-dtors-cfg-output.cpp
Index: test/Analysis/temp-obj-dtors-cfg-output.cpp =================================================================== --- test/Analysis/temp-obj-dtors-cfg-output.cpp +++ test/Analysis/temp-obj-dtors-cfg-output.cpp @@ -235,7 +235,7 @@ // CHECK: Succs (1): B1 // CHECK: [B1] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B1.2], [B1.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A) // CHECK: 2: [B1.1] (BindTemporary) // CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B1.3] @@ -295,7 +295,7 @@ // CHECK: Succs (1): B1 // CHECK: [B1] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B1.2], [B1.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A) // CHECK: 2: [B1.1] (BindTemporary) // CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B1.3] @@ -550,12 +550,12 @@ // CHECK: Succs (2): B6 B5 // CHECK: [B8] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B8.2], [B8.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B8.2], [B8.4], [B8.5], class A) // CHECK: 2: [B8.1] (BindTemporary) // CHECK: 3: [B8.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B8.3] // WARNINGS: 5: [B8.4] (CXXConstructExpr, class A) -// ANALYZER: 5: [B8.4] (CXXConstructExpr, [B8.6], [B7.3], class A) +// ANALYZER: 5: [B8.4] (CXXConstructExpr, [B8.6], [B7.3], [B7.4], class A) // CHECK: 6: [B8.5] (BindTemporary) // CHECK: Preds (1): B10 // CHECK: Succs (1): B7 @@ -570,13 +570,13 @@ // CHECK: 7: [B9.6] (ImplicitCastExpr, NoOp, const class A) // CHECK: 8: [B9.7] // WARNINGS: 9: [B9.8] (CXXConstructExpr, class A) -// ANALYZER: 9: [B9.8] (CXXConstructExpr, [B9.10], [B9.13], class A) +// ANALYZER: 9: [B9.8] (CXXConstructExpr, [B9.10], [B9.13], [B9.14], class A) // CHECK: 10: [B9.9] (BindTemporary) // CHECK: 11: A([B9.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A) // CHECK: 12: [B9.11] (ImplicitCastExpr, NoOp, const class A) // CHECK: 13: [B9.12] // WARNINGS: 14: [B9.13] (CXXConstructExpr, class A) -// ANALYZER: 14: [B9.13] (CXXConstructExpr, [B9.15], [B7.3], class A) +// ANALYZER: 14: [B9.13] (CXXConstructExpr, [B9.15], [B7.3], [B7.4], class A) // CHECK: 15: [B9.14] (BindTemporary) // CHECK: Preds (1): B10 // CHECK: Succs (1): B7 @@ -680,7 +680,7 @@ // CHECK: Succs (1): B0 // CHECK: [B4] // WARNINGS: 1: C() (CXXConstructExpr, struct C) -// ANALYZER: 1: C() (CXXConstructExpr, [B4.2], [B4.4], struct C) +// ANALYZER: 1: C() (CXXConstructExpr, [B4.2], [B4.4], [B4.5], struct C) // CHECK: 2: [B4.1] (BindTemporary) // CHECK: 3: [B4.2] (ImplicitCastExpr, NoOp, const struct C) // CHECK: 4: [B4.3] @@ -733,7 +733,7 @@ // CHECK: Succs (1): B0 // CHECK: [B3] // CXX98-WARNINGS: 1: D() (CXXConstructExpr, struct D) -// CXX98-ANALYZER: 1: D() (CXXConstructExpr, [B3.3], struct D) +// CXX98-ANALYZER: 1: D() (CXXConstructExpr, [B3.3], [B3.4], struct D) // CXX98: 2: [B3.1] (ImplicitCastExpr, NoOp, const struct D) // CXX98: 3: [B3.2] // CXX98-WARNINGS: 4: [B3.3] (CXXConstructExpr, struct D) @@ -745,7 +745,7 @@ // CXX98: 9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool) // CXX98: T: if [B3.9] // CXX11-WARNINGS: 1: D() (CXXConstructExpr, struct D) -// CXX11-ANALYZER: 1: D() (CXXConstructExpr, [B3.2], struct D) +// CXX11-ANALYZER: 1: D() (CXXConstructExpr, [B3.2], [B3.3], struct D) // CXX11: 2: [B3.1] // CXX11-WARNINGS: 3: [B3.2] (CXXConstructExpr, struct D) // CXX11-ANALYZER: 3: [B3.2] (CXXConstructExpr, [B3.4], struct D) @@ -789,7 +789,7 @@ // CHECK: Succs (2): B3 B2 // CHECK: [B5] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B5.2], [B5.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B5.2], [B5.4], [B5.5], class A) // CHECK: 2: [B5.1] (BindTemporary) // CHECK: 3: [B5.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B5.3] @@ -809,7 +809,7 @@ // CHECK: 7: [B6.6] (ImplicitCastExpr, NoOp, const class A) // CHECK: 8: [B6.7] // WARNINGS: 9: [B6.8] (CXXConstructExpr, class A) -// ANALYZER: 9: [B6.8] (CXXConstructExpr, [B6.10], [B6.13], class A) +// ANALYZER: 9: [B6.8] (CXXConstructExpr, [B6.10], [B6.13], [B6.14], class A) // CHECK: 10: [B6.9] (BindTemporary) // CHECK: 11: A([B6.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A) // CHECK: 12: [B6.11] (ImplicitCastExpr, NoOp, const class A) @@ -852,7 +852,7 @@ // CHECK: Succs (2): B9 B8 // CHECK: [B11] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B11.2], [B11.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B11.2], [B11.4], [B11.5], class A) // CHECK: 2: [B11.1] (BindTemporary) // CHECK: 3: [B11.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B11.3] @@ -872,7 +872,7 @@ // CHECK: 7: [B12.6] (ImplicitCastExpr, NoOp, const class A) // CHECK: 8: [B12.7] // WARNINGS: 9: [B12.8] (CXXConstructExpr, class A) -// ANALYZER: 9: [B12.8] (CXXConstructExpr, [B12.10], [B12.13], class A) +// ANALYZER: 9: [B12.8] (CXXConstructExpr, [B12.10], [B12.13], [B12.14], class A) // CHECK: 10: [B12.9] (BindTemporary) // CHECK: 11: A([B12.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A) // CHECK: 12: [B12.11] (ImplicitCastExpr, NoOp, const class A) @@ -935,7 +935,7 @@ // CHECK: Succs (1): B4 // CHECK: [B6] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B6.2], [B6.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B6.2], [B6.4], [B6.5], class A) // CHECK: 2: [B6.1] (BindTemporary) // CHECK: 3: [B6.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B6.3] @@ -1001,7 +1001,7 @@ // CHECK: Succs (1): B4 // CHECK: [B6] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B6.2], [B6.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B6.2], [B6.4], [B6.5], class A) // CHECK: 2: [B6.1] (BindTemporary) // CHECK: 3: [B6.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B6.3] @@ -1086,7 +1086,7 @@ // CHECK: Succs (1): B1 // CHECK: [B1] // WARNINGS: 1: A() (CXXConstructExpr, class A) -// ANALYZER: 1: A() (CXXConstructExpr, [B1.2], [B1.4], class A) +// ANALYZER: 1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A) // CHECK: 2: [B1.1] (BindTemporary) // CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A) // CHECK: 4: [B1.3] @@ -1130,7 +1130,7 @@ // CHECK: 1: A::make // CHECK: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void)) // WARNINGS: 3: [B1.2]() -// ANALYZER: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6]) +// ANALYZER: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7]) // CHECK: 4: [B1.3] (BindTemporary) // CHECK: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A) // CHECK: 6: [B1.5] Index: test/Analysis/cfg-rich-constructors.cpp =================================================================== --- test/Analysis/cfg-rich-constructors.cpp +++ test/Analysis/cfg-rich-constructors.cpp @@ -99,10 +99,11 @@ // CHECK: void simpleVariableInitializedByValue() // CHECK: 1: C::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) +// CXX11-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5]) // CXX11-NEXT: 4: [B1.3] // CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6], class C) // CXX11-NEXT: 6: C c = C::get(); +// CXX17-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) // CXX17-NEXT: 4: C c = C::get(); void simpleVariableInitializedByValue() { C c = C::get(); @@ -122,17 +123,17 @@ // CHECK: [B2] // CHECK-NEXT: 1: C::get // CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CXX11-NEXT: 3: [B2.2]() (CXXRecordTypedCall, [B2.4]) +// CXX11-NEXT: 3: [B2.2]() (CXXRecordTypedCall, [B2.4], [B2.5]) // CXX11-NEXT: 4: [B2.3] -// CXX11-NEXT: 5: [B2.4] (CXXConstructExpr, [B1.2], class C) +// CXX11-NEXT: 5: [B2.4] (CXXConstructExpr, [B1.2], [B1.3], class C) // CXX17-NEXT: 3: [B2.2]() // CHECK: [B3] // CHECK-NEXT: 1: 0 // CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *) -// CXX11-NEXT: 3: [B3.2] (CXXConstructExpr, [B3.5], class C) +// CXX11-NEXT: 3: [B3.2] (CXXConstructExpr, [B3.5], [B3.6], class C) // CXX11-NEXT: 4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C) // CXX11-NEXT: 5: [B3.4] -// CXX11-NEXT: 6: [B3.5] (CXXConstructExpr, [B1.2], class C) +// CXX11-NEXT: 6: [B3.5] (CXXConstructExpr, [B1.2], [B1.3], class C) // CXX17-NEXT: 3: [B3.2] (CXXConstructExpr, class C) // CXX17-NEXT: 4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C) // CHECK: [B4] @@ -146,7 +147,8 @@ // CHECK: void simpleVariableWithElidableCopy() // CHECK: 1: 0 // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *) -// CHECK-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class C) +// CXX11-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], [B1.6], class C) +// CXX17-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class C) // CHECK-NEXT: 4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C) // CXX11-NEXT: 5: [B1.4] // CXX11-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.7], class C) @@ -185,14 +187,14 @@ // CHECK: [B2] // CHECK-NEXT: 1: C::get // CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CXX11-NEXT: 3: [B2.2]() (CXXRecordTypedCall, [B2.4]) +// CXX11-NEXT: 3: [B2.2]() (CXXRecordTypedCall, [B2.4], [B2.5]) // CXX11-NEXT: 4: [B2.3] // CXX11-NEXT: 5: [B2.4] (CXXConstructExpr, [B1.3], class C) // CXX17-NEXT: 3: [B2.2]() (CXXRecordTypedCall, [B1.3]) // CHECK: [B3] // CHECK-NEXT: 1: 0 // CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *) -// CXX11-NEXT: 3: [B3.2] (CXXConstructExpr, [B3.5], class C) +// CXX11-NEXT: 3: [B3.2] (CXXConstructExpr, [B3.5], [B3.6], class C) // CXX11-NEXT: 4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C) // CXX11-NEXT: 5: [B3.4] // CXX11-NEXT: 6: [B3.5] (CXXConstructExpr, [B1.3], class C) @@ -242,7 +244,7 @@ // CHECK-NEXT: 7: CFGNewAllocator(C *) // CHECK-NEXT: 8: C::get // CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CXX11-NEXT: 10: [B1.9]() (CXXRecordTypedCall, [B1.11]) +// CXX11-NEXT: 10: [B1.9]() (CXXRecordTypedCall, [B1.11], [B1.12]) // CXX11-NEXT: 11: [B1.10] // CXX11-NEXT: 12: [B1.11] (CXXConstructExpr, [B1.13], class C) // CXX11-NEXT: 13: new C([B1.12]) @@ -270,7 +272,7 @@ // CHECK: F() // CHECK: 1: E::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class ctor_initializers::E (*)( -// CXX11-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6]) +// CXX11-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7]) // CXX11-NEXT: 4: [B1.3] (BindTemporary) // CXX11-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class ctor_initializers::E) // CXX11-NEXT: 6: [B1.5] @@ -326,19 +328,21 @@ } // CHECK: C returnTemporary() -// CHECK: 1: C() (CXXConstructExpr, [B1.2], class C) +// CXX11: 1: C() (CXXConstructExpr, [B1.2], [B1.3], class C) // CXX11-NEXT: 2: [B1.1] // CXX11-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.4], class C) // CXX11-NEXT: 4: return [B1.3]; +// CXX17: 1: C() (CXXConstructExpr, [B1.2], class C) // CXX17-NEXT: 2: return [B1.1]; C returnTemporary() { return C(); } // CHECK: C returnTemporaryWithArgument() // CHECK: 1: nullptr // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *) -// CHECK-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class C) +// CXX11-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], [B1.6], class C) +// CXX17-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class C) // CHECK-NEXT: 4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C) // CXX11-NEXT: 5: [B1.4] // CXX11-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.7], class C) @@ -352,21 +356,22 @@ // CHECK: C returnTemporaryConstructedByFunction() // CHECK: 1: C::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) +// CXX11-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [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: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) // CXX17-NEXT: 4: return [B1.3]; C returnTemporaryConstructedByFunction() { return C::get(); } // CHECK: C returnChainOfCopies() // CHECK: 1: C::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CXX11-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) +// CXX11-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5]) // CXX11-NEXT: 4: [B1.3] -// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], class C) +// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], [B1.8], class C) // CXX11-NEXT: 6: C([B1.5]) (CXXFunctionalCastExpr, ConstructorConversion, class C) // CXX11-NEXT: 7: [B1.6] // CXX11-NEXT: 8: [B1.7] (CXXConstructExpr, [B1.9], class C) @@ -390,7 +395,7 @@ // FIXME: There should be no temporary destructor in C++17. // CHECK: return_stmt_with_dtor::D returnTemporary() -// CXX11: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D) +// CXX11: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class return_stmt_with_dtor::D) // CXX11-NEXT: 2: [B1.1] (BindTemporary) // CXX11-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D) // CXX11-NEXT: 4: [B1.3] @@ -409,7 +414,7 @@ // 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: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7]) // CXX11-NEXT: 4: [B1.3] (BindTemporary) // CXX11-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D) // CXX11-NEXT: 6: [B1.5] @@ -451,7 +456,7 @@ } // CHECK: void temporaryInConditionVariable() -// CHECK: 1: C() (CXXConstructExpr, [B2.2], class C) +// CXX11: 1: C() (CXXConstructExpr, [B2.2], [B2.3], class C) // CXX11-NEXT: 2: [B2.1] // CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], class C) // CXX11-NEXT: 4: C c = C(); @@ -461,6 +466,7 @@ // CXX11-NEXT: 8: [B2.6] // CXX11-NEXT: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool) // CXX11-NEXT: T: if [B2.9] +// CXX17: 1: C() (CXXConstructExpr, [B2.2], class C) // CXX17-NEXT: 2: C c = C(); // CXX17-NEXT: 3: c // CXX17-NEXT: 4: [B2.3] (ImplicitCastExpr, NoOp, const class C) @@ -475,7 +481,7 @@ // CHECK: void temporaryInForLoopConditionVariable() // CHECK: [B2] -// CXX11-NEXT: 1: C() (CXXConstructExpr, [B2.2], class C) +// CXX11-NEXT: 1: C() (CXXConstructExpr, [B2.2], [B2.3], class C) // CXX11-NEXT: 2: [B2.1] // CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], class C) // CXX11-NEXT: 4: C c2 = C(); @@ -494,7 +500,7 @@ // CXX17-NEXT: 7: [B2.6] (ImplicitCastExpr, UserDefinedConversion, _Bool) // CXX17-NEXT: T: for (...; [B2.7]; ) // CHECK: [B3] -// CXX11-NEXT: 1: C() (CXXConstructExpr, [B3.2], class C) +// CXX11-NEXT: 1: C() (CXXConstructExpr, [B3.2], [B3.3], class C) // CXX11-NEXT: 2: [B3.1] // CXX11-NEXT: 3: [B3.2] (CXXConstructExpr, [B3.4], class C) // CXX11-NEXT: 4: C c1 = C(); @@ -505,9 +511,8 @@ } -// FIXME: Find construction context for the loop condition variable. // CHECK: void temporaryInWhileLoopConditionVariable() -// CXX11: 1: C() (CXXConstructExpr, [B2.2], class C) +// CXX11: 1: C() (CXXConstructExpr, [B2.2], [B2.3], class C) // CXX11-NEXT: 2: [B2.1] // CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], class C) // CXX11-NEXT: 4: C c = C(); @@ -603,15 +608,15 @@ // 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: 3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6], [B5.7]) // 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: 2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], [B6.7], class temporary_object_expr_with_dtors::D) // CXX11-NEXT: 3: [B6.2] (BindTemporary) // CXX11-NEXT: 4: temporary_object_expr_with_dtors::D([B6.3]) (CXXFunctionalCastExpr, ConstructorConversion, class temporary_object_expr_with_dtors::D) // CXX11-NEXT: 5: [B6.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D) @@ -699,7 +704,7 @@ // CHECK: 1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A) // CXX11-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) // CXX11-NEXT: 3: [B1.2] -// CXX11-NEXT: 4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B) +// CXX11-NEXT: 4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], [B1.9], class implicit_constructor_conversion::B) // CXX11-NEXT: 5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B) // CXX11-NEXT: 6: [B1.5] (BindTemporary) // CXX11-NEXT: 7: [B1.6] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B) @@ -724,7 +729,7 @@ // CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.5]) // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) // CHECK-NEXT: 5: [B1.4] -// CXX11-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B) +// CXX11-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], [B1.11], class implicit_constructor_conversion::B) // CXX11-NEXT: 7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B) // CXX11-NEXT: 8: [B1.7] (BindTemporary) // CXX11-NEXT: 9: [B1.8] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B) Index: lib/Analysis/ConstructionContext.cpp =================================================================== --- lib/Analysis/ConstructionContext.cpp +++ lib/Analysis/ConstructionContext.cpp @@ -61,7 +61,6 @@ // For temporaries with destructors, there may or may not be // lifetime extension on the parent layer. if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) { - assert(ParentLayer->isLast()); // C++17 *requires* elision of the constructor at the return site // and at variable/member initialization site, while previous standards // were allowing an optional elidable constructor. @@ -77,8 +76,30 @@ // both destruction and materialization info attached to it in the AST. if ((MTE = dyn_cast<MaterializeTemporaryExpr>( ParentLayer->getTriggerStmt()))) { - return create<TemporaryObjectConstructionContext>(C, BTE, MTE); + // Handle pre-C++17 copy and move elision. + const CXXConstructExpr *ElidedCE = nullptr; + const ConstructionContext *ElidedCC = nullptr; + if (const ConstructionContextLayer *ElidedLayer = + ParentLayer->getParent()) { + ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt()); + assert(ElidedCE->isElidable()); + // We're creating a construction context that might have already + // been created elsewhere. Maybe we should unique our construction + // contexts. That's what we often do, but in this case it's unlikely + // to bring any benefits. + ElidedCC = createFromLayers(C, ElidedLayer->getParent()); + if (!ElidedCC) { + // We may fail to create the elided construction context. + // In this case, skip copy elision entirely. + ElidedCE = nullptr; + } + } else { + assert(ParentLayer->isLast()); + } + return create<TemporaryObjectConstructionContext>(C, BTE, MTE, + ElidedCE, ElidedCC); } + assert(ParentLayer->isLast()); // This is a constructor into a function argument. Not implemented yet. if (isa<CallExpr>(ParentLayer->getTriggerStmt())) return nullptr; @@ -99,7 +120,13 @@ llvm_unreachable("Unexpected construction context with destructor!"); } // A temporary object that doesn't require materialization. - return create<TemporaryObjectConstructionContext>(C, BTE, /*MTE=*/nullptr); + // In particular, it shouldn't require copy elision, because + // copy/move constructors take a reference, which requires + // materialization to obtain the glvalue. + return create<TemporaryObjectConstructionContext>(C, BTE, + /*MTE=*/nullptr, + /*ElidedCE=*/nullptr, + /*ElidedCC=*/nullptr); } if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) { // If the object requires destruction and is not lifetime-extended, @@ -110,8 +137,27 @@ MTE->getStorageDuration() != SD_FullExpression)) return nullptr; - assert(TopLayer->isLast()); - return create<TemporaryObjectConstructionContext>(C, nullptr, MTE); + // Handle pre-C++17 copy and move elision. + const CXXConstructExpr *ElidedCE = nullptr; + const ConstructionContext *ElidedCC = nullptr; + if (const ConstructionContextLayer *ElidedLayer = TopLayer->getParent()) { + ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt()); + assert(ElidedCE->isElidable()); + // We're creating a construction context that might have already + // been created elsewhere. Maybe we should unique our construction + // contexts. That's what we often do, but in this case it's unlikely + // to bring any benefits. + ElidedCC = createFromLayers(C, ElidedLayer->getParent()); + if (!ElidedCC) { + // We may fail to create the elided construction context. + // In this case, skip copy elision entirely. + ElidedCE = nullptr; + } + } else { + assert(TopLayer->isLast()); + } + return create<TemporaryObjectConstructionContext>(C, nullptr, MTE, + ElidedCE, ElidedCC); } if (const auto *RS = dyn_cast<ReturnStmt>(S)) { assert(TopLayer->isLast()); Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -1255,7 +1255,16 @@ switch(Child->getStmtClass()) { case Stmt::CXXConstructExprClass: case Stmt::CXXTemporaryObjectExprClass: { - consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child)); + // Support pre-C++17 copy elision AST. + auto *CE = cast<CXXConstructExpr>(Child); + if (CE->isElidable()) { + assert(CE->getNumArgs() == 1); + findConstructionContexts(ConstructionContextLayer::create( + cfg->getBumpVectorContext(), CE, Layer), + CE->getArg(0)); + } + + consumeConstructionContext(Layer, CE); break; } // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr. @@ -1294,10 +1303,25 @@ } case Stmt::CXXBindTemporaryExprClass: { auto *BTE = cast<CXXBindTemporaryExpr>(Child); - findConstructionContexts( - ConstructionContextLayer::create(cfg->getBumpVectorContext(), - BTE, Layer), - BTE->getSubExpr()); + findConstructionContexts(ConstructionContextLayer::create( + cfg->getBumpVectorContext(), BTE, Layer), + BTE->getSubExpr()); + break; + } + case Stmt::MaterializeTemporaryExprClass: { + // Normally we don't want to search in MaterializeTemporaryExpr because + // it indicates the beginning of a temporary object construction context, + // so it shouldn't be found in the middle. However, if it is the beginning + // of an elidable copy or move construction context, we need to include it. + if (const auto *CE = + dyn_cast_or_null<CXXConstructExpr>(Layer->getTriggerStmt())) { + if (CE->isElidable()) { + auto *MTE = cast<MaterializeTemporaryExpr>(Child); + findConstructionContexts(ConstructionContextLayer::create( + cfg->getBumpVectorContext(), MTE, Layer), + MTE->GetTemporaryExpr()); + } + } break; } case Stmt::ConditionalOperatorClass: { @@ -4931,7 +4955,8 @@ static void print_construction_context(raw_ostream &OS, StmtPrinterHelper &Helper, const ConstructionContext *CC) { - const Stmt *S1 = nullptr, *S2 = nullptr; + const Stmt *S1 = nullptr, *S2 = nullptr, *S3 = nullptr; + const ConstructionContext *CC1 = nullptr; switch (CC->getKind()) { case ConstructionContext::SimpleConstructorInitializerKind: { OS << ", "; @@ -4977,6 +5002,10 @@ } case ConstructionContext::TemporaryObjectKind: { const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC); + if (const CXXConstructExpr *CE = TOCC->getConstructorAfterElision()) { + S3 = CE; + CC1 = TOCC->getConstructionContextAfterElision(); + } S1 = TOCC->getCXXBindTemporaryExpr(); S2 = TOCC->getMaterializedTemporaryExpr(); break; @@ -4990,6 +5019,10 @@ OS << ", "; Helper.handledStmt(const_cast<Stmt *>(S2), OS); } + if (S3) { + OS << ", "; + Helper.handledStmt(const_cast<Stmt *>(S3), OS); + } } static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, Index: include/clang/Analysis/ConstructionContext.h =================================================================== --- include/clang/Analysis/ConstructionContext.h +++ include/clang/Analysis/ConstructionContext.h @@ -304,17 +304,26 @@ class TemporaryObjectConstructionContext : public ConstructionContext { const CXXBindTemporaryExpr *BTE; const MaterializeTemporaryExpr *MTE; + const CXXConstructExpr *ElidedCE; + const ConstructionContext *ElidedCC; friend class ConstructionContext; // Allows to create<>() itself. explicit TemporaryObjectConstructionContext( - const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE) + const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE, + const CXXConstructExpr *ElidedCE, const ConstructionContext *ElidedCC) : ConstructionContext(ConstructionContext::TemporaryObjectKind), - BTE(BTE), MTE(MTE) { + BTE(BTE), MTE(MTE), ElidedCE(ElidedCE), ElidedCC(ElidedCC) { // Both BTE and MTE can be null here, all combinations possible. // Even though for now at least one should be non-null, we simply haven't - // implemented this case yet (this would be a temporary in the middle of - // nowhere that doesn't have a non-trivial destructor). + // implemented the other case yet (this would be a temporary in the middle + // of nowhere that doesn't have a non-trivial destructor). + + // Elided constructor and its context should be either both specified + // or both unspecified. In the former case, the constructor must be + // elidable. + assert((!ElidedCE && !ElidedCC) || + (ElidedCE && ElidedCE->isElidable() && ElidedCC)); } public: @@ -333,6 +342,14 @@ return MTE; } + const CXXConstructExpr *getConstructorAfterElision() const { + return ElidedCE; + } + + const ConstructionContext *getConstructionContextAfterElision() const { + return ElidedCC; + } + static bool classof(const ConstructionContext *CC) { return CC->getKind() == TemporaryObjectKind; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits