318 lines
9.7 KiB
C++
318 lines
9.7 KiB
C++
|
// 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 *) noexcept;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
struct TestPromise {
|
||
|
TestPromise initial_suspend();
|
||
|
TestPromise final_suspend() noexcept;
|
||
|
bool await_ready() noexcept;
|
||
|
void await_suspend(const std::experimental::coroutine_handle<TestPromise> &) noexcept;
|
||
|
void await_resume() noexcept;
|
||
|
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
|