@@ -621,6 +621,38 @@ namespace {
621621 contextStack.push_back (dc);
622622 }
623623
624+ // / Searches the applyStack from back to front for the inner-most CallExpr
625+ // / and marks that CallExpr as implicitly async.
626+ // /
627+ // / NOTE: Crashes if no CallExpr was found.
628+ // /
629+ // / For example, for global actor function `curryAdd`, if we have:
630+ // / ((curryAdd 1) 2)
631+ // / then we want to mark the inner-most CallExpr, `(curryAdd 1)`.
632+ // /
633+ // / The same goes for calls to member functions, such as calc.add(1, 2),
634+ // / aka ((add calc) 1 2), looks like this:
635+ // /
636+ // / (call_expr
637+ // / (dot_syntax_call_expr
638+ // / (declref_expr add)
639+ // / (declref_expr calc))
640+ // / (tuple_expr
641+ // / ...))
642+ // /
643+ // / and we reach up to mark the CallExpr.
644+ void markNearestCallAsImplicitlyAsync () {
645+ assert (applyStack.size () > 0 && " not contained within an Apply?" );
646+
647+ const auto End = applyStack.rend ();
648+ for (auto I = applyStack.rbegin (); I != End; ++I)
649+ if (auto call = dyn_cast<CallExpr>(*I)) {
650+ call->setImplicitlyAsync (true );
651+ return ;
652+ }
653+ llvm_unreachable (" expected a CallExpr in applyStack!" );
654+ }
655+
624656 bool shouldWalkCaptureInitializerExpressions () override { return true ; }
625657
626658 bool shouldWalkIntoTapExpression () override { return false ; }
@@ -661,10 +693,9 @@ namespace {
661693 if (auto memberRef = findMemberReference (partialApply->fn )) {
662694 // NOTE: partially-applied thunks are never annotated as
663695 // implicitly async, regardless of whether they are escaping.
664- // So, we do not pass the ApplyExpr along to checkMemberReference.
665696 checkMemberReference (
666697 partialApply->base , memberRef->first , memberRef->second ,
667- partialApply->isEscaping );
698+ partialApply->isEscaping , /* maybeImplicitAsync= */ false );
668699
669700 partialApply->base ->walk (*this );
670701
@@ -683,7 +714,7 @@ namespace {
683714 if (auto memberRef = findMemberReference (fn)) {
684715 checkMemberReference (
685716 call->getArg (), memberRef->first , memberRef->second ,
686- /* isEscapingPartialApply=*/ false , call );
717+ /* isEscapingPartialApply=*/ false , /* maybeImplicitAsync= */ true );
687718
688719 call->getArg ()->walk (*this );
689720
@@ -903,7 +934,7 @@ namespace {
903934 auto concDecl = memberRef->first ;
904935 if (value == concDecl.getDecl () && !apply->implicitlyAsync ()) {
905936 // then this ValueDecl appears as the called value of the ApplyExpr.
906- apply-> setImplicitlyAsync ( true );
937+ markNearestCallAsImplicitlyAsync ( );
907938 return true ;
908939 }
909940 }
@@ -1012,7 +1043,7 @@ namespace {
10121043 bool checkMemberReference (
10131044 Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
10141045 bool isEscapingPartialApply = false ,
1015- ApplyExpr * maybeImplicitAsync = nullptr ) {
1046+ bool maybeImplicitAsync = false ) {
10161047 if (!base || !memberRef)
10171048 return false ;
10181049
@@ -1028,7 +1059,7 @@ namespace {
10281059 if (!selfVar) {
10291060 // actor-isolated non-self calls are implicitly async and thus OK.
10301061 if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
1031- maybeImplicitAsync-> setImplicitlyAsync ( true );
1062+ markNearestCallAsImplicitlyAsync ( );
10321063 return false ;
10331064 }
10341065 ctx.Diags .diagnose (
0 commit comments