diff options
Diffstat (limited to 'test/Analysis')
-rw-r--r-- | test/Analysis/auto-obj-dtors-cfg-output.cpp | 4 | ||||
-rw-r--r-- | test/Analysis/cfg-rich-constructors.cpp | 33 | ||||
-rw-r--r-- | test/Analysis/cfg-rich-constructors.mm | 3 | ||||
-rw-r--r-- | test/Analysis/cfg.cpp | 4 | ||||
-rw-r--r-- | test/Analysis/missing-bind-temporary.cpp | 6 | ||||
-rw-r--r-- | test/Analysis/more-dtors-cfg-output.cpp | 317 | ||||
-rw-r--r-- | test/Analysis/scopes-cfg-output.cpp | 4 | ||||
-rw-r--r-- | test/Analysis/temporaries.cpp | 19 |
8 files changed, 343 insertions, 47 deletions
diff --git a/test/Analysis/auto-obj-dtors-cfg-output.cpp b/test/Analysis/auto-obj-dtors-cfg-output.cpp index 7e678a1ec7..716eaf3c0f 100644 --- a/test/Analysis/auto-obj-dtors-cfg-output.cpp +++ b/test/Analysis/auto-obj-dtors-cfg-output.cpp @@ -334,7 +334,7 @@ void test_aggregate_array_lifetime_extension() { // CHECK-NEXT: 61: ~A() (Temporary object destructor) // CHECK-NEXT: 62: ~A() (Temporary object destructor) // CHECK-NEXT: 63: ~A() (Temporary object destructor) -// CHECK-NEXT: 64: [B1.57].~D() (Implicit destructor) +// CHECK-NEXT: 64: [B1.57].~D [2]() (Implicit destructor) // CHECK-NEXT: 65: [B1.18].~D() (Implicit destructor) // CHECK-NEXT: Preds (1): B2 // CHECK-NEXT: Succs (1): B0 @@ -363,7 +363,7 @@ void test_aggregate_with_nontrivial_own_destructor() { // WARNINGS-NEXT: 3: (CXXConstructExpr, class A [0]) // ANALYZER-NEXT: 3: (CXXConstructExpr, [B1.4], class A [0]) // CHECK-NEXT: 4: A b[0]; -// CHECK-NEXT: 5: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B1.2].~A [2]() (Implicit destructor) // CHECK-NEXT: Preds (1): B2 // CHECK-NEXT: Succs (1): B0 // CHECK: [B0 (EXIT)] diff --git a/test/Analysis/cfg-rich-constructors.cpp b/test/Analysis/cfg-rich-constructors.cpp index cb1ed8eb6b..e33e57597d 100644 --- a/test/Analysis/cfg-rich-constructors.cpp +++ b/test/Analysis/cfg-rich-constructors.cpp @@ -412,7 +412,6 @@ public: ~D(); }; -// FIXME: There should be no temporary destructor in C++17. // CHECK: return_stmt_with_dtor::D returnTemporary() // CXX11-ELIDE: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class return_stmt_with_dtor::D) // CXX11-NOELIDE: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D) @@ -422,15 +421,13 @@ public: // 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: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.3], [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]; +// CXX17-NEXT: 3: 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)) @@ -442,12 +439,10 @@ D returnTemporary() { // 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) +// CXX11-NEXT: 10: [B1.8].~return_stmt_with_dtor::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(); } @@ -602,7 +597,7 @@ void temporaryInCondition() { // CHECK-NEXT: 3: [B1.2] (BindTemporary) // CHECK-NEXT: 4: [B1.3] // CHECK-NEXT: 5: const temporary_object_expr_with_dtors::D &d(0); -// CHECK-NEXT: 6: [B1.5].~D() (Implicit destructor) +// CHECK-NEXT: 6: [B1.5].~temporary_object_expr_with_dtors::D() (Implicit destructor) void referenceVariableWithConstructor() { const D &d(0); } @@ -613,14 +608,14 @@ void referenceVariableWithConstructor() { // CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D) // CHECK-NEXT: 4: [B1.3] // CHECK-NEXT: 5: const temporary_object_expr_with_dtors::D &d = temporary_object_expr_with_dtors::D(); -// CHECK-NEXT: 6: [B1.5].~D() (Implicit destructor) +// CHECK-NEXT: 6: [B1.5].~temporary_object_expr_with_dtors::D() (Implicit destructor) void referenceVariableWithInitializer() { const D &d = D(); } // CHECK: void referenceVariableWithTernaryOperator(bool coin) // CXX11: [B1] -// CXX11-NEXT: 1: [B4.4].~D() (Implicit destructor) +// CXX11-NEXT: 1: [B4.4].~temporary_object_expr_with_dtors::D() (Implicit destructor) // CXX11: [B2] // CXX11-NEXT: 1: ~temporary_object_expr_with_dtors::D() (Temporary object destructor) // CXX11: [B3] @@ -660,7 +655,7 @@ void referenceVariableWithInitializer() { // 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-NEXT: 5: [B1.4].~temporary_object_expr_with_dtors::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)) @@ -686,7 +681,7 @@ void referenceVariableWithTernaryOperator(bool coin) { // CHECK-NEXT: 4: temporary_object_expr_with_dtors::D([B1.3]) (CXXFunctionalCastExpr, ConstructorCon // CHECK-NEXT: 5: [B1.4] // CHECK-NEXT: 6: temporary_object_expr_with_dtors::D &&d = temporary_object_expr_with_dtors::D(1); -// CHECK-NEXT: 7: [B1.6].~D() (Implicit destructor) +// CHECK-NEXT: 7: [B1.6].~temporary_object_expr_with_dtors::D() (Implicit destructor) void referenceWithFunctionalCast() { D &&d = D(1); } @@ -743,13 +738,13 @@ public: // 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) +// CXX11-NEXT: 12: [B1.10].~implicit_constructor_conversion::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) +// CXX17-NEXT: 7: [B1.6].~implicit_constructor_conversion::B() (Implicit destructor) void implicitConstructionConversionFromTemporary() { B b = A(); } @@ -769,11 +764,11 @@ void implicitConstructionConversionFromTemporary() { // 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) +// CXX11-NEXT: 14: [B1.12].~implicit_constructor_conversion::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) +// CXX17-NEXT: 9: [B1.8].~implicit_constructor_conversion::B() (Implicit destructor) void implicitConstructionConversionFromFunctionValue() { B b = get(); } @@ -787,7 +782,7 @@ void implicitConstructionConversionFromFunctionValue() { // CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B) // CHECK-NEXT: 7: [B1.6] // CHECK-NEXT: 8: const implicit_constructor_conversion::B &b = implicit_constructor_conversion::A(); -// CHECK-NEXT: 9: [B1.8].~B() (Implicit destructor) +// CHECK-NEXT: 9: [B1.8].~implicit_constructor_conversion::B() (Implicit destructor) void implicitConstructionConversionFromTemporaryWithLifetimeExtension() { const B &b = A(); } @@ -803,7 +798,7 @@ void implicitConstructionConversionFromTemporaryWithLifetimeExtension() { // CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B) // CHECK-NEXT: 9: [B1.8] // CHECK-NEXT: 10: const implicit_constructor_conversion::B &b = get(); -// CHECK-NEXT: 11: [B1.10].~B() (Implicit destructor) +// CHECK-NEXT: 11: [B1.10].~implicit_constructor_conversion::B() (Implicit destructor) void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() { const B &b = get(); // no-crash } diff --git a/test/Analysis/cfg-rich-constructors.mm b/test/Analysis/cfg-rich-constructors.mm index 289094293e..e55928d4c5 100644 --- a/test/Analysis/cfg-rich-constructors.mm +++ b/test/Analysis/cfg-rich-constructors.mm @@ -59,8 +59,7 @@ void passArgumentIntoMessage(E *e) { // CXX17-NEXT: 3: {{\[}}[B1.2] bar] (CXXRecordTypedCall, [B1.5], [B1.4]) // CXX17-NEXT: 4: [B1.3] (BindTemporary) // CXX17-NEXT: 5: D d = [e bar]; -// CXX17-NEXT: 6: ~D() (Temporary object destructor) -// CXX17-NEXT: 7: [B1.5].~D() (Implicit destructor) +// CXX17-NEXT: 6: [B1.5].~D() (Implicit destructor) void returnObjectFromMessage(E *e) { D d = [e bar]; } diff --git a/test/Analysis/cfg.cpp b/test/Analysis/cfg.cpp index ea028e06f3..74f4d50f25 100644 --- a/test/Analysis/cfg.cpp +++ b/test/Analysis/cfg.cpp @@ -203,7 +203,7 @@ namespace NoReturnSingleSuccessor { // CHECK-LABEL: int test1(int *x) // CHECK: 1: 1 // CHECK-NEXT: 2: return -// CHECK-NEXT: ~B() (Implicit destructor) +// CHECK-NEXT: ~NoReturnSingleSuccessor::B() (Implicit destructor) // CHECK-NEXT: Preds (1) // CHECK-NEXT: Succs (1): B0 int test1(int *x) { @@ -477,7 +477,7 @@ void test_lifetime_extended_temporaries() { // WARNINGS-NEXT: 1: (CXXConstructExpr, struct pr37688_deleted_union_destructor::A) // ANALYZER-NEXT: 1: (CXXConstructExpr, [B1.2], struct pr37688_deleted_union_destructor::A) // CHECK-NEXT: 2: pr37688_deleted_union_destructor::A a; -// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B1.2].~pr37688_deleted_union_destructor::A() (Implicit destructor) // CHECK-NEXT: Preds (1): B2 // CHECK-NEXT: Succs (1): B0 // CHECK: [B0 (EXIT)] diff --git a/test/Analysis/missing-bind-temporary.cpp b/test/Analysis/missing-bind-temporary.cpp index 7be4e2ddf3..18204929bc 100644 --- a/test/Analysis/missing-bind-temporary.cpp +++ b/test/Analysis/missing-bind-temporary.cpp @@ -31,7 +31,7 @@ class B { // CHECK-NEXT: 8: [B1.7] // CHECK-NEXT: 9: [B1.5] = [B1.8] (OperatorCall) // CHECK-NEXT: 10: ~variant_0::B() (Temporary object destructor) -// CHECK-NEXT: 11: [B1.2].~B() (Implicit destructor) +// CHECK-NEXT: 11: [B1.2].~variant_0::B() (Implicit destructor) void foo(int) { B i; i = {}; @@ -71,7 +71,7 @@ class B { // CHECK-NEXT: 6: {} (CXXConstructExpr, class variant_1::B) // CHECK-NEXT: 7: [B1.6] // CHECK-NEXT: 8: [B1.5] = [B1.7] (OperatorCall) -// CHECK-NEXT: 9: [B1.2].~B() (Implicit destructor) +// CHECK-NEXT: 9: [B1.2].~variant_1::B() (Implicit destructor) template <typename T> void foo(T) { B i; i = {}; @@ -114,7 +114,7 @@ public: // CHECK-NEXT: 9: [B1.8] // CHECK-NEXT: 10: [B1.5] = [B1.9] (OperatorCall) // CHECK-NEXT: 11: ~variant_2::B() (Temporary object destructor) -// CHECK-NEXT: 12: [B1.2].~B() (Implicit destructor) +// CHECK-NEXT: 12: [B1.2].~variant_2::B() (Implicit destructor) template <typename T> void foo(T) { B i; i = {}; diff --git a/test/Analysis/more-dtors-cfg-output.cpp b/test/Analysis/more-dtors-cfg-output.cpp new file mode 100644 index 0000000000..c0df5953aa --- /dev/null +++ b/test/Analysis/more-dtors-cfg-output.cpp @@ -0,0 +1,317 @@ +// RUN: rm -f %t.14 %t.2a +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++14 -DCXX2A=0 -fblocks -Wall -Wno-unused -Werror %s > %t.14 2>&1 +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++2a -DCXX2A=1 -fblocks -Wall -Wno-unused -Werror %s > %t.2a 2>&1 +// RUN: FileCheck --input-file=%t.14 -check-prefixes=CHECK,CXX14 -implicit-check-not=destructor %s +// RUN: FileCheck --input-file=%t.2a -check-prefixes=CHECK,CXX2A -implicit-check-not=destructor %s + +int puts(const char *); + +struct Foo { + Foo() = delete; +#if CXX2A + // Guarantee that the elided examples are actually elided by deleting the + // copy constructor. + Foo(const Foo &) = delete; +#else + // No elision support, so we need a copy constructor. + Foo(const Foo &); +#endif + ~Foo(); +}; + +struct TwoFoos { + Foo foo1, foo2; + ~TwoFoos(); +}; + +Foo get_foo(); + +struct Bar { + Bar(); + Bar(const Bar &); + ~Bar(); + Bar &operator=(const Bar &); +}; + +Bar get_bar(); + +struct TwoBars { + Bar foo1, foo2; + ~TwoBars(); +}; + +// Start of tests: + +void elided_assign() { + Foo x = get_foo(); +} +// CHECK: void elided_assign() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Implicit destructor) + +void nonelided_assign() { + Bar x = (const Bar &)get_bar(); +} +// CHECK: void nonelided_assign() +// CHECK: (CXXConstructExpr{{.*}}, struct Bar) +// CHECK: ~Bar() (Temporary object destructor) +// CHECK: ~Bar() (Implicit destructor) + +void elided_paren_init() { + Foo x(get_foo()); +} +// CHECK: void elided_paren_init() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Implicit destructor) + +void nonelided_paren_init() { + Bar x((const Bar &)get_bar()); +} +// CHECK: void nonelided_paren_init() +// CHECK: (CXXConstructExpr{{.*}}, struct Bar) +// CHECK: ~Bar() (Temporary object destructor) +// CHECK: ~Bar() (Implicit destructor) + +void elided_brace_init() { + Foo x{get_foo()}; +} +// CHECK: void elided_brace_init() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Implicit destructor) + +void nonelided_brace_init() { + Bar x{(const Bar &)get_bar()}; +} +// CHECK: void nonelided_brace_init() +// CHECK: (CXXConstructExpr{{.*}}, struct Bar) +// CHECK: ~Bar() (Temporary object destructor) +// CHECK: ~Bar() (Implicit destructor) + +void elided_lambda_capture_init() { + // The copy from get_foo() into the lambda should be elided. Should call + // the lambda's destructor, but not ~Foo() separately. + // (This syntax is C++14 'generalized lambda captures'.) + auto z = [x=get_foo()]() {}; +} +// CHECK: void elided_lambda_capture_init() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~(lambda at {{.*}})() (Temporary object destructor) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~(lambda at {{.*}})() (Implicit destructor) + +void nonelided_lambda_capture_init() { + // Should call the lambda's destructor as well as ~Bar() for the temporary. + auto z = [x((const Bar &)get_bar())]() {}; +} +// CHECK: void nonelided_lambda_capture_init() +// CHECK: (CXXConstructExpr{{.*}}, struct Bar) +// CXX14: ~(lambda at {{.*}})() (Temporary object destructor) +// CHECK: ~Bar() (Temporary object destructor) +// CHECK: ~(lambda at {{.*}})() (Implicit destructor) + +Foo elided_return_stmt_expr() { + // Two copies, both elided in C++17. + return ({ get_foo(); }); +} +// CHECK: Foo elided_return_stmt_expr() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) + +void elided_stmt_expr() { + // One copy, elided in C++17. + ({ get_foo(); }); +} +// CHECK: void elided_stmt_expr() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Temporary object destructor) + + +void elided_stmt_expr_multiple_stmts() { + // Make sure that only the value returned out of a statement expression is + // elided. + ({ get_bar(); get_foo(); }); +} +// CHECK: void elided_stmt_expr_multiple_stmts() +// CHECK: ~Bar() (Temporary object destructor) +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Temporary object destructor) + + +void unelided_stmt_expr() { + ({ (const Bar &)get_bar(); }); +} +// CHECK: void unelided_stmt_expr() +// CHECK: (CXXConstructExpr{{.*}}, struct Bar) +// CHECK: ~Bar() (Temporary object destructor) +// CHECK: ~Bar() (Temporary object destructor) + +void elided_aggregate_init() { + TwoFoos x{get_foo(), get_foo()}; +} +// CHECK: void elided_aggregate_init() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~TwoFoos() (Implicit destructor) + +void nonelided_aggregate_init() { + TwoBars x{(const Bar &)get_bar(), (const Bar &)get_bar()}; +} +// CHECK: void nonelided_aggregate_init() +// CHECK: (CXXConstructExpr{{.*}}, struct Bar) +// CHECK: (CXXConstructExpr{{.*}}, struct Bar) +// CHECK: ~Bar() (Temporary object destructor) +// CHECK: ~Bar() (Temporary object destructor) +// CHECK: ~TwoBars() (Implicit destructor) + +TwoFoos return_aggregate_init() { + return TwoFoos{get_foo(), get_foo()}; +} +// CHECK: TwoFoos return_aggregate_init() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~TwoFoos() (Temporary object destructor) +// CXX14: ~Foo() (Temporary object destructor) +// CXX14: ~Foo() (Temporary object destructor) + +void lifetime_extended() { + const Foo &x = (get_foo(), get_foo()); + puts("one destroyed before, one destroyed after"); +} +// CHECK: void lifetime_extended() +// CHECK: ~Foo() (Temporary object destructor) +// CHECK: one destroyed before, one destroyed after +// CHECK: ~Foo() (Implicit destructor) + +void not_lifetime_extended() { + Foo x = (get_foo(), get_foo()); + puts("one destroyed before, one destroyed after"); +} +// CHECK: void not_lifetime_extended() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CHECK: ~Foo() (Temporary object destructor) +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: one destroyed before, one destroyed after +// CHECK: ~Foo() (Implicit destructor) + +void compound_literal() { + (void)(Bar[]){{}, {}}; +} +// CHECK: void compound_literal() +// CHECK: (CXXConstructExpr, struct Bar) +// CHECK: (CXXConstructExpr, struct Bar) +// CHECK: ~Bar [2]() (Temporary object destructor) + +Foo elided_return() { + return get_foo(); +} +// CHECK: Foo elided_return() +// CXX14: (CXXConstructExpr{{.*}}, struct Foo) +// CXX14: ~Foo() (Temporary object destructor) + +auto elided_return_lambda() { + return [x=get_foo()]() {}; +} +// CHECK: (lambda at {{.*}}) elided_return_lambda() +// CXX14: (CXXConstructExpr{{.*}}, class (lambda at {{.*}})) +// CXX14: ~(lambda at {{.*}})() (Temporary object destructor) +// CXX14: ~Foo() (Temporary object destructor) + +void const_auto_obj() { + const Bar bar; +} +// CHECK: void const_auto_obj() +// CHECK: .~Bar() (Implicit destructor) + +void has_default_arg(Foo foo = get_foo()); +void test_default_arg() { + // FIXME: This emits a destructor but no constructor. Search CFG.cpp for + // 'PR13385' for details. + has_default_arg(); +} +// CHECK: void test_default_arg() +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Temporary object destructor) + +struct DefaultArgInCtor { + DefaultArgInCtor(Foo foo = get_foo()); + ~DefaultArgInCtor(); +}; + +void default_ctor_with_default_arg() { + // FIXME: Default arguments are mishandled in two ways: + // - The constructor is not emitted at all (not specific to arrays; see fixme + // in CFG.cpp that mentions PR13385). + // - The destructor is emitted once, even though the default argument will be + // constructed and destructed once per array element. + // Ideally, the CFG would expand array constructions into a loop that + // constructs each array element, allowing default argument + // constructor/destructor calls to be correctly placed inside the loop. + DefaultArgInCtor qux[3]; +} +// CHECK: void default_ctor_with_default_arg() +// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor [3] +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Temporary object destructor) +// CHECK: .~DefaultArgInCtor [3]() (Implicit destructor) + +void new_default_ctor_with_default_arg(long count) { + // Same problems as above. + new DefaultArgInCtor[count]; +} +// CHECK: void new_default_ctor_with_default_arg(long count) +// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor [] +// CXX14: ~Foo() (Temporary object destructor) +// CHECK: ~Foo() (Temporary object destructor) + +#if CXX2A +// Boilerplate needed to test co_return: + +namespace std::experimental { + template <typename Promise> + struct coroutine_handle { + static coroutine_handle from_address(void *); + }; +} + +struct TestPromise { + TestPromise initial_suspend(); + TestPromise final_suspend(); + bool await_ready(); + void await_suspend(const std::experimental::coroutine_handle<TestPromise> &); + void await_resume(); + Foo return_value(const Bar &); + Bar get_return_object(); + void unhandled_exception(); +}; + +namespace std::experimental { + template <typename Ret, typename... Args> + struct coroutine_traits; + template <> + struct coroutine_traits<Bar> { + using promise_type = TestPromise; + }; +} + +Bar coreturn() { + co_return get_bar(); + // This expands to something like: + // promise.return_value(get_bar()); + // get_bar() is passed by reference to return_value() and is then destroyed; + // there is no equivalent of RVO. TestPromise::return_value also returns a + // Foo, which should be immediately destroyed. + // FIXME: The generated CFG completely ignores get_return_object(). +} +// CXX2A: Bar coreturn() +// CXX2A: ~Foo() (Temporary object destructor) +// CXX2A: ~Bar() (Temporary object destructor) +#endif diff --git a/test/Analysis/scopes-cfg-output.cpp b/test/Analysis/scopes-cfg-output.cpp index f8d84b60de..4b6e2a92c5 100644 --- a/test/Analysis/scopes-cfg-output.cpp +++ b/test/Analysis/scopes-cfg-output.cpp @@ -38,7 +38,7 @@ extern const bool UV; // CHECK-NEXT: 3: A a[2]; // CHECK-NEXT: 4: (CXXConstructExpr, [B1.5], class A [0]) // CHECK-NEXT: 5: A b[0]; -// CHECK-NEXT: 6: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B1.3].~A [2]() (Implicit destructor) // CHECK-NEXT: 7: CFGScopeEnd(a) // CHECK-NEXT: Preds (1): B2 // CHECK-NEXT: Succs (1): B0 @@ -810,7 +810,7 @@ void test_for_compound_and_break() { // CHECK-NEXT: 1: CFGScopeEnd(__end1) // CHECK-NEXT: 2: CFGScopeEnd(__begin1) // CHECK-NEXT: 3: CFGScopeEnd(__range1) -// CHECK-NEXT: 4: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B5.3].~A [10]() (Implicit destructor) // CHECK-NEXT: 5: CFGScopeEnd(a) // CHECK-NEXT: Preds (1): B2 // CHECK-NEXT: Succs (1): B0 diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp index 6191abfb4d..012cef52f1 100644 --- a/test/Analysis/temporaries.cpp +++ b/test/Analysis/temporaries.cpp @@ -830,12 +830,7 @@ void test_ternary_temporary_with_copy(int coin) { // On each branch the variable is constructed directly. if (coin) { clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} -#if __cplusplus < 201703L clang_analyzer_eval(y == 1); // expected-warning{{TRUE}} -#else - // FIXME: Destructor called twice in C++17? - clang_analyzer_eval(y == 2); // expected-warning{{TRUE}} -#endif clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} clang_analyzer_eval(w == 0); // expected-warning{{TRUE}} @@ -843,12 +838,7 @@ void test_ternary_temporary_with_copy(int coin) { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} clang_analyzer_eval(z == 1); // expected-warning{{TRUE}} -#if __cplusplus < 201703L clang_analyzer_eval(w == 1); // expected-warning{{TRUE}} -#else - // FIXME: Destructor called twice in C++17? - clang_analyzer_eval(w == 2); // expected-warning{{TRUE}} -#endif } } } // namespace test_match_constructors_and_destructors @@ -1055,16 +1045,11 @@ void foo(void (*bar4)(S)) { #endif bar2(S(2)); - // FIXME: Why are we losing information in C++17? clang_analyzer_eval(glob == 2); #ifdef TEMPORARY_DTORS -#if __cplusplus < 201703L - // expected-warning@-3{{TRUE}} -#else - // expected-warning@-5{{UNKNOWN}} -#endif + // expected-warning@-2{{TRUE}} #else - // expected-warning@-8{{UNKNOWN}} + // expected-warning@-4{{UNKNOWN}} #endif C *c = new D(); |