Index: clang/include/clang/AST/StmtCXX.h =================================================================== --- clang/include/clang/AST/StmtCXX.h +++ clang/include/clang/AST/StmtCXX.h @@ -461,19 +461,20 @@ enum SubStmt { Operand, PromiseCall, Count }; Stmt *SubStmts[SubStmt::Count]; + bool IsVoid : 1; // A void return with non-null operand. bool IsImplicit : 1; friend class ASTStmtReader; public: CoreturnStmt(SourceLocation CoreturnLoc, Stmt *Operand, Stmt *PromiseCall, - bool IsImplicit = false) - : Stmt(CoreturnStmtClass), CoreturnLoc(CoreturnLoc), + bool IsVoid, bool IsImplicit = false) + : Stmt(CoreturnStmtClass), CoreturnLoc(CoreturnLoc), IsVoid(IsVoid), IsImplicit(IsImplicit) { SubStmts[SubStmt::Operand] = Operand; SubStmts[SubStmt::PromiseCall] = PromiseCall; } - CoreturnStmt(EmptyShell) : CoreturnStmt({}, {}, {}) {} + CoreturnStmt(EmptyShell) : CoreturnStmt({}, {}, {}, {}) {} SourceLocation getKeywordLoc() const { return CoreturnLoc; } @@ -482,8 +483,7 @@ Expr *getOperand() const { return static_cast(SubStmts[Operand]); } /// Retrieve the promise call that results from this 'co_return' - /// statement. Will be nullptr if either the coroutine has not yet been - /// finalized or the coroutine has no eventual return type. + /// statement. Will encapsulate the operand, unless IsVoid is true. Expr *getPromiseCall() const { return static_cast(SubStmts[PromiseCall]); } @@ -491,6 +491,9 @@ bool isImplicit() const { return IsImplicit; } void setIsImplicit(bool value = true) { IsImplicit = value; } + bool getIsVoid() const { return IsVoid; } + void setIsVoid(bool value = true) { IsVoid = value; } + SourceLocation getBeginLoc() const LLVM_READONLY { return CoreturnLoc; } SourceLocation getEndLoc() const LLVM_READONLY { return getOperand() ? getOperand()->getEndLoc() : getBeginLoc(); Index: clang/lib/Analysis/CFG.cpp =================================================================== --- clang/lib/Analysis/CFG.cpp +++ clang/lib/Analysis/CFG.cpp @@ -3137,8 +3137,17 @@ return Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true); return Block; } - // co_return - return VisitChildren(S); + + CoreturnStmt *CRS = cast(S); + auto *B = Block; + if (CFGBlock *R = Visit(CRS->getPromiseCall())) + B = R; + + if (CRS->getIsVoid()) + if (CFGBlock *R = Visit(CRS->getOperand())) + B = R; + + return B; } CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) { Index: clang/lib/CodeGen/CGCoroutine.cpp =================================================================== --- clang/lib/CodeGen/CGCoroutine.cpp +++ clang/lib/CodeGen/CGCoroutine.cpp @@ -274,12 +274,10 @@ void CodeGenFunction::EmitCoreturnStmt(CoreturnStmt const &S) { ++CurCoro.Data->CoreturnCount; - const Expr *RV = S.getOperand(); - if (RV && RV->getType()->isVoidType() && !isa(RV)) { - // Make sure to evaluate the non initlist expression of a co_return - // with a void expression for side effects. + if (S.getIsVoid()) { + // Evaluate the void expression for side effects. RunCleanupsScope cleanupScope(*this); - EmitIgnoredExpr(RV); + EmitIgnoredExpr(S.getOperand()); } EmitStmt(S.getPromiseCall()); EmitBranchThroughCleanup(CurCoro.Data->FinalJD); Index: clang/lib/Sema/SemaCoroutine.cpp =================================================================== --- clang/lib/Sema/SemaCoroutine.cpp +++ clang/lib/Sema/SemaCoroutine.cpp @@ -978,6 +978,7 @@ } Expr *PCE = nullptr; + bool IsVoid = false; VarDecl *Promise = FSI->CoroutinePromise; if ((E && E->getType()->isDependentType()) || Promise->getType()->isDependentType()) { @@ -989,6 +990,7 @@ PC = buildPromiseCall(*this, Promise, Loc, "return_value", E); } else { E = MakeFullDiscardedValueExpr(E).get(); + IsVoid = bool(E); PC = buildPromiseCall(*this, Promise, Loc, "return_void", None); } if (PC.isInvalid()) @@ -997,7 +999,7 @@ PCE = ActOnFinishFullExpr(PC.get(), /*DiscardedValue*/ false).get(); } - Stmt *Res = new (Context) CoreturnStmt(Loc, E, PCE, IsImplicit); + Stmt *Res = new (Context) CoreturnStmt(Loc, E, PCE, IsVoid, IsImplicit); return Res; } Index: clang/test/SemaCXX/PR55406.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/PR55406.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++17 -fcoroutines-ts %s + +// expected-no-diagnostics + +namespace std { +template +struct coroutine_traits { + using promise_type = typename _Result::promise_type; +}; + +template +struct coroutine_handle; + +template <> +struct coroutine_handle { + static coroutine_handle from_address(void *__a) noexcept; + void resume() const noexcept; + void destroy() const noexcept; +}; + +template +struct coroutine_handle : coroutine_handle<> {}; + +struct suspend_always { + bool await_ready() const noexcept; + void await_suspend(coroutine_handle<>) const noexcept; + void await_resume() const noexcept; +}; +} // namespace std + +class Task { +public: + struct promise_type { + public: + std::suspend_always initial_suspend() noexcept; + std::suspend_always final_suspend() noexcept; + + Task get_return_object() noexcept; + void unhandled_exception() noexcept; + void return_value(int value) noexcept; + }; +}; + +Task Foo() noexcept { + // ICE'd + co_return({ int frame = 0; 0; }); +}