@@ -1559,23 +1559,62 @@ namespace {
15591559 FoundAsync, // successfully marked an implicitly-async operation
15601560 NotFound, // fail: no valid implicitly-async operation was found
15611561 SyncContext, // fail: a valid implicitly-async op, but in sync context
1562- NotSendable // fail: valid op and context, but not Sendable
1562+ NotSendable, // fail: valid op and context, but not Sendable
1563+ NotDistributed, // fail: non-distributed declaration in distributed actor
15631564 };
15641565
1566+ // / Determine whether we can access the given declaration that is
1567+ // / isolated to a distributed actor from a location that is potentially not
1568+ // / local to this process.
1569+ // /
1570+ // / \returns the (setThrows, isDistributedThunk) bits to implicitly
1571+ // / mark the access/call with on success, or emits an error and returns
1572+ // / \c None.
1573+ Optional<std::pair<bool , bool >>
1574+ checkDistributedAccess (SourceLoc declLoc, ValueDecl *decl,
1575+ Expr *context) {
1576+ // Cannot reference properties or subscripts of distributed actors.
1577+ if (isPropOrSubscript (decl)) {
1578+ ctx.Diags .diagnose (
1579+ declLoc, diag::distributed_actor_isolated_non_self_reference,
1580+ decl->getDescriptiveKind (), decl->getName ());
1581+ noteIsolatedActorMember (decl, context);
1582+ return None;
1583+ }
1584+
1585+ // Check that we have a distributed function.
1586+ auto func = dyn_cast<AbstractFunctionDecl>(decl);
1587+ if (!func || !func->isDistributed ()) {
1588+ ctx.Diags .diagnose (declLoc,
1589+ diag::distributed_actor_isolated_method)
1590+ .fixItInsert (decl->getAttributeInsertionLoc (true ), " distributed " );
1591+
1592+ noteIsolatedActorMember (decl, context);
1593+ return None;
1594+ }
1595+
1596+ return std::make_pair (!func->hasThrows (), true );
1597+ }
1598+
15651599 // / Attempts to identify and mark a valid cross-actor use of a synchronous
15661600 // / actor-isolated member (e.g., sync function application, property access)
15671601 AsyncMarkingResult tryMarkImplicitlyAsync (SourceLoc declLoc,
15681602 ConcreteDeclRef concDeclRef,
15691603 Expr* context,
1570- ImplicitActorHopTarget target) {
1604+ ImplicitActorHopTarget target,
1605+ bool isDistributed) {
15711606 ValueDecl *decl = concDeclRef.getDecl ();
15721607 AsyncMarkingResult result = AsyncMarkingResult::NotFound;
1608+ bool isAsyncCall = false ;
15731609
15741610 // is it an access to a property?
15751611 if (isPropOrSubscript (decl)) {
1612+ // Cannot reference properties or subscripts of distributed actors.
1613+ if (isDistributed && !checkDistributedAccess (declLoc, decl, context))
1614+ return AsyncMarkingResult::NotDistributed;
1615+
15761616 if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
15771617 if (usageEnv (declRef) == VarRefUseEnv::Read) {
1578-
15791618 if (!isInAsynchronousContext ())
15801619 return AsyncMarkingResult::SyncContext;
15811620
@@ -1601,9 +1640,7 @@ namespace {
16011640 if (!isInAsynchronousContext ())
16021641 return AsyncMarkingResult::SyncContext;
16031642
1604- markNearestCallAsImplicitly (/* setAsync=*/ target);
1605- result = AsyncMarkingResult::FoundAsync;
1606-
1643+ isAsyncCall = true ;
16071644 } else if (!applyStack.empty ()) {
16081645 // Check our applyStack metadata from the traversal.
16091646 // Our goal is to identify whether the actor reference appears
@@ -1620,13 +1657,33 @@ namespace {
16201657 return AsyncMarkingResult::SyncContext;
16211658
16221659 // then this ValueDecl appears as the called value of the ApplyExpr.
1623- markNearestCallAsImplicitly (/* setAsync=*/ target);
1624- result = AsyncMarkingResult::FoundAsync;
1660+ isAsyncCall = true ;
16251661 }
16261662 }
16271663 }
16281664
1665+ // Set up an implicit async call.
1666+ if (isAsyncCall) {
1667+ // If we're calling to a distributed actor, make sure the function
1668+ // is actually 'distributed'.
1669+ bool setThrows = false ;
1670+ bool usesDistributedThunk = false ;
1671+ if (isDistributed) {
1672+ if (auto access = checkDistributedAccess (declLoc, decl, context))
1673+ std::tie (setThrows, usesDistributedThunk) = *access;
1674+ else
1675+ return AsyncMarkingResult::NotDistributed;
1676+ }
1677+
1678+ // Mark call as implicitly 'async', and also potentially as
1679+ // throwing and using a distributed thunk.
1680+ markNearestCallAsImplicitly (
1681+ /* setAsync=*/ target, setThrows, usesDistributedThunk);
1682+ result = AsyncMarkingResult::FoundAsync;
1683+ }
1684+
16291685 if (result == AsyncMarkingResult::FoundAsync) {
1686+
16301687 // Check for non-sendable types.
16311688 bool problemFound =
16321689 diagnoseNonSendableTypesInReference (
@@ -1639,56 +1696,6 @@ namespace {
16391696 return result;
16401697 }
16411698
1642- enum ThrowsMarkingResult {
1643- FoundThrows,
1644- NotFound
1645- };
1646-
1647- ThrowsMarkingResult tryMarkImplicitlyThrows (SourceLoc declLoc,
1648- ConcreteDeclRef concDeclRef,
1649- Expr* context) {
1650- ValueDecl *decl = concDeclRef.getDecl ();
1651- ThrowsMarkingResult result = ThrowsMarkingResult::NotFound;
1652-
1653- if (isa_and_nonnull<SelfApplyExpr>(context)) {
1654- if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
1655- if (func->isDistributed () && !func->hasThrows ()) {
1656- // A distributed function is implicitly throwing if called from
1657- // outside of the actor.
1658- //
1659- // If it already is throwing, no need to mark it implicitly so.
1660- markNearestCallAsImplicitly (
1661- /* setAsync=*/ None, /* setThrows=*/ true );
1662- result = ThrowsMarkingResult::FoundThrows;
1663- }
1664- }
1665- } else if (!applyStack.empty ()) {
1666- // Check our applyStack metadata from the traversal.
1667- // Our goal is to identify whether the actor reference appears
1668- // as the called value of the enclosing ApplyExpr. We cannot simply
1669- // inspect Parent here because of expressions like (callee)()
1670- // and the fact that the reference may be just an argument to an apply
1671- ApplyExpr *apply = applyStack.back ();
1672- Expr *fn = apply->getFn ()->getValueProvidingExpr ();
1673- if (auto memberRef = findMemberReference (fn)) {
1674- auto concDecl = memberRef->first ;
1675- if (decl == concDecl.getDecl () && !apply->implicitlyThrows ()) {
1676-
1677- if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
1678- if (func->isDistributed () && !func->hasThrows ()) {
1679- // then this ValueDecl appears as the called value of the ApplyExpr.
1680- markNearestCallAsImplicitly (
1681- /* setAsync=*/ None, /* setThrows=*/ true );
1682- result = ThrowsMarkingResult::FoundThrows;
1683- }
1684- }
1685- }
1686- }
1687- }
1688-
1689- return result;
1690- }
1691-
16921699 // / Check actor isolation for a particular application.
16931700 bool checkApply (ApplyExpr *apply) {
16941701 auto fnExprType = apply->getFn ()->getType ();
@@ -1850,7 +1857,8 @@ namespace {
18501857 // Call is implicitly asynchronous.
18511858 auto result = tryMarkImplicitlyAsync (
18521859 loc, valueRef, context,
1853- ImplicitActorHopTarget::forGlobalActor (globalActor));
1860+ ImplicitActorHopTarget::forGlobalActor (globalActor),
1861+ /* FIXME if we get global distributed actors*/ false );
18541862 if (result == AsyncMarkingResult::FoundAsync)
18551863 return false ;
18561864
@@ -2197,39 +2205,21 @@ namespace {
21972205 if (isolatedActor)
21982206 return false ;
21992207
2208+ // If we have a distributed actor that might be remote, check that
2209+ // we are referencing a properly-distributed member.
22002210 bool performDistributedChecks =
22012211 isolation.getActorType ()->isDistributedActor () &&
2202- !(isolatedActor.isActorSelf () &&
2203- member->isInstanceMember () &&
2204- isActorInitOrDeInitContext (getDeclContext ())
2205- );
2206-
2212+ !isolatedActor.isPotentiallyIsolated &&
2213+ !isa<ConstructorDecl>(member) &&
2214+ !isActorInitOrDeInitContext (getDeclContext ());
22072215 if (performDistributedChecks) {
2208- if (auto func = dyn_cast<FuncDecl>(member)) {
2209- if (func->isStatic ()) {
2210- // no additional checks for static functions
2211- } else if (func->isDistributed ()) {
2212- tryMarkImplicitlyThrows (memberLoc, memberRef, context);
2213- markNearestCallAsImplicitly (/* setAsync*/ None, /* setThrows*/ false ,
2214- /* setDistributedThunk*/ true );
2215-
2216- } else {
2217- // neither static or distributed, apply full distributed isolation
2218- ctx.Diags .diagnose (memberLoc, diag::distributed_actor_isolated_method)
2219- .fixItInsert (member->getAttributeInsertionLoc (true ), " distributed " );
2220- noteIsolatedActorMember (member, context);
2221- return true ;
2222- }
2223- } // end FuncDecl
2224-
2225- if (isPropOrSubscript (member)) {
2226- ctx.Diags .diagnose (
2227- memberLoc, diag::distributed_actor_isolated_non_self_reference,
2228- member->getDescriptiveKind (),
2229- member->getName ());
2230- noteIsolatedActorMember (member, context);
2231- return true ;
2232- } // end VarDecl
2216+ if (auto access = checkDistributedAccess (memberLoc, member, context)){
2217+ // This is a distributed access, so mark it as throwing or
2218+ // using a distributed thunk as appropriate.
2219+ markNearestCallAsImplicitly (None, access->first , access->second );
2220+ } else {
2221+ return true ;
2222+ }
22332223 }
22342224
22352225 return diagnoseNonSendableTypesInReference (
@@ -2261,55 +2251,27 @@ namespace {
22612251 return true ;
22622252 }
22632253
2264-
2265- // Distributed actor properties cannot be accessed cross-actor.
2266- if (isolation.getActorType ()->isDistributedActor ()) {
2267- if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
2268- if (!func->isDistributed ()) {
2269- ctx.Diags .diagnose (memberLoc,
2270- diag::distributed_actor_isolated_method);
2271- noteIsolatedActorMember (member, context);
2272- return true ;
2273- } else {
2274- // it is some other member and since we only allow distributed
2275- // funcs this definitely is distributed-actor-isolated
2276- if (!func->isDistributed ()) {
2277- ctx.Diags .diagnose (
2278- memberLoc,
2279- diag::distributed_actor_isolated_non_self_reference,
2280- member->getDescriptiveKind (), member->getName ());
2281- noteIsolatedActorMember (member, context);
2282- return true ;
2283- }
2284- }
2285- }
2286-
2287- // It wasn't a distributed func, so ban the access
2288- if (isPropOrSubscript (member)) {
2289- ctx.Diags .diagnose (
2290- memberLoc, diag::distributed_actor_isolated_non_self_reference,
2291- member->getDescriptiveKind (), member->getName ());
2292- noteIsolatedActorMember (member, context);
2293- return true ;
2294- }
2295- }
2296-
22972254 // Try implicit asynchronous access.
2255+ bool isDistributed = isolation.getActorType ()->isDistributedActor () &&
2256+ !isolatedActor.isPotentiallyIsolated ;
22982257 auto implicitAsyncResult = tryMarkImplicitlyAsync (
22992258 memberLoc, memberRef, context,
2300- ImplicitActorHopTarget::forInstanceSelf ());
2301- if (isolation.getActorType ()->isDistributedActor () &&
2302- !isolatedActor.isPotentiallyIsolated ) {
2303- tryMarkImplicitlyThrows (memberLoc, memberRef, context);
2304- markNearestCallAsImplicitly (/* setAsync*/ None, /* setThrows*/ false ,
2305- /* setDistributedThunk*/ true );
2306- }
2259+ ImplicitActorHopTarget::forInstanceSelf (),
2260+ isDistributed);
23072261
2308- if (implicitAsyncResult == AsyncMarkingResult::FoundAsync)
2309- return false ; // no problems
2310- else if (implicitAsyncResult == AsyncMarkingResult::NotSendable)
2262+ switch (implicitAsyncResult) {
2263+ case AsyncMarkingResult::FoundAsync:
2264+ return false ;
2265+
2266+ case AsyncMarkingResult::NotSendable:
2267+ case AsyncMarkingResult::NotDistributed:
23112268 return true ;
23122269
2270+ case AsyncMarkingResult::NotFound:
2271+ case AsyncMarkingResult::SyncContext:
2272+ // Diagnose below.
2273+ break ;
2274+ }
23132275
23142276 // Complain about access outside of the isolation domain.
23152277 auto useKind = static_cast <unsigned >(
0 commit comments