diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 21:29:30 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 21:29:30 +0000 |
commit | 442906470441699a0cffb2c475ee2fa6e6e57515 (patch) | |
tree | bbbef432354ebeb711fa2c51fe72874e072a8be9 /contrib/llvm/tools/clang/lib/Sema/SemaCoroutine.cpp | |
parent | d88c1a5a572cdb661c111098831fa526e933756f (diff) | |
parent | bab175ec4b075c8076ba14c762900392533f6ee4 (diff) | |
download | src-442906470441699a0cffb2c475ee2fa6e6e57515.tar.gz src-442906470441699a0cffb2c475ee2fa6e6e57515.zip |
Update clang to trunk r290819 and resolve conflicts.
Notes
Notes:
svn path=/projects/clang400-import/; revision=311143
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Sema/SemaCoroutine.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Sema/SemaCoroutine.cpp | 373 |
1 files changed, 316 insertions, 57 deletions
diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaCoroutine.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaCoroutine.cpp index c8715fff4159..3109358df464 100644 --- a/contrib/llvm/tools/clang/lib/Sema/SemaCoroutine.cpp +++ b/contrib/llvm/tools/clang/lib/Sema/SemaCoroutine.cpp @@ -26,15 +26,15 @@ using namespace sema; static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType, SourceLocation Loc) { // FIXME: Cache std::coroutine_traits once we've found it. - NamespaceDecl *Std = S.getStdNamespace(); - if (!Std) { + NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace(); + if (!StdExp) { S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found); return QualType(); } LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_traits"), Loc, Sema::LookupOrdinaryName); - if (!S.LookupQualifiedName(Result, Std)) { + if (!S.LookupQualifiedName(Result, StdExp)) { S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found); return QualType(); } @@ -78,7 +78,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType, auto *Promise = R.getAsSingle<TypeDecl>(); if (!Promise) { S.Diag(Loc, diag::err_implied_std_coroutine_traits_promise_type_not_found) - << RD; + << RD; return QualType(); } @@ -86,75 +86,131 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType, QualType PromiseType = S.Context.getTypeDeclType(Promise); if (!PromiseType->getAsCXXRecordDecl()) { // Use the fully-qualified name of the type. - auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, Std); + auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, StdExp); NNS = NestedNameSpecifier::Create(S.Context, NNS, false, CoroTrait.getTypePtr()); PromiseType = S.Context.getElaboratedType(ETK_None, NNS, PromiseType); S.Diag(Loc, diag::err_implied_std_coroutine_traits_promise_type_not_class) - << PromiseType; + << PromiseType; return QualType(); } return PromiseType; } -/// Check that this is a context in which a coroutine suspension can appear. -static FunctionScopeInfo * -checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) { +static bool isValidCoroutineContext(Sema &S, SourceLocation Loc, + StringRef Keyword) { // 'co_await' and 'co_yield' are not permitted in unevaluated operands. if (S.isUnevaluatedContext()) { S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword; - return nullptr; + return false; } // 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) ? diag::err_coroutine_objc_method : diag::err_coroutine_outside_function) << Keyword; - } else if (isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD)) { - // Coroutines TS [special]/6: - // A special member function shall not be a coroutine. - // - // FIXME: We assume that this really means that a coroutine cannot - // be a constructor or destructor. - S.Diag(Loc, diag::err_coroutine_ctor_dtor) - << isa<CXXDestructorDecl>(FD) << Keyword; - } else if (FD->isConstexpr()) { - S.Diag(Loc, diag::err_coroutine_constexpr) << Keyword; - } else if (FD->isVariadic()) { - S.Diag(Loc, diag::err_coroutine_varargs) << Keyword; - } else { - auto *ScopeInfo = S.getCurFunction(); - assert(ScopeInfo && "missing function scope for function"); - - // If we don't have a promise variable, build one now. - if (!ScopeInfo->CoroutinePromise) { - QualType T = - FD->getType()->isDependentType() - ? S.Context.DependentTy - : lookupPromiseType(S, FD->getType()->castAs<FunctionProtoType>(), - Loc); - if (T.isNull()) - return nullptr; - - // Create and default-initialize the promise. - ScopeInfo->CoroutinePromise = - VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(), - &S.PP.getIdentifierTable().get("__promise"), T, - S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None); - S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise); - if (!ScopeInfo->CoroutinePromise->isInvalidDecl()) - S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise, false); - } + return false; + } - return ScopeInfo; + // An enumeration for mapping the diagnostic type to the correct diagnostic + // selection index. + enum InvalidFuncDiag { + DiagCtor = 0, + DiagDtor, + DiagCopyAssign, + DiagMoveAssign, + DiagMain, + DiagConstexpr, + DiagAutoRet, + DiagVarargs, + }; + bool Diagnosed = false; + auto DiagInvalid = [&](InvalidFuncDiag ID) { + S.Diag(Loc, diag::err_coroutine_invalid_func_context) << ID << Keyword; + Diagnosed = true; + return false; + }; + + // Diagnose when a constructor, destructor, copy/move assignment operator, + // or the function 'main' are declared as a coroutine. + auto *MD = dyn_cast<CXXMethodDecl>(FD); + if (MD && isa<CXXConstructorDecl>(MD)) + return DiagInvalid(DiagCtor); + else if (MD && isa<CXXDestructorDecl>(MD)) + return DiagInvalid(DiagDtor); + else if (MD && MD->isCopyAssignmentOperator()) + return DiagInvalid(DiagCopyAssign); + else if (MD && MD->isMoveAssignmentOperator()) + return DiagInvalid(DiagMoveAssign); + else if (FD->isMain()) + return DiagInvalid(DiagMain); + + // Emit a diagnostics for each of the following conditions which is not met. + if (FD->isConstexpr()) + DiagInvalid(DiagConstexpr); + if (FD->getReturnType()->isUndeducedType()) + DiagInvalid(DiagAutoRet); + if (FD->isVariadic()) + DiagInvalid(DiagVarargs); + + return !Diagnosed; +} + +/// Check that this is a context in which a coroutine suspension can appear. +static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc, + StringRef Keyword) { + if (!isValidCoroutineContext(S, Loc, Keyword)) + return nullptr; + + assert(isa<FunctionDecl>(S.CurContext) && "not in a function scope"); + auto *FD = cast<FunctionDecl>(S.CurContext); + auto *ScopeInfo = S.getCurFunction(); + assert(ScopeInfo && "missing function scope for function"); + + // If we don't have a promise variable, build one now. + if (!ScopeInfo->CoroutinePromise) { + QualType T = FD->getType()->isDependentType() + ? S.Context.DependentTy + : lookupPromiseType( + S, FD->getType()->castAs<FunctionProtoType>(), Loc); + if (T.isNull()) + return nullptr; + + // Create and default-initialize the promise. + ScopeInfo->CoroutinePromise = + VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(), + &S.PP.getIdentifierTable().get("__promise"), T, + S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None); + S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise); + if (!ScopeInfo->CoroutinePromise->isInvalidDecl()) + S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise, false); } - return nullptr; + return ScopeInfo; +} + +static Expr *buildBuiltinCall(Sema &S, SourceLocation Loc, Builtin::ID Id, + MutableArrayRef<Expr *> CallArgs) { + StringRef Name = S.Context.BuiltinInfo.getName(Id); + LookupResult R(S, &S.Context.Idents.get(Name), Loc, Sema::LookupOrdinaryName); + S.LookupName(R, S.TUScope, /*AllowBuiltinCreation=*/true); + + auto *BuiltInDecl = R.getAsSingle<FunctionDecl>(); + assert(BuiltInDecl && "failed to find builtin declaration"); + + ExprResult DeclRef = + S.BuildDeclRefExpr(BuiltInDecl, BuiltInDecl->getType(), VK_LValue, Loc); + assert(DeclRef.isUsable() && "Builtin reference cannot fail"); + + ExprResult Call = + S.ActOnCallExpr(/*Scope=*/nullptr, DeclRef.get(), Loc, CallArgs, Loc); + + assert(!Call.isInvalid() && "Call to builtin cannot fail!"); + return Call.get(); } /// Build a call to 'operator co_await' if there is a suitable operator for @@ -199,7 +255,7 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, SourceLocation Loc, const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"}; for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) { Expr *Operand = new (S.Context) OpaqueValueExpr( - Loc, E->getType(), VK_LValue, E->getObjectKind(), E); + Loc, E->getType(), VK_LValue, E->getObjectKind(), E); // FIXME: Pass coroutine handle to await_suspend. ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], None); @@ -213,6 +269,11 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, SourceLocation Loc, } ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { + auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await"); + if (!Coroutine) { + CorrectDelayedTyposInExpr(E); + return ExprError(); + } if (E->getType()->isPlaceholderType()) { ExprResult R = CheckPlaceholderExpr(E); if (R.isInvalid()) return ExprError(); @@ -222,6 +283,7 @@ ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { ExprResult Awaitable = buildOperatorCoawaitCall(*this, S, Loc, E); if (Awaitable.isInvalid()) return ExprError(); + return BuildCoawaitExpr(Loc, Awaitable.get()); } ExprResult Sema::BuildCoawaitExpr(SourceLocation Loc, Expr *E) { @@ -275,8 +337,10 @@ static ExprResult buildPromiseCall(Sema &S, FunctionScopeInfo *Coroutine, ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) { auto *Coroutine = checkCoroutineContext(*this, Loc, "co_yield"); - if (!Coroutine) + if (!Coroutine) { + CorrectDelayedTyposInExpr(E); return ExprError(); + } // Build yield_value call. ExprResult Awaitable = @@ -325,8 +389,14 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { } StmtResult Sema::ActOnCoreturnStmt(SourceLocation Loc, Expr *E) { + auto *Coroutine = checkCoroutineContext(*this, Loc, "co_return"); + if (!Coroutine) { + CorrectDelayedTyposInExpr(E); + return StmtError(); + } return BuildCoreturnStmt(Loc, E); } + StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E) { auto *Coroutine = checkCoroutineContext(*this, Loc, "co_return"); if (!Coroutine) @@ -343,7 +413,7 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E) { // of scope, we should treat the operand as an xvalue for this overload // resolution. ExprResult PC; - if (E && !E->getType()->isVoidType()) { + if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) { PC = buildPromiseCall(*this, Coroutine, Loc, "return_value", E); } else { E = MakeFullDiscardedValueExpr(E).get(); @@ -359,6 +429,141 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E) { return Res; } +static ExprResult buildStdCurrentExceptionCall(Sema &S, SourceLocation Loc) { + NamespaceDecl *Std = S.getStdNamespace(); + if (!Std) { + S.Diag(Loc, diag::err_implied_std_current_exception_not_found); + return ExprError(); + } + LookupResult Result(S, &S.PP.getIdentifierTable().get("current_exception"), + Loc, Sema::LookupOrdinaryName); + if (!S.LookupQualifiedName(Result, Std)) { + S.Diag(Loc, diag::err_implied_std_current_exception_not_found); + return ExprError(); + } + + // FIXME The STL is free to provide more than one overload. + FunctionDecl *FD = Result.getAsSingle<FunctionDecl>(); + if (!FD) { + S.Diag(Loc, diag::err_malformed_std_current_exception); + return ExprError(); + } + ExprResult Res = S.BuildDeclRefExpr(FD, FD->getType(), VK_LValue, Loc); + Res = S.ActOnCallExpr(/*Scope*/ nullptr, Res.get(), Loc, None, Loc); + if (Res.isInvalid()) { + S.Diag(Loc, diag::err_malformed_std_current_exception); + return ExprError(); + } + return Res; +} + +// Find an appropriate delete for the promise. +static FunctionDecl *findDeleteForPromise(Sema &S, SourceLocation Loc, + QualType PromiseType) { + FunctionDecl *OperatorDelete = nullptr; + + DeclarationName DeleteName = + S.Context.DeclarationNames.getCXXOperatorName(OO_Delete); + + auto *PointeeRD = PromiseType->getAsCXXRecordDecl(); + assert(PointeeRD && "PromiseType must be a CxxRecordDecl type"); + + if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete)) + return nullptr; + + if (!OperatorDelete) { + // Look for a global declaration. + const bool CanProvideSize = S.isCompleteType(Loc, PromiseType); + const bool Overaligned = false; + OperatorDelete = S.FindUsualDeallocationFunction(Loc, CanProvideSize, + Overaligned, DeleteName); + } + S.MarkFunctionReferenced(Loc, OperatorDelete); + return OperatorDelete; +} + +// Builds allocation and deallocation for the coroutine. Returns false on +// failure. +static bool buildAllocationAndDeallocation(Sema &S, SourceLocation Loc, + FunctionScopeInfo *Fn, + Expr *&Allocation, + Stmt *&Deallocation) { + TypeSourceInfo *TInfo = Fn->CoroutinePromise->getTypeSourceInfo(); + QualType PromiseType = TInfo->getType(); + if (PromiseType->isDependentType()) + return true; + + if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type)) + return false; + + // FIXME: Add support for get_return_object_on_allocation failure. + // FIXME: Add support for stateful allocators. + + FunctionDecl *OperatorNew = nullptr; + FunctionDecl *OperatorDelete = nullptr; + FunctionDecl *UnusedResult = nullptr; + bool PassAlignment = false; + + S.FindAllocationFunctions(Loc, SourceRange(), + /*UseGlobal*/ false, PromiseType, + /*isArray*/ false, PassAlignment, + /*PlacementArgs*/ None, OperatorNew, UnusedResult); + + OperatorDelete = findDeleteForPromise(S, Loc, PromiseType); + + if (!OperatorDelete || !OperatorNew) + return false; + + Expr *FramePtr = + buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {}); + + Expr *FrameSize = + buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_size, {}); + + // Make new call. + + ExprResult NewRef = + S.BuildDeclRefExpr(OperatorNew, OperatorNew->getType(), VK_LValue, Loc); + if (NewRef.isInvalid()) + return false; + + ExprResult NewExpr = + S.ActOnCallExpr(S.getCurScope(), NewRef.get(), Loc, FrameSize, Loc); + if (NewExpr.isInvalid()) + return false; + + Allocation = NewExpr.get(); + + // Make delete call. + + QualType OpDeleteQualType = OperatorDelete->getType(); + + ExprResult DeleteRef = + S.BuildDeclRefExpr(OperatorDelete, OpDeleteQualType, VK_LValue, Loc); + if (DeleteRef.isInvalid()) + return false; + + Expr *CoroFree = + buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_free, {FramePtr}); + + SmallVector<Expr *, 2> DeleteArgs{CoroFree}; + + // Check if we need to pass the size. + const auto *OpDeleteType = + OpDeleteQualType.getTypePtr()->getAs<FunctionProtoType>(); + if (OpDeleteType->getNumParams() > 1) + DeleteArgs.push_back(FrameSize); + + ExprResult DeleteExpr = + S.ActOnCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc); + if (DeleteExpr.isInvalid()) + return false; + + Deallocation = DeleteExpr.get(); + + return true; +} + void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { FunctionScopeInfo *Fn = getCurFunction(); assert(Fn && !Fn->CoroutineStmts.empty() && "not a coroutine"); @@ -369,8 +574,8 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { Diag(Fn->FirstReturnLoc, diag::err_return_in_coroutine); auto *First = Fn->CoroutineStmts[0]; Diag(First->getLocStart(), diag::note_declared_coroutine_here) - << (isa<CoawaitExpr>(First) ? 0 : - isa<CoyieldExpr>(First) ? 1 : 2); + << (isa<CoawaitExpr>(First) ? 0 : + isa<CoyieldExpr>(First) ? 1 : 2); } bool AnyCoawaits = false; @@ -413,15 +618,69 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { if (FinalSuspend.isInvalid()) return FD->setInvalidDecl(); - // FIXME: Perform analysis of set_exception call. + // Form and check allocation and deallocation calls. + Expr *Allocation = nullptr; + Stmt *Deallocation = nullptr; + if (!buildAllocationAndDeallocation(*this, Loc, Fn, Allocation, Deallocation)) + return FD->setInvalidDecl(); - // FIXME: Try to form 'p.return_void();' expression statement to handle // control flowing off the end of the coroutine. + // Also try to form 'p.set_exception(std::current_exception());' to handle + // uncaught exceptions. + ExprResult SetException; + StmtResult Fallthrough; + if (Fn->CoroutinePromise && + !Fn->CoroutinePromise->getType()->isDependentType()) { + CXXRecordDecl *RD = Fn->CoroutinePromise->getType()->getAsCXXRecordDecl(); + assert(RD && "Type should have already been checked"); + // [dcl.fct.def.coroutine]/4 + // The unqualified-ids 'return_void' and 'return_value' are looked up in + // the scope of class P. If both are found, the program is ill-formed. + DeclarationName RVoidDN = PP.getIdentifierInfo("return_void"); + LookupResult RVoidResult(*this, RVoidDN, Loc, Sema::LookupMemberName); + const bool HasRVoid = LookupQualifiedName(RVoidResult, RD); + + DeclarationName RValueDN = PP.getIdentifierInfo("return_value"); + LookupResult RValueResult(*this, RValueDN, Loc, Sema::LookupMemberName); + const bool HasRValue = LookupQualifiedName(RValueResult, RD); + + if (HasRVoid && HasRValue) { + // FIXME Improve this diagnostic + Diag(FD->getLocation(), diag::err_coroutine_promise_return_ill_formed) + << RD; + return FD->setInvalidDecl(); + } else if (HasRVoid) { + // If the unqualified-id return_void is found, flowing off the end of a + // coroutine is equivalent to a co_return with no operand. Otherwise, + // flowing off the end of a coroutine results in undefined behavior. + Fallthrough = BuildCoreturnStmt(FD->getLocation(), nullptr); + Fallthrough = ActOnFinishFullStmt(Fallthrough.get()); + if (Fallthrough.isInvalid()) + return FD->setInvalidDecl(); + } + + // [dcl.fct.def.coroutine]/3 + // The unqualified-id set_exception is found in the scope of P by class + // member access lookup (3.4.5). + DeclarationName SetExDN = PP.getIdentifierInfo("set_exception"); + LookupResult SetExResult(*this, SetExDN, Loc, Sema::LookupMemberName); + if (LookupQualifiedName(SetExResult, RD)) { + // Form the call 'p.set_exception(std::current_exception())' + SetException = buildStdCurrentExceptionCall(*this, Loc); + if (SetException.isInvalid()) + return FD->setInvalidDecl(); + Expr *E = SetException.get(); + SetException = buildPromiseCall(*this, Fn, Loc, "set_exception", E); + SetException = ActOnFinishFullExpr(SetException.get(), Loc); + if (SetException.isInvalid()) + return FD->setInvalidDecl(); + } + } // 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); + buildPromiseCall(*this, Fn, Loc, "get_return_object", None); if (ReturnObject.isInvalid()) return FD->setInvalidDecl(); QualType RetType = FD->getReturnType(); @@ -443,6 +702,6 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { // Build body for the coroutine wrapper statement. Body = new (Context) CoroutineBodyStmt( Body, PromiseStmt.get(), InitialSuspend.get(), FinalSuspend.get(), - /*SetException*/nullptr, /*Fallthrough*/nullptr, + SetException.get(), Fallthrough.get(), Allocation, Deallocation, ReturnObject.get(), ParamMoves); } |