diff options
Diffstat (limited to 'clang/lib/Sema/SemaCoroutine.cpp')
-rw-r--r-- | clang/lib/Sema/SemaCoroutine.cpp | 118 |
1 files changed, 99 insertions, 19 deletions
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index c709a1a723d..be89cd16deb 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -373,7 +373,6 @@ static ExprResult buildPromiseCall(Sema &S, VarDecl *Promise, if (PromiseRef.isInvalid()) return ExprError(); - // Call 'yield_value', passing in E. return buildMemberCall(S, PromiseRef.get(), Loc, Name, Args); } @@ -780,7 +779,8 @@ bool CoroutineStmtBuilder::buildDependentStatements() { assert(!this->IsPromiseDependentType && "coroutine cannot have a dependent promise type"); this->IsValid = makeOnException() && makeOnFallthrough() && - makeReturnOnAllocFailure() && makeNewAndDeleteExpr(); + makeGroDeclAndReturnStmt() && makeReturnOnAllocFailure() && + makeNewAndDeleteExpr(); return this->IsValid; } @@ -857,15 +857,16 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() { if (ReturnObjectOnAllocationFailure.isInvalid()) return false; - // FIXME: ActOnReturnStmt expects a scope that is inside of the function, due - // to CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent()); - // S.getCurScope()->getFnParent() == nullptr at ActOnFinishFunctionBody when - // CoroutineBodyStmt is built. Figure it out and fix it. - // Use BuildReturnStmt here to unbreak sanitized tests. (Gor:3/27/2017) StmtResult ReturnStmt = S.BuildReturnStmt(Loc, ReturnObjectOnAllocationFailure.get()); - if (ReturnStmt.isInvalid()) + if (ReturnStmt.isInvalid()) { + S.Diag(Found.getFoundDecl()->getLocation(), + diag::note_promise_member_declared_here) + << DN.getAsString(); + S.Diag(Fn.FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) + << Fn.getFirstCoroutineStmtKeyword(); return false; + } this->ReturnStmtOnAllocFailure = ReturnStmt.get(); return true; @@ -1047,27 +1048,106 @@ bool CoroutineStmtBuilder::makeOnException() { } bool CoroutineStmtBuilder::makeReturnObject() { - // Build implicit 'p.get_return_object()' expression and form initialization // of return type from it. ExprResult ReturnObject = buildPromiseCall(S, Fn.CoroutinePromise, Loc, "get_return_object", None); if (ReturnObject.isInvalid()) return false; - QualType RetType = FD.getReturnType(); - if (!RetType->isDependentType()) { - InitializedEntity Entity = - InitializedEntity::InitializeResult(Loc, RetType, false); - ReturnObject = S.PerformMoveOrCopyInitialization(Entity, nullptr, RetType, - ReturnObject.get()); - if (ReturnObject.isInvalid()) + + this->ReturnValue = ReturnObject.get(); + return true; +} + +static void noteMemberDeclaredHere(Sema &S, Expr *E, FunctionScopeInfo &Fn) { + if (auto *MbrRef = dyn_cast<CXXMemberCallExpr>(E)) { + auto *MethodDecl = MbrRef->getMethodDecl(); + S.Diag(MethodDecl->getLocation(), diag::note_promise_member_declared_here) + << MethodDecl->getName(); + } + S.Diag(Fn.FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) + << Fn.getFirstCoroutineStmtKeyword(); +} + +bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() { + assert(!IsPromiseDependentType && + "cannot make statement while the promise type is dependent"); + assert(this->ReturnValue && "ReturnValue must be already formed"); + + QualType const GroType = this->ReturnValue->getType(); + assert(!GroType->isDependentType() && + "get_return_object type must no longer be dependent"); + + QualType const FnRetType = FD.getReturnType(); + assert(!FnRetType->isDependentType() && + "get_return_object type must no longer be dependent"); + + if (FnRetType->isVoidType()) { + ExprResult Res = S.ActOnFinishFullExpr(this->ReturnValue, Loc); + if (Res.isInvalid()) return false; + + this->ResultDecl = Res.get(); + return true; } - ReturnObject = S.ActOnFinishFullExpr(ReturnObject.get(), Loc); - if (ReturnObject.isInvalid()) + + if (GroType->isVoidType()) { + // Trigger a nice error message. + InitializedEntity Entity = + InitializedEntity::InitializeResult(Loc, FnRetType, false); + S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue); + noteMemberDeclaredHere(S, ReturnValue, Fn); return false; + } - this->ReturnValue = ReturnObject.get(); + auto *GroDecl = VarDecl::Create( + S.Context, &FD, FD.getLocation(), FD.getLocation(), + &S.PP.getIdentifierTable().get("__coro_gro"), GroType, + S.Context.getTrivialTypeSourceInfo(GroType, Loc), SC_None); + + S.CheckVariableDeclarationType(GroDecl); + if (GroDecl->isInvalidDecl()) + return false; + + InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); + ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType, + this->ReturnValue); + if (Res.isInvalid()) + return false; + + Res = S.ActOnFinishFullExpr(Res.get()); + if (Res.isInvalid()) + return false; + + if (GroType == FnRetType) { + GroDecl->setNRVOVariable(true); + } + + S.AddInitializerToDecl(GroDecl, Res.get(), + /*DirectInit=*/false); + + S.FinalizeDeclaration(GroDecl); + + // Form a declaration statement for the return declaration, so that AST + // visitors can more easily find it. + StmtResult GroDeclStmt = + S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(GroDecl), Loc, Loc); + if (GroDeclStmt.isInvalid()) + return false; + + this->ResultDecl = GroDeclStmt.get(); + + ExprResult declRef = S.BuildDeclRefExpr(GroDecl, GroType, VK_LValue, Loc); + if (declRef.isInvalid()) + return false; + + StmtResult ReturnStmt = S.BuildReturnStmt(Loc, declRef.get()); + if (ReturnStmt.isInvalid()) { + noteMemberDeclaredHere(S, ReturnValue, Fn); + return false; + } + + this->ReturnStmt = ReturnStmt.get(); return true; } |