// 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 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 &); void await_resume(); Foo return_value(const Bar &); Bar get_return_object(); void unhandled_exception(); }; namespace std::experimental { template struct coroutine_traits; template <> struct coroutine_traits { 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