//===--- SemaCoroutines.cpp - Semantic Analysis for Coroutines ------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements semantic analysis for C++ Coroutines. // //===----------------------------------------------------------------------===// #include "clang/Sema/SemaInternal.h" using namespace clang; using namespace sema; static FunctionScopeInfo * checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) { // 'co_await' and 'co_yield' are permitted in unevaluated operands. if (S.isUnevaluatedContext()) return nullptr; // Any other usage must be within a function. auto *FD = dyn_cast(S.CurContext); if (!FD) { S.Diag(Loc, isa(S.CurContext) ? diag::err_coroutine_objc_method : diag::err_coroutine_outside_function) << Keyword; } else if (isa(FD) || isa(FD)) { // Coroutines TS [special]/6: // A special member function shall not be a coroutine. // // FIXME: We assume that this really means that a coroutine cannot // be a constructor or destructor. S.Diag(Loc, diag::err_coroutine_ctor_dtor) << isa(FD) << Keyword; } else if (FD->isConstexpr()) { S.Diag(Loc, diag::err_coroutine_constexpr) << Keyword; } else if (FD->isVariadic()) { S.Diag(Loc, diag::err_coroutine_varargs) << Keyword; } else { auto *ScopeInfo = S.getCurFunction(); assert(ScopeInfo && "missing function scope for function"); return ScopeInfo; } return nullptr; } ExprResult Sema::ActOnCoawaitExpr(SourceLocation Loc, Expr *E) { auto *Context = checkCoroutineContext(*this, Loc, "co_await"); ExprResult Res = ExprError(); if (Context && !Res.isInvalid()) Context->CoroutineStmts.push_back(Res.get()); return Res; } ExprResult Sema::ActOnCoyieldExpr(SourceLocation Loc, Expr *E) { auto *Context = checkCoroutineContext(*this, Loc, "co_yield"); ExprResult Res = ExprError(); if (Context && !Res.isInvalid()) Context->CoroutineStmts.push_back(Res.get()); return Res; } StmtResult Sema::ActOnCoreturnStmt(SourceLocation Loc, Expr *E) { auto *Context = checkCoroutineContext(*this, Loc, "co_return"); StmtResult Res = StmtError(); if (Context && !Res.isInvalid()) Context->CoroutineStmts.push_back(Res.get()); return Res; } void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body) { FunctionScopeInfo *Fn = getCurFunction(); assert(Fn && !Fn->CoroutineStmts.empty() && "not a coroutine"); // Coroutines [stmt.return]p1: // A return statement shall not appear in a coroutine. if (!Fn->Returns.empty()) { Diag(Fn->Returns.front()->getLocStart(), diag::err_return_in_coroutine); auto *First = Fn->CoroutineStmts[0]; Diag(First->getLocStart(), diag::note_declared_coroutine_here) << 0; // FIXME: Indicate the kind here } bool AnyCoawaits = false; bool AnyCoyields = false; for (auto *CoroutineStmt : Fn->CoroutineStmts) { (void)CoroutineStmt; AnyCoawaits = AnyCoyields = true; // FIXME } if (!AnyCoawaits && !AnyCoyields) Diag(Fn->CoroutineStmts.front()->getLocStart(), diag::ext_coroutine_without_coawait_coyield); // FIXME: If we have a deduced return type, resolve it now. // FIXME: Compute the promise type. // FIXME: Perform analysis of initial and final suspend, and set_exception call. // FIXME: Complete the semantic analysis of the CoroutineStmts. }