@@ -604,6 +604,24 @@ findMemberReference(Expr *expr) {
604
604
return None;
605
605
}
606
606
607
+ // / Return true if the callee of an ApplyExpr is async
608
+ // /
609
+ // / Note that this must be called after the implicitlyAsync flag has been set,
610
+ // / or implicitly async calls will not return the correct value.
611
+ static bool isAsyncCall (const ApplyExpr *call) {
612
+ if (call->implicitlyAsync ())
613
+ return true ;
614
+
615
+ // Effectively the same as doing a
616
+ // `cast_or_null<FunctionType>(call->getFn()->getType())`, check the
617
+ // result of that and then checking `isAsync` if it's defined.
618
+ Type funcTypeType = call->getFn ()->getType ();
619
+ if (!funcTypeType)
620
+ return false ;
621
+ FunctionType *funcType = funcTypeType->castTo <FunctionType>();
622
+ return funcType->isAsync ();
623
+ }
624
+
607
625
namespace {
608
626
// / Check for adherence to the actor isolation rules, emitting errors
609
627
// / when actor-isolated declarations are used in an unsafe manner.
@@ -672,6 +690,11 @@ namespace {
672
690
return { true , expr };
673
691
}
674
692
693
+ if (auto inout = dyn_cast<InOutExpr>(expr)) {
694
+ if (!applyStack.empty ())
695
+ diagnoseInOutArg (applyStack.back (), inout, false );
696
+ }
697
+
675
698
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
676
699
checkMemberReference (lookup->getBase (), lookup->getMember (),
677
700
lookup->getLoc ());
@@ -713,11 +736,22 @@ namespace {
713
736
Expr *fn = call->getFn ()->getValueProvidingExpr ();
714
737
if (auto memberRef = findMemberReference (fn)) {
715
738
checkMemberReference (
716
- call->getArg (), memberRef->first , memberRef->second ,
739
+ call->getArg (), memberRef->first , memberRef->second ,
717
740
/* isEscapingPartialApply=*/ false , /* maybeImplicitAsync=*/ true );
718
741
719
742
call->getArg ()->walk (*this );
720
743
744
+ if (applyStack.size () >= 2 ) {
745
+ ApplyExpr *outerCall = applyStack[applyStack.size () - 2 ];
746
+ if (isAsyncCall (outerCall)) {
747
+ // This call is a partial application within an async call.
748
+ // If the partial application take a value inout, it is bad.
749
+ if (InOutExpr *inoutArg = dyn_cast<InOutExpr>(
750
+ call->getArg ()->getSemanticsProvidingExpr ()))
751
+ diagnoseInOutArg (outerCall, inoutArg, true );
752
+ }
753
+ }
754
+
721
755
// manual clean-up since normal traversal is skipped
722
756
assert (applyStack.back () == dyn_cast<ApplyExpr>(expr));
723
757
applyStack.pop_back ();
@@ -853,6 +887,58 @@ namespace {
853
887
return true ;
854
888
}
855
889
890
+ // / Diagnose an inout argument passed into an async call
891
+ // /
892
+ // / \returns true if we diagnosed the entity, \c false otherwise.
893
+ bool diagnoseInOutArg (const ApplyExpr *call, const InOutExpr *arg,
894
+ bool isPartialApply) {
895
+ // check that the call is actually async
896
+ if (!isAsyncCall (call))
897
+ return false ;
898
+
899
+ Expr *subArg = arg->getSubExpr ();
900
+ if (LookupExpr *baseArg = dyn_cast<LookupExpr>(subArg)) {
901
+ while (LookupExpr *nextLayer = dyn_cast<LookupExpr>(baseArg->getBase ()))
902
+ baseArg = nextLayer;
903
+ // subArg: the actual property being passed inout
904
+ // baseArg: the property in the actor who's property is being passed
905
+ // inout
906
+
907
+ auto memberDecl = baseArg->getMember ().getDecl ();
908
+ auto isolation = ActorIsolationRestriction::forDeclaration (memberDecl);
909
+ switch (isolation) {
910
+ case ActorIsolationRestriction::Unrestricted:
911
+ case ActorIsolationRestriction::LocalCapture:
912
+ case ActorIsolationRestriction::Unsafe:
913
+ case ActorIsolationRestriction::GlobalActor: // TODO: handle global
914
+ // actors
915
+ break ;
916
+ case ActorIsolationRestriction::ActorSelf: {
917
+ if (isPartialApply) {
918
+ // The partially applied InoutArg is a property of actor. This can
919
+ // really only happen when the property is a struct with a mutating
920
+ // async method.
921
+ if (auto partialApply = dyn_cast<ApplyExpr>(call->getFn ())) {
922
+ ValueDecl *fnDecl =
923
+ cast<DeclRefExpr>(partialApply->getFn ())->getDecl ();
924
+ ctx.Diags .diagnose (
925
+ call->getLoc (), diag::actor_isolated_mutating_func,
926
+ fnDecl->getName (), memberDecl->getDescriptiveKind (),
927
+ memberDecl->getName ());
928
+ return true ;
929
+ }
930
+ } else {
931
+ ctx.Diags .diagnose (
932
+ subArg->getLoc (), diag::actor_isolated_inout_state,
933
+ memberDecl->getDescriptiveKind (), memberDecl->getName ());
934
+ return true ;
935
+ }
936
+ }
937
+ }
938
+ }
939
+ return false ;
940
+ }
941
+
856
942
// / Get the actor isolation of the innermost relevant context.
857
943
ActorIsolation getInnermostIsolatedContext (const DeclContext *constDC) {
858
944
// Retrieve the actor isolation for a declaration somewhere in our
@@ -1138,7 +1224,9 @@ namespace {
1138
1224
llvm_unreachable (" Locals cannot be referenced with member syntax" );
1139
1225
1140
1226
case ActorIsolationRestriction::Unsafe:
1141
- return diagnoseReferenceToUnsafe (member, memberLoc);
1227
+ // This case is hit when passing actor state inout to functions in some
1228
+ // cases. The error is emitted by diagnoseInOutArg.
1229
+ return false ;
1142
1230
}
1143
1231
llvm_unreachable (" unhandled actor isolation kind!" );
1144
1232
}
0 commit comments