aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaCoroutine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/SemaCoroutine.cpp')
-rw-r--r--lib/Sema/SemaCoroutine.cpp51
1 files changed, 41 insertions, 10 deletions
diff --git a/lib/Sema/SemaCoroutine.cpp b/lib/Sema/SemaCoroutine.cpp
index dc7d8e4e9cec..e6b640f878c2 100644
--- a/lib/Sema/SemaCoroutine.cpp
+++ b/lib/Sema/SemaCoroutine.cpp
@@ -363,6 +363,32 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc,
return S.ActOnCallExpr(nullptr, Result.get(), Loc, Args, Loc, nullptr);
}
+// See if return type is coroutine-handle and if so, invoke builtin coro-resume
+// on its address. This is to enable experimental support for coroutine-handle
+// returning await_suspend that results in a guranteed tail call to the target
+// coroutine.
+static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E,
+ SourceLocation Loc) {
+ if (RetType->isReferenceType())
+ return nullptr;
+ Type const *T = RetType.getTypePtr();
+ if (!T->isClassType() && !T->isStructureType())
+ return nullptr;
+
+ // FIXME: Add convertability check to coroutine_handle<>. Possibly via
+ // EvaluateBinaryTypeTrait(BTT_IsConvertible, ...) which is at the moment
+ // a private function in SemaExprCXX.cpp
+
+ ExprResult AddressExpr = buildMemberCall(S, E, Loc, "address", None);
+ if (AddressExpr.isInvalid())
+ return nullptr;
+
+ Expr *JustAddress = AddressExpr.get();
+ // FIXME: Check that the type of AddressExpr is void*
+ return buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_resume,
+ JustAddress);
+}
+
/// Build calls to await_ready, await_suspend, and await_resume for a co_await
/// expression.
static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise,
@@ -412,16 +438,21 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise,
// - await-suspend is the expression e.await_suspend(h), which shall be
// a prvalue of type void or bool.
QualType RetType = AwaitSuspend->getCallReturnType(S.Context);
- // non-class prvalues always have cv-unqualified types
- QualType AdjRetType = RetType.getUnqualifiedType();
- if (RetType->isReferenceType() ||
- (AdjRetType != S.Context.BoolTy && AdjRetType != S.Context.VoidTy)) {
- S.Diag(AwaitSuspend->getCalleeDecl()->getLocation(),
- diag::err_await_suspend_invalid_return_type)
- << RetType;
- S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
- << AwaitSuspend->getDirectCallee();
- Calls.IsInvalid = true;
+
+ // Experimental support for coroutine_handle returning await_suspend.
+ if (Expr *TailCallSuspend = maybeTailCall(S, RetType, AwaitSuspend, Loc))
+ Calls.Results[ACT::ACT_Suspend] = TailCallSuspend;
+ else {
+ // non-class prvalues always have cv-unqualified types
+ if (RetType->isReferenceType() ||
+ (!RetType->isBooleanType() && !RetType->isVoidType())) {
+ S.Diag(AwaitSuspend->getCalleeDecl()->getLocation(),
+ diag::err_await_suspend_invalid_return_type)
+ << RetType;
+ S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
+ << AwaitSuspend->getDirectCallee();
+ Calls.IsInvalid = true;
+ }
}
}