diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9800,6 +9800,9 @@ void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body); ClassTemplateDecl *lookupCoroutineTraits(SourceLocation KwLoc, SourceLocation FuncLoc); + /// Check that the expression co_await promise.final_suspend() shall not be + /// potentially-throwing. + bool checkFinalSuspendNoThrow(const Stmt *FinalSuspend); //===--------------------------------------------------------------------===// // OpenCL extensions. diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -631,7 +631,6 @@ } else if (SC == Expr::CallExprClass || SC == Expr::CXXMemberCallExprClass || SC == Expr::CXXOperatorCallExprClass) { if (!cast(E)->isTypeDependent()) { - // FIXME: Handle dependent types. checkDeclNoexcept(cast(E)->getCalleeDecl()); auto ReturnType = cast(E)->getCallReturnType(S.getASTContext()); // Check the destructor of the call return type, if any. @@ -651,22 +650,20 @@ } } -/// Check that the expression co_await promise.final_suspend() shall not be -/// potentially-throwing. -static bool checkNoThrow(Sema &S, const Stmt *FinalSuspend) { +bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) { llvm::SmallPtrSet ThrowingDecls; // We first collect all declarations that should not throw but not declared // with noexcept. We then sort them based on the location before printing. // This is to avoid emitting the same note multiple times on the same // declaration, and also provide a deterministic order for the messages. - checkNoThrow(S, FinalSuspend, ThrowingDecls); + checkNoThrow(*this, FinalSuspend, ThrowingDecls); auto SortedDecls = llvm::SmallVector{ThrowingDecls.begin(), ThrowingDecls.end()}; sort(SortedDecls, [](const Decl *A, const Decl *B) { return A->getEndLoc() < B->getEndLoc(); }); for (const auto *D : SortedDecls) { - S.Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept); + Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept); } return ThrowingDecls.empty(); } @@ -713,7 +710,7 @@ return true; StmtResult FinalSuspend = buildSuspends("final_suspend"); - if (FinalSuspend.isInvalid() || !checkNoThrow(*this, FinalSuspend.get())) + if (FinalSuspend.isInvalid() || !checkFinalSuspendNoThrow(FinalSuspend.get())) return true; ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get()); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7630,7 +7630,8 @@ return StmtError(); StmtResult FinalSuspend = getDerived().TransformStmt(S->getFinalSuspendStmt()); - if (FinalSuspend.isInvalid()) + if (FinalSuspend.isInvalid() || + !SemaRef.checkFinalSuspendNoThrow(FinalSuspend.get())) return StmtError(); ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get()); assert(isa(InitSuspend.get()) && isa(FinalSuspend.get())); diff --git a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp --- a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp +++ b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp @@ -11,27 +11,27 @@ template struct coroutine_handle { - static coroutine_handle from_address(void *); // expected-note {{must be declared with 'noexcept'}} + static coroutine_handle from_address(void *); // expected-note 2 {{must be declared with 'noexcept'}} }; template <> struct coroutine_handle { template - coroutine_handle(coroutine_handle); // expected-note {{must be declared with 'noexcept'}} + coroutine_handle(coroutine_handle); // expected-note 2 {{must be declared with 'noexcept'}} }; struct suspend_never { - bool await_ready() { return true; } // expected-note {{must be declared with 'noexcept'}} - void await_suspend(coroutine_handle<>) {} // expected-note {{must be declared with 'noexcept'}} - void await_resume() {} // expected-note {{must be declared with 'noexcept'}} - ~suspend_never() noexcept(false); // expected-note {{must be declared with 'noexcept'}} + 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'}} }; struct suspend_always { bool await_ready() { return false; } void await_suspend(coroutine_handle<>) {} void await_resume() {} - suspend_never operator co_await(); // expected-note {{must be declared with 'noexcept'}} - ~suspend_always() noexcept(false); // expected-note {{must be declared with 'noexcept'}} + 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'}} }; } // namespace experimental @@ -50,7 +50,7 @@ struct promise_type { coro_t get_return_object(); suspend_never initial_suspend(); - suspend_always final_suspend(); // expected-note {{must be declared with 'noexcept'}} + suspend_always final_suspend(); // expected-note 2 {{must be declared with 'noexcept'}} void return_void(); static void unhandled_exception(); }; @@ -60,3 +60,13 @@ A a{}; co_await a; } + +template +coro_t f_dep(T n) { // expected-error {{the expression 'co_await __promise.final_suspend()' is required to be non-throwing}} + A a{}; + co_await a; +} + +void foo() { + f_dep(5); // expected-note {{in instantiation of function template specialization 'f_dep' requested here}} +}