@@ -584,6 +584,7 @@ namespace {
584
584
class ActorIsolationChecker : public ASTWalker {
585
585
ASTContext &ctx;
586
586
SmallVector<const DeclContext *, 4 > contextStack;
587
+ SmallVector<ApplyExpr*, 4 > applyStack;
587
588
588
589
const DeclContext *getDeclContext () const {
589
590
return contextStack.back ();
@@ -625,27 +626,45 @@ namespace {
625
626
}
626
627
627
628
if (auto apply = dyn_cast<ApplyExpr>(expr)) {
629
+ applyStack.push_back (apply); // record this encounter
630
+
628
631
// If this is a call to a partial apply thunk, decompose it to check it
629
632
// like based on the original written syntax, e.g., "self.method".
630
633
if (auto partialApply = decomposePartialApplyThunk (
631
634
apply, Parent.getAsExpr ())) {
632
635
if (auto memberRef = findMemberReference (partialApply->fn )) {
636
+ // NOTE: partially-applied thunks are never annotated as
637
+ // implicitly async, regardless of whether they are escaping.
638
+ // So, we do not pass the ApplyExpr along to checkMemberReference.
633
639
checkMemberReference (
634
640
partialApply->base , memberRef->first , memberRef->second ,
635
641
partialApply->isEscaping );
636
642
637
643
partialApply->base ->walk (*this );
644
+
645
+ // manual clean-up since normal traversal is skipped
646
+ assert (applyStack.back () == apply);
647
+ applyStack.pop_back ();
648
+
638
649
return { false , expr };
639
650
}
640
651
}
641
652
}
642
653
654
+ // NOTE: SelfApplyExpr is a subtype of ApplyExpr
643
655
if (auto call = dyn_cast<SelfApplyExpr>(expr)) {
644
656
Expr *fn = call->getFn ()->getValueProvidingExpr ();
645
657
if (auto memberRef = findMemberReference (fn)) {
646
658
checkMemberReference (
647
- call->getArg (), memberRef->first , memberRef->second );
659
+ call->getArg (), memberRef->first , memberRef->second ,
660
+ /* isEscapingPartialApply=*/ false , call);
661
+
648
662
call->getArg ()->walk (*this );
663
+
664
+ // manual clean-up since normal traversal is skipped
665
+ assert (applyStack.back () == dyn_cast<ApplyExpr>(expr));
666
+ applyStack.pop_back ();
667
+
649
668
return { false , expr };
650
669
}
651
670
}
@@ -659,6 +678,11 @@ namespace {
659
678
contextStack.pop_back ();
660
679
}
661
680
681
+ if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
682
+ assert (applyStack.back () == apply);
683
+ applyStack.pop_back ();
684
+ }
685
+
662
686
return expr;
663
687
}
664
688
@@ -835,9 +859,39 @@ namespace {
835
859
// / Check a reference to an entity within a global actor.
836
860
bool checkGlobalActorReference (
837
861
ValueDecl *value, SourceLoc loc, Type globalActor) {
862
+
863
+ // / Returns true if this global actor reference is the callee of an Apply.
864
+ // / NOTE: This check mutates the identified ApplyExpr if it returns true!
865
+ auto inspectForImplicitlyAsync = [&] () -> bool {
866
+
867
+ // Is this global actor reference outside of an ApplyExpr?
868
+ if (applyStack.size () == 0 )
869
+ return false ;
870
+
871
+ // Check our applyStack metadata from the traversal.
872
+ // Our goal is to identify whether this global actor reference appears
873
+ // as the called value of the enclosing ApplyExpr. We cannot simply
874
+ // inspect Parent here because of expressions like (callee)()
875
+ ApplyExpr *apply = applyStack.back ();
876
+ Expr *fn = apply->getFn ()->getValueProvidingExpr ();
877
+ if (auto memberRef = findMemberReference (fn)) {
878
+ auto concDecl = memberRef->first ;
879
+ if (value == concDecl.getDecl () && !apply->implicitlyAsync ()) {
880
+ // then this ValueDecl appears as the called value of the ApplyExpr.
881
+ apply->setImplicitlyAsync (true );
882
+ return true ;
883
+ }
884
+ }
885
+
886
+ return false ;
887
+ };
888
+
838
889
switch (auto contextIsolation =
839
890
getInnermostIsolatedContext (getDeclContext ())) {
840
891
case ActorIsolation::ActorInstance:
892
+ if (inspectForImplicitlyAsync ())
893
+ return false ;
894
+
841
895
ctx.Diags .diagnose (
842
896
loc, diag::global_actor_from_instance_actor_context,
843
897
value->getDescriptiveKind (), value->getName (), globalActor,
@@ -850,6 +904,12 @@ namespace {
850
904
if (contextIsolation.getGlobalActor ()->isEqual (globalActor))
851
905
return false ;
852
906
907
+ // Otherwise, we check if this decl reference is the callee of the
908
+ // enclosing Apply, making it OK as an implicitly async call.
909
+ if (inspectForImplicitlyAsync ())
910
+ return false ;
911
+
912
+ // Otherwise, this is a problematic global actor decl reference.
853
913
ctx.Diags .diagnose (
854
914
loc, diag::global_actor_from_other_global_actor_context,
855
915
value->getDescriptiveKind (), value->getName (), globalActor,
@@ -860,14 +920,18 @@ namespace {
860
920
861
921
case ActorIsolation::Independent:
862
922
case ActorIsolation::IndependentUnsafe:
923
+ if (inspectForImplicitlyAsync ())
924
+ return false ;
925
+
863
926
ctx.Diags .diagnose (
864
927
loc, diag::global_actor_from_independent_context,
865
928
value->getDescriptiveKind (), value->getName (), globalActor);
866
929
noteIsolatedActorMember (value);
867
930
return true ;
868
931
869
932
case ActorIsolation::Unspecified:
870
- // Okay.
933
+ // Okay no matter what, but still must inspect for implicitly async.
934
+ inspectForImplicitlyAsync ();
871
935
return false ;
872
936
}
873
937
llvm_unreachable (" unhandled actor isolation kind!" );
@@ -918,9 +982,12 @@ namespace {
918
982
}
919
983
920
984
// / Check a reference with the given base expression to the given member.
985
+ // / Returns true iff the member reference refers to actor-isolated state
986
+ // / in an invalid or unsafe way such that a diagnostic was emitted.
921
987
bool checkMemberReference (
922
988
Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
923
- bool isEscapingPartialApply = false ) {
989
+ bool isEscapingPartialApply = false ,
990
+ ApplyExpr *maybeImplicitAsync = nullptr ) {
924
991
if (!base || !memberRef)
925
992
return false ;
926
993
@@ -934,6 +1001,11 @@ namespace {
934
1001
// Must reference actor-isolated state on 'self'.
935
1002
auto selfVar = getSelfReference (base);
936
1003
if (!selfVar) {
1004
+ // actor-isolated non-self calls are implicitly async and thus OK.
1005
+ if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
1006
+ maybeImplicitAsync->setImplicitlyAsync (true );
1007
+ return false ;
1008
+ }
937
1009
ctx.Diags .diagnose (
938
1010
memberLoc, diag::actor_isolated_non_self_reference,
939
1011
member->getDescriptiveKind (),
0 commit comments