Author: rsmith Date: Thu Oct 22 01:13:50 2015 New Revision: 250993 URL: http://llvm.org/viewvc/llvm-project?rev=250993&view=rev Log: [coroutines] Initial stub Sema functionality for handling coroutine await / yield / return.
Added: cfe/trunk/lib/Sema/SemaCoroutine.cpp cfe/trunk/test/SemaCXX/coroutines.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Sema/ScopeInfo.h cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Parse/ParseExpr.cpp cfe/trunk/lib/Parse/ParseExprCXX.cpp cfe/trunk/lib/Parse/ParseStmt.cpp cfe/trunk/lib/Sema/CMakeLists.txt cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaStmt.cpp cfe/trunk/lib/Sema/TreeTransform.h Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Oct 22 01:13:50 2015 @@ -7827,6 +7827,27 @@ def err_module_import_in_implementation "@import of module '%0' in implementation of '%1'; use #import">; } +let CategoryName = "Coroutines Issue" in { +def err_return_in_coroutine : Error<"return statement in coroutine">; +def note_declared_coroutine_here : Note< + "function is a coroutine due to use of " + "'%select{co_await|co_yield|co_return}0' here">; +def err_coroutine_objc_method : Error< + "Objective-C methods as coroutines are not yet supported">; +def err_coroutine_outside_function : Error< + "'%0' cannot be used outside a function">; +def err_coroutine_ctor_dtor : Error< + "'%1' cannot be used in a %select{constructor|destructor}0">; +def err_coroutine_constexpr : Error< + "'%0' cannot be used in a constexpr function">; +def err_coroutine_varargs : Error< + "'%0' cannot be used in a varargs function">; +def ext_coroutine_without_coawait_coyield : ExtWarn< + "'co_return' used in a function " + "that uses neither 'co_await' nor 'co_yield'">, + InGroup<DiagGroup<"coreturn-without-coawait">>; +} + let CategoryName = "Documentation Issue" in { def warn_not_a_doxygen_trailing_member_comment : Warning< "not a Doxygen trailing comment">, InGroup<Documentation>, DefaultIgnore; Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/ScopeInfo.h (original) +++ cfe/trunk/include/clang/Sema/ScopeInfo.h Thu Oct 22 01:13:50 2015 @@ -142,6 +142,11 @@ public: /// optimization, or if we need to infer a return type. SmallVector<ReturnStmt*, 4> Returns; + /// \brief The list of coroutine control flow constructs (co_await, co_yield, + /// co_return) that occur within the function or block. Empty if and only if + /// this function or block is not (yet known to be) a coroutine. + SmallVector<Stmt*, 4> CoroutineStmts; + /// \brief The stack of currently active compound stamement scopes in the /// function. SmallVector<CompoundScopeInfo, 4> CompoundScopes; Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Thu Oct 22 01:13:50 2015 @@ -3334,11 +3334,14 @@ public: BFRK_Check }; - StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, Stmt *LoopVar, + StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, + SourceLocation CoawaitLoc, + Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, SourceLocation RParenLoc, BuildForRangeKind Kind); StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc, + SourceLocation CoawaitLoc, SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *BeginEndDecl, Expr *Cond, Expr *Inc, @@ -7703,7 +7706,18 @@ public: void AddLaunchBoundsAttr(SourceRange AttrRange, Decl *D, Expr *MaxThreads, Expr *MinBlocks, unsigned SpellingListIndex); + //===--------------------------------------------------------------------===// + // C++ Coroutines TS + // + ExprResult ActOnCoawaitExpr(SourceLocation KwLoc, Expr *E); + ExprResult ActOnCoyieldExpr(SourceLocation KwLoc, Expr *E); + StmtResult ActOnCoreturnStmt(SourceLocation KwLoc, Expr *E); + + void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body); + + //===--------------------------------------------------------------------===// // OpenMP directives and clauses. + // private: void *VarDataSharingAttributesStack; /// \brief Initialization of data-sharing attributes stack. Modified: cfe/trunk/lib/Parse/ParseExpr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseExpr.cpp (original) +++ cfe/trunk/lib/Parse/ParseExpr.cpp Thu Oct 22 01:13:50 2015 @@ -1045,10 +1045,10 @@ ExprResult Parser::ParseCastExpression(b } case tok::kw_co_await: { // unary-expression: 'co_await' cast-expression - SourceLocation SavedLoc = ConsumeToken(); + SourceLocation CoawaitLoc = ConsumeToken(); Res = ParseCastExpression(false); - (void)SavedLoc; - // FIXME: Pass to Sema. + if (!Res.isInvalid()) + Res = Actions.ActOnCoawaitExpr(CoawaitLoc, Res.get()); return Res; } Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original) +++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Thu Oct 22 01:13:50 2015 @@ -1567,8 +1567,8 @@ ExprResult Parser::ParseCoyieldExpressio SourceLocation Loc = ConsumeToken(); ExprResult Expr = ParseAssignmentExpression(); - (void)Loc; - // FIXME: Pass to Sema. + if (!Expr.isInvalid()) + Expr = Actions.ActOnCoyieldExpr(Loc, Expr.get()); return Expr; } Modified: cfe/trunk/lib/Parse/ParseStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseStmt.cpp?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseStmt.cpp (original) +++ cfe/trunk/lib/Parse/ParseStmt.cpp Thu Oct 22 01:13:50 2015 @@ -1691,8 +1691,8 @@ StmtResult Parser::ParseForStatement(Sou StmtResult ForEachStmt; if (ForRange) { - // FIXME: Pass CoawaitLoc to Sema. - ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, FirstPart.get(), + ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, + FirstPart.get(), ForRangeInit.ColonLoc, ForRangeInit.RangeExpr.get(), T.getCloseLocation(), @@ -1851,7 +1851,8 @@ StmtResult Parser::ParseReturnStatement( return StmtError(); } } - // FIXME: Pass IsCoreturn to Sema. + if (IsCoreturn) + return Actions.ActOnCoreturnStmt(ReturnLoc, R.get()); return Actions.ActOnReturnStmt(ReturnLoc, R.get(), getCurScope()); } Modified: cfe/trunk/lib/Sema/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/CMakeLists.txt?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/lib/Sema/CMakeLists.txt (original) +++ cfe/trunk/lib/Sema/CMakeLists.txt Thu Oct 22 01:13:50 2015 @@ -21,6 +21,7 @@ add_clang_library(clangSema SemaChecking.cpp SemaCodeComplete.cpp SemaConsumer.cpp + SemaCoroutine.cpp SemaCUDA.cpp SemaDecl.cpp SemaDeclAttr.cpp Added: cfe/trunk/lib/Sema/SemaCoroutine.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=250993&view=auto ============================================================================== --- cfe/trunk/lib/Sema/SemaCoroutine.cpp (added) +++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Thu Oct 22 01:13:50 2015 @@ -0,0 +1,106 @@ +//===--- 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<FunctionDecl>(S.CurContext); + if (!FD) { + S.Diag(Loc, isa<ObjCMethodDecl>(S.CurContext) + ? diag::err_coroutine_objc_method + : diag::err_coroutine_outside_function) << Keyword; + } else if (isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(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<CXXDestructorDecl>(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. +} Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Oct 22 01:13:50 2015 @@ -10907,6 +10907,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; + if (getLangOpts().Coroutines && !getCurFunction()->CoroutineStmts.empty()) + CheckCompletedCoroutineBody(FD, Body); + if (FD) { FD->setBody(Body); Modified: cfe/trunk/lib/Sema/SemaStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaStmt.cpp (original) +++ cfe/trunk/lib/Sema/SemaStmt.cpp Thu Oct 22 01:13:50 2015 @@ -1924,7 +1924,7 @@ static bool ObjCEnumerationCollection(Ex /// The body of the loop is not available yet, since it cannot be analysed until /// we have determined the type of the for-range-declaration. StmtResult -Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, +Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc, BuildForRangeKind Kind) { if (!First) @@ -1948,6 +1948,13 @@ Sema::ActOnCXXForRangeStmt(SourceLocatio return StmtError(); } + // Coroutines: 'for co_await' implicitly co_awaits its range. + if (CoawaitLoc.isValid()) { + ExprResult Coawait = ActOnCoawaitExpr(CoawaitLoc, Range); + if (Coawait.isInvalid()) return StmtError(); + Range = Coawait.get(); + } + // Build auto && __range = range-init SourceLocation RangeLoc = Range->getLocStart(); VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc, @@ -1969,7 +1976,7 @@ Sema::ActOnCXXForRangeStmt(SourceLocatio return StmtError(); } - return BuildCXXForRangeStmt(ForLoc, ColonLoc, RangeDecl.get(), + return BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc, RangeDecl.get(), /*BeginEndDecl=*/nullptr, /*Cond=*/nullptr, /*Inc=*/nullptr, DS, RParenLoc, Kind); } @@ -2063,6 +2070,7 @@ static Sema::ForRangeStatus BuildNonArra /// and emit no diagnostics. static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S, SourceLocation ForLoc, + SourceLocation CoawaitLoc, Stmt *LoopVarDecl, SourceLocation ColonLoc, Expr *Range, @@ -2079,7 +2087,7 @@ static StmtResult RebuildForRangeWithDer return StmtResult(); StmtResult SR = - SemaRef.ActOnCXXForRangeStmt(ForLoc, LoopVarDecl, ColonLoc, + SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, AdjustedRange.get(), RParenLoc, Sema::BFRK_Check); if (SR.isInvalid()) @@ -2091,7 +2099,7 @@ static StmtResult RebuildForRangeWithDer // case there are any other (non-fatal) problems with it. SemaRef.Diag(RangeLoc, diag::err_for_range_dereference) << Range->getType() << FixItHint::CreateInsertion(RangeLoc, "*"); - return SemaRef.ActOnCXXForRangeStmt(ForLoc, LoopVarDecl, ColonLoc, + return SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild); } @@ -2114,7 +2122,8 @@ struct InvalidateOnErrorScope { /// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement. StmtResult -Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, +Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, + SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *BeginEnd, Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc, BuildForRangeKind Kind) { @@ -2244,6 +2253,7 @@ Sema::BuildCXXForRangeStmt(SourceLocatio // If building the range failed, try dereferencing the range expression // unless a diagnostic was issued or the end function is problematic. StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc, + CoawaitLoc, LoopVarDecl, ColonLoc, Range, RangeLoc, RParenLoc); @@ -2314,7 +2324,10 @@ Sema::BuildCXXForRangeStmt(SourceLocatio return StmtError(); IncrExpr = ActOnUnaryOp(S, ColonLoc, tok::plusplus, BeginRef.get()); - IncrExpr = ActOnFinishFullExpr(IncrExpr.get()); + if (!IncrExpr.isInvalid() && CoawaitLoc.isValid()) + IncrExpr = ActOnCoawaitExpr(CoawaitLoc, IncrExpr.get()); + if (!IncrExpr.isInvalid()) + IncrExpr = ActOnFinishFullExpr(IncrExpr.get()); if (IncrExpr.isInvalid()) { Diag(RangeLoc, diag::note_for_range_invalid_iterator) << RangeLoc << 2 << BeginRangeRef.get()->getType() ; @@ -2351,6 +2364,7 @@ Sema::BuildCXXForRangeStmt(SourceLocatio if (Kind == BFRK_Check) return StmtResult(); + // FIXME: Pass in CoawaitLoc in the dependent case. return new (Context) CXXForRangeStmt( RangeDS, cast_or_null<DeclStmt>(BeginEndDecl.get()), NotEqExpr.get(), IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, ColonLoc, RParenLoc); Modified: cfe/trunk/lib/Sema/TreeTransform.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=250993&r1=250992&r2=250993&view=diff ============================================================================== --- cfe/trunk/lib/Sema/TreeTransform.h (original) +++ cfe/trunk/lib/Sema/TreeTransform.h Thu Oct 22 01:13:50 2015 @@ -1737,7 +1737,9 @@ public: } } - return getSema().BuildCXXForRangeStmt(ForLoc, ColonLoc, Range, BeginEnd, + SourceLocation CoawaitLoc; // FIXME + return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc, + Range, BeginEnd, Cond, Inc, LoopVar, RParenLoc, Sema::BFRK_Rebuild); } Added: cfe/trunk/test/SemaCXX/coroutines.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutines.cpp?rev=250993&view=auto ============================================================================== --- cfe/trunk/test/SemaCXX/coroutines.cpp (added) +++ cfe/trunk/test/SemaCXX/coroutines.cpp Thu Oct 22 01:13:50 2015 @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -std=c++14 -fcoroutines -verify %s + +void mixed_yield() { + // FIXME: diagnose + co_yield 0; + return; +} + +void mixed_await() { + // FIXME: diagnose + co_await 0; + return; +} + +void only_coreturn() { + // FIXME: diagnose + co_return; +} + +void mixed_coreturn(bool b) { + // FIXME: diagnose + if (b) + co_return; + else + return; +} + +struct CtorDtor { + CtorDtor() { + co_yield 0; // expected-error {{'co_yield' cannot be used in a constructor}} + } + CtorDtor(int n) { + // The spec doesn't say this is ill-formed, but it must be. + co_await n; // expected-error {{'co_await' cannot be used in a constructor}} + } + ~CtorDtor() { + co_return 0; // expected-error {{'co_return' cannot be used in a destructor}} + } + // FIXME: The spec says this is ill-formed. + void operator=(CtorDtor&) { + co_yield 0; + } +}; + +constexpr void constexpr_coroutine() { + co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}} +} + +void varargs_coroutine(const char *, ...) { + co_await 0; // expected-error {{'co_await' cannot be used in a varargs function}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits