diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaCoroutine.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaCoroutine.cpp | 958 |
1 files changed, 657 insertions, 301 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaCoroutine.cpp b/contrib/llvm-project/clang/lib/Sema/SemaCoroutine.cpp index 3d1899a57c72..4e600fd29ee7 100644 --- a/contrib/llvm-project/clang/lib/Sema/SemaCoroutine.cpp +++ b/contrib/llvm-project/clang/lib/Sema/SemaCoroutine.cpp @@ -16,10 +16,12 @@ #include "CoroutineStmtBuilder.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Overload.h" #include "clang/Sema/ScopeInfo.h" @@ -53,18 +55,11 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD, SourceLocation KwLoc) { const FunctionProtoType *FnType = FD->getType()->castAs<FunctionProtoType>(); const SourceLocation FuncLoc = FD->getLocation(); - // FIXME: Cache std::coroutine_traits once we've found it. - NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace(); - if (!StdExp) { - S.Diag(KwLoc, diag::err_implied_coroutine_type_not_found) - << "std::experimental::coroutine_traits"; - return QualType(); - } - ClassTemplateDecl *CoroTraits = S.lookupCoroutineTraits(KwLoc, FuncLoc); - if (!CoroTraits) { + ClassTemplateDecl *CoroTraits = + S.lookupCoroutineTraits(KwLoc, FuncLoc); + if (!CoroTraits) return QualType(); - } // Form template argument list for coroutine_traits<R, P1, P2, ...> according // to [dcl.fct.def.coroutine]3 @@ -77,7 +72,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD, // If the function is a non-static member function, add the type // of the implicit object parameter before the formal parameters. if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - if (MD->isInstance()) { + if (MD->isImplicitObjectMemberFunction()) { // [over.match.funcs]4 // For non-static member functions, the type of the implicit object // parameter is @@ -85,7 +80,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD, // ref-qualifier or with the & ref-qualifier // -- "rvalue reference to cv X" for functions declared with the && // ref-qualifier - QualType T = MD->getThisType()->castAs<PointerType>()->getPointeeType(); + QualType T = MD->getFunctionObjectParameterType(); T = FnType->getRefQualifier() == RQ_RValue ? S.Context.getRValueReferenceType(T) : S.Context.getLValueReferenceType(T, /*SpelledAsLValue*/ true); @@ -122,10 +117,11 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD, QualType PromiseType = S.Context.getTypeDeclType(Promise); auto buildElaboratedType = [&]() { - auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, StdExp); + auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, S.getStdNamespace()); NNS = NestedNameSpecifier::Create(S.Context, NNS, false, CoroTrait.getTypePtr()); - return S.Context.getElaboratedType(ETK_None, NNS, PromiseType); + return S.Context.getElaboratedType(ElaboratedTypeKeyword::None, NNS, + PromiseType); }; if (!PromiseType->getAsCXXRecordDecl()) { @@ -141,20 +137,20 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD, return PromiseType; } -/// Look up the std::experimental::coroutine_handle<PromiseType>. +/// Look up the std::coroutine_handle<PromiseType>. static QualType lookupCoroutineHandleType(Sema &S, QualType PromiseType, SourceLocation Loc) { if (PromiseType.isNull()) return QualType(); - NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace(); - assert(StdExp && "Should already be diagnosed"); + NamespaceDecl *CoroNamespace = S.getStdNamespace(); + assert(CoroNamespace && "Should already be diagnosed"); LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_handle"), Loc, Sema::LookupOrdinaryName); - if (!S.LookupQualifiedName(Result, StdExp)) { + if (!S.LookupQualifiedName(Result, CoroNamespace)) { S.Diag(Loc, diag::err_implied_coroutine_type_not_found) - << "std::experimental::coroutine_handle"; + << "std::coroutine_handle"; return QualType(); } @@ -242,53 +238,31 @@ static bool isValidCoroutineContext(Sema &S, SourceLocation Loc, // placeholder type shall not be a coroutine." if (FD->getReturnType()->isUndeducedType()) DiagInvalid(DiagAutoRet); - // [dcl.fct.def.coroutine]p1: "The parameter-declaration-clause of the - // coroutine shall not terminate with an ellipsis that is not part of a - // parameter-declaration." + // [dcl.fct.def.coroutine]p1 + // The parameter-declaration-clause of the coroutine shall not terminate with + // an ellipsis that is not part of a parameter-declaration. if (FD->isVariadic()) DiagInvalid(DiagVarargs); return !Diagnosed; } -static ExprResult buildOperatorCoawaitLookupExpr(Sema &SemaRef, Scope *S, - SourceLocation Loc) { - DeclarationName OpName = - SemaRef.Context.DeclarationNames.getCXXOperatorName(OO_Coawait); - LookupResult Operators(SemaRef, OpName, SourceLocation(), - Sema::LookupOperatorName); - SemaRef.LookupName(Operators, S); - - assert(!Operators.isAmbiguous() && "Operator lookup cannot be ambiguous"); - const auto &Functions = Operators.asUnresolvedSet(); - bool IsOverloaded = - Functions.size() > 1 || - (Functions.size() == 1 && isa<FunctionTemplateDecl>(*Functions.begin())); - Expr *CoawaitOp = UnresolvedLookupExpr::Create( - SemaRef.Context, /*NamingClass*/ nullptr, NestedNameSpecifierLoc(), - DeclarationNameInfo(OpName, Loc), /*RequiresADL*/ true, IsOverloaded, - Functions.begin(), Functions.end()); - assert(CoawaitOp); - return CoawaitOp; -} - /// Build a call to 'operator co_await' if there is a suitable operator for /// the given expression. -static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, SourceLocation Loc, - Expr *E, - UnresolvedLookupExpr *Lookup) { +ExprResult Sema::BuildOperatorCoawaitCall(SourceLocation Loc, Expr *E, + UnresolvedLookupExpr *Lookup) { UnresolvedSet<16> Functions; Functions.append(Lookup->decls_begin(), Lookup->decls_end()); - return SemaRef.CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E); + return CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E); } static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, Scope *S, SourceLocation Loc, Expr *E) { - ExprResult R = buildOperatorCoawaitLookupExpr(SemaRef, S, Loc); + ExprResult R = SemaRef.BuildOperatorCoawaitLookupExpr(S, Loc); if (R.isInvalid()) return ExprError(); - return buildOperatorCoawaitCall(SemaRef, Loc, E, - cast<UnresolvedLookupExpr>(R.get())); + return SemaRef.BuildOperatorCoawaitCall(Loc, E, + cast<UnresolvedLookupExpr>(R.get())); } static ExprResult buildCoroutineHandle(Sema &S, QualType PromiseType, @@ -347,11 +321,12 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc, return ExprError(); } - return S.BuildCallExpr(nullptr, Result.get(), Loc, Args, Loc, nullptr); + auto EndLoc = Args.empty() ? Loc : Args.back()->getEndLoc(); + return S.BuildCallExpr(nullptr, Result.get(), Loc, Args, EndLoc, 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 +// on its address. This is to enable the support for coroutine-handle // returning await_suspend that results in a guaranteed tail call to the target // coroutine. static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, @@ -366,12 +341,34 @@ static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, // EvaluateBinaryTypeTrait(BTT_IsConvertible, ...) which is at the moment // a private function in SemaExprCXX.cpp - ExprResult AddressExpr = buildMemberCall(S, E, Loc, "address", None); + ExprResult AddressExpr = buildMemberCall(S, E, Loc, "address", std::nullopt); if (AddressExpr.isInvalid()) return nullptr; Expr *JustAddress = AddressExpr.get(); + // FIXME: Without optimizations, the temporary result from `await_suspend()` + // may be put on the coroutine frame since the coroutine frame constructor + // will think the temporary variable will escape from the + // `coroutine_handle<>::address()` call. This is problematic since the + // coroutine should be considered to be suspended after it enters + // `await_suspend` so it shouldn't access/update the coroutine frame after + // that. + // + // See https://github.com/llvm/llvm-project/issues/65054 for the report. + // + // The long term solution may wrap the whole logic about `await-suspend` + // into a standalone function. This is similar to the proposed solution + // in tryMarkAwaitSuspendNoInline. See the comments there for details. + // + // The short term solution here is to mark `coroutine_handle<>::address()` + // function as always-inline so that the coroutine frame constructor won't + // think the temporary result is escaped incorrectly. + if (auto *FD = cast<CallExpr>(JustAddress)->getDirectCallee()) + if (!FD->hasAttr<AlwaysInlineAttr>() && !FD->hasAttr<NoInlineAttr>()) + FD->addAttr(AlwaysInlineAttr::CreateImplicit(S.getASTContext(), + FD->getLocation())); + // Check that the type of AddressExpr is void* if (!JustAddress->getType().getTypePtr()->isVoidPointerType()) S.Diag(cast<CallExpr>(JustAddress)->getCalleeDecl()->getLocation(), @@ -388,6 +385,63 @@ static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E, JustAddress); } +/// The await_suspend call performed by co_await is essentially asynchronous +/// to the execution of the coroutine. Inlining it normally into an unsplit +/// coroutine can cause miscompilation because the coroutine CFG misrepresents +/// the true control flow of the program: things that happen in the +/// await_suspend are not guaranteed to happen prior to the resumption of the +/// coroutine, and things that happen after the resumption of the coroutine +/// (including its exit and the potential deallocation of the coroutine frame) +/// are not guaranteed to happen only after the end of await_suspend. +/// +/// See https://github.com/llvm/llvm-project/issues/56301 and +/// https://reviews.llvm.org/D157070 for the example and the full discussion. +/// +/// The short-term solution to this problem is to mark the call as uninlinable. +/// But we don't want to do this if the call is known to be trivial, which is +/// very common. +/// +/// The long-term solution may introduce patterns like: +/// +/// call @llvm.coro.await_suspend(ptr %awaiter, ptr %handle, +/// ptr @awaitSuspendFn) +/// +/// Then it is much easier to perform the safety analysis in the middle end. +/// If it is safe to inline the call to awaitSuspend, we can replace it in the +/// CoroEarly pass. Otherwise we could replace it in the CoroSplit pass. +static void tryMarkAwaitSuspendNoInline(Sema &S, OpaqueValueExpr *Awaiter, + CallExpr *AwaitSuspend) { + // The method here to extract the awaiter decl is not precise. + // This is intentional. Since it is hard to perform the analysis in the + // frontend due to the complexity of C++'s type systems. + // And we prefer to perform such analysis in the middle end since it is + // easier to implement and more powerful. + CXXRecordDecl *AwaiterDecl = + Awaiter->getType().getNonReferenceType()->getAsCXXRecordDecl(); + + if (AwaiterDecl && AwaiterDecl->field_empty()) + return; + + FunctionDecl *FD = AwaitSuspend->getDirectCallee(); + + assert(FD); + + // If the `await_suspend()` function is marked as `always_inline` explicitly, + // we should give the user the right to control the codegen. + if (FD->hasAttr<NoInlineAttr>() || FD->hasAttr<AlwaysInlineAttr>()) + return; + + // This is problematic if the user calls the await_suspend standalone. But on + // the on hand, it is not incorrect semantically since inlining is not part + // of the standard. On the other hand, it is relatively rare to call + // the await_suspend function standalone. + // + // And given we've already had the long-term plan, the current workaround + // looks relatively tolerant. + FD->addAttr( + NoInlineAttr::CreateImplicit(S.getASTContext(), FD->getLocation())); +} + /// Build calls to await_ready, await_suspend, and await_resume for a co_await /// expression. /// The generated AST tries to clean up temporary objects as early as @@ -422,8 +476,8 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, return Result.get(); }; - CallExpr *AwaitReady = - cast_or_null<CallExpr>(BuildSubExpr(ACT::ACT_Ready, "await_ready", None)); + CallExpr *AwaitReady = cast_or_null<CallExpr>( + BuildSubExpr(ACT::ACT_Ready, "await_ready", std::nullopt)); if (!AwaitReady) return Calls; if (!AwaitReady->getType()->isDependentType()) { @@ -459,7 +513,11 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, // type Z. QualType RetType = AwaitSuspend->getCallReturnType(S.Context); - // Experimental support for coroutine_handle returning await_suspend. + // We need to mark await_suspend as noinline temporarily. See the comment + // of tryMarkAwaitSuspendNoInline for details. + tryMarkAwaitSuspendNoInline(S, Operand, AwaitSuspend); + + // Support for coroutine_handle returning await_suspend. if (Expr *TailCallSuspend = maybeTailCall(S, RetType, AwaitSuspend, Loc)) // Note that we don't wrap the expression with ExprWithCleanups here @@ -484,7 +542,7 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, } } - BuildSubExpr(ACT::ACT_Resume, "await_resume", None); + BuildSubExpr(ACT::ACT_Resume, "await_resume", std::nullopt); // Make sure the awaiter object gets a chance to be cleaned up. S.Cleanup.setExprNeedsCleanups(true); @@ -509,10 +567,10 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) { assert(isa<FunctionDecl>(CurContext) && "not in a function scope"); auto *FD = cast<FunctionDecl>(CurContext); bool IsThisDependentType = [&] { - if (auto *MD = dyn_cast_or_null<CXXMethodDecl>(FD)) - return MD->isInstance() && MD->getThisType()->isDependentType(); - else - return false; + if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(FD)) + return MD->isImplicitObjectMemberFunction() && + MD->getThisType()->isDependentType(); + return false; }(); QualType T = FD->getType()->isDependentType() || IsThisDependentType @@ -537,7 +595,7 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) { // Add implicit object parameter. if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - if (MD->isInstance() && !isLambdaCallOperator(MD)) { + if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) { ExprResult ThisExpr = ActOnCXXThis(Loc); if (ThisExpr.isInvalid()) return nullptr; @@ -584,8 +642,12 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) { /*TopLevelOfInitList=*/false, /*TreatUnavailableAsInvalid=*/false); - // Attempt to initialize the promise type with the arguments. - // If that fails, fall back to the promise type's default constructor. + // [dcl.fct.def.coroutine]5.7 + // promise-constructor-arguments is determined as follows: overload + // resolution is performed on a promise constructor call created by + // assembling an argument list q_1 ... q_n . If a viable constructor is + // found ([over.match.viable]), then promise-constructor-arguments is ( q_1 + // , ..., q_n ), otherwise promise-constructor-arguments is empty. if (InitSeq) { ExprResult Result = InitSeq.Perform(*this, Entity, Kind, CtorArgExprs); if (Result.isInvalid()) { @@ -653,6 +715,10 @@ static void checkNoThrow(Sema &S, const Stmt *E, return; } if (ThrowingDecls.empty()) { + // [dcl.fct.def.coroutine]p15 + // The expression co_await promise.final_suspend() shall not be + // potentially-throwing ([except.spec]). + // // First time seeing an error, emit the error message. S.Diag(cast<FunctionDecl>(S.CurContext)->getLocation(), diag::err_coroutine_promise_final_suspend_requires_nothrow); @@ -660,32 +726,32 @@ static void checkNoThrow(Sema &S, const Stmt *E, ThrowingDecls.insert(D); } }; - auto SC = E->getStmtClass(); - if (SC == Expr::CXXConstructExprClass) { - auto const *Ctor = cast<CXXConstructExpr>(E)->getConstructor(); + + if (auto *CE = dyn_cast<CXXConstructExpr>(E)) { + CXXConstructorDecl *Ctor = CE->getConstructor(); checkDeclNoexcept(Ctor); // Check the corresponding destructor of the constructor. - checkDeclNoexcept(Ctor->getParent()->getDestructor(), true); - } else if (SC == Expr::CallExprClass || SC == Expr::CXXMemberCallExprClass || - SC == Expr::CXXOperatorCallExprClass) { - if (!cast<CallExpr>(E)->isTypeDependent()) { - checkDeclNoexcept(cast<CallExpr>(E)->getCalleeDecl()); - auto ReturnType = cast<CallExpr>(E)->getCallReturnType(S.getASTContext()); - // Check the destructor of the call return type, if any. - if (ReturnType.isDestructedType() == - QualType::DestructionKind::DK_cxx_destructor) { - const auto *T = - cast<RecordType>(ReturnType.getCanonicalType().getTypePtr()); - checkDeclNoexcept( - dyn_cast<CXXRecordDecl>(T->getDecl())->getDestructor(), true); - } + checkDeclNoexcept(Ctor->getParent()->getDestructor(), /*IsDtor=*/true); + } else if (auto *CE = dyn_cast<CallExpr>(E)) { + if (CE->isTypeDependent()) + return; + + checkDeclNoexcept(CE->getCalleeDecl()); + QualType ReturnType = CE->getCallReturnType(S.getASTContext()); + // Check the destructor of the call return type, if any. + if (ReturnType.isDestructedType() == + QualType::DestructionKind::DK_cxx_destructor) { + const auto *T = + cast<RecordType>(ReturnType.getCanonicalType().getTypePtr()); + checkDeclNoexcept(cast<CXXRecordDecl>(T->getDecl())->getDestructor(), + /*IsDtor=*/true); + } + } else + for (const auto *Child : E->children()) { + if (!Child) + continue; + checkNoThrow(S, Child, ThrowingDecls); } - } - for (const auto *Child : E->children()) { - if (!Child) - continue; - checkNoThrow(S, Child, ThrowingDecls); - } } bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) { @@ -708,6 +774,9 @@ bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) { bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc, StringRef Keyword) { + // Ignore previous expr evaluation contexts. + EnterExpressionEvaluationContext PotentiallyEvaluated( + *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); if (!checkCoroutineContext(*this, KWLoc, Keyword)) return false; auto *ScopeInfo = getCurFunction(); @@ -724,14 +793,15 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc, SourceLocation Loc = Fn->getLocation(); // Build the initial suspend point auto buildSuspends = [&](StringRef Name) mutable -> StmtResult { - ExprResult Suspend = - buildPromiseCall(*this, ScopeInfo->CoroutinePromise, Loc, Name, None); - if (Suspend.isInvalid()) + ExprResult Operand = buildPromiseCall(*this, ScopeInfo->CoroutinePromise, + Loc, Name, std::nullopt); + if (Operand.isInvalid()) return StmtError(); - Suspend = buildOperatorCoawaitCall(*this, SC, Loc, Suspend.get()); + ExprResult Suspend = + buildOperatorCoawaitCall(*this, SC, Loc, Operand.get()); if (Suspend.isInvalid()) return StmtError(); - Suspend = BuildResolvedCoawaitExpr(Loc, Suspend.get(), + Suspend = BuildResolvedCoawaitExpr(Loc, Operand.get(), Suspend.get(), /*IsImplicit*/ true); Suspend = ActOnFinishFullExpr(Suspend.get(), /*DiscardedValue*/ false); if (Suspend.isInvalid()) { @@ -773,8 +843,8 @@ static bool isWithinCatchScope(Scope *S) { // }(); // } // } - while (S && !(S->getFlags() & Scope::FnScope)) { - if (S->getFlags() & Scope::CatchScope) + while (S && !S->isFunctionScope()) { + if (S->isCatchScope()) return true; S = S->getParent(); } @@ -786,126 +856,158 @@ static bool isWithinCatchScope(Scope *S) { // function-body *outside of a handler* [...] A context within a function // where an await-expression can appear is called a suspension context of the // function." -static void checkSuspensionContext(Sema &S, SourceLocation Loc, +static bool checkSuspensionContext(Sema &S, SourceLocation Loc, StringRef Keyword) { // First emphasis of [expr.await]p2: must be a potentially evaluated context. // That is, 'co_await' and 'co_yield' cannot appear in subexpressions of // \c sizeof. - if (S.isUnevaluatedContext()) + if (S.isUnevaluatedContext()) { S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword; + return false; + } // Second emphasis of [expr.await]p2: must be outside of an exception handler. - if (isWithinCatchScope(S.getCurScope())) + if (isWithinCatchScope(S.getCurScope())) { S.Diag(Loc, diag::err_coroutine_within_handler) << Keyword; + return false; + } + + return true; } ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { + if (!checkSuspensionContext(*this, Loc, "co_await")) + return ExprError(); + if (!ActOnCoroutineBodyStart(S, Loc, "co_await")) { CorrectDelayedTyposInExpr(E); return ExprError(); } - checkSuspensionContext(*this, Loc, "co_await"); - - if (E->getType()->isPlaceholderType()) { + if (E->hasPlaceholderType()) { ExprResult R = CheckPlaceholderExpr(E); if (R.isInvalid()) return ExprError(); E = R.get(); } - ExprResult Lookup = buildOperatorCoawaitLookupExpr(*this, S, Loc); + ExprResult Lookup = BuildOperatorCoawaitLookupExpr(S, Loc); if (Lookup.isInvalid()) return ExprError(); return BuildUnresolvedCoawaitExpr(Loc, E, cast<UnresolvedLookupExpr>(Lookup.get())); } -ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *E, +ExprResult Sema::BuildOperatorCoawaitLookupExpr(Scope *S, SourceLocation Loc) { + DeclarationName OpName = + Context.DeclarationNames.getCXXOperatorName(OO_Coawait); + LookupResult Operators(*this, OpName, SourceLocation(), + Sema::LookupOperatorName); + LookupName(Operators, S); + + assert(!Operators.isAmbiguous() && "Operator lookup cannot be ambiguous"); + const auto &Functions = Operators.asUnresolvedSet(); + bool IsOverloaded = + Functions.size() > 1 || + (Functions.size() == 1 && isa<FunctionTemplateDecl>(*Functions.begin())); + Expr *CoawaitOp = UnresolvedLookupExpr::Create( + Context, /*NamingClass*/ nullptr, NestedNameSpecifierLoc(), + DeclarationNameInfo(OpName, Loc), /*RequiresADL*/ true, IsOverloaded, + Functions.begin(), Functions.end()); + assert(CoawaitOp); + return CoawaitOp; +} + +// Attempts to resolve and build a CoawaitExpr from "raw" inputs, bailing out to +// DependentCoawaitExpr if needed. +ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *Operand, UnresolvedLookupExpr *Lookup) { auto *FSI = checkCoroutineContext(*this, Loc, "co_await"); if (!FSI) return ExprError(); - if (E->getType()->isPlaceholderType()) { - ExprResult R = CheckPlaceholderExpr(E); + if (Operand->hasPlaceholderType()) { + ExprResult R = CheckPlaceholderExpr(Operand); if (R.isInvalid()) return ExprError(); - E = R.get(); + Operand = R.get(); } auto *Promise = FSI->CoroutinePromise; if (Promise->getType()->isDependentType()) { - Expr *Res = - new (Context) DependentCoawaitExpr(Loc, Context.DependentTy, E, Lookup); + Expr *Res = new (Context) + DependentCoawaitExpr(Loc, Context.DependentTy, Operand, Lookup); return Res; } auto *RD = Promise->getType()->getAsCXXRecordDecl(); + auto *Transformed = Operand; if (lookupMember(*this, "await_transform", RD, Loc)) { - ExprResult R = buildPromiseCall(*this, Promise, Loc, "await_transform", E); + ExprResult R = + buildPromiseCall(*this, Promise, Loc, "await_transform", Operand); if (R.isInvalid()) { Diag(Loc, diag::note_coroutine_promise_implicit_await_transform_required_here) - << E->getSourceRange(); + << Operand->getSourceRange(); return ExprError(); } - E = R.get(); + Transformed = R.get(); } - ExprResult Awaitable = buildOperatorCoawaitCall(*this, Loc, E, Lookup); - if (Awaitable.isInvalid()) + ExprResult Awaiter = BuildOperatorCoawaitCall(Loc, Transformed, Lookup); + if (Awaiter.isInvalid()) return ExprError(); - return BuildResolvedCoawaitExpr(Loc, Awaitable.get()); + return BuildResolvedCoawaitExpr(Loc, Operand, Awaiter.get()); } -ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E, - bool IsImplicit) { +ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *Operand, + Expr *Awaiter, bool IsImplicit) { auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await", IsImplicit); if (!Coroutine) return ExprError(); - if (E->getType()->isPlaceholderType()) { - ExprResult R = CheckPlaceholderExpr(E); + if (Awaiter->hasPlaceholderType()) { + ExprResult R = CheckPlaceholderExpr(Awaiter); if (R.isInvalid()) return ExprError(); - E = R.get(); + Awaiter = R.get(); } - if (E->getType()->isDependentType()) { + if (Awaiter->getType()->isDependentType()) { Expr *Res = new (Context) - CoawaitExpr(Loc, Context.DependentTy, E, IsImplicit); + CoawaitExpr(Loc, Context.DependentTy, Operand, Awaiter, IsImplicit); return Res; } // If the expression is a temporary, materialize it as an lvalue so that we // can use it multiple times. - if (E->isPRValue()) - E = CreateMaterializeTemporaryExpr(E->getType(), E, true); + if (Awaiter->isPRValue()) + Awaiter = CreateMaterializeTemporaryExpr(Awaiter->getType(), Awaiter, true); // The location of the `co_await` token cannot be used when constructing // the member call expressions since it's before the location of `Expr`, which // is used as the start of the member call expression. - SourceLocation CallLoc = E->getExprLoc(); + SourceLocation CallLoc = Awaiter->getExprLoc(); // Build the await_ready, await_suspend, await_resume calls. - ReadySuspendResumeResult RSS = buildCoawaitCalls( - *this, Coroutine->CoroutinePromise, CallLoc, E); + ReadySuspendResumeResult RSS = + buildCoawaitCalls(*this, Coroutine->CoroutinePromise, CallLoc, Awaiter); if (RSS.IsInvalid) return ExprError(); - Expr *Res = - new (Context) CoawaitExpr(Loc, E, RSS.Results[0], RSS.Results[1], - RSS.Results[2], RSS.OpaqueValue, IsImplicit); + Expr *Res = new (Context) + CoawaitExpr(Loc, Operand, Awaiter, RSS.Results[0], RSS.Results[1], + RSS.Results[2], RSS.OpaqueValue, IsImplicit); return Res; } ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) { + if (!checkSuspensionContext(*this, Loc, "co_yield")) + return ExprError(); + if (!ActOnCoroutineBodyStart(S, Loc, "co_yield")) { CorrectDelayedTyposInExpr(E); return ExprError(); } - checkSuspensionContext(*this, Loc, "co_yield"); - // Build yield_value call. ExprResult Awaitable = buildPromiseCall( *this, getCurFunction()->CoroutinePromise, Loc, "yield_value", E); @@ -924,14 +1026,16 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { if (!Coroutine) return ExprError(); - if (E->getType()->isPlaceholderType()) { + if (E->hasPlaceholderType()) { ExprResult R = CheckPlaceholderExpr(E); if (R.isInvalid()) return ExprError(); E = R.get(); } + Expr *Operand = E; + if (E->getType()->isDependentType()) { - Expr *Res = new (Context) CoyieldExpr(Loc, Context.DependentTy, E); + Expr *Res = new (Context) CoyieldExpr(Loc, Context.DependentTy, Operand, E); return Res; } @@ -947,7 +1051,7 @@ ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) { return ExprError(); Expr *Res = - new (Context) CoyieldExpr(Loc, E, RSS.Results[0], RSS.Results[1], + new (Context) CoyieldExpr(Loc, Operand, E, RSS.Results[0], RSS.Results[1], RSS.Results[2], RSS.OpaqueValue); return Res; @@ -967,8 +1071,8 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E, if (!FSI) return StmtError(); - if (E && E->getType()->isPlaceholderType() && - !E->getType()->isSpecificPlaceholderType(BuiltinType::Overload)) { + if (E && E->hasPlaceholderType() && + !E->hasPlaceholderType(BuiltinType::Overload)) { ExprResult R = CheckPlaceholderExpr(E); if (R.isInvalid()) return StmtError(); E = R.get(); @@ -981,7 +1085,7 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E, PC = buildPromiseCall(*this, Promise, Loc, "return_value", E); } else { E = MakeFullDiscardedValueExpr(E).get(); - PC = buildPromiseCall(*this, Promise, Loc, "return_void", None); + PC = buildPromiseCall(*this, Promise, Loc, "return_void", std::nullopt); } if (PC.isInvalid()) return StmtError(); @@ -1000,9 +1104,8 @@ static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) { LookupResult Result(S, &S.PP.getIdentifierTable().get("nothrow"), Loc, Sema::LookupOrdinaryName); if (!S.LookupQualifiedName(Result, Std)) { - // FIXME: <experimental/coroutine> should have been included already. - // If we require it to include <new> then this diagnostic is no longer - // needed. + // <coroutine> is not requred to include <new>, so we couldn't omit + // the check here. S.Diag(Loc, diag::err_implicit_coroutine_std_nothrow_type_not_found); return nullptr; } @@ -1023,29 +1126,55 @@ static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) { return DR.get(); } -// Find an appropriate delete for the promise. -static FunctionDecl *findDeleteForPromise(Sema &S, SourceLocation Loc, - QualType PromiseType) { - FunctionDecl *OperatorDelete = nullptr; +static TypeSourceInfo *getTypeSourceInfoForStdAlignValT(Sema &S, + SourceLocation Loc) { + EnumDecl *StdAlignValT = S.getStdAlignValT(); + QualType StdAlignValDecl = S.Context.getTypeDeclType(StdAlignValT); + return S.Context.getTrivialTypeSourceInfo(StdAlignValDecl); +} +// Find an appropriate delete for the promise. +static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseType, + FunctionDecl *&OperatorDelete) { 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; + const bool Overaligned = S.getLangOpts().CoroAlignedAllocation; + + // [dcl.fct.def.coroutine]p12 + // The deallocation function's name is looked up by searching for it in the + // scope of the promise type. If nothing is found, a search is performed in + // the global scope. + if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete, + /*Diagnose*/ true, /*WantSize*/ true, + /*WantAligned*/ Overaligned)) + return false; + // [dcl.fct.def.coroutine]p12 + // If both a usual deallocation function with only a pointer parameter and a + // usual deallocation function with both a pointer parameter and a size + // parameter are found, then the selected deallocation function shall be the + // one with two parameters. Otherwise, the selected deallocation function + // shall be the function with one parameter. if (!OperatorDelete) { // Look for a global declaration. - const bool CanProvideSize = S.isCompleteType(Loc, PromiseType); - const bool Overaligned = false; + // Coroutines can always provide their required size. + const bool CanProvideSize = true; + // Sema::FindUsualDeallocationFunction will try to find the one with two + // parameters first. It will return the deallocation function with one + // parameter if failed. OperatorDelete = S.FindUsualDeallocationFunction(Loc, CanProvideSize, Overaligned, DeleteName); + + if (!OperatorDelete) + return false; } + S.MarkFunctionReferenced(Loc, OperatorDelete); - return OperatorDelete; + return true; } @@ -1067,8 +1196,21 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { return; } - // Coroutines [stmt.return]p1: - // A return statement shall not appear in a coroutine. + // The always_inline attribute doesn't reliably apply to a coroutine, + // because the coroutine will be split into pieces and some pieces + // might be called indirectly, as in a virtual call. Even the ramp + // function cannot be inlined at -O0, due to pipeline ordering + // problems (see https://llvm.org/PR53413). Tell the user about it. + if (FD->hasAttr<AlwaysInlineAttr>()) + Diag(FD->getLocation(), diag::warn_always_inline_coroutine); + + // The design of coroutines means we cannot allow use of VLAs within one, so + // diagnose if we've seen a VLA in the body of this function. + if (Fn->FirstVLALoc.isValid()) + Diag(Fn->FirstVLALoc, diag::err_vla_in_coroutine_unsupported); + + // [stmt.return.coroutine]p1: + // A coroutine shall not enclose a return statement ([stmt.return]). if (Fn->FirstReturnLoc.isValid()) { assert(Fn->FirstCoroutineStmtLoc.isValid() && "first coroutine location not set"); @@ -1076,6 +1218,12 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { Diag(Fn->FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) << Fn->getFirstCoroutineStmtKeyword(); } + + // Coroutines will get splitted into pieces. The GNU address of label + // extension wouldn't be meaningful in coroutines. + for (AddrLabelExpr *ALE : Fn->AddrLabels) + Diag(ALE->getBeginLoc(), diag::err_coro_invalid_addr_of_label); + CoroutineStmtBuilder Builder(*this, *FD, *Fn, Body); if (Builder.isInvalid() || !Builder.buildStatements()) return FD->setInvalidDecl(); @@ -1084,6 +1232,18 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { Body = CoroutineBodyStmt::Create(Context, Builder); } +static CompoundStmt *buildCoroutineBody(Stmt *Body, ASTContext &Context) { + if (auto *CS = dyn_cast<CompoundStmt>(Body)) + return CS; + + // The body of the coroutine may be a try statement if it is in + // 'function-try-block' syntax. Here we wrap it into a compound + // statement for consistency. + assert(isa<CXXTryStmt>(Body) && "Unimaged coroutine body type"); + return CompoundStmt::Create(Context, {Body}, FPOptionsOverride(), + SourceLocation(), SourceLocation()); +} + CoroutineStmtBuilder::CoroutineStmtBuilder(Sema &S, FunctionDecl &FD, sema::FunctionScopeInfo &Fn, Stmt *Body) @@ -1091,7 +1251,7 @@ CoroutineStmtBuilder::CoroutineStmtBuilder(Sema &S, FunctionDecl &FD, IsPromiseDependentType( !Fn.CoroutinePromise || Fn.CoroutinePromise->getType()->isDependentType()) { - this->Body = Body; + this->Body = buildCoroutineBody(Body, S.getASTContext()); for (auto KV : Fn.CoroutineParameterMoves) this->ParamMovesVector.push_back(KV.second); @@ -1169,12 +1329,15 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() { assert(!IsPromiseDependentType && "cannot make statement while the promise type is dependent"); - // [dcl.fct.def.coroutine]/8 - // The unqualified-id get_return_object_on_allocation_failure is looked up in - // the scope of class P by class member access lookup (3.4.5). ... - // If an allocation function returns nullptr, ... the coroutine return value - // is obtained by a call to ... get_return_object_on_allocation_failure(). - + // [dcl.fct.def.coroutine]p10 + // If a search for the name get_return_object_on_allocation_failure in + // the scope of the promise type ([class.member.lookup]) finds any + // declarations, then the result of a call to an allocation function used to + // obtain storage for the coroutine state is assumed to return nullptr if it + // fails to obtain storage, ... If the allocation function returns nullptr, + // ... and the return value is obtained by a call to + // T::get_return_object_on_allocation_failure(), where T is the + // promise type. DeclarationName DN = S.PP.getIdentifierInfo("get_return_object_on_allocation_failure"); LookupResult Found(S, DN, Loc, Sema::LookupMemberName); @@ -1209,47 +1372,13 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() { return true; } -bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { - // Form and check allocation and deallocation calls. - assert(!IsPromiseDependentType && - "cannot make statement while the promise type is dependent"); - QualType PromiseType = Fn.CoroutinePromise->getType(); - - if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type)) - return false; - - const bool RequiresNoThrowAlloc = ReturnStmtOnAllocFailure != nullptr; - - // [dcl.fct.def.coroutine]/7 - // Lookup allocation functions using a parameter list composed of the - // requested size of the coroutine state being allocated, followed by - // the coroutine function's arguments. If a matching allocation function - // exists, use it. Otherwise, use an allocation function that just takes - // the requested size. - - FunctionDecl *OperatorNew = nullptr; - FunctionDecl *OperatorDelete = nullptr; - FunctionDecl *UnusedResult = nullptr; - bool PassAlignment = false; - SmallVector<Expr *, 1> PlacementArgs; - - // [dcl.fct.def.coroutine]/7 - // "The allocation function’s name is looked up in the scope of P. - // [...] If the lookup finds an allocation function in the scope of P, - // overload resolution is performed on a function call created by assembling - // an argument list. The first argument is the amount of space requested, - // and has type std::size_t. The lvalues p1 ... pn are the succeeding - // arguments." - // - // ...where "p1 ... pn" are defined earlier as: - // - // [dcl.fct.def.coroutine]/3 - // "For a coroutine f that is a non-static member function, let P1 denote the - // type of the implicit object parameter (13.3.1) and P2 ... Pn be the types - // of the function parameters; otherwise let P1 ... Pn be the types of the - // function parameters. Let p1 ... pn be lvalues denoting those objects." +// Collect placement arguments for allocation function of coroutine FD. +// Return true if we collect placement arguments succesfully. Return false, +// otherwise. +static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc, + SmallVectorImpl<Expr *> &PlacementArgs) { if (auto *MD = dyn_cast<CXXMethodDecl>(&FD)) { - if (MD->isInstance() && !isLambdaCallOperator(MD)) { + if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) { ExprResult ThisExpr = S.ActOnCXXThis(Loc); if (ThisExpr.isInvalid()) return false; @@ -1259,6 +1388,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { PlacementArgs.push_back(ThisExpr.get()); } } + for (auto *PD : FD.parameters()) { if (PD->getType()->isDependentType()) continue; @@ -1273,34 +1403,154 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { PlacementArgs.push_back(PDRefExpr.get()); } - S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class, - /*DeleteScope*/ Sema::AFS_Both, PromiseType, - /*isArray*/ false, PassAlignment, PlacementArgs, - OperatorNew, UnusedResult, /*Diagnose*/ false); - // [dcl.fct.def.coroutine]/7 - // "If no matching function is found, overload resolution is performed again - // on a function call created by passing just the amount of space required as - // an argument of type std::size_t." - if (!OperatorNew && !PlacementArgs.empty()) { - PlacementArgs.clear(); - S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class, + return true; +} + +bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { + // Form and check allocation and deallocation calls. + assert(!IsPromiseDependentType && + "cannot make statement while the promise type is dependent"); + QualType PromiseType = Fn.CoroutinePromise->getType(); + + if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type)) + return false; + + const bool RequiresNoThrowAlloc = ReturnStmtOnAllocFailure != nullptr; + + // According to [dcl.fct.def.coroutine]p9, Lookup allocation functions using a + // parameter list composed of the requested size of the coroutine state being + // allocated, followed by the coroutine function's arguments. If a matching + // allocation function exists, use it. Otherwise, use an allocation function + // that just takes the requested size. + // + // [dcl.fct.def.coroutine]p9 + // An implementation may need to allocate additional storage for a + // coroutine. + // This storage is known as the coroutine state and is obtained by calling a + // non-array allocation function ([basic.stc.dynamic.allocation]). The + // allocation function's name is looked up by searching for it in the scope of + // the promise type. + // - If any declarations are found, overload resolution is performed on a + // function call created by assembling an argument list. The first argument is + // the amount of space requested, and has type std::size_t. The + // lvalues p1 ... pn are the succeeding arguments. + // + // ...where "p1 ... pn" are defined earlier as: + // + // [dcl.fct.def.coroutine]p3 + // The promise type of a coroutine is `std::coroutine_traits<R, P1, ..., + // Pn>` + // , where R is the return type of the function, and `P1, ..., Pn` are the + // sequence of types of the non-object function parameters, preceded by the + // type of the object parameter ([dcl.fct]) if the coroutine is a non-static + // member function. [dcl.fct.def.coroutine]p4 In the following, p_i is an + // lvalue of type P_i, where p1 denotes the object parameter and p_i+1 denotes + // the i-th non-object function parameter for a non-static member function, + // and p_i denotes the i-th function parameter otherwise. For a non-static + // member function, q_1 is an lvalue that denotes *this; any other q_i is an + // lvalue that denotes the parameter copy corresponding to p_i. + + FunctionDecl *OperatorNew = nullptr; + SmallVector<Expr *, 1> PlacementArgs; + + const bool PromiseContainsNew = [this, &PromiseType]() -> bool { + DeclarationName NewName = + S.getASTContext().DeclarationNames.getCXXOperatorName(OO_New); + LookupResult R(S, NewName, Loc, Sema::LookupOrdinaryName); + + if (PromiseType->isRecordType()) + S.LookupQualifiedName(R, PromiseType->getAsCXXRecordDecl()); + + return !R.empty() && !R.isAmbiguous(); + }(); + + // Helper function to indicate whether the last lookup found the aligned + // allocation function. + bool PassAlignment = S.getLangOpts().CoroAlignedAllocation; + auto LookupAllocationFunction = [&](Sema::AllocationFunctionScope NewScope = + Sema::AFS_Both, + bool WithoutPlacementArgs = false, + bool ForceNonAligned = false) { + // [dcl.fct.def.coroutine]p9 + // The allocation function's name is looked up by searching for it in the + // scope of the promise type. + // - If any declarations are found, ... + // - If no declarations are found in the scope of the promise type, a search + // is performed in the global scope. + if (NewScope == Sema::AFS_Both) + NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global; + + PassAlignment = !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation; + FunctionDecl *UnusedResult = nullptr; + S.FindAllocationFunctions(Loc, SourceRange(), NewScope, /*DeleteScope*/ Sema::AFS_Both, PromiseType, - /*isArray*/ false, PassAlignment, PlacementArgs, + /*isArray*/ false, PassAlignment, + WithoutPlacementArgs ? MultiExprArg{} + : PlacementArgs, OperatorNew, UnusedResult, /*Diagnose*/ false); - } + }; - // [dcl.fct.def.coroutine]/7 - // "The allocation function’s name is looked up in the scope of P. If this - // lookup fails, the allocation function’s name is looked up in the global - // scope." - if (!OperatorNew) { - S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Global, - /*DeleteScope*/ Sema::AFS_Both, PromiseType, - /*isArray*/ false, PassAlignment, PlacementArgs, - OperatorNew, UnusedResult); + // We don't expect to call to global operator new with (size, p0, …, pn). + // So if we choose to lookup the allocation function in global scope, we + // shouldn't lookup placement arguments. + if (PromiseContainsNew && !collectPlacementArgs(S, FD, Loc, PlacementArgs)) + return false; + + LookupAllocationFunction(); + + if (PromiseContainsNew && !PlacementArgs.empty()) { + // [dcl.fct.def.coroutine]p9 + // If no viable function is found ([over.match.viable]), overload + // resolution + // is performed again on a function call created by passing just the amount + // of space required as an argument of type std::size_t. + // + // Proposed Change of [dcl.fct.def.coroutine]p9 in P2014R0: + // Otherwise, overload resolution is performed again on a function call + // created + // by passing the amount of space requested as an argument of type + // std::size_t as the first argument, and the requested alignment as + // an argument of type std:align_val_t as the second argument. + if (!OperatorNew || + (S.getLangOpts().CoroAlignedAllocation && !PassAlignment)) + LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class, + /*WithoutPlacementArgs*/ true); } + // Proposed Change of [dcl.fct.def.coroutine]p12 in P2014R0: + // Otherwise, overload resolution is performed again on a function call + // created + // by passing the amount of space requested as an argument of type + // std::size_t as the first argument, and the lvalues p1 ... pn as the + // succeeding arguments. Otherwise, overload resolution is performed again + // on a function call created by passing just the amount of space required as + // an argument of type std::size_t. + // + // So within the proposed change in P2014RO, the priority order of aligned + // allocation functions wiht promise_type is: + // + // void* operator new( std::size_t, std::align_val_t, placement_args... ); + // void* operator new( std::size_t, std::align_val_t); + // void* operator new( std::size_t, placement_args... ); + // void* operator new( std::size_t); + + // Helper variable to emit warnings. + bool FoundNonAlignedInPromise = false; + if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation) + if (!OperatorNew || !PassAlignment) { + FoundNonAlignedInPromise = OperatorNew; + + LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class, + /*WithoutPlacementArgs*/ false, + /*ForceNonAligned*/ true); + + if (!OperatorNew && !PlacementArgs.empty()) + LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class, + /*WithoutPlacementArgs*/ true, + /*ForceNonAligned*/ true); + } + bool IsGlobalOverload = OperatorNew && !isa<CXXRecordDecl>(OperatorNew->getDeclContext()); // If we didn't find a class-local new declaration and non-throwing new @@ -1312,14 +1562,27 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { return false; PlacementArgs = {StdNoThrow}; OperatorNew = nullptr; - S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Both, - /*DeleteScope*/ Sema::AFS_Both, PromiseType, - /*isArray*/ false, PassAlignment, PlacementArgs, - OperatorNew, UnusedResult); + LookupAllocationFunction(Sema::AFS_Global); + } + + // If we found a non-aligned allocation function in the promise_type, + // it indicates the user forgot to update the allocation function. Let's emit + // a warning here. + if (FoundNonAlignedInPromise) { + S.Diag(OperatorNew->getLocation(), + diag::warn_non_aligned_allocation_function) + << &FD; } - if (!OperatorNew) + if (!OperatorNew) { + if (PromiseContainsNew) + S.Diag(Loc, diag::err_coroutine_unusable_new) << PromiseType << &FD; + else if (RequiresNoThrowAlloc) + S.Diag(Loc, diag::err_coroutine_unfound_nothrow_new) + << &FD << S.getLangOpts().CoroAlignedAllocation; + return false; + } if (RequiresNoThrowAlloc) { const auto *FT = OperatorNew->getType()->castAs<FunctionProtoType>(); @@ -1333,8 +1596,13 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { } } - if ((OperatorDelete = findDeleteForPromise(S, Loc, PromiseType)) == nullptr) + FunctionDecl *OperatorDelete = nullptr; + if (!findDeleteForPromise(S, Loc, PromiseType, OperatorDelete)) { + // FIXME: We should add an error here. According to: + // [dcl.fct.def.coroutine]p12 + // If no usual deallocation function is found, the program is ill-formed. return false; + } Expr *FramePtr = S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_frame, {}); @@ -1342,16 +1610,34 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { Expr *FrameSize = S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_size, {}); - // Make new call. + Expr *FrameAlignment = nullptr; + + if (S.getLangOpts().CoroAlignedAllocation) { + FrameAlignment = + S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_align, {}); + + TypeSourceInfo *AlignValTy = getTypeSourceInfoForStdAlignValT(S, Loc); + if (!AlignValTy) + return false; + + FrameAlignment = S.BuildCXXNamedCast(Loc, tok::kw_static_cast, AlignValTy, + FrameAlignment, SourceRange(Loc, Loc), + SourceRange(Loc, Loc)) + .get(); + } + // Make new call. ExprResult NewRef = S.BuildDeclRefExpr(OperatorNew, OperatorNew->getType(), VK_LValue, Loc); if (NewRef.isInvalid()) return false; SmallVector<Expr *, 2> NewArgs(1, FrameSize); - for (auto Arg : PlacementArgs) - NewArgs.push_back(Arg); + if (S.getLangOpts().CoroAlignedAllocation && PassAlignment) + NewArgs.push_back(FrameAlignment); + + if (OperatorNew->getNumParams() > NewArgs.size()) + llvm::append_range(NewArgs, PlacementArgs); ExprResult NewExpr = S.BuildCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc); @@ -1373,12 +1659,36 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { SmallVector<Expr *, 2> DeleteArgs{CoroFree}; - // Check if we need to pass the size. + // [dcl.fct.def.coroutine]p12 + // The selected deallocation function shall be called with the address of + // the block of storage to be reclaimed as its first argument. If a + // deallocation function with a parameter of type std::size_t is + // used, the size of the block is passed as the corresponding argument. const auto *OpDeleteType = OpDeleteQualType.getTypePtr()->castAs<FunctionProtoType>(); - if (OpDeleteType->getNumParams() > 1) + if (OpDeleteType->getNumParams() > DeleteArgs.size() && + S.getASTContext().hasSameUnqualifiedType( + OpDeleteType->getParamType(DeleteArgs.size()), FrameSize->getType())) DeleteArgs.push_back(FrameSize); + // Proposed Change of [dcl.fct.def.coroutine]p12 in P2014R0: + // If deallocation function lookup finds a usual deallocation function with + // a pointer parameter, size parameter and alignment parameter then this + // will be the selected deallocation function, otherwise if lookup finds a + // usual deallocation function with both a pointer parameter and a size + // parameter, then this will be the selected deallocation function. + // Otherwise, if lookup finds a usual deallocation function with only a + // pointer parameter, then this will be the selected deallocation + // function. + // + // So we are not forced to pass alignment to the deallocation function. + if (S.getLangOpts().CoroAlignedAllocation && + OpDeleteType->getNumParams() > DeleteArgs.size() && + S.getASTContext().hasSameUnqualifiedType( + OpDeleteType->getParamType(DeleteArgs.size()), + FrameAlignment->getType())) + DeleteArgs.push_back(FrameAlignment); + ExprResult DeleteExpr = S.BuildCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc); DeleteExpr = @@ -1396,9 +1706,13 @@ bool CoroutineStmtBuilder::makeOnFallthrough() { assert(!IsPromiseDependentType && "cannot make statement while the promise type is dependent"); - // [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. + // [dcl.fct.def.coroutine]/p6 + // If searches for the names return_void and return_value in the scope of + // the promise type each find any declarations, the program is ill-formed. + // [Note 1: If 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 ([stmt.return.coroutine]). — + // end note] bool HasRVoid, HasRValue; LookupResult LRVoid = lookupMember(S, "return_void", PromiseRecordDecl, Loc, HasRVoid); @@ -1419,18 +1733,20 @@ bool CoroutineStmtBuilder::makeOnFallthrough() { << LRValue.getLookupName(); return false; } else if (!HasRVoid && !HasRValue) { - // FIXME: The PDTS currently specifies this case as UB, not ill-formed. - // However we still diagnose this as an error since until the PDTS is fixed. - S.Diag(FD.getLocation(), - diag::err_coroutine_promise_requires_return_function) - << PromiseRecordDecl; - S.Diag(PromiseRecordDecl->getLocation(), diag::note_defined_here) - << PromiseRecordDecl; - return false; + // We need to set 'Fallthrough'. Otherwise the other analysis part might + // think the coroutine has defined a return_value method. So it might emit + // **false** positive warning. e.g., + // + // promise_without_return_func foo() { + // co_await something(); + // } + // + // Then AnalysisBasedWarning would emit a warning about `foo()` lacking a + // co_return statements, which isn't correct. + Fallthrough = S.ActOnNullStmt(PromiseRecordDecl->getLocation()); + if (Fallthrough.isInvalid()) + return false; } 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 = S.BuildCoreturnStmt(FD.getLocation(), nullptr, /*IsImplicit*/false); Fallthrough = S.ActOnFinishFullStmt(Fallthrough.get()); @@ -1465,8 +1781,8 @@ bool CoroutineStmtBuilder::makeOnException() { if (!S.getLangOpts().CXXExceptions) return true; - ExprResult UnhandledException = buildPromiseCall(S, Fn.CoroutinePromise, Loc, - "unhandled_exception", None); + ExprResult UnhandledException = buildPromiseCall( + S, Fn.CoroutinePromise, Loc, "unhandled_exception", std::nullopt); UnhandledException = S.ActOnFinishFullExpr(UnhandledException.get(), Loc, /*DiscardedValue*/ false); if (UnhandledException.isInvalid()) @@ -1486,10 +1802,11 @@ 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); + // [dcl.fct.def.coroutine]p7 + // The expression promise.get_return_object() is used to initialize the + // returned reference or prvalue result object of a call to a coroutine. + ExprResult ReturnObject = buildPromiseCall(S, Fn.CoroutinePromise, Loc, + "get_return_object", std::nullopt); if (ReturnObject.isInvalid()) return false; @@ -1520,13 +1837,22 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() { assert(!FnRetType->isDependentType() && "get_return_object type must no longer be dependent"); + // The call to get_return_object is sequenced before the call to + // initial_suspend and is invoked at most once, but there are caveats + // regarding on whether the prvalue result object may be initialized + // directly/eager or delayed, depending on the types involved. + // + // More info at https://github.com/cplusplus/papers/issues/1414 + bool GroMatchesRetType = S.getASTContext().hasSameType(GroType, FnRetType); + if (FnRetType->isVoidType()) { ExprResult Res = S.ActOnFinishFullExpr(this->ReturnValue, Loc, /*DiscardedValue*/ false); if (Res.isInvalid()) return false; - this->ResultDecl = Res.get(); + if (!GroMatchesRetType) + this->ResultDecl = Res.get(); return true; } @@ -1539,50 +1865,59 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() { return false; } - 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); - GroDecl->setImplicit(); + StmtResult ReturnStmt; + clang::VarDecl *GroDecl = nullptr; + if (GroMatchesRetType) { + ReturnStmt = S.BuildReturnStmt(Loc, ReturnValue); + } else { + GroDecl = VarDecl::Create( + S.Context, &FD, FD.getLocation(), FD.getLocation(), + &S.PP.getIdentifierTable().get("__coro_gro"), GroType, + S.Context.getTrivialTypeSourceInfo(GroType, Loc), SC_None); + GroDecl->setImplicit(); + + S.CheckVariableDeclarationType(GroDecl); + if (GroDecl->isInvalidDecl()) + return false; - S.CheckVariableDeclarationType(GroDecl); - if (GroDecl->isInvalidDecl()) - return false; + InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); + ExprResult Res = + S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue); + if (Res.isInvalid()) + return false; - InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); - ExprResult Res = - S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue); - if (Res.isInvalid()) - return false; + Res = S.ActOnFinishFullExpr(Res.get(), /*DiscardedValue*/ false); + if (Res.isInvalid()) + return false; - Res = S.ActOnFinishFullExpr(Res.get(), /*DiscardedValue*/ false); - if (Res.isInvalid()) - return false; + S.AddInitializerToDecl(GroDecl, Res.get(), + /*DirectInit=*/false); - S.AddInitializerToDecl(GroDecl, Res.get(), - /*DirectInit=*/false); + S.FinalizeDeclaration(GroDecl); - 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; - // 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(); - this->ResultDecl = GroDeclStmt.get(); + ExprResult declRef = S.BuildDeclRefExpr(GroDecl, GroType, VK_LValue, Loc); + if (declRef.isInvalid()) + return false; - ExprResult declRef = S.BuildDeclRefExpr(GroDecl, GroType, VK_LValue, Loc); - if (declRef.isInvalid()) - return false; + ReturnStmt = S.BuildReturnStmt(Loc, declRef.get()); + } - StmtResult ReturnStmt = S.BuildReturnStmt(Loc, declRef.get()); if (ReturnStmt.isInvalid()) { noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } - if (cast<clang::ReturnStmt>(ReturnStmt.get())->getNRVOCandidate() == GroDecl) + + if (!GroMatchesRetType && + cast<clang::ReturnStmt>(ReturnStmt.get())->getNRVOCandidate() == GroDecl) GroDecl->setNRVOVariable(true); this->ReturnStmt = ReturnStmt.get(); @@ -1625,13 +1960,25 @@ bool Sema::buildCoroutineParameterMoves(SourceLocation Loc) { if (!ScopeInfo->CoroutineParameterMoves.empty()) return false; + // [dcl.fct.def.coroutine]p13 + // When a coroutine is invoked, after initializing its parameters + // ([expr.call]), a copy is created for each coroutine parameter. For a + // parameter of type cv T, the copy is a variable of type cv T with + // automatic storage duration that is direct-initialized from an xvalue of + // type T referring to the parameter. for (auto *PD : FD->parameters()) { if (PD->getType()->isDependentType()) continue; + // Preserve the referenced state for unused parameter diagnostics. + bool DeclReferenced = PD->isReferenced(); + ExprResult PDRefExpr = BuildDeclRefExpr(PD, PD->getType().getNonReferenceType(), ExprValueKind::VK_LValue, Loc); // FIXME: scope? + + PD->setReferenced(DeclReferenced); + if (PDRefExpr.isInvalid()) return false; @@ -1641,8 +1988,10 @@ bool Sema::buildCoroutineParameterMoves(SourceLocation Loc) { CExpr = castForMoving(*this, PDRefExpr.get()); else CExpr = PDRefExpr.get(); - - auto D = buildVarDecl(*this, Loc, PD->getType(), PD->getIdentifier()); + // [dcl.fct.def.coroutine]p13 + // The initialization and destruction of each parameter copy occurs in the + // context of the called coroutine. + auto *D = buildVarDecl(*this, Loc, PD->getType(), PD->getIdentifier()); AddInitializerToDecl(D, CExpr, /*DirectInit=*/true); // Convert decl to a statement. @@ -1664,24 +2013,31 @@ StmtResult Sema::BuildCoroutineBodyStmt(CoroutineBodyStmt::CtorArgs Args) { ClassTemplateDecl *Sema::lookupCoroutineTraits(SourceLocation KwLoc, SourceLocation FuncLoc) { + if (StdCoroutineTraitsCache) + return StdCoroutineTraitsCache; + + IdentifierInfo const &TraitIdent = + PP.getIdentifierTable().get("coroutine_traits"); + + NamespaceDecl *StdSpace = getStdNamespace(); + LookupResult Result(*this, &TraitIdent, FuncLoc, LookupOrdinaryName); + bool Found = StdSpace && LookupQualifiedName(Result, StdSpace); + + if (!Found) { + // The goggles, we found nothing! + Diag(KwLoc, diag::err_implied_coroutine_type_not_found) + << "std::coroutine_traits"; + return nullptr; + } + + // coroutine_traits is required to be a class template. + StdCoroutineTraitsCache = Result.getAsSingle<ClassTemplateDecl>(); if (!StdCoroutineTraitsCache) { - if (auto StdExp = lookupStdExperimentalNamespace()) { - LookupResult Result(*this, - &PP.getIdentifierTable().get("coroutine_traits"), - FuncLoc, LookupOrdinaryName); - if (!LookupQualifiedName(Result, StdExp)) { - Diag(KwLoc, diag::err_implied_coroutine_type_not_found) - << "std::experimental::coroutine_traits"; - return nullptr; - } - if (!(StdCoroutineTraitsCache = - Result.getAsSingle<ClassTemplateDecl>())) { - Result.suppressDiagnostics(); - NamedDecl *Found = *Result.begin(); - Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits); - return nullptr; - } - } + Result.suppressDiagnostics(); + NamedDecl *Found = *Result.begin(); + Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits); + return nullptr; } + return StdCoroutineTraitsCache; } |