@@ -611,6 +611,24 @@ findMemberReference(Expr *expr) {
611
611
return None;
612
612
}
613
613
614
+ // / Return true if the callee of an ApplyExpr is async
615
+ // /
616
+ // / Note that this must be called after the implicitlyAsync flag has been set,
617
+ // / or implicitly async calls will not return the correct value.
618
+ static bool isAsyncCall (const ApplyExpr *call) {
619
+ if (call->implicitlyAsync ())
620
+ return true ;
621
+
622
+ // Effectively the same as doing a
623
+ // `cast_or_null<FunctionType>(call->getFn()->getType())`, check the
624
+ // result of that and then checking `isAsync` if it's defined.
625
+ Type funcTypeType = call->getFn ()->getType ();
626
+ if (!funcTypeType)
627
+ return false ;
628
+ FunctionType *funcType = funcTypeType->castTo <FunctionType>();
629
+ return funcType->isAsync ();
630
+ }
631
+
614
632
namespace {
615
633
// / Check for adherence to the actor isolation rules, emitting errors
616
634
// / when actor-isolated declarations are used in an unsafe manner.
@@ -679,6 +697,11 @@ namespace {
679
697
return { true , expr };
680
698
}
681
699
700
+ if (auto inout = dyn_cast<InOutExpr>(expr)) {
701
+ if (!applyStack.empty ())
702
+ diagnoseInOutArg (applyStack.back (), inout, false );
703
+ }
704
+
682
705
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
683
706
checkMemberReference (lookup->getBase (), lookup->getMember (),
684
707
lookup->getLoc ());
@@ -720,11 +743,22 @@ namespace {
720
743
Expr *fn = call->getFn ()->getValueProvidingExpr ();
721
744
if (auto memberRef = findMemberReference (fn)) {
722
745
checkMemberReference (
723
- call->getArg (), memberRef->first , memberRef->second ,
746
+ call->getArg (), memberRef->first , memberRef->second ,
724
747
/* isEscapingPartialApply=*/ false , /* maybeImplicitAsync=*/ true );
725
748
726
749
call->getArg ()->walk (*this );
727
750
751
+ if (applyStack.size () >= 2 ) {
752
+ ApplyExpr *outerCall = applyStack[applyStack.size () - 2 ];
753
+ if (isAsyncCall (outerCall)) {
754
+ // This call is a partial application within an async call.
755
+ // If the partial application take a value inout, it is bad.
756
+ if (InOutExpr *inoutArg = dyn_cast<InOutExpr>(
757
+ call->getArg ()->getSemanticsProvidingExpr ()))
758
+ diagnoseInOutArg (outerCall, inoutArg, true );
759
+ }
760
+ }
761
+
728
762
// manual clean-up since normal traversal is skipped
729
763
assert (applyStack.back () == dyn_cast<ApplyExpr>(expr));
730
764
applyStack.pop_back ();
@@ -860,6 +894,59 @@ namespace {
860
894
return true ;
861
895
}
862
896
897
+ // / Diagnose an inout argument passed into an async call
898
+ // /
899
+ // / \returns true if we diagnosed the entity, \c false otherwise.
900
+ bool diagnoseInOutArg (const ApplyExpr *call, const InOutExpr *arg,
901
+ bool isPartialApply) {
902
+ // check that the call is actually async
903
+ if (!isAsyncCall (call))
904
+ return false ;
905
+
906
+ Expr *subArg = arg->getSubExpr ();
907
+ if (LookupExpr *baseArg = dyn_cast<LookupExpr>(subArg)) {
908
+ while (LookupExpr *nextLayer = dyn_cast<LookupExpr>(baseArg->getBase ()))
909
+ baseArg = nextLayer;
910
+ // subArg: the actual property being passed inout
911
+ // baseArg: the property in the actor who's property is being passed
912
+ // inout
913
+
914
+ auto memberDecl = baseArg->getMember ().getDecl ();
915
+ auto isolation = ActorIsolationRestriction::forDeclaration (memberDecl);
916
+ switch (isolation) {
917
+ case ActorIsolationRestriction::Unrestricted:
918
+ case ActorIsolationRestriction::LocalCapture:
919
+ case ActorIsolationRestriction::Unsafe:
920
+ case ActorIsolationRestriction::GlobalActor: // TODO: handle global
921
+ // actors
922
+ break ;
923
+ case ActorIsolationRestriction::ActorSelf: {
924
+ if (isPartialApply) {
925
+ // The partially applied InoutArg is a property of actor. This can
926
+ // really only happen when the property is a struct with a mutating
927
+ // async method.
928
+ if (auto partialApply = dyn_cast<ApplyExpr>(call->getFn ())) {
929
+ ValueDecl *fnDecl =
930
+ cast<DeclRefExpr>(partialApply->getFn ())->getDecl ();
931
+ ctx.Diags .diagnose (
932
+ call->getLoc (), diag::actor_isolated_mutating_func,
933
+ fnDecl->getName (), memberDecl->getDescriptiveKind (),
934
+ memberDecl->getName ());
935
+ return true ;
936
+ }
937
+ } else {
938
+ ctx.Diags .diagnose (
939
+ subArg->getLoc (), diag::actor_isolated_inout_state,
940
+ memberDecl->getDescriptiveKind (), memberDecl->getName (),
941
+ call->implicitlyAsync ());
942
+ return true ;
943
+ }
944
+ }
945
+ }
946
+ }
947
+ return false ;
948
+ }
949
+
863
950
// / Get the actor isolation of the innermost relevant context.
864
951
ActorIsolation getInnermostIsolatedContext (const DeclContext *constDC) {
865
952
// Retrieve the actor isolation for a declaration somewhere in our
@@ -1196,7 +1283,9 @@ namespace {
1196
1283
llvm_unreachable (" Locals cannot be referenced with member syntax" );
1197
1284
1198
1285
case ActorIsolationRestriction::Unsafe:
1199
- return diagnoseReferenceToUnsafe (member, memberLoc);
1286
+ // This case is hit when passing actor state inout to functions in some
1287
+ // cases. The error is emitted by diagnoseInOutArg.
1288
+ return false ;
1200
1289
}
1201
1290
llvm_unreachable (" unhandled actor isolation kind!" );
1202
1291
}
0 commit comments