@@ -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
@@ -696,10 +720,9 @@ namespace {
696
720
// FIXME: Make this diagnostic more sensitive to the isolation context
697
721
// of the declaration.
698
722
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
699
- // FIXME: We'd like to insert 'async' at the appropriate place, but
700
- // FuncDecl/AbstractFunctionDecl doesn't have the right source-location
701
- // information to do so.
702
- func->diagnose (diag::actor_isolated_method);
723
+ func->diagnose (diag::actor_isolated_sync_func,
724
+ decl->getDescriptiveKind (),
725
+ decl->getName ());
703
726
} else if (isa<VarDecl>(decl)) {
704
727
decl->diagnose (diag::actor_mutable_state);
705
728
} else {
@@ -835,9 +858,39 @@ namespace {
835
858
// / Check a reference to an entity within a global actor.
836
859
bool checkGlobalActorReference (
837
860
ValueDecl *value, SourceLoc loc, Type globalActor) {
861
+
862
+ // / Returns true if this global actor reference is the callee of an Apply.
863
+ // / NOTE: This check mutates the identified ApplyExpr if it returns true!
864
+ auto inspectForImplicitlyAsync = [&] () -> bool {
865
+
866
+ // Is this global actor reference outside of an ApplyExpr?
867
+ if (applyStack.size () == 0 )
868
+ return false ;
869
+
870
+ // Check our applyStack metadata from the traversal.
871
+ // Our goal is to identify whether this global actor reference appears
872
+ // as the called value of the enclosing ApplyExpr. We cannot simply
873
+ // inspect Parent here because of expressions like (callee)()
874
+ ApplyExpr *apply = applyStack.back ();
875
+ Expr *fn = apply->getFn ()->getValueProvidingExpr ();
876
+ if (auto memberRef = findMemberReference (fn)) {
877
+ auto concDecl = memberRef->first ;
878
+ if (value == concDecl.getDecl () && !apply->implicitlyAsync ()) {
879
+ // then this ValueDecl appears as the called value of the ApplyExpr.
880
+ apply->setImplicitlyAsync (true );
881
+ return true ;
882
+ }
883
+ }
884
+
885
+ return false ;
886
+ };
887
+
838
888
switch (auto contextIsolation =
839
889
getInnermostIsolatedContext (getDeclContext ())) {
840
890
case ActorIsolation::ActorInstance:
891
+ if (inspectForImplicitlyAsync ())
892
+ return false ;
893
+
841
894
ctx.Diags .diagnose (
842
895
loc, diag::global_actor_from_instance_actor_context,
843
896
value->getDescriptiveKind (), value->getName (), globalActor,
@@ -850,6 +903,12 @@ namespace {
850
903
if (contextIsolation.getGlobalActor ()->isEqual (globalActor))
851
904
return false ;
852
905
906
+ // Otherwise, we check if this decl reference is the callee of the
907
+ // enclosing Apply, making it OK as an implicitly async call.
908
+ if (inspectForImplicitlyAsync ())
909
+ return false ;
910
+
911
+ // Otherwise, this is a problematic global actor decl reference.
853
912
ctx.Diags .diagnose (
854
913
loc, diag::global_actor_from_other_global_actor_context,
855
914
value->getDescriptiveKind (), value->getName (), globalActor,
@@ -860,14 +919,18 @@ namespace {
860
919
861
920
case ActorIsolation::Independent:
862
921
case ActorIsolation::IndependentUnsafe:
922
+ if (inspectForImplicitlyAsync ())
923
+ return false ;
924
+
863
925
ctx.Diags .diagnose (
864
926
loc, diag::global_actor_from_independent_context,
865
927
value->getDescriptiveKind (), value->getName (), globalActor);
866
928
noteIsolatedActorMember (value);
867
929
return true ;
868
930
869
931
case ActorIsolation::Unspecified:
870
- // Okay.
932
+ // Okay no matter what, but still must inspect for implicitly async.
933
+ inspectForImplicitlyAsync ();
871
934
return false ;
872
935
}
873
936
llvm_unreachable (" unhandled actor isolation kind!" );
@@ -918,9 +981,12 @@ namespace {
918
981
}
919
982
920
983
// / Check a reference with the given base expression to the given member.
984
+ // / Returns true iff the member reference refers to actor-isolated state
985
+ // / in an invalid or unsafe way such that a diagnostic was emitted.
921
986
bool checkMemberReference (
922
987
Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,
923
- bool isEscapingPartialApply = false ) {
988
+ bool isEscapingPartialApply = false ,
989
+ ApplyExpr *maybeImplicitAsync = nullptr ) {
924
990
if (!base || !memberRef)
925
991
return false ;
926
992
@@ -934,6 +1000,11 @@ namespace {
934
1000
// Must reference actor-isolated state on 'self'.
935
1001
auto selfVar = getSelfReference (base);
936
1002
if (!selfVar) {
1003
+ // actor-isolated non-self calls are implicitly async and thus OK.
1004
+ if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
1005
+ maybeImplicitAsync->setImplicitlyAsync (true );
1006
+ return false ;
1007
+ }
937
1008
ctx.Diags .diagnose (
938
1009
memberLoc, diag::actor_isolated_non_self_reference,
939
1010
member->getDescriptiveKind (),
0 commit comments