@@ -1559,23 +1559,62 @@ namespace {
1559
1559
FoundAsync, // successfully marked an implicitly-async operation
1560
1560
NotFound, // fail: no valid implicitly-async operation was found
1561
1561
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
1563
1564
};
1564
1565
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
+
1565
1599
// / Attempts to identify and mark a valid cross-actor use of a synchronous
1566
1600
// / actor-isolated member (e.g., sync function application, property access)
1567
1601
AsyncMarkingResult tryMarkImplicitlyAsync (SourceLoc declLoc,
1568
1602
ConcreteDeclRef concDeclRef,
1569
1603
Expr* context,
1570
- ImplicitActorHopTarget target) {
1604
+ ImplicitActorHopTarget target,
1605
+ bool isDistributed) {
1571
1606
ValueDecl *decl = concDeclRef.getDecl ();
1572
1607
AsyncMarkingResult result = AsyncMarkingResult::NotFound;
1608
+ bool isAsyncCall = false ;
1573
1609
1574
1610
// is it an access to a property?
1575
1611
if (isPropOrSubscript (decl)) {
1612
+ // Cannot reference properties or subscripts of distributed actors.
1613
+ if (isDistributed && !checkDistributedAccess (declLoc, decl, context))
1614
+ return AsyncMarkingResult::NotDistributed;
1615
+
1576
1616
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
1577
1617
if (usageEnv (declRef) == VarRefUseEnv::Read) {
1578
-
1579
1618
if (!isInAsynchronousContext ())
1580
1619
return AsyncMarkingResult::SyncContext;
1581
1620
@@ -1601,9 +1640,7 @@ namespace {
1601
1640
if (!isInAsynchronousContext ())
1602
1641
return AsyncMarkingResult::SyncContext;
1603
1642
1604
- markNearestCallAsImplicitly (/* setAsync=*/ target);
1605
- result = AsyncMarkingResult::FoundAsync;
1606
-
1643
+ isAsyncCall = true ;
1607
1644
} else if (!applyStack.empty ()) {
1608
1645
// Check our applyStack metadata from the traversal.
1609
1646
// Our goal is to identify whether the actor reference appears
@@ -1620,13 +1657,33 @@ namespace {
1620
1657
return AsyncMarkingResult::SyncContext;
1621
1658
1622
1659
// then this ValueDecl appears as the called value of the ApplyExpr.
1623
- markNearestCallAsImplicitly (/* setAsync=*/ target);
1624
- result = AsyncMarkingResult::FoundAsync;
1660
+ isAsyncCall = true ;
1625
1661
}
1626
1662
}
1627
1663
}
1628
1664
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
+
1629
1685
if (result == AsyncMarkingResult::FoundAsync) {
1686
+
1630
1687
// Check for non-sendable types.
1631
1688
bool problemFound =
1632
1689
diagnoseNonSendableTypesInReference (
@@ -1639,56 +1696,6 @@ namespace {
1639
1696
return result;
1640
1697
}
1641
1698
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
-
1692
1699
// / Check actor isolation for a particular application.
1693
1700
bool checkApply (ApplyExpr *apply) {
1694
1701
auto fnExprType = apply->getFn ()->getType ();
@@ -1850,7 +1857,8 @@ namespace {
1850
1857
// Call is implicitly asynchronous.
1851
1858
auto result = tryMarkImplicitlyAsync (
1852
1859
loc, valueRef, context,
1853
- ImplicitActorHopTarget::forGlobalActor (globalActor));
1860
+ ImplicitActorHopTarget::forGlobalActor (globalActor),
1861
+ /* FIXME if we get global distributed actors*/ false );
1854
1862
if (result == AsyncMarkingResult::FoundAsync)
1855
1863
return false ;
1856
1864
@@ -2197,39 +2205,21 @@ namespace {
2197
2205
if (isolatedActor)
2198
2206
return false ;
2199
2207
2208
+ // If we have a distributed actor that might be remote, check that
2209
+ // we are referencing a properly-distributed member.
2200
2210
bool performDistributedChecks =
2201
2211
isolation.getActorType ()->isDistributedActor () &&
2202
- !(isolatedActor.isActorSelf () &&
2203
- member->isInstanceMember () &&
2204
- isActorInitOrDeInitContext (getDeclContext ())
2205
- );
2206
-
2212
+ !isolatedActor.isPotentiallyIsolated &&
2213
+ !isa<ConstructorDecl>(member) &&
2214
+ !isActorInitOrDeInitContext (getDeclContext ());
2207
2215
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
+ }
2233
2223
}
2234
2224
2235
2225
return diagnoseNonSendableTypesInReference (
@@ -2261,55 +2251,27 @@ namespace {
2261
2251
return true ;
2262
2252
}
2263
2253
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
-
2297
2254
// Try implicit asynchronous access.
2255
+ bool isDistributed = isolation.getActorType ()->isDistributedActor () &&
2256
+ !isolatedActor.isPotentiallyIsolated ;
2298
2257
auto implicitAsyncResult = tryMarkImplicitlyAsync (
2299
2258
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);
2307
2261
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:
2311
2268
return true ;
2312
2269
2270
+ case AsyncMarkingResult::NotFound:
2271
+ case AsyncMarkingResult::SyncContext:
2272
+ // Diagnose below.
2273
+ break ;
2274
+ }
2313
2275
2314
2276
// Complain about access outside of the isolation domain.
2315
2277
auto useKind = static_cast <unsigned >(
0 commit comments