[Clang] Warn about [[noreturn]] on coroutines (#127623)

Declaring a coroutine `[[noreturn]]` doesn't make sense, because it will
always return its handle. Clang previously crashed when trying to warn
about this (diagnostic ID was 0).

Fixes #127327.
This commit is contained in:
nerix 2025-02-18 22:30:39 +01:00 committed by GitHub
parent 8529bd7b96
commit db5bc8e9d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 43 additions and 4 deletions

View File

@ -10606,6 +10606,9 @@ def warn_noreturn_function_has_return_expr : Warning<
def warn_falloff_noreturn_function : Warning<
"function declared 'noreturn' should not return">,
InGroup<InvalidNoreturn>;
def warn_noreturn_coroutine : Warning<
"coroutine %0 cannot be declared 'noreturn' as it always returns a coroutine handle">,
InGroup<InvalidNoreturn>;
def err_noreturn_block_has_return_expr : Error<
"block declared 'noreturn' should not return">;
def err_carries_dependency_missing_on_first_decl : Error<

View File

@ -697,10 +697,12 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
return;
SourceLocation LBrace = Body->getBeginLoc(), RBrace = Body->getEndLoc();
auto EmitDiag = [&](SourceLocation Loc, unsigned DiagID) {
if (IsCoroutine)
S.Diag(Loc, DiagID) << FSI->CoroutinePromise->getType();
else
if (IsCoroutine) {
if (DiagID != 0)
S.Diag(Loc, DiagID) << FSI->CoroutinePromise->getType();
} else {
S.Diag(Loc, DiagID);
}
};
// cpu_dispatch functions permit empty function bodies for ICC compatibility.

View File

@ -1176,6 +1176,10 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
for (AddrLabelExpr *ALE : Fn->AddrLabels)
Diag(ALE->getBeginLoc(), diag::err_coro_invalid_addr_of_label);
// Coroutines always return a handle, so they can't be [[noreturn]].
if (FD->isNoReturn())
Diag(FD->getLocation(), diag::warn_noreturn_coroutine) << FD;
CoroutineStmtBuilder Builder(*this, *FD, *Fn, Body);
if (Builder.isInvalid() || !Builder.buildStatements())
return FD->setInvalidDecl();

View File

@ -3910,7 +3910,7 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
FnRetType = FD->getReturnType();
if (FD->hasAttrs())
Attrs = &FD->getAttrs();
if (FD->isNoReturn())
if (FD->isNoReturn() && !getCurFunction()->isCoroutine())
Diag(ReturnLoc, diag::warn_noreturn_function_has_return_expr) << FD;
if (FD->isMain() && RetValExp)
if (isa<CXXBoolLiteralExpr>(RetValExp))

View File

@ -0,0 +1,30 @@
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -Winvalid-noreturn -verify
#include "Inputs/std-coroutine.h"
struct Promise;
struct Awaitable {
bool await_ready();
void await_suspend(std::coroutine_handle<>);
void await_resume();
};
struct Coro : std::coroutine_handle<> {
using promise_type = Promise;
};
struct Promise {
Coro get_return_object();
std::suspend_always initial_suspend() noexcept;
std::suspend_always final_suspend() noexcept;
void return_void();
void unhandled_exception();
};
[[noreturn]] Coro test() { // expected-warning {{coroutine 'test' cannot be declared 'noreturn' as it always returns a coroutine handle}}
co_await Awaitable{};
}
// NO warning here. This could be a regular function returning a `Coro` object.
[[noreturn]] Coro test2();