r253946 - [coroutines] Build a CoroutineBodyStmt when finishing parsing a coroutine, and form the initial_suspend, final_suspend, and get_return_object calls.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 23 18:34:40 PST 2015
Author: rsmith
Date: Mon Nov 23 20:34:39 2015
New Revision: 253946
URL: http://llvm.org/viewvc/llvm-project?rev=253946&view=rev
Log:
[coroutines] Build a CoroutineBodyStmt when finishing parsing a coroutine, and form the initial_suspend, final_suspend, and get_return_object calls.
Modified:
cfe/trunk/include/clang/AST/StmtCXX.h
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Sema/SemaCoroutine.cpp
cfe/trunk/test/SemaCXX/coroutines.cpp
Modified: cfe/trunk/include/clang/AST/StmtCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/StmtCXX.h?rev=253946&r1=253945&r2=253946&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/StmtCXX.h (original)
+++ cfe/trunk/include/clang/AST/StmtCXX.h Mon Nov 23 20:34:39 2015
@@ -292,14 +292,33 @@ public:
/// body and holds the additional semantic context required to set up and tear
/// down the coroutine frame.
class CoroutineBodyStmt : public Stmt {
- enum SubStmt { Body, Count };
- Stmt *SubStmts[SubStmt::Count];
+ enum SubStmt {
+ Body, ///< The body of the coroutine.
+ Promise, ///< The promise statement.
+ InitSuspend, ///< The initial suspend statement, run before the body.
+ FinalSuspend, ///< The final suspend statement, run after the body.
+ OnException, ///< Handler for exceptions thrown in the body.
+ OnFallthrough, ///< Handler for control flow falling off the body.
+ ReturnValue, ///< Return value for thunk function.
+ FirstParamMove ///< First offset for move construction of parameter copies.
+ };
+ Stmt *SubStmts[SubStmt::FirstParamMove];
friend class ASTStmtReader;
public:
- CoroutineBodyStmt(Stmt *Body)
+ CoroutineBodyStmt(Stmt *Body, Stmt *Promise, Stmt *InitSuspend,
+ Stmt *FinalSuspend, Stmt *OnException, Stmt *OnFallthrough,
+ Expr *ReturnValue, ArrayRef<Expr *> ParamMoves)
: Stmt(CoroutineBodyStmtClass) {
SubStmts[CoroutineBodyStmt::Body] = Body;
+ SubStmts[CoroutineBodyStmt::Promise] = Promise;
+ SubStmts[CoroutineBodyStmt::InitSuspend] = InitSuspend;
+ SubStmts[CoroutineBodyStmt::FinalSuspend] = FinalSuspend;
+ SubStmts[CoroutineBodyStmt::OnException] = OnException;
+ SubStmts[CoroutineBodyStmt::OnFallthrough] = OnFallthrough;
+ SubStmts[CoroutineBodyStmt::ReturnValue] = ReturnValue;
+ // FIXME: Tail-allocate space for parameter move expressions and store them.
+ assert(ParamMoves.empty() && "not implemented yet");
}
/// \brief Retrieve the body of the coroutine as written. This will be either
@@ -308,6 +327,23 @@ public:
return SubStmts[SubStmt::Body];
}
+ Stmt *getPromiseDeclStmt() const { return SubStmts[SubStmt::Promise]; }
+ VarDecl *getPromiseDecl() const {
+ return cast<VarDecl>(cast<DeclStmt>(getPromiseDeclStmt())->getSingleDecl());
+ }
+
+ Stmt *getInitSuspendStmt() const { return SubStmts[SubStmt::InitSuspend]; }
+ Stmt *getFinalSuspendStmt() const { return SubStmts[SubStmt::FinalSuspend]; }
+
+ Stmt *getExceptionHandler() const { return SubStmts[SubStmt::OnException]; }
+ Stmt *getFallthroughHandler() const {
+ return SubStmts[SubStmt::OnFallthrough];
+ }
+
+ Expr *getReturnValueInit() const {
+ return cast<Expr>(SubStmts[SubStmt::ReturnValue]);
+ }
+
SourceLocation getLocStart() const LLVM_READONLY {
return getBody()->getLocStart();
}
@@ -316,7 +352,7 @@ public:
}
child_range children() {
- return child_range(SubStmts, SubStmts + SubStmt::Count);
+ return child_range(SubStmts, SubStmts + SubStmt::FirstParamMove);
}
static bool classof(const Stmt *T) {
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=253946&r1=253945&r2=253946&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Nov 23 20:34:39 2015
@@ -7738,7 +7738,7 @@ public:
ExprResult BuildCoyieldExpr(SourceLocation KwLoc, Expr *E);
StmtResult BuildCoreturnStmt(SourceLocation KwLoc, Expr *E);
- void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body);
+ void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body);
//===--------------------------------------------------------------------===//
// OpenMP directives and clauses.
Modified: cfe/trunk/lib/Sema/SemaCoroutine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=253946&r1=253945&r2=253946&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCoroutine.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Mon Nov 23 20:34:39 2015
@@ -16,6 +16,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/Initialization.h"
#include "clang/Sema/Overload.h"
using namespace clang;
using namespace sema;
@@ -108,6 +109,7 @@ checkCoroutineContext(Sema &S, SourceLoc
}
// Any other usage must be within a function.
+ // FIXME: Reject a coroutine with a deduced return type.
auto *FD = dyn_cast<FunctionDecl>(S.CurContext);
if (!FD) {
S.Diag(Loc, isa<ObjCMethodDecl>(S.CurContext)
@@ -338,7 +340,7 @@ StmtResult Sema::BuildCoreturnStmt(Sourc
}
// FIXME: If the operand is a reference to a variable that's about to go out
- // ot scope, we should treat the operand as an xvalue for this overload
+ // of scope, we should treat the operand as an xvalue for this overload
// resolution.
ExprResult PC;
if (E && !E->getType()->isVoidType()) {
@@ -357,7 +359,7 @@ StmtResult Sema::BuildCoreturnStmt(Sourc
return Res;
}
-void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body) {
+void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
FunctionScopeInfo *Fn = getCurFunction();
assert(Fn && !Fn->CoroutineStmts.empty() && "not a coroutine");
@@ -382,6 +384,65 @@ void Sema::CheckCompletedCoroutineBody(F
Diag(Fn->CoroutineStmts.front()->getLocStart(),
diag::ext_coroutine_without_co_await_co_yield);
- // FIXME: Perform analysis of initial and final suspend,
- // and set_exception call.
+ SourceLocation Loc = FD->getLocation();
+
+ // Form a declaration statement for the promise declaration, so that AST
+ // visitors can more easily find it.
+ StmtResult PromiseStmt =
+ ActOnDeclStmt(ConvertDeclToDeclGroup(Fn->CoroutinePromise), Loc, Loc);
+ if (PromiseStmt.isInvalid())
+ return FD->setInvalidDecl();
+
+ // Form and check implicit 'co_await p.initial_suspend();' statement.
+ ExprResult InitialSuspend =
+ buildPromiseCall(*this, Fn, Loc, "initial_suspend", None);
+ // FIXME: Support operator co_await here.
+ if (!InitialSuspend.isInvalid())
+ InitialSuspend = BuildCoawaitExpr(Loc, InitialSuspend.get());
+ InitialSuspend = ActOnFinishFullExpr(InitialSuspend.get());
+ if (InitialSuspend.isInvalid())
+ return FD->setInvalidDecl();
+
+ // Form and check implicit 'co_await p.final_suspend();' statement.
+ ExprResult FinalSuspend =
+ buildPromiseCall(*this, Fn, Loc, "final_suspend", None);
+ // FIXME: Support operator co_await here.
+ if (!FinalSuspend.isInvalid())
+ FinalSuspend = BuildCoawaitExpr(Loc, FinalSuspend.get());
+ FinalSuspend = ActOnFinishFullExpr(FinalSuspend.get());
+ if (FinalSuspend.isInvalid())
+ return FD->setInvalidDecl();
+
+ // FIXME: Perform analysis of set_exception call.
+
+ // FIXME: Try to form 'p.return_void();' expression statement to handle
+ // control flowing off the end of the coroutine.
+
+ // Build implicit 'p.get_return_object()' expression and form initialization
+ // of return type from it.
+ ExprResult ReturnObject =
+ buildPromiseCall(*this, Fn, Loc, "get_return_object", None);
+ if (ReturnObject.isInvalid())
+ return FD->setInvalidDecl();
+ QualType RetType = FD->getReturnType();
+ if (!RetType->isDependentType()) {
+ InitializedEntity Entity =
+ InitializedEntity::InitializeResult(Loc, RetType, false);
+ ReturnObject = PerformMoveOrCopyInitialization(Entity, nullptr, RetType,
+ ReturnObject.get());
+ if (ReturnObject.isInvalid())
+ return FD->setInvalidDecl();
+ }
+ ReturnObject = ActOnFinishFullExpr(ReturnObject.get(), Loc);
+ if (ReturnObject.isInvalid())
+ return FD->setInvalidDecl();
+
+ // FIXME: Perform move-initialization of parameters into frame-local copies.
+ SmallVector<Expr*, 16> ParamMoves;
+
+ // Build body for the coroutine wrapper statement.
+ Body = new (Context) CoroutineBodyStmt(
+ Body, PromiseStmt.get(), InitialSuspend.get(), FinalSuspend.get(),
+ /*SetException*/nullptr, /*Fallthrough*/nullptr,
+ ReturnObject.get(), ParamMoves);
}
Modified: cfe/trunk/test/SemaCXX/coroutines.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutines.cpp?rev=253946&r1=253945&r2=253946&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/coroutines.cpp (original)
+++ cfe/trunk/test/SemaCXX/coroutines.cpp Mon Nov 23 20:34:39 2015
@@ -6,6 +6,18 @@ struct awaitable {
void await_resume();
} a;
+struct suspend_always {
+ bool await_ready() { return false; }
+ void await_suspend() {}
+ void await_resume() {}
+};
+
+struct suspend_never {
+ bool await_ready() { return true; }
+ void await_suspend() {}
+ void await_resume() {}
+};
+
void no_coroutine_traits() {
co_await a; // expected-error {{need to include <coroutine>}}
}
@@ -14,6 +26,12 @@ namespace std {
template<typename ...T> struct coroutine_traits; // expected-note {{declared here}}
};
+template<typename Promise> struct coro {};
+template<typename Promise, typename... Ps>
+struct std::coroutine_traits<coro<Promise>, Ps...> {
+ using promise_type = Promise;
+};
+
void no_specialization() {
co_await a; // expected-error {{implicit instantiation of undefined template 'std::coroutine_traits<void>'}}
}
@@ -36,11 +54,13 @@ double bad_promise_type_2(int) {
co_yield 0; // expected-error {{no member named 'yield_value' in 'std::coroutine_traits<double, int>::promise_type'}}
}
-struct promise; // expected-note {{forward declaration}}
+struct promise; // expected-note 2{{forward declaration}}
template<typename ...T> struct std::coroutine_traits<void, T...> { using promise_type = promise; };
// FIXME: This diagnostic is terrible.
void undefined_promise() { // expected-error {{variable has incomplete type 'promise_type'}}
+ // FIXME: This diagnostic doesn't make any sense.
+ // expected-error at -2 {{incomplete definition of type 'promise'}}
co_await a;
}
@@ -49,6 +69,9 @@ struct yielded_thing { const char *p; sh
struct not_awaitable {};
struct promise {
+ void get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend();
awaitable yield_value(int); // expected-note 2{{candidate}}
awaitable yield_value(yielded_thing); // expected-note 2{{candidate}}
not_awaitable yield_value(void()); // expected-note 2{{candidate}}
@@ -165,6 +188,10 @@ template<> struct std::coroutine_traits<
// FIXME: add an await_transform overload for functions
awaitable yield_value(int());
void return_value(int());
+
+ suspend_never initial_suspend();
+ suspend_never final_suspend();
+ void get_return_object();
};
};
@@ -193,3 +220,49 @@ namespace placeholder {
co_return g;
}
}
+
+struct bad_promise_1 {
+ suspend_always initial_suspend();
+ suspend_always final_suspend();
+};
+coro<bad_promise_1> missing_get_return_object() { // expected-error {{no member named 'get_return_object' in 'bad_promise_1'}}
+ co_await a;
+}
+
+struct bad_promise_2 {
+ coro<bad_promise_2> get_return_object();
+ // FIXME: We shouldn't offer a typo-correction here!
+ suspend_always final_suspend(); // expected-note {{here}}
+};
+coro<bad_promise_2> missing_initial_suspend() { // expected-error {{no member named 'initial_suspend' in 'bad_promise_2'}}
+ co_await a;
+}
+
+struct bad_promise_3 {
+ coro<bad_promise_3> get_return_object();
+ // FIXME: We shouldn't offer a typo-correction here!
+ suspend_always initial_suspend(); // expected-note {{here}}
+};
+coro<bad_promise_3> missing_final_suspend() { // expected-error {{no member named 'final_suspend' in 'bad_promise_3'}}
+ co_await a;
+}
+
+struct bad_promise_4 {
+ coro<bad_promise_4> get_return_object();
+ not_awaitable initial_suspend();
+ suspend_always final_suspend();
+};
+// FIXME: This diagnostic is terrible.
+coro<bad_promise_4> bad_initial_suspend() { // expected-error {{no member named 'await_ready' in 'not_awaitable'}}
+ co_await a;
+}
+
+struct bad_promise_5 {
+ coro<bad_promise_5> get_return_object();
+ suspend_always initial_suspend();
+ not_awaitable final_suspend();
+};
+// FIXME: This diagnostic is terrible.
+coro<bad_promise_5> bad_final_suspend() { // expected-error {{no member named 'await_ready' in 'not_awaitable'}}
+ co_await a;
+}
More information about the cfe-commits
mailing list