@@ -621,6 +621,38 @@ namespace {
621
621
contextStack.push_back (dc);
622
622
}
623
623
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
+
624
656
bool shouldWalkCaptureInitializerExpressions () override { return true ; }
625
657
626
658
bool shouldWalkIntoTapExpression () override { return false ; }
@@ -661,10 +693,9 @@ namespace {
661
693
if (auto memberRef = findMemberReference (partialApply->fn )) {
662
694
// NOTE: partially-applied thunks are never annotated as
663
695
// implicitly async, regardless of whether they are escaping.
664
- // So, we do not pass the ApplyExpr along to checkMemberReference.
665
696
checkMemberReference (
666
697
partialApply->base , memberRef->first , memberRef->second ,
667
- partialApply->isEscaping );
698
+ partialApply->isEscaping , /* maybeImplicitAsync= */ false );
668
699
669
700
partialApply->base ->walk (*this );
670
701
@@ -683,7 +714,7 @@ namespace {
683
714
if (auto memberRef = findMemberReference (fn)) {
684
715
checkMemberReference (
685
716
call->getArg (), memberRef->first , memberRef->second ,
686
- /* isEscapingPartialApply=*/ false , call );
717
+ /* isEscapingPartialApply=*/ false , /* maybeImplicitAsync= */ true );
687
718
688
719
call->getArg ()->walk (*this );
689
720
@@ -903,7 +934,7 @@ namespace {
903
934
auto concDecl = memberRef->first ;
904
935
if (value == concDecl.getDecl () && !apply->implicitlyAsync ()) {
905
936
// then this ValueDecl appears as the called value of the ApplyExpr.
906
- apply-> setImplicitlyAsync ( true );
937
+ markNearestCallAsImplicitlyAsync ( );
907
938
return true ;
908
939
}
909
940
}
@@ -1012,7 +1043,7 @@ namespace {
1012
1043
bool checkMemberReference (
1013
1044
Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
1014
1045
bool isEscapingPartialApply = false ,
1015
- ApplyExpr * maybeImplicitAsync = nullptr ) {
1046
+ bool maybeImplicitAsync = false ) {
1016
1047
if (!base || !memberRef)
1017
1048
return false ;
1018
1049
@@ -1028,7 +1059,7 @@ namespace {
1028
1059
if (!selfVar) {
1029
1060
// actor-isolated non-self calls are implicitly async and thus OK.
1030
1061
if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
1031
- maybeImplicitAsync-> setImplicitlyAsync ( true );
1062
+ markNearestCallAsImplicitlyAsync ( );
1032
1063
return false ;
1033
1064
}
1034
1065
ctx.Diags .diagnose (
0 commit comments