123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908 |
- //===-- SemaCoroutine.cpp - Semantic Analysis for Coroutines --------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file implements semantic analysis for C++ Coroutines.
- //
- // This file contains references to sections of the Coroutines TS, which
- // can be found at http://wg21.link/coroutines.
- //
- //===----------------------------------------------------------------------===//
- #include "CoroutineStmtBuilder.h"
- #include "clang/AST/ASTLambda.h"
- #include "clang/AST/Decl.h"
- #include "clang/AST/ExprCXX.h"
- #include "clang/AST/StmtCXX.h"
- #include "clang/Basic/Builtins.h"
- #include "clang/Lex/Preprocessor.h"
- #include "clang/Sema/Initialization.h"
- #include "clang/Sema/Overload.h"
- #include "clang/Sema/ScopeInfo.h"
- #include "clang/Sema/SemaInternal.h"
- #include "llvm/ADT/SmallSet.h"
- using namespace clang;
- using namespace sema;
- static LookupResult lookupMember(Sema &S, const char *Name, CXXRecordDecl *RD,
- SourceLocation Loc, bool &Res) {
- DeclarationName DN = S.PP.getIdentifierInfo(Name);
- LookupResult LR(S, DN, Loc, Sema::LookupMemberName);
- // Suppress diagnostics when a private member is selected. The same warnings
- // will be produced again when building the call.
- LR.suppressDiagnostics();
- Res = S.LookupQualifiedName(LR, RD);
- return LR;
- }
- static bool lookupMember(Sema &S, const char *Name, CXXRecordDecl *RD,
- SourceLocation Loc) {
- bool Res;
- lookupMember(S, Name, RD, Loc, Res);
- return Res;
- }
- /// Look up the std::coroutine_traits<...>::promise_type for the given
- /// function type.
- static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
- SourceLocation KwLoc) {
- const FunctionProtoType *FnType = FD->getType()->castAs<FunctionProtoType>();
- const SourceLocation FuncLoc = FD->getLocation();
- NamespaceDecl *CoroNamespace = nullptr;
- ClassTemplateDecl *CoroTraits =
- S.lookupCoroutineTraits(KwLoc, FuncLoc, CoroNamespace);
- if (!CoroTraits) {
- return QualType();
- }
- // Form template argument list for coroutine_traits<R, P1, P2, ...> according
- // to [dcl.fct.def.coroutine]3
- TemplateArgumentListInfo Args(KwLoc, KwLoc);
- auto AddArg = [&](QualType T) {
- Args.addArgument(TemplateArgumentLoc(
- TemplateArgument(T), S.Context.getTrivialTypeSourceInfo(T, KwLoc)));
- };
- AddArg(FnType->getReturnType());
- // 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()) {
- // [over.match.funcs]4
- // For non-static member functions, the type of the implicit object
- // parameter is
- // -- "lvalue reference to cv X" for functions declared without a
- // 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();
- T = FnType->getRefQualifier() == RQ_RValue
- ? S.Context.getRValueReferenceType(T)
- : S.Context.getLValueReferenceType(T, /*SpelledAsLValue*/ true);
- AddArg(T);
- }
- }
- for (QualType T : FnType->getParamTypes())
- AddArg(T);
- // Build the template-id.
- QualType CoroTrait =
- S.CheckTemplateIdType(TemplateName(CoroTraits), KwLoc, Args);
- if (CoroTrait.isNull())
- return QualType();
- if (S.RequireCompleteType(KwLoc, CoroTrait,
- diag::err_coroutine_type_missing_specialization))
- return QualType();
- auto *RD = CoroTrait->getAsCXXRecordDecl();
- assert(RD && "specialization of class template is not a class?");
- // Look up the ::promise_type member.
- LookupResult R(S, &S.PP.getIdentifierTable().get("promise_type"), KwLoc,
- Sema::LookupOrdinaryName);
- S.LookupQualifiedName(R, RD);
- auto *Promise = R.getAsSingle<TypeDecl>();
- if (!Promise) {
- S.Diag(FuncLoc,
- diag::err_implied_std_coroutine_traits_promise_type_not_found)
- << RD;
- return QualType();
- }
- // The promise type is required to be a class type.
- QualType PromiseType = S.Context.getTypeDeclType(Promise);
- auto buildElaboratedType = [&]() {
- auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, CoroNamespace);
- NNS = NestedNameSpecifier::Create(S.Context, NNS, false,
- CoroTrait.getTypePtr());
- return S.Context.getElaboratedType(ETK_None, NNS, PromiseType);
- };
- if (!PromiseType->getAsCXXRecordDecl()) {
- S.Diag(FuncLoc,
- diag::err_implied_std_coroutine_traits_promise_type_not_class)
- << buildElaboratedType();
- return QualType();
- }
- if (S.RequireCompleteType(FuncLoc, buildElaboratedType(),
- diag::err_coroutine_promise_type_incomplete))
- return QualType();
- return PromiseType;
- }
- /// Look up the std::coroutine_handle<PromiseType>.
- static QualType lookupCoroutineHandleType(Sema &S, QualType PromiseType,
- SourceLocation Loc) {
- if (PromiseType.isNull())
- return QualType();
- NamespaceDecl *CoroNamespace = S.getCachedCoroNamespace();
- assert(CoroNamespace && "Should already be diagnosed");
- LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_handle"),
- Loc, Sema::LookupOrdinaryName);
- if (!S.LookupQualifiedName(Result, CoroNamespace)) {
- S.Diag(Loc, diag::err_implied_coroutine_type_not_found)
- << "std::coroutine_handle";
- return QualType();
- }
- ClassTemplateDecl *CoroHandle = Result.getAsSingle<ClassTemplateDecl>();
- if (!CoroHandle) {
- Result.suppressDiagnostics();
- // We found something weird. Complain about the first thing we found.
- NamedDecl *Found = *Result.begin();
- S.Diag(Found->getLocation(), diag::err_malformed_std_coroutine_handle);
- return QualType();
- }
- // Form template argument list for coroutine_handle<Promise>.
- TemplateArgumentListInfo Args(Loc, Loc);
- Args.addArgument(TemplateArgumentLoc(
- TemplateArgument(PromiseType),
- S.Context.getTrivialTypeSourceInfo(PromiseType, Loc)));
- // Build the template-id.
- QualType CoroHandleType =
- S.CheckTemplateIdType(TemplateName(CoroHandle), Loc, Args);
- if (CoroHandleType.isNull())
- return QualType();
- if (S.RequireCompleteType(Loc, CoroHandleType,
- diag::err_coroutine_type_missing_specialization))
- return QualType();
- return CoroHandleType;
- }
- static bool isValidCoroutineContext(Sema &S, SourceLocation Loc,
- StringRef Keyword) {
- // [expr.await]p2 dictates that 'co_await' and 'co_yield' must be used within
- // a function body.
- // FIXME: This also covers [expr.await]p2: "An await-expression shall not
- // appear in a default argument." But the diagnostic QoI here could be
- // improved to inform the user that default arguments specifically are not
- // allowed.
- 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;
- return false;
- }
- // An enumeration for mapping the diagnostic type to the correct diagnostic
- // selection index.
- enum InvalidFuncDiag {
- DiagCtor = 0,
- DiagDtor,
- DiagMain,
- DiagConstexpr,
- DiagAutoRet,
- DiagVarargs,
- DiagConsteval,
- };
- 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
- // or the function 'main' are declared as a coroutine.
- auto *MD = dyn_cast<CXXMethodDecl>(FD);
- // [class.ctor]p11: "A constructor shall not be a coroutine."
- if (MD && isa<CXXConstructorDecl>(MD))
- return DiagInvalid(DiagCtor);
- // [class.dtor]p17: "A destructor shall not be a coroutine."
- else if (MD && isa<CXXDestructorDecl>(MD))
- return DiagInvalid(DiagDtor);
- // [basic.start.main]p3: "The function main shall not be a coroutine."
- else if (FD->isMain())
- return DiagInvalid(DiagMain);
- // Emit a diagnostics for each of the following conditions which is not met.
- // [expr.const]p2: "An expression e is a core constant expression unless the
- // evaluation of e [...] would evaluate one of the following expressions:
- // [...] an await-expression [...] a yield-expression."
- if (FD->isConstexpr())
- DiagInvalid(FD->isConsteval() ? DiagConsteval : DiagConstexpr);
- // [dcl.spec.auto]p15: "A function declared with a return type that uses a
- // 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.
- if (FD->isVariadic())
- DiagInvalid(DiagVarargs);
- return !Diagnosed;
- }
- /// Build a call to 'operator co_await' if there is a suitable operator for
- /// the given expression.
- ExprResult Sema::BuildOperatorCoawaitCall(SourceLocation Loc, Expr *E,
- UnresolvedLookupExpr *Lookup) {
- UnresolvedSet<16> Functions;
- Functions.append(Lookup->decls_begin(), Lookup->decls_end());
- return CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E);
- }
- static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, Scope *S,
- SourceLocation Loc, Expr *E) {
- ExprResult R = SemaRef.BuildOperatorCoawaitLookupExpr(S, Loc);
- if (R.isInvalid())
- return ExprError();
- return SemaRef.BuildOperatorCoawaitCall(Loc, E,
- cast<UnresolvedLookupExpr>(R.get()));
- }
- static ExprResult buildCoroutineHandle(Sema &S, QualType PromiseType,
- SourceLocation Loc) {
- QualType CoroHandleType = lookupCoroutineHandleType(S, PromiseType, Loc);
- if (CoroHandleType.isNull())
- return ExprError();
- DeclContext *LookupCtx = S.computeDeclContext(CoroHandleType);
- LookupResult Found(S, &S.PP.getIdentifierTable().get("from_address"), Loc,
- Sema::LookupOrdinaryName);
- if (!S.LookupQualifiedName(Found, LookupCtx)) {
- S.Diag(Loc, diag::err_coroutine_handle_missing_member)
- << "from_address";
- return ExprError();
- }
- Expr *FramePtr =
- S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_frame, {});
- CXXScopeSpec SS;
- ExprResult FromAddr =
- S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false);
- if (FromAddr.isInvalid())
- return ExprError();
- return S.BuildCallExpr(nullptr, FromAddr.get(), Loc, FramePtr, Loc);
- }
- struct ReadySuspendResumeResult {
- enum AwaitCallType { ACT_Ready, ACT_Suspend, ACT_Resume };
- Expr *Results[3];
- OpaqueValueExpr *OpaqueValue;
- bool IsInvalid;
- };
- static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc,
- StringRef Name, MultiExprArg Args) {
- DeclarationNameInfo NameInfo(&S.PP.getIdentifierTable().get(Name), Loc);
- // FIXME: Fix BuildMemberReferenceExpr to take a const CXXScopeSpec&.
- CXXScopeSpec SS;
- ExprResult Result = S.BuildMemberReferenceExpr(
- Base, Base->getType(), Loc, /*IsPtr=*/false, SS,
- SourceLocation(), nullptr, NameInfo, /*TemplateArgs=*/nullptr,
- /*Scope=*/nullptr);
- if (Result.isInvalid())
- return ExprError();
- // We meant exactly what we asked for. No need for typo correction.
- if (auto *TE = dyn_cast<TypoExpr>(Result.get())) {
- S.clearDelayedTypo(TE);
- S.Diag(Loc, diag::err_no_member)
- << NameInfo.getName() << Base->getType()->getAsCXXRecordDecl()
- << Base->getSourceRange();
- return ExprError();
- }
- return S.BuildCallExpr(nullptr, Result.get(), Loc, Args, Loc, 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
- // returning await_suspend that results in a guaranteed tail call to the target
- // coroutine.
- static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E,
- SourceLocation Loc) {
- if (RetType->isReferenceType())
- return nullptr;
- Type const *T = RetType.getTypePtr();
- if (!T->isClassType() && !T->isStructureType())
- return nullptr;
- // FIXME: Add convertability check to coroutine_handle<>. Possibly via
- // EvaluateBinaryTypeTrait(BTT_IsConvertible, ...) which is at the moment
- // a private function in SemaExprCXX.cpp
- ExprResult AddressExpr = buildMemberCall(S, E, Loc, "address", std::nullopt);
- if (AddressExpr.isInvalid())
- return nullptr;
- Expr *JustAddress = AddressExpr.get();
- // Check that the type of AddressExpr is void*
- if (!JustAddress->getType().getTypePtr()->isVoidPointerType())
- S.Diag(cast<CallExpr>(JustAddress)->getCalleeDecl()->getLocation(),
- diag::warn_coroutine_handle_address_invalid_return_type)
- << JustAddress->getType();
- // Clean up temporary objects so that they don't live across suspension points
- // unnecessarily. We choose to clean up before the call to
- // __builtin_coro_resume so that the cleanup code are not inserted in-between
- // the resume call and return instruction, which would interfere with the
- // musttail call contract.
- JustAddress = S.MaybeCreateExprWithCleanups(JustAddress);
- return S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_resume,
- JustAddress);
- }
- /// 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
- /// possible so that they don't live across suspension points if possible.
- /// Having temporary objects living across suspension points unnecessarily can
- /// lead to large frame size, and also lead to memory corruptions if the
- /// coroutine frame is destroyed after coming back from suspension. This is done
- /// by wrapping both the await_ready call and the await_suspend call with
- /// ExprWithCleanups. In the end of this function, we also need to explicitly
- /// set cleanup state so that the CoawaitExpr is also wrapped with an
- /// ExprWithCleanups to clean up the awaiter associated with the 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 valid until we see otherwise.
- // Further operations are responsible for setting IsInalid to true.
- ReadySuspendResumeResult Calls = {{}, Operand, /*IsInvalid=*/false};
- using ACT = ReadySuspendResumeResult::AwaitCallType;
- auto BuildSubExpr = [&](ACT CallType, StringRef Func,
- MultiExprArg Arg) -> Expr * {
- ExprResult Result = buildMemberCall(S, Operand, Loc, Func, Arg);
- if (Result.isInvalid()) {
- Calls.IsInvalid = true;
- return nullptr;
- }
- Calls.Results[CallType] = Result.get();
- return Result.get();
- };
- CallExpr *AwaitReady = cast_or_null<CallExpr>(
- BuildSubExpr(ACT::ACT_Ready, "await_ready", std::nullopt));
- if (!AwaitReady)
- return Calls;
- 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()->getBeginLoc(),
- diag::note_await_ready_no_bool_conversion);
- S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
- << AwaitReady->getDirectCallee() << E->getSourceRange();
- Calls.IsInvalid = true;
- } else
- Calls.Results[ACT::ACT_Ready] = S.MaybeCreateExprWithCleanups(Conv.get());
- }
- ExprResult CoroHandleRes =
- buildCoroutineHandle(S, CoroPromise->getType(), Loc);
- if (CoroHandleRes.isInvalid()) {
- Calls.IsInvalid = true;
- return Calls;
- }
- Expr *CoroHandle = CoroHandleRes.get();
- CallExpr *AwaitSuspend = cast_or_null<CallExpr>(
- BuildSubExpr(ACT::ACT_Suspend, "await_suspend", CoroHandle));
- if (!AwaitSuspend)
- return Calls;
- if (!AwaitSuspend->getType()->isDependentType()) {
- // [expr.await]p3 [...]
- // - await-suspend is the expression e.await_suspend(h), which shall be
- // a prvalue of type void, bool, or std::coroutine_handle<Z> for some
- // type Z.
- QualType RetType = AwaitSuspend->getCallReturnType(S.Context);
- // Experimental 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
- // because that might interfere with tailcall contract (e.g. inserting
- // clean up instructions in-between tailcall and return). Instead
- // ExprWithCleanups is wrapped within maybeTailCall() prior to the resume
- // call.
- Calls.Results[ACT::ACT_Suspend] = TailCallSuspend;
- else {
- // non-class prvalues always have cv-unqualified types
- if (RetType->isReferenceType() ||
- (!RetType->isBooleanType() && !RetType->isVoidType())) {
- 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;
- } else
- Calls.Results[ACT::ACT_Suspend] =
- S.MaybeCreateExprWithCleanups(AwaitSuspend);
- }
- }
- BuildSubExpr(ACT::ACT_Resume, "await_resume", std::nullopt);
- // Make sure the awaiter object gets a chance to be cleaned up.
- S.Cleanup.setExprNeedsCleanups(true);
- return Calls;
- }
- static ExprResult buildPromiseCall(Sema &S, VarDecl *Promise,
- SourceLocation Loc, StringRef Name,
- MultiExprArg Args) {
- // Form a reference to the promise.
- ExprResult PromiseRef = S.BuildDeclRefExpr(
- Promise, Promise->getType().getNonReferenceType(), VK_LValue, Loc);
- if (PromiseRef.isInvalid())
- return ExprError();
- return buildMemberCall(S, PromiseRef.get(), Loc, Name, Args);
- }
- 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;
- }();
- QualType T = FD->getType()->isDependentType() || IsThisDependentType
- ? Context.DependentTy
- : lookupPromiseType(*this, FD, Loc);
- if (T.isNull())
- return nullptr;
- auto *VD = VarDecl::Create(Context, FD, FD->getLocation(), FD->getLocation(),
- &PP.getIdentifierTable().get("__promise"), T,
- Context.getTrivialTypeSourceInfo(T, Loc), SC_None);
- VD->setImplicit();
- CheckVariableDeclarationType(VD);
- if (VD->isInvalidDecl())
- return nullptr;
- auto *ScopeInfo = getCurFunction();
- // Build a list of arguments, based on the coroutine function's arguments,
- // that if present will be passed to the promise type's constructor.
- llvm::SmallVector<Expr *, 4> CtorArgExprs;
- // Add implicit object parameter.
- if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
- if (MD->isInstance() && !isLambdaCallOperator(MD)) {
- ExprResult ThisExpr = ActOnCXXThis(Loc);
- if (ThisExpr.isInvalid())
- return nullptr;
- ThisExpr = CreateBuiltinUnaryOp(Loc, UO_Deref, ThisExpr.get());
- if (ThisExpr.isInvalid())
- return nullptr;
- CtorArgExprs.push_back(ThisExpr.get());
- }
- }
- // Add the coroutine function's parameters.
- auto &Moves = ScopeInfo->CoroutineParameterMoves;
- for (auto *PD : FD->parameters()) {
- if (PD->getType()->isDependentType())
- continue;
- auto RefExpr = ExprEmpty();
- auto Move = Moves.find(PD);
- assert(Move != Moves.end() &&
- "Coroutine function parameter not inserted into move map");
- // If a reference to the function parameter exists in the coroutine
- // frame, use that reference.
- auto *MoveDecl =
- cast<VarDecl>(cast<DeclStmt>(Move->second)->getSingleDecl());
- RefExpr =
- BuildDeclRefExpr(MoveDecl, MoveDecl->getType().getNonReferenceType(),
- ExprValueKind::VK_LValue, FD->getLocation());
- if (RefExpr.isInvalid())
- return nullptr;
- CtorArgExprs.push_back(RefExpr.get());
- }
- // If we have a non-zero number of constructor arguments, try to use them.
- // Otherwise, fall back to the promise type's default constructor.
- if (!CtorArgExprs.empty()) {
- // Create an initialization sequence for the promise type using the
- // constructor arguments, wrapped in a parenthesized list expression.
- Expr *PLE = ParenListExpr::Create(Context, FD->getLocation(),
- CtorArgExprs, FD->getLocation());
- InitializedEntity Entity = InitializedEntity::InitializeVariable(VD);
- InitializationKind Kind = InitializationKind::CreateForInit(
- VD->getLocation(), /*DirectInit=*/true, PLE);
- InitializationSequence InitSeq(*this, Entity, Kind, CtorArgExprs,
- /*TopLevelOfInitList=*/false,
- /*TreatUnavailableAsInvalid=*/false);
- // [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()) {
- VD->setInvalidDecl();
- } else if (Result.get()) {
- VD->setInit(MaybeCreateExprWithCleanups(Result.get()));
- VD->setInitStyle(VarDecl::CallInit);
- CheckCompleteVariableDeclaration(VD);
- }
- } else
- ActOnUninitializedDecl(VD);
- } else
- ActOnUninitializedDecl(VD);
- FD->addDecl(VD);
- return VD;
- }
- /// Check that this is a context in which a coroutine suspension can appear.
- static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc,
- StringRef Keyword,
- bool IsImplicit = false) {
- if (!isValidCoroutineContext(S, Loc, Keyword))
- return nullptr;
- assert(isa<FunctionDecl>(S.CurContext) && "not in a function scope");
- auto *ScopeInfo = S.getCurFunction();
- assert(ScopeInfo && "missing function scope for function");
- if (ScopeInfo->FirstCoroutineStmtLoc.isInvalid() && !IsImplicit)
- ScopeInfo->setFirstCoroutineStmt(Loc, Keyword);
- if (ScopeInfo->CoroutinePromise)
- return ScopeInfo;
- if (!S.buildCoroutineParameterMoves(Loc))
- return nullptr;
- ScopeInfo->CoroutinePromise = S.buildCoroutinePromise(Loc);
- if (!ScopeInfo->CoroutinePromise)
- return nullptr;
- return ScopeInfo;
- }
- /// Recursively check \p E and all its children to see if any call target
- /// (including constructor call) is declared noexcept. Also any value returned
- /// from the call has a noexcept destructor.
- static void checkNoThrow(Sema &S, const Stmt *E,
- llvm::SmallPtrSetImpl<const Decl *> &ThrowingDecls) {
- auto checkDeclNoexcept = [&](const Decl *D, bool IsDtor = false) {
- // In the case of dtor, the call to dtor is implicit and hence we should
- // pass nullptr to canCalleeThrow.
- if (Sema::canCalleeThrow(S, IsDtor ? nullptr : cast<Expr>(E), D)) {
- if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
- // co_await promise.final_suspend() could end up calling
- // __builtin_coro_resume for symmetric transfer if await_suspend()
- // returns a handle. In that case, even __builtin_coro_resume is not
- // declared as noexcept and may throw, it does not throw _into_ the
- // coroutine that just suspended, but rather throws back out from
- // whoever called coroutine_handle::resume(), hence we claim that
- // logically it does not throw.
- if (FD->getBuiltinID() == Builtin::BI__builtin_coro_resume)
- 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);
- }
- ThrowingDecls.insert(D);
- }
- };
- if (auto *CE = dyn_cast<CXXConstructExpr>(E)) {
- CXXConstructorDecl *Ctor = CE->getConstructor();
- checkDeclNoexcept(Ctor);
- // Check the corresponding destructor of the constructor.
- 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);
- }
- }
- bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) {
- llvm::SmallPtrSet<const Decl *, 4> ThrowingDecls;
- // We first collect all declarations that should not throw but not declared
- // with noexcept. We then sort them based on the location before printing.
- // This is to avoid emitting the same note multiple times on the same
- // declaration, and also provide a deterministic order for the messages.
- checkNoThrow(*this, FinalSuspend, ThrowingDecls);
- auto SortedDecls = llvm::SmallVector<const Decl *, 4>{ThrowingDecls.begin(),
- ThrowingDecls.end()};
- sort(SortedDecls, [](const Decl *A, const Decl *B) {
- return A->getEndLoc() < B->getEndLoc();
- });
- for (const auto *D : SortedDecls) {
- Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept);
- }
- return ThrowingDecls.empty();
- }
- bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc,
- StringRef Keyword) {
- if (!checkCoroutineContext(*this, KWLoc, Keyword))
- return false;
- auto *ScopeInfo = getCurFunction();
- assert(ScopeInfo->CoroutinePromise);
- // If we have existing coroutine statements then we have already built
- // the initial and final suspend points.
- if (!ScopeInfo->NeedsCoroutineSuspends)
- return true;
- ScopeInfo->setNeedsCoroutineSuspends(false);
- auto *Fn = cast<FunctionDecl>(CurContext);
- SourceLocation Loc = Fn->getLocation();
- // Build the initial suspend point
- auto buildSuspends = [&](StringRef Name) mutable -> StmtResult {
- ExprResult Operand = buildPromiseCall(*this, ScopeInfo->CoroutinePromise,
- Loc, Name, std::nullopt);
- if (Operand.isInvalid())
- return StmtError();
- ExprResult Suspend =
- buildOperatorCoawaitCall(*this, SC, Loc, Operand.get());
- if (Suspend.isInvalid())
- return StmtError();
- Suspend = BuildResolvedCoawaitExpr(Loc, Operand.get(), Suspend.get(),
- /*IsImplicit*/ true);
- Suspend = ActOnFinishFullExpr(Suspend.get(), /*DiscardedValue*/ false);
- if (Suspend.isInvalid()) {
- Diag(Loc, diag::note_coroutine_promise_suspend_implicitly_required)
- << ((Name == "initial_suspend") ? 0 : 1);
- Diag(KWLoc, diag::note_declared_coroutine_here) << Keyword;
- return StmtError();
- }
- return cast<Stmt>(Suspend.get());
- };
- StmtResult InitSuspend = buildSuspends("initial_suspend");
- if (InitSuspend.isInvalid())
- return true;
- StmtResult FinalSuspend = buildSuspends("final_suspend");
- if (FinalSuspend.isInvalid() || !checkFinalSuspendNoThrow(FinalSuspend.get()))
- return true;
- ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get());
- return true;
- }
- // Recursively walks up the scope hierarchy until either a 'catch' or a function
- // scope is found, whichever comes first.
- static bool isWithinCatchScope(Scope *S) {
- // 'co_await' and 'co_yield' keywords are disallowed within catch blocks, but
- // lambdas that use 'co_await' are allowed. The loop below ends when a
- // function scope is found in order to ensure the following behavior:
- //
- // void foo() { // <- function scope
- // try { //
- // co_await x; // <- 'co_await' is OK within a function scope
- // } catch { // <- catch scope
- // co_await x; // <- 'co_await' is not OK within a catch scope
- // []() { // <- function scope
- // co_await x; // <- 'co_await' is OK within a function scope
- // }();
- // }
- // }
- while (S && !S->isFunctionScope()) {
- if (S->isCatchScope())
- return true;
- S = S->getParent();
- }
- return false;
- }
- // [expr.await]p2, emphasis added: "An await-expression shall appear only in
- // a *potentially evaluated* expression within the compound-statement of a
- // 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 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()) {
- 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())) {
- 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();
- }
- if (E->hasPlaceholderType()) {
- ExprResult R = CheckPlaceholderExpr(E);
- if (R.isInvalid()) return ExprError();
- E = R.get();
- }
- ExprResult Lookup = BuildOperatorCoawaitLookupExpr(S, Loc);
- if (Lookup.isInvalid())
- return ExprError();
- return BuildUnresolvedCoawaitExpr(Loc, E,
- cast<UnresolvedLookupExpr>(Lookup.get()));
- }
- 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 (Operand->hasPlaceholderType()) {
- ExprResult R = CheckPlaceholderExpr(Operand);
- if (R.isInvalid())
- return ExprError();
- Operand = R.get();
- }
- auto *Promise = FSI->CoroutinePromise;
- if (Promise->getType()->isDependentType()) {
- 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", Operand);
- if (R.isInvalid()) {
- Diag(Loc,
- diag::note_coroutine_promise_implicit_await_transform_required_here)
- << Operand->getSourceRange();
- return ExprError();
- }
- Transformed = R.get();
- }
- ExprResult Awaiter = BuildOperatorCoawaitCall(Loc, Transformed, Lookup);
- if (Awaiter.isInvalid())
- return ExprError();
- return BuildResolvedCoawaitExpr(Loc, Operand, Awaiter.get());
- }
- ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *Operand,
- Expr *Awaiter, bool IsImplicit) {
- auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await", IsImplicit);
- if (!Coroutine)
- return ExprError();
- if (Awaiter->hasPlaceholderType()) {
- ExprResult R = CheckPlaceholderExpr(Awaiter);
- if (R.isInvalid()) return ExprError();
- Awaiter = R.get();
- }
- if (Awaiter->getType()->isDependentType()) {
- Expr *Res = new (Context)
- 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 (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 = Awaiter->getExprLoc();
- // Build the await_ready, await_suspend, await_resume calls.
- ReadySuspendResumeResult RSS =
- buildCoawaitCalls(*this, Coroutine->CoroutinePromise, CallLoc, Awaiter);
- if (RSS.IsInvalid)
- return ExprError();
- 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();
- }
- // Build yield_value call.
- ExprResult Awaitable = buildPromiseCall(
- *this, getCurFunction()->CoroutinePromise, Loc, "yield_value", E);
- if (Awaitable.isInvalid())
- return ExprError();
- // Build 'operator co_await' call.
- Awaitable = buildOperatorCoawaitCall(*this, S, Loc, Awaitable.get());
- if (Awaitable.isInvalid())
- return ExprError();
- return BuildCoyieldExpr(Loc, Awaitable.get());
- }
- ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) {
- auto *Coroutine = checkCoroutineContext(*this, Loc, "co_yield");
- if (!Coroutine)
- return ExprError();
- 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, Operand, E);
- 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);
- // Build the await_ready, await_suspend, await_resume calls.
- ReadySuspendResumeResult RSS = buildCoawaitCalls(
- *this, Coroutine->CoroutinePromise, Loc, E);
- if (RSS.IsInvalid)
- return ExprError();
- Expr *Res =
- new (Context) CoyieldExpr(Loc, Operand, E, RSS.Results[0], RSS.Results[1],
- RSS.Results[2], RSS.OpaqueValue);
- return Res;
- }
- StmtResult Sema::ActOnCoreturnStmt(Scope *S, SourceLocation Loc, Expr *E) {
- if (!ActOnCoroutineBodyStart(S, Loc, "co_return")) {
- CorrectDelayedTyposInExpr(E);
- return StmtError();
- }
- return BuildCoreturnStmt(Loc, E);
- }
- StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
- bool IsImplicit) {
- auto *FSI = checkCoroutineContext(*this, Loc, "co_return", IsImplicit);
- if (!FSI)
- return StmtError();
- if (E && E->hasPlaceholderType() &&
- !E->hasPlaceholderType(BuiltinType::Overload)) {
- ExprResult R = CheckPlaceholderExpr(E);
- if (R.isInvalid()) return StmtError();
- E = R.get();
- }
- VarDecl *Promise = FSI->CoroutinePromise;
- ExprResult PC;
- if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
- getNamedReturnInfo(E, SimplerImplicitMoveMode::ForceOn);
- PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
- } else {
- E = MakeFullDiscardedValueExpr(E).get();
- PC = buildPromiseCall(*this, Promise, Loc, "return_void", std::nullopt);
- }
- if (PC.isInvalid())
- return StmtError();
- Expr *PCE = ActOnFinishFullExpr(PC.get(), /*DiscardedValue*/ false).get();
- Stmt *Res = new (Context) CoreturnStmt(Loc, E, PCE, IsImplicit);
- return Res;
- }
- /// Look up the std::nothrow object.
- static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) {
- NamespaceDecl *Std = S.getStdNamespace();
- assert(Std && "Should already be diagnosed");
- LookupResult Result(S, &S.PP.getIdentifierTable().get("nothrow"), Loc,
- Sema::LookupOrdinaryName);
- if (!S.LookupQualifiedName(Result, Std)) {
- // <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;
- }
- auto *VD = Result.getAsSingle<VarDecl>();
- if (!VD) {
- Result.suppressDiagnostics();
- // We found something weird. Complain about the first thing we found.
- NamedDecl *Found = *Result.begin();
- S.Diag(Found->getLocation(), diag::err_malformed_std_nothrow);
- return nullptr;
- }
- ExprResult DR = S.BuildDeclRefExpr(VD, VD->getType(), VK_LValue, Loc);
- if (DR.isInvalid())
- return nullptr;
- return DR.get();
- }
- 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");
- 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.
- // 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 true;
- }
- void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
- FunctionScopeInfo *Fn = getCurFunction();
- assert(Fn && Fn->isCoroutine() && "not a coroutine");
- if (!Body) {
- assert(FD->isInvalidDecl() &&
- "a null body is only allowed for invalid declarations");
- return;
- }
- // We have a function that uses coroutine keywords, but we failed to build
- // the promise type.
- if (!Fn->CoroutinePromise)
- return FD->setInvalidDecl();
- if (isa<CoroutineBodyStmt>(Body)) {
- // Nothing todo. the body is already a transformed coroutine body statement.
- return;
- }
- // 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);
- // [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");
- Diag(Fn->FirstReturnLoc, diag::err_return_in_coroutine);
- 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();
- // Build body for the coroutine wrapper statement.
- Body = CoroutineBodyStmt::Create(Context, Builder);
- }
- CoroutineStmtBuilder::CoroutineStmtBuilder(Sema &S, FunctionDecl &FD,
- sema::FunctionScopeInfo &Fn,
- Stmt *Body)
- : S(S), FD(FD), Fn(Fn), Loc(FD.getLocation()),
- IsPromiseDependentType(
- !Fn.CoroutinePromise ||
- Fn.CoroutinePromise->getType()->isDependentType()) {
- this->Body = Body;
- for (auto KV : Fn.CoroutineParameterMoves)
- this->ParamMovesVector.push_back(KV.second);
- this->ParamMoves = this->ParamMovesVector;
- if (!IsPromiseDependentType) {
- PromiseRecordDecl = Fn.CoroutinePromise->getType()->getAsCXXRecordDecl();
- assert(PromiseRecordDecl && "Type should have already been checked");
- }
- this->IsValid = makePromiseStmt() && makeInitialAndFinalSuspend();
- }
- bool CoroutineStmtBuilder::buildStatements() {
- assert(this->IsValid && "coroutine already invalid");
- this->IsValid = makeReturnObject();
- if (this->IsValid && !IsPromiseDependentType)
- buildDependentStatements();
- return this->IsValid;
- }
- bool CoroutineStmtBuilder::buildDependentStatements() {
- assert(this->IsValid && "coroutine already invalid");
- assert(!this->IsPromiseDependentType &&
- "coroutine cannot have a dependent promise type");
- this->IsValid = makeOnException() && makeOnFallthrough() &&
- makeGroDeclAndReturnStmt() && makeReturnOnAllocFailure() &&
- makeNewAndDeleteExpr();
- return this->IsValid;
- }
- bool CoroutineStmtBuilder::makePromiseStmt() {
- // Form a declaration statement for the promise declaration, so that AST
- // visitors can more easily find it.
- StmtResult PromiseStmt =
- S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(Fn.CoroutinePromise), Loc, Loc);
- if (PromiseStmt.isInvalid())
- return false;
- this->Promise = PromiseStmt.get();
- return true;
- }
- bool CoroutineStmtBuilder::makeInitialAndFinalSuspend() {
- if (Fn.hasInvalidCoroutineSuspends())
- return false;
- this->InitialSuspend = cast<Expr>(Fn.CoroutineSuspends.first);
- this->FinalSuspend = cast<Expr>(Fn.CoroutineSuspends.second);
- return true;
- }
- static bool diagReturnOnAllocFailure(Sema &S, Expr *E,
- CXXRecordDecl *PromiseRecordDecl,
- FunctionScopeInfo &Fn) {
- auto Loc = E->getExprLoc();
- if (auto *DeclRef = dyn_cast_or_null<DeclRefExpr>(E)) {
- auto *Decl = DeclRef->getDecl();
- if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(Decl)) {
- if (Method->isStatic())
- return true;
- else
- Loc = Decl->getLocation();
- }
- }
- S.Diag(
- Loc,
- diag::err_coroutine_promise_get_return_object_on_allocation_failure)
- << PromiseRecordDecl;
- S.Diag(Fn.FirstCoroutineStmtLoc, diag::note_declared_coroutine_here)
- << Fn.getFirstCoroutineStmtKeyword();
- return false;
- }
- bool CoroutineStmtBuilder::makeReturnOnAllocFailure() {
- assert(!IsPromiseDependentType &&
- "cannot make statement while the promise type is dependent");
- // [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);
- if (!S.LookupQualifiedName(Found, PromiseRecordDecl))
- return true;
- CXXScopeSpec SS;
- ExprResult DeclNameExpr =
- S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false);
- if (DeclNameExpr.isInvalid())
- return false;
- if (!diagReturnOnAllocFailure(S, DeclNameExpr.get(), PromiseRecordDecl, Fn))
- return false;
- ExprResult ReturnObjectOnAllocationFailure =
- S.BuildCallExpr(nullptr, DeclNameExpr.get(), Loc, {}, Loc);
- if (ReturnObjectOnAllocationFailure.isInvalid())
- return false;
- StmtResult ReturnStmt =
- S.BuildReturnStmt(Loc, ReturnObjectOnAllocationFailure.get());
- if (ReturnStmt.isInvalid()) {
- S.Diag(Found.getFoundDecl()->getLocation(), diag::note_member_declared_here)
- << DN;
- S.Diag(Fn.FirstCoroutineStmtLoc, diag::note_declared_coroutine_here)
- << Fn.getFirstCoroutineStmtKeyword();
- return false;
- }
- this->ReturnStmtOnAllocFailure = ReturnStmt.get();
- return true;
- }
- // 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)) {
- ExprResult ThisExpr = S.ActOnCXXThis(Loc);
- if (ThisExpr.isInvalid())
- return false;
- ThisExpr = S.CreateBuiltinUnaryOp(Loc, UO_Deref, ThisExpr.get());
- if (ThisExpr.isInvalid())
- return false;
- PlacementArgs.push_back(ThisExpr.get());
- }
- }
- for (auto *PD : FD.parameters()) {
- if (PD->getType()->isDependentType())
- continue;
- // Build a reference to the parameter.
- auto PDLoc = PD->getLocation();
- ExprResult PDRefExpr =
- S.BuildDeclRefExpr(PD, PD->getOriginalType().getNonReferenceType(),
- ExprValueKind::VK_LValue, PDLoc);
- if (PDRefExpr.isInvalid())
- return false;
- PlacementArgs.push_back(PDRefExpr.get());
- }
- 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,
- WithoutPlacementArgs ? MultiExprArg{}
- : PlacementArgs,
- OperatorNew, UnusedResult, /*Diagnose*/ false);
- };
- // 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
- // was is required then we need to lookup the non-throwing global operator
- // instead.
- if (RequiresNoThrowAlloc && (!OperatorNew || IsGlobalOverload)) {
- auto *StdNoThrow = buildStdNoThrowDeclRef(S, Loc);
- if (!StdNoThrow)
- return false;
- PlacementArgs = {StdNoThrow};
- OperatorNew = nullptr;
- 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 (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>();
- if (!FT->isNothrow(/*ResultIfDependent*/ false)) {
- S.Diag(OperatorNew->getLocation(),
- diag::err_coroutine_promise_new_requires_nothrow)
- << OperatorNew;
- S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
- << OperatorNew;
- return false;
- }
- }
- 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, {});
- Expr *FrameSize =
- S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_size, {});
- 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);
- 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);
- NewExpr = S.ActOnFinishFullExpr(NewExpr.get(), /*DiscardedValue*/ false);
- if (NewExpr.isInvalid())
- return false;
- // Make delete call.
- QualType OpDeleteQualType = OperatorDelete->getType();
- ExprResult DeleteRef =
- S.BuildDeclRefExpr(OperatorDelete, OpDeleteQualType, VK_LValue, Loc);
- if (DeleteRef.isInvalid())
- return false;
- Expr *CoroFree =
- S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_free, {FramePtr});
- SmallVector<Expr *, 2> DeleteArgs{CoroFree};
- // [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() > DeleteArgs.size() &&
- S.getASTContext().hasSameType(
- 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().hasSameType(
- OpDeleteType->getParamType(DeleteArgs.size()),
- FrameAlignment->getType()))
- DeleteArgs.push_back(FrameAlignment);
- ExprResult DeleteExpr =
- S.BuildCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
- DeleteExpr =
- S.ActOnFinishFullExpr(DeleteExpr.get(), /*DiscardedValue*/ false);
- if (DeleteExpr.isInvalid())
- return false;
- this->Allocate = NewExpr.get();
- this->Deallocate = DeleteExpr.get();
- return true;
- }
- bool CoroutineStmtBuilder::makeOnFallthrough() {
- assert(!IsPromiseDependentType &&
- "cannot make statement while the promise type is dependent");
- // [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);
- LookupResult LRValue =
- lookupMember(S, "return_value", PromiseRecordDecl, Loc, HasRValue);
- StmtResult Fallthrough;
- if (HasRVoid && HasRValue) {
- // FIXME Improve this diagnostic
- S.Diag(FD.getLocation(),
- diag::err_coroutine_promise_incompatible_return_functions)
- << PromiseRecordDecl;
- S.Diag(LRVoid.getRepresentativeDecl()->getLocation(),
- diag::note_member_first_declared_here)
- << LRVoid.getLookupName();
- S.Diag(LRValue.getRepresentativeDecl()->getLocation(),
- diag::note_member_first_declared_here)
- << LRValue.getLookupName();
- return false;
- } else if (!HasRVoid && !HasRValue) {
- // 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) {
- Fallthrough = S.BuildCoreturnStmt(FD.getLocation(), nullptr,
- /*IsImplicit*/false);
- Fallthrough = S.ActOnFinishFullStmt(Fallthrough.get());
- if (Fallthrough.isInvalid())
- return false;
- }
- this->OnFallthrough = Fallthrough.get();
- return true;
- }
- bool CoroutineStmtBuilder::makeOnException() {
- // Try to form 'p.unhandled_exception();'
- assert(!IsPromiseDependentType &&
- "cannot make statement while the promise type is dependent");
- const bool RequireUnhandledException = S.getLangOpts().CXXExceptions;
- if (!lookupMember(S, "unhandled_exception", PromiseRecordDecl, Loc)) {
- auto DiagID =
- RequireUnhandledException
- ? diag::err_coroutine_promise_unhandled_exception_required
- : diag::
- warn_coroutine_promise_unhandled_exception_required_with_exceptions;
- S.Diag(Loc, DiagID) << PromiseRecordDecl;
- S.Diag(PromiseRecordDecl->getLocation(), diag::note_defined_here)
- << PromiseRecordDecl;
- return !RequireUnhandledException;
- }
- // If exceptions are disabled, don't try to build OnException.
- if (!S.getLangOpts().CXXExceptions)
- return true;
- ExprResult UnhandledException = buildPromiseCall(
- S, Fn.CoroutinePromise, Loc, "unhandled_exception", std::nullopt);
- UnhandledException = S.ActOnFinishFullExpr(UnhandledException.get(), Loc,
- /*DiscardedValue*/ false);
- if (UnhandledException.isInvalid())
- return false;
- // Since the body of the coroutine will be wrapped in try-catch, it will
- // be incompatible with SEH __try if present in a function.
- if (!S.getLangOpts().Borland && Fn.FirstSEHTryLoc.isValid()) {
- S.Diag(Fn.FirstSEHTryLoc, diag::err_seh_in_a_coroutine_with_cxx_exceptions);
- S.Diag(Fn.FirstCoroutineStmtLoc, diag::note_declared_coroutine_here)
- << Fn.getFirstCoroutineStmtKeyword();
- return false;
- }
- this->OnException = UnhandledException.get();
- return true;
- }
- bool CoroutineStmtBuilder::makeReturnObject() {
- // [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;
- 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_member_declared_here)
- << MethodDecl;
- }
- 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, /*DiscardedValue*/ false);
- if (Res.isInvalid())
- return false;
- return true;
- }
- if (GroType->isVoidType()) {
- // Trigger a nice error message.
- InitializedEntity Entity =
- InitializedEntity::InitializeResult(Loc, FnRetType);
- S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue);
- noteMemberDeclaredHere(S, ReturnValue, Fn);
- return false;
- }
- StmtResult ReturnStmt = S.BuildReturnStmt(Loc, ReturnValue);
- if (ReturnStmt.isInvalid()) {
- noteMemberDeclaredHere(S, ReturnValue, Fn);
- return false;
- }
- this->ReturnStmt = ReturnStmt.get();
- return true;
- }
- // Create a static_cast\<T&&>(expr).
- static Expr *castForMoving(Sema &S, Expr *E, QualType T = QualType()) {
- if (T.isNull())
- T = E->getType();
- QualType TargetType = S.BuildReferenceType(
- T, /*SpelledAsLValue*/ false, SourceLocation(), DeclarationName());
- SourceLocation ExprLoc = E->getBeginLoc();
- TypeSourceInfo *TargetLoc =
- S.Context.getTrivialTypeSourceInfo(TargetType, ExprLoc);
- return S
- .BuildCXXNamedCast(ExprLoc, tok::kw_static_cast, TargetLoc, E,
- SourceRange(ExprLoc, ExprLoc), E->getSourceRange())
- .get();
- }
- /// Build a variable declaration for move parameter.
- static VarDecl *buildVarDecl(Sema &S, SourceLocation Loc, QualType Type,
- IdentifierInfo *II) {
- TypeSourceInfo *TInfo = S.Context.getTrivialTypeSourceInfo(Type, Loc);
- VarDecl *Decl = VarDecl::Create(S.Context, S.CurContext, Loc, Loc, II, Type,
- TInfo, SC_None);
- Decl->setImplicit();
- return Decl;
- }
- // Build statements that move coroutine function parameters to the coroutine
- // frame, and store them on the function scope info.
- bool Sema::buildCoroutineParameterMoves(SourceLocation Loc) {
- assert(isa<FunctionDecl>(CurContext) && "not in a function scope");
- auto *FD = cast<FunctionDecl>(CurContext);
- auto *ScopeInfo = getCurFunction();
- 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;
- ExprResult PDRefExpr =
- BuildDeclRefExpr(PD, PD->getType().getNonReferenceType(),
- ExprValueKind::VK_LValue, Loc); // FIXME: scope?
- if (PDRefExpr.isInvalid())
- return false;
- Expr *CExpr = nullptr;
- if (PD->getType()->getAsCXXRecordDecl() ||
- PD->getType()->isRValueReferenceType())
- CExpr = castForMoving(*this, PDRefExpr.get());
- else
- CExpr = PDRefExpr.get();
- // [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.
- StmtResult Stmt = ActOnDeclStmt(ConvertDeclToDeclGroup(D), Loc, Loc);
- if (Stmt.isInvalid())
- return false;
- ScopeInfo->CoroutineParameterMoves.insert(std::make_pair(PD, Stmt.get()));
- }
- return true;
- }
- StmtResult Sema::BuildCoroutineBodyStmt(CoroutineBodyStmt::CtorArgs Args) {
- CoroutineBodyStmt *Res = CoroutineBodyStmt::Create(Context, Args);
- if (!Res)
- return StmtError();
- return Res;
- }
- ClassTemplateDecl *Sema::lookupCoroutineTraits(SourceLocation KwLoc,
- SourceLocation FuncLoc,
- NamespaceDecl *&Namespace) {
- if (!StdCoroutineTraitsCache) {
- // Because coroutines moved from std::experimental in the TS to std in
- // C++20, we look in both places to give users time to transition their
- // TS-specific code to C++20. Diagnostics are given when the TS usage is
- // discovered.
- // TODO: Become stricter when <experimental/coroutine> is removed.
- IdentifierInfo const &TraitIdent =
- PP.getIdentifierTable().get("coroutine_traits");
- NamespaceDecl *StdSpace = getStdNamespace();
- LookupResult ResStd(*this, &TraitIdent, FuncLoc, LookupOrdinaryName);
- bool InStd = StdSpace && LookupQualifiedName(ResStd, StdSpace);
- NamespaceDecl *ExpSpace = lookupStdExperimentalNamespace();
- LookupResult ResExp(*this, &TraitIdent, FuncLoc, LookupOrdinaryName);
- bool InExp = ExpSpace && LookupQualifiedName(ResExp, ExpSpace);
- if (!InStd && !InExp) {
- // The goggles, they found nothing!
- Diag(KwLoc, diag::err_implied_coroutine_type_not_found)
- << "std::coroutine_traits";
- return nullptr;
- }
- // Prefer ::std to std::experimental.
- LookupResult &Result = InStd ? ResStd : ResExp;
- CoroTraitsNamespaceCache = InStd ? StdSpace : ExpSpace;
- // coroutine_traits is required to be a class template.
- StdCoroutineTraitsCache = Result.getAsSingle<ClassTemplateDecl>();
- if (!StdCoroutineTraitsCache) {
- Result.suppressDiagnostics();
- NamedDecl *Found = *Result.begin();
- Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits);
- return nullptr;
- }
- if (InExp) {
- // Found in std::experimental
- Diag(KwLoc, diag::warn_deprecated_coroutine_namespace)
- << "coroutine_traits";
- ResExp.suppressDiagnostics();
- NamedDecl *Found = *ResExp.begin();
- Diag(Found->getLocation(), diag::note_entity_declared_at) << Found;
- if (InStd &&
- StdCoroutineTraitsCache != ResExp.getAsSingle<ClassTemplateDecl>()) {
- // Also found something different in std
- Diag(KwLoc,
- diag::err_mixed_use_std_and_experimental_namespace_for_coroutine);
- Diag(StdCoroutineTraitsCache->getLocation(),
- diag::note_entity_declared_at)
- << StdCoroutineTraitsCache;
- return nullptr;
- }
- }
- }
- Namespace = CoroTraitsNamespaceCache;
- return StdCoroutineTraitsCache;
- }
|