| // 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 { |
| template <typename Promise> |
| struct coroutine_handle { |
| static coroutine_handle from_address(void *) noexcept; |
| }; |
| } // namespace std |
| |
| struct TestPromise { |
| TestPromise initial_suspend(); |
| TestPromise final_suspend() noexcept; |
| bool await_ready() noexcept; |
| void await_suspend(const std::coroutine_handle<TestPromise> &) noexcept; |
| void await_resume() noexcept; |
| Foo return_value(const Bar &); |
| Bar get_return_object(); |
| void unhandled_exception(); |
| }; |
| |
| namespace std { |
| template <typename Ret, typename... Args> |
| struct coroutine_traits; |
| template <> |
| struct coroutine_traits<Bar> { |
| using promise_type = TestPromise; |
| }; |
| } // namespace std |
| |
| 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 |