Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8973,6 +8973,12 @@ "type declares 'get_return_object_on_allocation_failure()'">; def note_coroutine_promise_call_implicitly_required : Note< "call to %0 implicitly required by coroutine function here">; +def err_await_suspend_invalid_return_type : Error< + "the return type of 'await_suspend' is required to be 'void' or 'bool' (have %0)" +>; +def note_await_ready_no_bool_conversion : Note< + "the return type of 'await_ready' is required to be contextually convertible to 'bool'" +>; } let CategoryName = "Documentation Issue" in { Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -321,6 +321,7 @@ } struct ReadySuspendResumeResult { + enum AwaitCallType { ACT_Ready, ACT_Suspend, ACT_Resume }; Expr *Results[3]; OpaqueValueExpr *OpaqueValue; bool IsInvalid; @@ -366,7 +367,41 @@ Calls.Results[I] = Result.get(); } + // Assume the calls are valid; all further checking should make them invalid. Calls.IsInvalid = false; + + using ACT = ReadySuspendResumeResult::AwaitCallType; + CallExpr *AwaitReady = cast(Calls.Results[ACT::ACT_Ready]); + if (!AwaitReady->getType()->isDependentType()) { + // [expr.await]p3 [...] + // — await-ready is the expression e.await_ready(), contextually converted + // to bool. + ExprResult Conv = S.PerformContextuallyConvertToBool(AwaitReady); + if (Conv.isInvalid()) { + S.Diag(AwaitReady->getDirectCallee()->getLocStart(), + diag::note_await_ready_no_bool_conversion); + S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required) + << AwaitReady->getDirectCallee() << E->getSourceRange(); + Calls.IsInvalid = true; + } + Calls.Results[ACT::ACT_Ready] = Conv.get(); + } + CallExpr *AwaitSuspend = cast(Calls.Results[ACT::ACT_Suspend]); + if (!AwaitSuspend->getType()->isDependentType()) { + // [expr.await]p3 [...] + // - await-suspend is the expression e.await_suspend(h), which shall be + // a prvalue of type void or bool. + QualType RetType = AwaitSuspend->getType(); + if (RetType != S.Context.BoolTy && RetType != S.Context.VoidTy) { + S.Diag(AwaitSuspend->getCalleeDecl()->getLocation(), + diag::err_await_suspend_invalid_return_type) + << RetType; + S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required) + << AwaitSuspend->getDirectCallee(); + Calls.IsInvalid = true; + } + } + return Calls; } Index: test/SemaCXX/coroutines.cpp =================================================================== --- test/SemaCXX/coroutines.cpp +++ test/SemaCXX/coroutines.cpp @@ -837,3 +837,28 @@ // expected-error@-1 {{'bad_promise_no_return_func' must declare either 'return_value' or 'return_void'}} co_await a; } + +struct bad_await_suspend_return { + bool await_ready(); + // expected-error@+1 {{the return type of 'await_suspend' is required to be 'void' or 'bool' (have 'char')}} + char await_suspend(std::experimental::coroutine_handle<>); + void await_resume(); +}; +struct bad_await_ready_return { + // expected-note@+1 {{the return type of 'await_ready' is required to be contextually convertible to 'bool'}} + void await_ready(); + bool await_suspend(std::experimental::coroutine_handle<>); + void await_resume(); +}; +void test_bad_suspend() { + { + // FIXME: The actual error emitted here is terrible, and no number of notes can save it. + bad_await_ready_return a; + // expected-error@+1 {{value of type 'void' is not contextually convertible to 'bool'}} + co_await a; // expected-note {{call to 'await_ready' implicitly required by coroutine function here}} + } + { + bad_await_suspend_return b; + co_await b; // expected-note {{call to 'await_suspend' implicitly required by coroutine function here}} + } +}