2019-08-29 02:44:42 +08:00
|
|
|
// 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)
|
2021-10-15 05:48:17 +08:00
|
|
|
// CHECK: ~Bar [2]() (Temporary object destructor)
|
2019-08-29 02:44:42 +08:00
|
|
|
|
|
|
|
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()
|
2021-10-15 05:48:17 +08:00
|
|
|
// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor [3]
|
2019-08-29 02:44:42 +08:00
|
|
|
// CXX14: ~Foo() (Temporary object destructor)
|
|
|
|
// CHECK: ~Foo() (Temporary object destructor)
|
2021-10-15 05:48:17 +08:00
|
|
|
// CHECK: .~DefaultArgInCtor [3]() (Implicit destructor)
|
2019-08-29 02:44:42 +08:00
|
|
|
|
|
|
|
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)
|
2021-10-15 05:48:17 +08:00
|
|
|
// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor []
|
2019-08-29 02:44:42 +08:00
|
|
|
// CXX14: ~Foo() (Temporary object destructor)
|
|
|
|
// CHECK: ~Foo() (Temporary object destructor)
|
|
|
|
|
|
|
|
#if CXX2A
|
|
|
|
// Boilerplate needed to test co_return:
|
|
|
|
|
2021-09-04 04:01:09 +08:00
|
|
|
namespace std::experimental {
|
|
|
|
template <typename Promise>
|
|
|
|
struct coroutine_handle {
|
|
|
|
static coroutine_handle from_address(void *) noexcept;
|
|
|
|
};
|
|
|
|
}
|
2019-08-29 02:44:42 +08:00
|
|
|
|
|
|
|
struct TestPromise {
|
|
|
|
TestPromise initial_suspend();
|
[Coroutines] Ensure co_await promise.final_suspend() does not throw
Summary:
This patch addresses https://bugs.llvm.org/show_bug.cgi?id=46256
The spec of coroutine requires that the expression co_await promise.final_suspend() shall not be potentially-throwing.
To check this, we recursively look at every call (including Call, MemberCall, OperatorCall and Constructor) in all code
generated by the final suspend, and ensure that the callees are declared with noexcept. We also look at any returned data
type that requires explicit destruction, and check their destructors for noexcept.
This patch does not check declarations with dependent types yet, which will be done in future patches.
Updated all tests to add noexcept to the required functions, and added a dedicated test for this patch.
This patch might start to cause existing codebase fail to compile because most people may not have been strict in tagging
all the related functions noexcept.
Reviewers: lewissbaker, modocache, junparser
Reviewed By: modocache
Subscribers: arphaman, junparser, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D82029
2020-06-16 07:27:41 +08:00
|
|
|
TestPromise final_suspend() noexcept;
|
|
|
|
bool await_ready() noexcept;
|
2021-09-04 04:01:09 +08:00
|
|
|
void await_suspend(const std::experimental::coroutine_handle<TestPromise> &) noexcept;
|
[Coroutines] Ensure co_await promise.final_suspend() does not throw
Summary:
This patch addresses https://bugs.llvm.org/show_bug.cgi?id=46256
The spec of coroutine requires that the expression co_await promise.final_suspend() shall not be potentially-throwing.
To check this, we recursively look at every call (including Call, MemberCall, OperatorCall and Constructor) in all code
generated by the final suspend, and ensure that the callees are declared with noexcept. We also look at any returned data
type that requires explicit destruction, and check their destructors for noexcept.
This patch does not check declarations with dependent types yet, which will be done in future patches.
Updated all tests to add noexcept to the required functions, and added a dedicated test for this patch.
This patch might start to cause existing codebase fail to compile because most people may not have been strict in tagging
all the related functions noexcept.
Reviewers: lewissbaker, modocache, junparser
Reviewed By: modocache
Subscribers: arphaman, junparser, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D82029
2020-06-16 07:27:41 +08:00
|
|
|
void await_resume() noexcept;
|
2019-08-29 02:44:42 +08:00
|
|
|
Foo return_value(const Bar &);
|
|
|
|
Bar get_return_object();
|
|
|
|
void unhandled_exception();
|
|
|
|
};
|
|
|
|
|
2021-09-04 04:01:09 +08:00
|
|
|
namespace std::experimental {
|
|
|
|
template <typename Ret, typename... Args>
|
|
|
|
struct coroutine_traits;
|
|
|
|
template <>
|
|
|
|
struct coroutine_traits<Bar> {
|
|
|
|
using promise_type = TestPromise;
|
|
|
|
};
|
|
|
|
}
|
2019-08-29 02:44:42 +08:00
|
|
|
|
|
|
|
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
|