forked from OSchip/llvm-project
[NFC] [Coroutines] Refactor implementation in checkFinalSuspendNoThrow
Now when we are checking if the expression `co_await promise.final_suspend()` is not throw, we would check unconditionally for its child expressions recursively. It takes unnecessary time. And the compiler would complains if the implementation in final_suspend() may throw even if the higher level function signature marked noexcept already. This fixes bug48453 too.
This commit is contained in:
parent
8d20d83dd7
commit
4f4340ee2a
|
@ -663,32 +663,32 @@ static void checkNoThrow(Sema &S, const Stmt *E,
|
|||
ThrowingDecls.insert(D);
|
||||
}
|
||||
};
|
||||
auto SC = E->getStmtClass();
|
||||
if (SC == Expr::CXXConstructExprClass) {
|
||||
auto const *Ctor = cast<CXXConstructExpr>(E)->getConstructor();
|
||||
|
||||
if (auto *CE = dyn_cast<CXXConstructExpr>(E)) {
|
||||
CXXConstructorDecl *Ctor = CE->getConstructor();
|
||||
checkDeclNoexcept(Ctor);
|
||||
// Check the corresponding destructor of the constructor.
|
||||
checkDeclNoexcept(Ctor->getParent()->getDestructor(), true);
|
||||
} else if (SC == Expr::CallExprClass || SC == Expr::CXXMemberCallExprClass ||
|
||||
SC == Expr::CXXOperatorCallExprClass) {
|
||||
if (!cast<CallExpr>(E)->isTypeDependent()) {
|
||||
checkDeclNoexcept(cast<CallExpr>(E)->getCalleeDecl());
|
||||
auto ReturnType = cast<CallExpr>(E)->getCallReturnType(S.getASTContext());
|
||||
// Check the destructor of the call return type, if any.
|
||||
if (ReturnType.isDestructedType() ==
|
||||
QualType::DestructionKind::DK_cxx_destructor) {
|
||||
const auto *T =
|
||||
cast<RecordType>(ReturnType.getCanonicalType().getTypePtr());
|
||||
checkDeclNoexcept(
|
||||
dyn_cast<CXXRecordDecl>(T->getDecl())->getDestructor(), true);
|
||||
}
|
||||
checkDeclNoexcept(Ctor->getParent()->getDestructor(), /*IsDtor=*/true);
|
||||
} else if (auto *CE = dyn_cast<CallExpr>(E)) {
|
||||
if (CE->isTypeDependent())
|
||||
return;
|
||||
|
||||
checkDeclNoexcept(CE->getCalleeDecl());
|
||||
QualType ReturnType = CE->getCallReturnType(S.getASTContext());
|
||||
// Check the destructor of the call return type, if any.
|
||||
if (ReturnType.isDestructedType() ==
|
||||
QualType::DestructionKind::DK_cxx_destructor) {
|
||||
const auto *T =
|
||||
cast<RecordType>(ReturnType.getCanonicalType().getTypePtr());
|
||||
checkDeclNoexcept(dyn_cast<CXXRecordDecl>(T->getDecl())->getDestructor(),
|
||||
/*IsDtor=*/true);
|
||||
}
|
||||
} else
|
||||
for (const auto *Child : E->children()) {
|
||||
if (!Child)
|
||||
continue;
|
||||
checkNoThrow(S, Child, ThrowingDecls);
|
||||
}
|
||||
}
|
||||
for (const auto *Child : E->children()) {
|
||||
if (!Child)
|
||||
continue;
|
||||
checkNoThrow(S, Child, ThrowingDecls);
|
||||
}
|
||||
}
|
||||
|
||||
bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) {
|
||||
|
|
|
@ -11,32 +11,26 @@ struct coroutine_traits { using promise_type = typename Ret::promise_type; };
|
|||
|
||||
template <class Promise = void>
|
||||
struct coroutine_handle {
|
||||
static coroutine_handle from_address(void *); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
static coroutine_handle from_address(void *);
|
||||
void *address() const noexcept;
|
||||
};
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; } // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_suspend(coroutine_handle<>) {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_resume() {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
~suspend_never() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
coroutine_handle(coroutine_handle<PromiseType>);
|
||||
void *address() const noexcept;
|
||||
};
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend(coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
suspend_never operator co_await(); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
~suspend_always() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
bool await_ready() { return false; } // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_suspend(coroutine_handle<>) {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_resume() {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
~suspend_always() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
};
|
||||
} // namespace experimental
|
||||
} // namespace std
|
||||
|
||||
using namespace std;
|
||||
using namespace std::experimental;
|
||||
|
||||
struct A {
|
||||
bool await_ready();
|
||||
|
@ -48,8 +42,8 @@ struct A {
|
|||
struct coro_t {
|
||||
struct promise_type {
|
||||
coro_t get_return_object();
|
||||
std::experimental::suspend_never initial_suspend();
|
||||
std::experimental::suspend_always final_suspend(); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend(); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void return_void();
|
||||
static void unhandled_exception();
|
||||
};
|
||||
|
@ -69,3 +63,44 @@ coro_t f_dep(T n) { // expected-error {{the expression 'co_await __promise.final
|
|||
void foo() {
|
||||
f_dep<int>(5); // expected-note {{in instantiation of function template specialization 'f_dep<int>' requested here}}
|
||||
}
|
||||
|
||||
struct PositiveFinalSuspend {
|
||||
bool await_ready() noexcept;
|
||||
coroutine_handle<> await_suspend(coroutine_handle<>) noexcept;
|
||||
void await_resume() noexcept;
|
||||
};
|
||||
|
||||
struct correct_coro {
|
||||
struct promise_type {
|
||||
correct_coro get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
PositiveFinalSuspend final_suspend() noexcept;
|
||||
void return_void();
|
||||
static void unhandled_exception();
|
||||
};
|
||||
};
|
||||
|
||||
correct_coro f2(int n) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
struct NegativeFinalSuspend {
|
||||
bool await_ready() noexcept;
|
||||
coroutine_handle<> await_suspend(coroutine_handle<>) noexcept;
|
||||
void await_resume() noexcept;
|
||||
~NegativeFinalSuspend() noexcept(false); // expected-note {{must be declared with 'noexcept'}}
|
||||
};
|
||||
|
||||
struct incorrect_coro {
|
||||
struct promise_type {
|
||||
incorrect_coro get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
NegativeFinalSuspend final_suspend() noexcept;
|
||||
void return_void();
|
||||
static void unhandled_exception();
|
||||
};
|
||||
};
|
||||
|
||||
incorrect_coro f3(int n) { // expected-error {{the expression 'co_await __promise.final_suspend()' is required to be non-throwing}}
|
||||
co_return;
|
||||
}
|
||||
|
|
|
@ -10,27 +10,21 @@ struct coroutine_traits { using promise_type = typename Ret::promise_type; };
|
|||
|
||||
template <class Promise = void>
|
||||
struct coroutine_handle {
|
||||
static coroutine_handle from_address(void *); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
static coroutine_handle from_address(void *);
|
||||
void *address() const noexcept;
|
||||
};
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
template <class PromiseType>
|
||||
coroutine_handle(coroutine_handle<PromiseType>); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; } // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_suspend(coroutine_handle<>) {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_resume() {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
~suspend_never() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
coroutine_handle(coroutine_handle<PromiseType>);
|
||||
void *address() const noexcept;
|
||||
};
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend(coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
suspend_never operator co_await(); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
~suspend_always() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
bool await_ready() { return false; } // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_suspend(coroutine_handle<>) {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void await_resume() {} // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
~suspend_always() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
@ -47,7 +41,7 @@ struct A {
|
|||
struct coro_t {
|
||||
struct promise_type {
|
||||
coro_t get_return_object();
|
||||
suspend_never initial_suspend();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend(); // expected-note 2 {{must be declared with 'noexcept'}}
|
||||
void return_void();
|
||||
static void unhandled_exception();
|
||||
|
@ -68,3 +62,44 @@ coro_t f_dep(T n) { // expected-error {{the expression 'co_await __promise.final
|
|||
void foo() {
|
||||
f_dep<int>(5); // expected-note {{in instantiation of function template specialization 'f_dep<int>' requested here}}
|
||||
}
|
||||
|
||||
struct PositiveFinalSuspend {
|
||||
bool await_ready() noexcept;
|
||||
coroutine_handle<> await_suspend(coroutine_handle<>) noexcept;
|
||||
void await_resume() noexcept;
|
||||
};
|
||||
|
||||
struct correct_coro {
|
||||
struct promise_type {
|
||||
correct_coro get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
PositiveFinalSuspend final_suspend() noexcept;
|
||||
void return_void();
|
||||
static void unhandled_exception();
|
||||
};
|
||||
};
|
||||
|
||||
correct_coro f2(int n) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
struct NegativeFinalSuspend {
|
||||
bool await_ready() noexcept;
|
||||
coroutine_handle<> await_suspend(coroutine_handle<>) noexcept;
|
||||
void await_resume() noexcept;
|
||||
~NegativeFinalSuspend() noexcept(false); // expected-note {{must be declared with 'noexcept'}}
|
||||
};
|
||||
|
||||
struct incorrect_coro {
|
||||
struct promise_type {
|
||||
incorrect_coro get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
NegativeFinalSuspend final_suspend() noexcept;
|
||||
void return_void();
|
||||
static void unhandled_exception();
|
||||
};
|
||||
};
|
||||
|
||||
incorrect_coro f3(int n) { // expected-error {{the expression 'co_await __promise.final_suspend()' is required to be non-throwing}}
|
||||
co_return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue