/// 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; }
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); // First, try to inline the call. if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) return; // First handle the return value. StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); // Get the callee. const Expr *Callee = CE->getCallee()->IgnoreParens(); if (state == 0) state = Pred->getState(); SVal L = state->getSVal(Callee, Pred->getLocationContext()); // Figure out the result type. We do this dance to handle references. QualType ResultTy; if (const FunctionDecl *FD = L.getAsFunctionDecl()) ResultTy = FD->getResultType(); else ResultTy = CE->getType(); if (CE->isLValue()) ResultTy = Eng.getContext().getPointerType(ResultTy); // Conjure a symbol value to use as the result. SValBuilder &SVB = Eng.getSValBuilder(); unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount(); const LocationContext *LCtx = Pred->getLocationContext(); SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); // Generate a new state with the return value set. state = state->BindExpr(CE, LCtx, RetVal); // Invalidate the arguments. state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx), LCtx); // And make the result node. Bldr.generateNode(CE, Pred, state); }