std::vector<CallExpr *> Utils::callListForChain(CallExpr *lastCallExpr) { if (!lastCallExpr) return {}; const bool isOperator = isa<CXXOperatorCallExpr>(lastCallExpr); vector<CallExpr *> callexprs = { lastCallExpr }; Stmt *s = lastCallExpr; do { const int childCount = std::distance(s->child_begin(), s->child_end()); if (isOperator && childCount > 1) { // for operator case, the chained call childs are in the second child s = *(++s->child_begin()); } else { s = childCount > 0 ? *s->child_begin() : nullptr; } if (s) { CallExpr *callExpr = dyn_cast<CallExpr>(s); if (callExpr && callExpr->getCalleeDecl()) { callexprs.push_back(callExpr); } else if (MemberExpr *memberExpr = dyn_cast<MemberExpr>(s)) { if (isa<FieldDecl>(memberExpr->getMemberDecl())) break; // accessing a public member via . or -> breaks the chain } } } while (s); return callexprs; }
/// Build calls to await_ready, await_suspend, and await_resume for a co_await /// expression. static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, SourceLocation Loc, Expr *E) { OpaqueValueExpr *Operand = new (S.Context) OpaqueValueExpr(Loc, E->getType(), VK_LValue, E->getObjectKind(), E); // Assume invalid until we see otherwise. ReadySuspendResumeResult Calls = {{}, Operand, /*IsInvalid=*/true}; ExprResult CoroHandleRes = buildCoroutineHandle(S, CoroPromise->getType(), Loc); if (CoroHandleRes.isInvalid()) return Calls; Expr *CoroHandle = CoroHandleRes.get(); const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"}; MultiExprArg Args[] = {None, CoroHandle, None}; for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) { ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], Args[I]); if (Result.isInvalid()) return Calls; Calls.Results[I] = Result.get(); } // Assume the calls are valid; all further checking should make them invalid. Calls.IsInvalid = false; using ACT = ReadySuspendResumeResult::AwaitCallType; CallExpr *AwaitReady = cast<CallExpr>(Calls.Results[ACT::ACT_Ready]); if (!AwaitReady->getType()->isDependentType()) { // [expr.await]p3 [...] // — await-ready is the expression e.await_ready(), contextually converted // to bool. ExprResult Conv = S.PerformContextuallyConvertToBool(AwaitReady); if (Conv.isInvalid()) { S.Diag(AwaitReady->getDirectCallee()->getLocStart(), diag::note_await_ready_no_bool_conversion); S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required) << AwaitReady->getDirectCallee() << E->getSourceRange(); Calls.IsInvalid = true; } Calls.Results[ACT::ACT_Ready] = Conv.get(); } CallExpr *AwaitSuspend = cast<CallExpr>(Calls.Results[ACT::ACT_Suspend]); if (!AwaitSuspend->getType()->isDependentType()) { // [expr.await]p3 [...] // - await-suspend is the expression e.await_suspend(h), which shall be // a prvalue of type void or bool. QualType RetType = AwaitSuspend->getType(); if (RetType != S.Context.BoolTy && RetType != 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; } } return Calls; }