https://github.com/YexuanXiao created https://github.com/llvm/llvm-project/pull/179192
[Part1](https://github.com/llvm/llvm-project/pull/179189) addresses the code generation issue. When user code relies on Part1, this patch will issue a warning to indicate that the code requires improvement or contains defects, as HALO may not take effect and unexpected execution results may occur. This is achieved by introducing a new warning "-Winitial-suspend-throw". A considerable amount of existing code will indeed trigger this warning, but in most cases, marking a few functions as noexcept is sufficient to resolve the warning without affecting the behavior of the code itself. If users genuinely need to throw exceptions, they can disable the warning, though this scenario is typically rare. Note: This patch must be merged after Part1. Additionally, after merging Part1, the new warnings generated by Part1's tests need to be suppressed to avoid CI failures. >From c6187ee07c8318ec452f864d6736d4c19ee759bc Mon Sep 17 00:00:00 2001 From: Yexuan Xiao <[email protected]> Date: Mon, 2 Feb 2026 14:26:31 +0800 Subject: [PATCH] Add a new warning "-Winitial-suspend-throw" to warn about potential HALO unavailability and unexpected execution outcomes --- clang/include/clang/Basic/DiagnosticGroups.td | 10 ++- .../clang/Basic/DiagnosticSemaKinds.td | 6 ++ clang/include/clang/Sema/Sema.h | 1 + clang/lib/Sema/SemaCoroutine.cpp | 55 ++++++++++---- clang/test/CodeGenCoroutines/coro-halo.cpp | 2 +- .../coro-init-await-nontrivial-return.cpp | 2 +- clang/test/CodeGenCoroutines/pr56919.cpp | 2 +- .../test/SemaCXX/addr-label-in-coroutines.cpp | 2 +- clang/test/SemaCXX/co_await-range-for.cpp | 8 +-- clang/test/SemaCXX/coreturn-eh.cpp | 4 +- clang/test/SemaCXX/coreturn.cpp | 14 ++-- clang/test/SemaCXX/coro-lifetimebound.cpp | 4 +- .../SemaCXX/coro-return-type-and-wrapper.cpp | 2 +- clang/test/SemaCXX/coroutine-alloc-2.cpp | 2 +- clang/test/SemaCXX/coroutine-alloc-3.cpp | 2 +- clang/test/SemaCXX/coroutine-alloc-4.cpp | 12 ++-- clang/test/SemaCXX/coroutine-allocs.cpp | 4 +- clang/test/SemaCXX/coroutine-dealloc.cpp | 4 +- .../coroutine-final-suspend-noexcept.cpp | 12 +++- clang/test/SemaCXX/coroutine-no-move-ctor.cpp | 2 +- .../SemaCXX/coroutine-no-valid-dealloc.cpp | 2 +- clang/test/SemaCXX/coroutine-rvo.cpp | 4 +- clang/test/SemaCXX/coroutine-unevaluate.cpp | 2 +- .../coroutine-unhandled_exception-warning.cpp | 2 +- .../SemaCXX/coroutine-unreachable-warning.cpp | 2 +- .../coroutine_handle-address-return-type.cpp | 2 +- ...tine_initial_suspend_exception_warning.cpp | 62 ++++++++++++++++ clang/test/SemaCXX/coroutines.cpp | 72 +++++++++---------- .../test/SemaCXX/cxx2b-deducing-this-coro.cpp | 2 +- clang/test/SemaCXX/type-aware-coroutines.cpp | 10 +-- clang/test/SemaCXX/warn-unsequenced-coro.cpp | 2 +- .../coroutine.handle.prom/promise.pass.cpp | 2 +- .../end.to.end/await_result.pass.cpp | 2 +- .../end.to.end/bool_await_suspend.pass.cpp | 2 +- .../end.to.end/expected.pass.cpp | 2 +- .../end.to.end/fullexpr-dtor.pass.cpp | 2 +- .../end.to.end/generator.pass.cpp | 4 +- .../support.coroutines/end.to.end/go.pass.cpp | 4 +- 38 files changed, 218 insertions(+), 112 deletions(-) create mode 100644 clang/test/SemaCXX/coroutine_initial_suspend_exception_warning.cpp diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index d36ee57fe7651..1872a26f3cf09 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -69,9 +69,13 @@ def CoroNonAlignedAllocationFunction : DiagGroup<"coro-non-aligned-allocation-function">; def CoroTypeAwareAllocationFunction : DiagGroup<"coro-type-aware-allocation-function">; -def Coroutine : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, DeprecatedCoroutine, - AlwaysInlineCoroutine, CoroNonAlignedAllocationFunction, - CoroTypeAwareAllocationFunction]>; +def InitialSuspendThrowing : DiagGroup<"initial-suspend-throw">; +def Coroutine + : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, + DeprecatedCoroutine, AlwaysInlineCoroutine, + CoroNonAlignedAllocationFunction, + CoroTypeAwareAllocationFunction, + InitialSuspendThrowing]>; def ObjCBoolConstantConversion : DiagGroup<"objc-bool-constant-conversion">; def ConstantConversion : DiagGroup<"constant-conversion", [BitFieldConstantConversion, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 807440c107897..c67fd393ecfe3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12755,6 +12755,12 @@ def warn_coroutine_handle_address_invalid_return_type : Warning < def err_coroutine_promise_final_suspend_requires_nothrow : Error< "the expression 'co_await __promise.final_suspend()' is required to be non-throwing" >; +def warn_coroutine_promise_initial_suspend_throw + : Warning<"a potentially throwing 'co_await __promise.initial_suspend()' " + "may disable heap allocation elision; " + "if it throws, the coroutine return value and state are " + "destroyed in the reverse order of their construction">, + InGroup<InitialSuspendThrowing>; def note_coroutine_function_declare_noexcept : Note< "must be declared with 'noexcept'" >; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0ba3daab764b7..4e88b77c26641 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3199,6 +3199,7 @@ class Sema final : public SemaBase { /// Check that the expression co_await promise.final_suspend() shall not be /// potentially-throwing. bool checkFinalSuspendNoThrow(const Stmt *FinalSuspend); + void warnInitialSuspendThrow(const Stmt *InitialSuspend); ///@} diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index c0aba832dba94..a9b289e3c0b67 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -594,8 +594,12 @@ static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc, /// Recursively check \p E and all its children to see if any call target /// (including constructor call) is declared noexcept. Also any value returned /// from the call has a noexcept destructor. +/// +/// \param DiagID The error diagnostic ID to emit if a throwing function is +/// found. static void checkNoThrow(Sema &S, const Stmt *E, - llvm::SmallPtrSetImpl<const Decl *> &ThrowingDecls) { + llvm::SmallPtrSetImpl<const Decl *> &ThrowingDecls, + unsigned DiagID, bool InitialSuspend = false) { auto checkDeclNoexcept = [&](const Decl *D, bool IsDtor = false) { // In the case of dtor, the call to dtor is implicit and hence we should // pass nullptr to canCalleeThrow. @@ -608,17 +612,19 @@ static void checkNoThrow(Sema &S, const Stmt *E, // coroutine that just suspended, but rather throws back out from // whoever called coroutine_handle::resume(), hence we claim that // logically it does not throw. - if (FD->getBuiltinID() == Builtin::BI__builtin_coro_resume) + if (!InitialSuspend && + (FD->getBuiltinID() == Builtin::BI__builtin_coro_resume)) + return; + // the call to initial-suspend-resume is not part of the coroutine + // startup phase, so any exception thrown by it will be handled by + // unhandled_exception. Therefore, it need to be excluded. + if (InitialSuspend && FD->getIdentifier() && + (FD->getIdentifier()->getName() == "await_resume")) return; } if (ThrowingDecls.empty()) { - // [dcl.fct.def.coroutine]p15 - // The expression co_await promise.final_suspend() shall not be - // potentially-throwing ([except.spec]). - // // First time seeing an error, emit the error message. - S.Diag(cast<FunctionDecl>(S.CurContext)->getLocation(), - diag::err_coroutine_promise_final_suspend_requires_nothrow); + S.Diag(cast<FunctionDecl>(S.CurContext)->getLocation(), DiagID); } ThrowingDecls.insert(D); } @@ -648,28 +654,49 @@ static void checkNoThrow(Sema &S, const Stmt *E, for (const auto *Child : E->children()) { if (!Child) continue; - checkNoThrow(S, Child, ThrowingDecls); + checkNoThrow(S, Child, ThrowingDecls, DiagID, InitialSuspend); } } -bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) { +static bool checkSuspendNoThrow(Sema &S, const Stmt *SuspendExpr, + unsigned DiagID, bool InitialSuspend = false) { llvm::SmallPtrSet<const Decl *, 4> 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(*this, FinalSuspend, ThrowingDecls); + checkNoThrow(S, SuspendExpr, ThrowingDecls, DiagID, InitialSuspend); + auto SortedDecls = llvm::SmallVector<const Decl *, 4>{ThrowingDecls.begin(), ThrowingDecls.end()}; sort(SortedDecls, [](const Decl *A, const Decl *B) { return A->getEndLoc() < B->getEndLoc(); }); for (const auto *D : SortedDecls) { - Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept); + S.Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept); } return ThrowingDecls.empty(); } +bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) { + // [dcl.fct.def.coroutine]p15 + // The expression co_await promise.final_suspend() shall not be + // potentially-throwing ([except.spec]). + return checkSuspendNoThrow( + *this, FinalSuspend, + diag::err_coroutine_promise_final_suspend_requires_nothrow); +} + +void Sema::warnInitialSuspendThrow(const Stmt *InitialSuspend) { + // CWG2935/PR177628 + // If a potentially thrown exception occurs before the call to + // initial-await-resume and the return value of the coroutine is constructed + // in-place, it prevents HALO and causes both the return value and the + // coroutine state to be destroyed in the reverse order of their construction. + checkSuspendNoThrow(*this, InitialSuspend, + diag::warn_coroutine_promise_initial_suspend_throw, true); +} + // [stmt.return.coroutine]p1: // A coroutine shall not enclose a return statement ([stmt.return]). static void checkReturnStmtInCoroutine(Sema &S, FunctionScopeInfo *FSI) { @@ -731,8 +758,10 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc, }; StmtResult InitSuspend = buildSuspends("initial_suspend"); - if (InitSuspend.isInvalid()) + if (InitSuspend.isInvalid()) { return true; + } + warnInitialSuspendThrow(InitSuspend.get()); StmtResult FinalSuspend = buildSuspends("final_suspend"); if (FinalSuspend.isInvalid() || !checkFinalSuspendNoThrow(FinalSuspend.get())) diff --git a/clang/test/CodeGenCoroutines/coro-halo.cpp b/clang/test/CodeGenCoroutines/coro-halo.cpp index e75bedaf81fa2..915e0efa9bbea 100644 --- a/clang/test/CodeGenCoroutines/coro-halo.cpp +++ b/clang/test/CodeGenCoroutines/coro-halo.cpp @@ -13,7 +13,7 @@ template <typename T> struct generator { this->current_value = value; return {}; } - std::suspend_always initial_suspend() { return {}; } + std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } generator get_return_object() { return generator{this}; }; void unhandled_exception() {} diff --git a/clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp b/clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp index 052b4e235e739..a6c8becf00fd4 100644 --- a/clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp +++ b/clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp @@ -69,7 +69,7 @@ struct NoexceptResumeTask { struct promise_type { void return_void() {} void unhandled_exception() {} - initial_suspend_awaiter initial_suspend() { return {}; } + initial_suspend_awaiter initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } NoexceptResumeTask get_return_object() { return NoexceptResumeTask{handle_type::from_promise(*this)}; diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp b/clang/test/CodeGenCoroutines/pr56919.cpp index baa8c27ce6649..84a27a302e607 100644 --- a/clang/test/CodeGenCoroutines/pr56919.cpp +++ b/clang/test/CodeGenCoroutines/pr56919.cpp @@ -30,7 +30,7 @@ class Task final { void unhandled_exception() {} - std::suspend_always initial_suspend() { return {}; } + std::suspend_always initial_suspend() noexcept { return {}; } auto await_transform(Task<void> co) { return await_transform(std::move(co.handle_.promise())); diff --git a/clang/test/SemaCXX/addr-label-in-coroutines.cpp b/clang/test/SemaCXX/addr-label-in-coroutines.cpp index 65d78636e5cdd..aa354a24c8525 100644 --- a/clang/test/SemaCXX/addr-label-in-coroutines.cpp +++ b/clang/test/SemaCXX/addr-label-in-coroutines.cpp @@ -5,7 +5,7 @@ struct resumable { struct promise_type { resumable get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; diff --git a/clang/test/SemaCXX/co_await-range-for.cpp b/clang/test/SemaCXX/co_await-range-for.cpp index 064a35038e1c7..898d042f4729e 100644 --- a/clang/test/SemaCXX/co_await-range-for.cpp +++ b/clang/test/SemaCXX/co_await-range-for.cpp @@ -42,7 +42,7 @@ struct MyForLoopArrayAwaiter { MyForLoopArrayAwaiter get_return_object() { return {}; } void return_void(); void unhandled_exception(); - suspend_never initial_suspend(); + suspend_never initial_suspend() noexcept; suspend_never final_suspend() noexcept; template <class T> Awaiter<T *> await_transform(T *) = delete; // expected-note {{explicitly deleted}} @@ -60,7 +60,7 @@ struct ForLoopAwaiterBadBeginTransform { ForLoopAwaiterBadBeginTransform get_return_object(); void return_void(); void unhandled_exception(); - suspend_never initial_suspend(); + suspend_never initial_suspend() noexcept; suspend_never final_suspend() noexcept; template <class T> @@ -94,7 +94,7 @@ struct ForLoopAwaiterBadIncTransform { ForLoopAwaiterBadIncTransform get_return_object(); void return_void(); void unhandled_exception(); - suspend_never initial_suspend(); + suspend_never initial_suspend() noexcept; suspend_never final_suspend() noexcept; template <class T> @@ -135,7 +135,7 @@ struct ForLoopAwaiterCoawaitLookup { ForLoopAwaiterCoawaitLookup get_return_object(); void return_void(); void unhandled_exception(); - suspend_never initial_suspend(); + suspend_never initial_suspend() noexcept; suspend_never final_suspend() noexcept; template <class T> CoawaitTag<T, false> await_transform(BeginTag<T> e); diff --git a/clang/test/SemaCXX/coreturn-eh.cpp b/clang/test/SemaCXX/coreturn-eh.cpp index 0d409b9b99bb6..e844df43a2626 100644 --- a/clang/test/SemaCXX/coreturn-eh.cpp +++ b/clang/test/SemaCXX/coreturn-eh.cpp @@ -16,7 +16,7 @@ struct object { ~object() {} }; struct promise_void_return_value { void get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_value(object); @@ -25,7 +25,7 @@ struct promise_void_return_value { struct VoidTagReturnValue { struct promise_type { VoidTagReturnValue get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_value(object); diff --git a/clang/test/SemaCXX/coreturn.cpp b/clang/test/SemaCXX/coreturn.cpp index 7069a1040db23..1a9be6c58e72e 100644 --- a/clang/test/SemaCXX/coreturn.cpp +++ b/clang/test/SemaCXX/coreturn.cpp @@ -12,7 +12,7 @@ struct awaitable { struct promise_void { void get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -20,7 +20,7 @@ struct promise_void { struct promise_void_return_value { void get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_value(int); @@ -29,7 +29,7 @@ struct promise_void_return_value { struct VoidTagNoReturn { struct promise_type { VoidTagNoReturn get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); }; @@ -38,7 +38,7 @@ struct VoidTagNoReturn { struct VoidTagReturnValue { struct promise_type { VoidTagReturnValue get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_value(int); @@ -48,7 +48,7 @@ struct VoidTagReturnValue { struct VoidTagReturnVoid { struct promise_type { VoidTagReturnVoid get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_void(); @@ -57,7 +57,7 @@ struct VoidTagReturnVoid { struct promise_float { float get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -65,7 +65,7 @@ struct promise_float { struct promise_int { int get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_value(int); void unhandled_exception(); diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp b/clang/test/SemaCXX/coro-lifetimebound.cpp index 9e96a296562a0..897dd8140050d 100644 --- a/clang/test/SemaCXX/coro-lifetimebound.cpp +++ b/clang/test/SemaCXX/coro-lifetimebound.cpp @@ -10,7 +10,7 @@ template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_typ Co<T> get_return_object() { return {}; } - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_value(const T &t); @@ -151,7 +151,7 @@ template <typename T> struct [[clang::coro_lifetimebound]] CoNoCRT { CoNoCRT<T> get_return_object() { return {}; } - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_value(const T &t); diff --git a/clang/test/SemaCXX/coro-return-type-and-wrapper.cpp b/clang/test/SemaCXX/coro-return-type-and-wrapper.cpp index b08e1c9c065a0..c111906f3c5c9 100644 --- a/clang/test/SemaCXX/coro-return-type-and-wrapper.cpp +++ b/clang/test/SemaCXX/coro-return-type-and-wrapper.cpp @@ -22,7 +22,7 @@ template <typename T> struct [[clang::coro_return_type]] Gen { static Gen<T> get_return_object_on_allocation_failure() { return {}; } - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_value(T t); diff --git a/clang/test/SemaCXX/coroutine-alloc-2.cpp b/clang/test/SemaCXX/coroutine-alloc-2.cpp index bcebadc0a1f53..b8697cbad9437 100644 --- a/clang/test/SemaCXX/coroutine-alloc-2.cpp +++ b/clang/test/SemaCXX/coroutine-alloc-2.cpp @@ -38,7 +38,7 @@ template <> struct std::coroutine_traits<int, promise_on_alloc_failure_tag> { struct promise_type { int get_return_object() { return 0; } - std::suspend_always initial_suspend() { return {}; } + std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() {} diff --git a/clang/test/SemaCXX/coroutine-alloc-3.cpp b/clang/test/SemaCXX/coroutine-alloc-3.cpp index 629ac88a3df8e..0eb5f94cd274e 100644 --- a/clang/test/SemaCXX/coroutine-alloc-3.cpp +++ b/clang/test/SemaCXX/coroutine-alloc-3.cpp @@ -38,7 +38,7 @@ template <> struct std::coroutine_traits<int, promise_on_alloc_failure_tag> { struct promise_type { int get_return_object() { return 0; } - std::suspend_always initial_suspend() { return {}; } + std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() {} diff --git a/clang/test/SemaCXX/coroutine-alloc-4.cpp b/clang/test/SemaCXX/coroutine-alloc-4.cpp index 262c163fb1789..84383b99ed1c7 100644 --- a/clang/test/SemaCXX/coroutine-alloc-4.cpp +++ b/clang/test/SemaCXX/coroutine-alloc-4.cpp @@ -10,7 +10,7 @@ namespace std { struct task { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task{}; } void unhandled_exception() {} @@ -25,7 +25,7 @@ task f() { struct task2 { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task2{}; } void unhandled_exception() {} @@ -41,7 +41,7 @@ task2 f1() { struct task3 { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task3{}; } void unhandled_exception() {} @@ -59,7 +59,7 @@ task3 f2() { struct task4 { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task4{}; } void unhandled_exception() {} @@ -75,7 +75,7 @@ task4 f3(int, double, int) { struct task5 { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task5{}; } void unhandled_exception() {} @@ -96,7 +96,7 @@ namespace std { struct task6 { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task6{}; } void unhandled_exception() {} diff --git a/clang/test/SemaCXX/coroutine-allocs.cpp b/clang/test/SemaCXX/coroutine-allocs.cpp index e6b086bd1c720..c267367baff66 100644 --- a/clang/test/SemaCXX/coroutine-allocs.cpp +++ b/clang/test/SemaCXX/coroutine-allocs.cpp @@ -12,7 +12,7 @@ struct resumable { void *operator new(std::size_t sz, Allocator &); resumable get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; @@ -71,7 +71,7 @@ struct promise_base2 { struct resumable2 { struct promise_type : public promise_base1, public promise_base2 { resumable2 get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; diff --git a/clang/test/SemaCXX/coroutine-dealloc.cpp b/clang/test/SemaCXX/coroutine-dealloc.cpp index 762a14465b297..b538041781f34 100644 --- a/clang/test/SemaCXX/coroutine-dealloc.cpp +++ b/clang/test/SemaCXX/coroutine-dealloc.cpp @@ -11,7 +11,7 @@ namespace std { struct task { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task{}; } void unhandled_exception() {} @@ -30,7 +30,7 @@ task f() { struct generator { struct promise_type { generator get_return_object(); - std::suspend_always initial_suspend(); + std::suspend_always initial_suspend() noexcept; std::suspend_always final_suspend() noexcept; void return_void(); [[noreturn]] void unhandled_exception(); diff --git a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp index 35c00b84ea398..744baec43eeea 100644 --- a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp +++ b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp @@ -20,6 +20,12 @@ struct coroutine_handle<void> { void *address() const noexcept; }; +struct suspend_always_noexcept { + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + struct suspend_always { 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'}} @@ -41,7 +47,7 @@ struct A { struct coro_t { struct promise_type { coro_t get_return_object(); - suspend_always initial_suspend(); + suspend_always_noexcept initial_suspend() noexcept; suspend_always final_suspend(); // expected-note 2 {{must be declared with 'noexcept'}} void return_void(); static void unhandled_exception(); @@ -72,7 +78,7 @@ struct PositiveFinalSuspend { struct correct_coro { struct promise_type { correct_coro get_return_object(); - suspend_always initial_suspend(); + suspend_always_noexcept initial_suspend() noexcept; PositiveFinalSuspend final_suspend() noexcept; void return_void(); static void unhandled_exception(); @@ -93,7 +99,7 @@ struct NegativeFinalSuspend { struct incorrect_coro { struct promise_type { incorrect_coro get_return_object(); - suspend_always initial_suspend(); + suspend_always_noexcept initial_suspend() noexcept; NegativeFinalSuspend final_suspend() noexcept; void return_void(); static void unhandled_exception(); diff --git a/clang/test/SemaCXX/coroutine-no-move-ctor.cpp b/clang/test/SemaCXX/coroutine-no-move-ctor.cpp index 824dea375ebde..813585e45b43b 100644 --- a/clang/test/SemaCXX/coroutine-no-move-ctor.cpp +++ b/clang/test/SemaCXX/coroutine-no-move-ctor.cpp @@ -8,7 +8,7 @@ class invoker { class invoker_promise { public: invoker get_return_object() { return invoker{}; } - auto initial_suspend() { return std::suspend_never{}; } + auto initial_suspend() noexcept { return std::suspend_never{}; } auto final_suspend() noexcept { return std::suspend_never{}; } void return_void() {} void unhandled_exception() {} diff --git a/clang/test/SemaCXX/coroutine-no-valid-dealloc.cpp b/clang/test/SemaCXX/coroutine-no-valid-dealloc.cpp index a5727d6f003bf..e393a47bc3a1e 100644 --- a/clang/test/SemaCXX/coroutine-no-valid-dealloc.cpp +++ b/clang/test/SemaCXX/coroutine-no-valid-dealloc.cpp @@ -11,7 +11,7 @@ namespace std { struct task { struct promise_type { - auto initial_suspend() { return std::suspend_always{}; } + auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } auto get_return_object() { return task{}; } void unhandled_exception() {} diff --git a/clang/test/SemaCXX/coroutine-rvo.cpp b/clang/test/SemaCXX/coroutine-rvo.cpp index 6bf1dee67557c..e31839175b2a1 100644 --- a/clang/test/SemaCXX/coroutine-rvo.cpp +++ b/clang/test/SemaCXX/coroutine-rvo.cpp @@ -52,7 +52,7 @@ struct NoCopyNoMove { template <typename T> struct task { struct promise_type { - auto initial_suspend() { return suspend_never{}; } + auto initial_suspend() noexcept { return suspend_never{}; } auto final_suspend() noexcept { return suspend_never{}; } auto get_return_object() { return task{}; } static void unhandled_exception() {} @@ -128,7 +128,7 @@ struct is_same<T, T> { static constexpr bool value = true; }; template <typename T> struct generic_task { struct promise_type { - auto initial_suspend() { return suspend_never{}; } + auto initial_suspend() noexcept { return suspend_never{}; } auto final_suspend() noexcept { return suspend_never{}; } auto get_return_object() { return generic_task{}; } static void unhandled_exception(); diff --git a/clang/test/SemaCXX/coroutine-unevaluate.cpp b/clang/test/SemaCXX/coroutine-unevaluate.cpp index 164caed2836a1..61d65dc610974 100644 --- a/clang/test/SemaCXX/coroutine-unevaluate.cpp +++ b/clang/test/SemaCXX/coroutine-unevaluate.cpp @@ -4,7 +4,7 @@ struct MyTask{ struct promise_type { MyTask get_return_object(); - std::suspend_always initial_suspend() { return {}; } + std::suspend_always initial_suspend() noexcept { return {}; } void unhandled_exception(); void return_void(); diff --git a/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp b/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp index 5ea1e5d672442..83b33bbcaf208 100644 --- a/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp +++ b/clang/test/SemaCXX/coroutine-unhandled_exception-warning.cpp @@ -22,7 +22,7 @@ struct promise_void { // expected-note {{defined here}} struct promise_void { #endif void get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); }; diff --git a/clang/test/SemaCXX/coroutine-unreachable-warning.cpp b/clang/test/SemaCXX/coroutine-unreachable-warning.cpp index 35890ee83a1be..f7e87fb35db77 100644 --- a/clang/test/SemaCXX/coroutine-unreachable-warning.cpp +++ b/clang/test/SemaCXX/coroutine-unreachable-warning.cpp @@ -6,7 +6,7 @@ extern void abort(void) __attribute__((__noreturn__)); struct task { struct promise_type { - std::suspend_always initial_suspend(); + std::suspend_always initial_suspend() noexcept; std::suspend_always final_suspend() noexcept; void return_void(); std::suspend_always yield_value(int) { return {}; } diff --git a/clang/test/SemaCXX/coroutine_handle-address-return-type.cpp b/clang/test/SemaCXX/coroutine_handle-address-return-type.cpp index 884ff3680e1a1..f90769de7de8c 100644 --- a/clang/test/SemaCXX/coroutine_handle-address-return-type.cpp +++ b/clang/test/SemaCXX/coroutine_handle-address-return-type.cpp @@ -42,7 +42,7 @@ struct suspend_never { struct task { struct promise_type { - auto initial_suspend() { return suspend_never{}; } + auto initial_suspend() noexcept { return suspend_never{}; } auto final_suspend() noexcept { return suspend_never{}; } auto get_return_object() { return task{}; } static void unhandled_exception() {} diff --git a/clang/test/SemaCXX/coroutine_initial_suspend_exception_warning.cpp b/clang/test/SemaCXX/coroutine_initial_suspend_exception_warning.cpp new file mode 100644 index 0000000000000..50775668ae1f9 --- /dev/null +++ b/clang/test/SemaCXX/coroutine_initial_suspend_exception_warning.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -Wunused-result + +namespace std { + +template <class Ret, typename... T> +struct coroutine_traits { using promise_type = typename Ret::promise_type; }; + +template <class Promise = void> +struct coroutine_handle { + static coroutine_handle from_address(void *); + void *address() const noexcept; +}; +template <> +struct coroutine_handle<void> { + template <class PromiseType> + coroutine_handle(coroutine_handle<PromiseType>); + void *address() const noexcept; +}; + +struct suspend_always { + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +struct suspend_always_throws { + bool await_ready() { return false; } // expected-note 1 {{must be declared with 'noexcept'}} + void await_suspend(coroutine_handle<>) {} // expected-note 1 {{must be declared with 'noexcept'}} + void await_resume() {} // no-warning +}; + +} // namespace std + +using namespace std; + +struct coro_t_1 { + struct promise_type { + coro_t_1 get_return_object(); + suspend_always initial_suspend(); // expected-note 1 {{must be declared with 'noexcept'}} + suspend_always final_suspend() noexcept; + void return_void(); + static void unhandled_exception(); + }; +}; + +coro_t_1 f1() { // expected-warning {{a potentially throwing 'co_await __promise.initial_suspend()' may disable heap allocation elision; if it throws, the coroutine return value and state are destroyed in the reverse order of their construction}} + co_return; +} + +struct coro_t_2 { + struct promise_type { + coro_t_2 get_return_object(); + suspend_always_throws initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_void(); + static void unhandled_exception(); + }; +}; + +coro_t_2 f2() { // expected-warning {{a potentially throwing 'co_await __promise.initial_suspend()' may disable heap allocation elision; if it throws, the coroutine return value and state are destroyed in the reverse order of their construction}} + co_return; +} diff --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp index 098c1c21a5962..4fec0e7fd99f5 100644 --- a/clang/test/SemaCXX/coroutines.cpp +++ b/clang/test/SemaCXX/coroutines.cpp @@ -128,7 +128,7 @@ struct not_awaitable {}; struct promise { void get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; awaitable yield_value(int); // expected-note 2{{candidate}} awaitable yield_value(yielded_thing); // expected-note 2{{candidate}} @@ -139,7 +139,7 @@ struct promise { struct promise_void { void get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -511,9 +511,9 @@ namespace dependent_operator_co_await_lookup { struct transform_promise { typedef transform_awaitable await_arg; coro<transform_promise> get_return_object(); - transformed initial_suspend(); + transformed initial_suspend() noexcept; ::adl_ns::coawait_arg_type final_suspend() noexcept; - transformed await_transform(transform_awaitable); + transformed await_transform(transform_awaitable) noexcept; void unhandled_exception(); void return_void(); }; @@ -521,7 +521,7 @@ namespace dependent_operator_co_await_lookup { struct basic_promise { typedef AwaitArg await_arg; coro<basic_promise> get_return_object(); - awaitable initial_suspend(); + awaitable initial_suspend() noexcept; awaitable final_suspend() noexcept; void unhandled_exception(); void return_void(); @@ -576,7 +576,7 @@ namespace dependent_operator_co_await_lookup { } void operator co_await(transform_awaitable) = delete; - awaitable operator co_await(transformed); + awaitable operator co_await(transformed) noexcept; template coro<transform_promise> dependent_member<long>::dep_mem_fn<transform_promise>(transform_awaitable); @@ -606,7 +606,7 @@ struct std::coroutine_traits<void, yield_fn_tag> { awaitable yield_value(int()); void return_value(int()); - suspend_never initial_suspend(); + suspend_never initial_suspend() noexcept; suspend_never final_suspend() noexcept; void get_return_object(); void unhandled_exception(); @@ -640,7 +640,7 @@ namespace placeholder { } struct bad_promise_1 { - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_void(); @@ -662,7 +662,7 @@ coro<bad_promise_2> missing_initial_suspend() { // expected-error {{no member na struct bad_promise_3 { coro<bad_promise_3> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; void unhandled_exception(); void return_void(); }; @@ -672,7 +672,7 @@ coro<bad_promise_3> missing_final_suspend() noexcept { // expected-error {{no me struct bad_promise_4 { coro<bad_promise_4> get_return_object(); - not_awaitable initial_suspend(); + not_awaitable initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); }; @@ -684,7 +684,7 @@ coro<bad_promise_4> bad_initial_suspend() { // expected-error {{no member named struct bad_promise_5 { coro<bad_promise_5> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; not_awaitable final_suspend() noexcept; void return_void(); }; @@ -696,7 +696,7 @@ coro<bad_promise_5> bad_final_suspend() { // expected-error {{no member named 'a struct bad_promise_6 { coro<bad_promise_6> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_void(); // expected-note 2 {{member 'return_void' first declared here}} @@ -715,7 +715,7 @@ template coro<bad_promise_6> bad_implicit_return_dependent(bad_promise_6); // ex struct bad_promise_7 { // expected-note 2 {{defined here}} coro<bad_promise_7> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); }; @@ -735,7 +735,7 @@ struct bad_promise_base { }; struct bad_promise_8 : bad_promise_base { coro<bad_promise_8> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception() __attribute__((unavailable)); // expected-note 2 {{marked unavailable here}} void unhandled_exception() const; @@ -757,7 +757,7 @@ template coro<bad_promise_8> calls_unhandled_exception_dependent(bad_promise_8); struct bad_promise_9 { coro<bad_promise_9> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void await_transform(void *); awaitable await_transform(int) __attribute__((unavailable)); // expected-note {{explicitly marked unavailable}} @@ -770,7 +770,7 @@ coro<bad_promise_9> calls_await_transform() { struct bad_promise_10 { coro<bad_promise_10> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; int await_transform; void return_void(); @@ -789,7 +789,7 @@ struct call_operator { void ret_void(); struct good_promise_1 { coro<good_promise_1> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); static const call_operator await_transform; @@ -826,7 +826,7 @@ int main(int, const char**) { struct good_promise_2 { float get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -858,7 +858,7 @@ template <> struct std::coroutine_traits<int, promise_on_alloc_failure_tag> { struct promise_type { int get_return_object() {} - suspend_always initial_suspend() { return {}; } + suspend_always initial_suspend() noexcept { return {}; } suspend_always final_suspend() noexcept { return {}; } void return_void() {} int get_return_object_on_allocation_failure(); // expected-error{{'promise_type': 'get_return_object_on_allocation_failure()' must be a static member function}} @@ -872,7 +872,7 @@ extern "C" int f(promise_on_alloc_failure_tag) { struct bad_promise_11 { coro<bad_promise_11> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_void(); @@ -895,7 +895,7 @@ template coro<bad_promise_11> dependent_private_alloc_failure_handler(bad_promis struct bad_promise_12 { coro<bad_promise_12> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_void(); @@ -917,7 +917,7 @@ template coro<bad_promise_12> dependent_throwing_in_class_new(bad_promise_12); / struct good_promise_13 { coro<good_promise_13> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); void return_void(); @@ -935,7 +935,7 @@ template coro<good_promise_13> dependent_uses_nothrow_new(good_promise_13); struct good_promise_custom_new_operator { coro<good_promise_custom_new_operator> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -951,7 +951,7 @@ struct coroutine_nonstatic_member_struct; struct good_promise_nonstatic_member_custom_new_operator { coro<good_promise_nonstatic_member_custom_new_operator> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -961,7 +961,7 @@ struct good_promise_nonstatic_member_custom_new_operator { struct good_promise_noexcept_custom_new_operator { static coro<good_promise_noexcept_custom_new_operator> get_return_object_on_allocation_failure(); coro<good_promise_noexcept_custom_new_operator> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -978,7 +978,7 @@ template <> struct std::coroutine_traits<int, mismatch_gro_type_tag1> { struct promise_type { void get_return_object() {} //expected-note {{member 'get_return_object' declared here}} - suspend_always initial_suspend() { return {}; } + suspend_always initial_suspend() noexcept { return {}; } suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception(); @@ -995,7 +995,7 @@ template <> struct std::coroutine_traits<int, mismatch_gro_type_tag2> { struct promise_type { void *get_return_object() {} //expected-note {{member 'get_return_object' declared here}} - suspend_always initial_suspend() { return {}; } + suspend_always initial_suspend() noexcept { return {}; } suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception(); @@ -1014,7 +1014,7 @@ struct std::coroutine_traits<int, mismatch_gro_type_tag3> { struct promise_type { int get_return_object() {} static void get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared here}} - suspend_always initial_suspend() { return {}; } + suspend_always initial_suspend() noexcept { return {}; } suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception(); @@ -1033,7 +1033,7 @@ struct std::coroutine_traits<int, mismatch_gro_type_tag4> { struct promise_type { int get_return_object() {} static char *get_return_object_on_allocation_failure() {} //expected-note {{member 'get_return_object_on_allocation_failure' declared}} - suspend_always initial_suspend() { return {}; } + suspend_always initial_suspend() noexcept { return {}; } suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception(); @@ -1047,7 +1047,7 @@ extern "C" int f(mismatch_gro_type_tag4) { struct promise_no_return_func { coro<promise_no_return_func> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void unhandled_exception(); }; @@ -1177,7 +1177,7 @@ struct CoroMemberPromise { using AwaitTestT = AwaitReturnsType<TypeTestT>; CoroMemberTag get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; AwaitTestT yield_value(int); @@ -1388,7 +1388,7 @@ struct bad_promise_deleted_constructor { // expected-note@+1 {{'bad_promise_deleted_constructor' has been explicitly marked deleted here}} bad_promise_deleted_constructor() = delete; coro<bad_promise_deleted_constructor> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -1410,7 +1410,7 @@ struct good_promise_default_constructor { good_promise_default_constructor(double, float, int); good_promise_default_constructor() = default; coro<good_promise_default_constructor> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -1428,7 +1428,7 @@ struct good_promise_custom_constructor { good_promise_custom_constructor(double, float, int); good_promise_custom_constructor() = delete; coro<good_promise_custom_constructor> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -1455,7 +1455,7 @@ struct bad_promise_no_matching_constructor { // expected-note@+1 2 {{'bad_promise_no_matching_constructor' has been explicitly marked deleted here}} bad_promise_no_matching_constructor() = delete; coro<bad_promise_no_matching_constructor> get_return_object(); - suspend_always initial_suspend(); + suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void(); void unhandled_exception(); @@ -1497,7 +1497,7 @@ class awaitable_unused_warn { template <class Await> struct check_warning_promise { coro<check_warning_promise> get_return_object(); - Await initial_suspend(); + Await initial_suspend() noexcept; Await final_suspend() noexcept; Await yield_value(int); void return_void(); diff --git a/clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp b/clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp index dfa50cb75acfa..a5de082c70e8b 100644 --- a/clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp +++ b/clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp @@ -12,7 +12,7 @@ class coro_test { promise_type(const promise_type&) = delete; // #copy-ctr promise_type(T); // #candidate coro_test get_return_object(); - std::suspend_never initial_suspend(); + std::suspend_never initial_suspend() noexcept; std::suspend_never final_suspend() noexcept; void return_void(); void unhandled_exception(); diff --git a/clang/test/SemaCXX/type-aware-coroutines.cpp b/clang/test/SemaCXX/type-aware-coroutines.cpp index e41d07b9bccf5..1f2da2cdeb8dc 100644 --- a/clang/test/SemaCXX/type-aware-coroutines.cpp +++ b/clang/test/SemaCXX/type-aware-coroutines.cpp @@ -21,7 +21,7 @@ struct resumable { template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t sz, std::align_val_t) = delete; // #resumable_tad2 resumable get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; @@ -35,7 +35,7 @@ struct resumable2 { void operator delete(std::type_identity<promise_type>, void *, std::size_t sz, std::align_val_t); // #resumable2_tad2 resumable2 get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; @@ -53,7 +53,7 @@ struct resumable3 { void operator delete(void *); resumable3 get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; @@ -68,7 +68,7 @@ struct resumable4 { template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable4_tad resumable4 get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; @@ -84,7 +84,7 @@ struct resumable5 { template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable5_tad resumable5 get_return_object() { return {}; } - auto initial_suspend() { return std::suspend_always(); } + auto initial_suspend() noexcept { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() {} void return_void(){}; diff --git a/clang/test/SemaCXX/warn-unsequenced-coro.cpp b/clang/test/SemaCXX/warn-unsequenced-coro.cpp index 56d2edcf30155..e3544795deb47 100644 --- a/clang/test/SemaCXX/warn-unsequenced-coro.cpp +++ b/clang/test/SemaCXX/warn-unsequenced-coro.cpp @@ -44,7 +44,7 @@ class generator struct Promise { auto get_return_object() { return generator{*this}; } - auto initial_suspend() { return suspend_never{}; } + auto initial_suspend() noexcept { return suspend_never{}; } auto final_suspend() noexcept { return suspend_always{}; } void unhandled_exception() {} void return_void() {} diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp index 402d196d95a50..d244cf6f5dda8 100644 --- a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp +++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp @@ -28,7 +28,7 @@ struct MyCoro { struct promise_type { void unhandled_exception() {} void return_void() {} - std::suspend_never initial_suspend() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } MyCoro get_return_object() { do_runtime_test(); diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp index de6d09011f528..a3782fbc73100 100644 --- a/libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp +++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp @@ -19,7 +19,7 @@ struct coro_t { std::coroutine_handle<promise_type>{}; return {}; } - std::suspend_never initial_suspend() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() {} static void unhandled_exception() {} diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp index 56622341dfc55..df43bc584a8a2 100644 --- a/libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp +++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp @@ -21,7 +21,7 @@ struct coro_t { coro_t get_return_object() { return std::coroutine_handle<promise_type>::from_promise(*this); } - std::suspend_never initial_suspend() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() {} diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp index 3fa746fbae0e1..aa89294bd3653 100644 --- a/libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp +++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp @@ -34,7 +34,7 @@ struct expected { struct promise_type { std::shared_ptr<Data> data; expected get_return_object() { data = std::make_shared<Data>(); return {data}; } - std::suspend_never initial_suspend() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_value(T v) { data->val = v; data->error = {}; } void unhandled_exception() {} diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp index 93308e31512db..8fcd92c11820c 100644 --- a/libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp +++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp @@ -35,7 +35,7 @@ struct Bug { }; struct coro2 { struct promise_type { - std::suspend_never initial_suspend() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } coro2 get_return_object() { return {}; } void return_void() {} diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp index f3d400aa93269..f752588465b6a 100644 --- a/libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp +++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp @@ -24,7 +24,7 @@ template <typename Ty> struct generator { this->current_value = value; return {}; } - std::suspend_always initial_suspend() { return {}; } + std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } generator get_return_object() { return generator{this}; }; void return_void() {} @@ -83,7 +83,7 @@ struct minig { this->current_value = value; return {}; } - std::suspend_always initial_suspend() { return {}; } + std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } minig get_return_object() { return minig{this}; }; void return_void() {} diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp index 434858d87bb4f..8f4f66a22db15 100644 --- a/libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp +++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp @@ -42,9 +42,7 @@ struct goroutine struct promise_type { - std::suspend_never initial_suspend() { - return {}; - } + std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() {} goroutine get_return_object() { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
