@@ -611,6 +611,24 @@ findMemberReference(Expr *expr) {
611611 return None;
612612}
613613
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+
614632namespace {
615633 // / Check for adherence to the actor isolation rules, emitting errors
616634 // / when actor-isolated declarations are used in an unsafe manner.
@@ -679,6 +697,11 @@ namespace {
679697 return { true , expr };
680698 }
681699
700+ if (auto inout = dyn_cast<InOutExpr>(expr)) {
701+ if (!applyStack.empty ())
702+ diagnoseInOutArg (applyStack.back (), inout, false );
703+ }
704+
682705 if (auto lookup = dyn_cast<LookupExpr>(expr)) {
683706 checkMemberReference (lookup->getBase (), lookup->getMember (),
684707 lookup->getLoc ());
@@ -720,11 +743,22 @@ namespace {
720743 Expr *fn = call->getFn ()->getValueProvidingExpr ();
721744 if (auto memberRef = findMemberReference (fn)) {
722745 checkMemberReference (
723- call->getArg (), memberRef->first , memberRef->second ,
746+ call->getArg (), memberRef->first , memberRef->second ,
724747 /* isEscapingPartialApply=*/ false , /* maybeImplicitAsync=*/ true );
725748
726749 call->getArg ()->walk (*this );
727750
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+
728762 // manual clean-up since normal traversal is skipped
729763 assert (applyStack.back () == dyn_cast<ApplyExpr>(expr));
730764 applyStack.pop_back ();
@@ -860,6 +894,59 @@ namespace {
860894 return true ;
861895 }
862896
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+
863950 // / Get the actor isolation of the innermost relevant context.
864951 ActorIsolation getInnermostIsolatedContext (const DeclContext *constDC) {
865952 // Retrieve the actor isolation for a declaration somewhere in our
@@ -1196,7 +1283,9 @@ namespace {
11961283 llvm_unreachable (" Locals cannot be referenced with member syntax" );
11971284
11981285 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 ;
12001289 }
12011290 llvm_unreachable (" unhandled actor isolation kind!" );
12021291 }
0 commit comments