@@ -1558,70 +1558,95 @@ namespace {
1558
1558
return false ;
1559
1559
}
1560
1560
1561
- // / Check a reference to an entity within a global actor.
1562
- bool checkGlobalActorReference (
1563
- ConcreteDeclRef valueRef, SourceLoc loc, Type globalActor,
1564
- bool isCrossActor,
1565
- Expr * context) {
1566
- ValueDecl *value = valueRef. getDecl () ;
1561
+ enum class AsyncMarkingResult {
1562
+ FoundAsync, // successfully marked an implicitly-async operation
1563
+ NotFound, // fail: no valid implicitly-async operation was found
1564
+ SyncContext, // fail: a valid implicitly-async op, but in sync context
1565
+ NotConcurrentValue // fail: valid op and context, but not ConcurrentValue
1566
+ } ;
1567
1567
1568
- // / Returns true if this global-actor reference is acceptable because
1569
- // / it is part of an implicitly async operation, such as a call or
1570
- // / property access.
1571
- // / NOTE: This check will mutate the AST if it returns true!
1572
- auto inspectForImplicitlyAsync = [&] () -> bool {
1573
- // If our current context isn't an asynchronous one, don't
1574
- if (!isInAsynchronousContext ())
1575
- return false ;
1568
+ // / Attempts to identify and mark a valid cross-actor use of a synchronous
1569
+ // / actor-isolated member (e.g., sync function application, property access)
1570
+ AsyncMarkingResult tryMarkImplicitlyAsync (SourceLoc declLoc,
1571
+ ConcreteDeclRef concDeclRef,
1572
+ Expr* context) {
1573
+ ValueDecl *decl = concDeclRef.getDecl ();
1574
+ AsyncMarkingResult result = AsyncMarkingResult::NotFound;
1576
1575
1577
- bool asyncAccess = false ;
1576
+ // is it an access to a property?
1577
+ if (isPropOrSubscript (decl)) {
1578
+ if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
1579
+ if (usageEnv (declRef) == VarRefUseEnv::Read) {
1578
1580
1579
- // Is this global-actor reference part of a LookupExpr or DeclRefExpr?
1580
- if (isPropOrSubscript (valueRef.getDecl ())) {
1581
- if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
1582
- if (usageEnv (declRef) == VarRefUseEnv::Read) {
1583
- declRef->setImplicitlyAsync (true );
1584
- asyncAccess = true ;
1585
- }
1586
- } else if (auto lookupExpr = dyn_cast_or_null<LookupExpr>(context)) {
1587
- if (usageEnv (lookupExpr) == VarRefUseEnv::Read) {
1588
- lookupExpr->setImplicitlyAsync (true );
1589
- asyncAccess = true ;
1590
- }
1581
+ if (!isInAsynchronousContext ())
1582
+ return AsyncMarkingResult::SyncContext;
1583
+
1584
+ declRef->setImplicitlyAsync (true );
1585
+ result = AsyncMarkingResult::FoundAsync;
1591
1586
}
1592
- }
1587
+ } else if (auto lookupExpr = dyn_cast_or_null<LookupExpr>(context)) {
1588
+ if (usageEnv (lookupExpr) == VarRefUseEnv::Read) {
1593
1589
1594
- // Is this global-actor reference within an apply?
1595
- if (!applyStack.empty ()) {
1596
- // Check our applyStack metadata from the traversal.
1597
- // Our goal is to identify whether this global actor reference appears
1598
- // as the called value of the enclosing ApplyExpr. We cannot simply
1599
- // inspect Parent here because of expressions like (callee)()
1600
- // and the fact that the reference may be just an argument to an apply
1601
- ApplyExpr *apply = applyStack.back ();
1602
- Expr *fn = apply->getFn ()->getValueProvidingExpr ();
1603
- if (auto memberRef = findMemberReference (fn)) {
1604
- auto concDecl = memberRef->first ;
1605
- if (value == concDecl.getDecl () && !apply->implicitlyAsync ()) {
1606
- // then this ValueDecl appears as the called value of the ApplyExpr.
1607
- markNearestCallAsImplicitlyAsync ();
1608
- asyncAccess = true ;
1609
- }
1590
+ if (!isInAsynchronousContext ())
1591
+ return AsyncMarkingResult::SyncContext;
1592
+
1593
+ lookupExpr->setImplicitlyAsync (true );
1594
+ result = AsyncMarkingResult::FoundAsync;
1610
1595
}
1611
1596
}
1612
1597
1613
- if (asyncAccess) {
1614
- // Check for non-concurrent types.
1615
- (void )diagnoseNonConcurrentTypesInReference (
1616
- valueRef, getDeclContext (), loc,
1617
- ConcurrentReferenceKind::SynchronousAsAsyncCall);
1598
+ } else if (llvm::isa_and_nonnull<SelfApplyExpr>(context) &&
1599
+ isa<AbstractFunctionDecl>(decl)) {
1600
+ // actor-isolated non-isolated-self calls are implicitly async
1601
+ // and thus OK.
1618
1602
1619
- return true ;
1603
+ if (!isInAsynchronousContext ())
1604
+ return AsyncMarkingResult::SyncContext;
1605
+
1606
+ markNearestCallAsImplicitlyAsync ();
1607
+ result = AsyncMarkingResult::FoundAsync;
1608
+
1609
+ } else if (!applyStack.empty ()) {
1610
+ // Check our applyStack metadata from the traversal.
1611
+ // Our goal is to identify whether the actor reference appears
1612
+ // as the called value of the enclosing ApplyExpr. We cannot simply
1613
+ // inspect Parent here because of expressions like (callee)()
1614
+ // and the fact that the reference may be just an argument to an apply
1615
+ ApplyExpr *apply = applyStack.back ();
1616
+ Expr *fn = apply->getFn ()->getValueProvidingExpr ();
1617
+ if (auto memberRef = findMemberReference (fn)) {
1618
+ auto concDecl = memberRef->first ;
1619
+ if (decl == concDecl.getDecl () && !apply->implicitlyAsync ()) {
1620
+
1621
+ if (!isInAsynchronousContext ())
1622
+ return AsyncMarkingResult::SyncContext;
1623
+
1624
+ // then this ValueDecl appears as the called value of the ApplyExpr.
1625
+ markNearestCallAsImplicitlyAsync ();
1626
+ result = AsyncMarkingResult::FoundAsync;
1627
+ }
1620
1628
}
1629
+ }
1621
1630
1622
- return false ;
1623
- };
1631
+ if (result == AsyncMarkingResult::FoundAsync) {
1632
+ // Check for non-concurrent types.
1633
+ bool problemFound =
1634
+ diagnoseNonConcurrentTypesInReference (
1635
+ concDeclRef, getDeclContext (), declLoc,
1636
+ ConcurrentReferenceKind::SynchronousAsAsyncCall);
1637
+ if (problemFound)
1638
+ result = AsyncMarkingResult::NotConcurrentValue;
1639
+ }
1624
1640
1641
+ return result;
1642
+ }
1643
+
1644
+ // / Check a reference to an entity within a global actor.
1645
+ bool checkGlobalActorReference (
1646
+ ConcreteDeclRef valueRef, SourceLoc loc, Type globalActor,
1647
+ bool isCrossActor,
1648
+ Expr *context) {
1649
+ ValueDecl *value = valueRef.getDecl ();
1625
1650
auto declContext = getDeclContext ();
1626
1651
1627
1652
// Check whether we are within the same isolation context, in which
@@ -1641,7 +1666,8 @@ namespace {
1641
1666
1642
1667
switch (contextIsolation) {
1643
1668
case ActorIsolation::ActorInstance: {
1644
- if (inspectForImplicitlyAsync ())
1669
+ auto result = tryMarkImplicitlyAsync (loc, valueRef, context);
1670
+ if (result == AsyncMarkingResult::FoundAsync)
1645
1671
return false ;
1646
1672
1647
1673
auto useKind = static_cast <unsigned >(
@@ -1650,7 +1676,7 @@ namespace {
1650
1676
ctx.Diags .diagnose (loc, diag::global_actor_from_instance_actor_context,
1651
1677
value->getDescriptiveKind (), value->getName (),
1652
1678
globalActor, contextIsolation.getActor ()->getName (),
1653
- useKind);
1679
+ useKind, result == AsyncMarkingResult::SyncContext );
1654
1680
noteIsolatedActorMember (value, context);
1655
1681
return true ;
1656
1682
}
@@ -1659,7 +1685,8 @@ namespace {
1659
1685
case ActorIsolation::GlobalActorUnsafe: {
1660
1686
// Check if this decl reference is the callee of the enclosing Apply,
1661
1687
// making it OK as an implicitly async call.
1662
- if (inspectForImplicitlyAsync ())
1688
+ auto result = tryMarkImplicitlyAsync (loc, valueRef, context);
1689
+ if (result == AsyncMarkingResult::FoundAsync)
1663
1690
return false ;
1664
1691
1665
1692
auto useKind = static_cast <unsigned >(
@@ -1669,7 +1696,8 @@ namespace {
1669
1696
ctx.Diags .diagnose (
1670
1697
loc, diag::global_actor_from_other_global_actor_context,
1671
1698
value->getDescriptiveKind (), value->getName (), globalActor,
1672
- contextIsolation.getGlobalActor (), useKind);
1699
+ contextIsolation.getGlobalActor (), useKind,
1700
+ result == AsyncMarkingResult::SyncContext);
1673
1701
noteIsolatedActorMember (value, context);
1674
1702
return true ;
1675
1703
}
@@ -1679,7 +1707,8 @@ namespace {
1679
1707
return false ;
1680
1708
1681
1709
case ActorIsolation::Independent: {
1682
- if (inspectForImplicitlyAsync ())
1710
+ auto result = tryMarkImplicitlyAsync (loc, valueRef, context);
1711
+ if (result == AsyncMarkingResult::FoundAsync)
1683
1712
return false ;
1684
1713
1685
1714
auto useKind = static_cast <unsigned >(
@@ -1688,14 +1717,16 @@ namespace {
1688
1717
ctx.Diags .diagnose (loc, diag::global_actor_from_nonactor_context,
1689
1718
value->getDescriptiveKind (), value->getName (),
1690
1719
globalActor,
1691
- /* actorIndependent=*/ true , useKind);
1720
+ /* actorIndependent=*/ true , useKind,
1721
+ result == AsyncMarkingResult::SyncContext);
1692
1722
noteIsolatedActorMember (value, context);
1693
1723
return true ;
1694
1724
}
1695
1725
1696
1726
case ActorIsolation::Unspecified: {
1697
1727
// NOTE: we must always inspect for implicitlyAsync
1698
- bool implicitlyAsyncExpr = inspectForImplicitlyAsync ();
1728
+ auto result = tryMarkImplicitlyAsync (loc, valueRef, context);
1729
+ bool implicitlyAsyncExpr = (result == AsyncMarkingResult::FoundAsync);
1699
1730
bool didEmitDiagnostic = false ;
1700
1731
1701
1732
auto emitError = [&](bool justNote = false ) {
@@ -1706,7 +1737,8 @@ namespace {
1706
1737
ctx.Diags .diagnose (
1707
1738
loc, diag::global_actor_from_nonactor_context,
1708
1739
value->getDescriptiveKind (), value->getName (), globalActor,
1709
- /* actorIndependent=*/ false , useKind);
1740
+ /* actorIndependent=*/ false , useKind,
1741
+ result == AsyncMarkingResult::SyncContext);
1710
1742
}
1711
1743
noteIsolatedActorMember (value, context);
1712
1744
};
@@ -1938,47 +1970,15 @@ namespace {
1938
1970
}
1939
1971
1940
1972
case ActorIsolationRestriction::ActorSelf: {
1941
- // / Local function to check for implicit async promotion.
1942
- // / returns None if it is not applicable; true if there is an error.
1943
- auto checkImplicitlyAsync = [&]() -> Optional<bool > {
1944
- if (!isInAsynchronousContext ())
1945
- return None;
1946
-
1947
- bool validAccess = false ;
1948
-
1949
- // actor-isolated non-isolated-self calls are implicitly async
1950
- // and thus OK.
1951
- if (llvm::isa_and_nonnull<SelfApplyExpr>(context) &&
1952
- isa<AbstractFunctionDecl>(member)) {
1953
- markNearestCallAsImplicitlyAsync ();
1954
- validAccess = true ;
1955
-
1956
- } else if (llvm::isa_and_nonnull<LookupExpr>(context) &&
1957
- isPropOrSubscript (member) &&
1958
- usageEnv (cast<LookupExpr>(context)) == VarRefUseEnv::Read) {
1959
- cast<LookupExpr>(context)->setImplicitlyAsync (true );
1960
- validAccess = true ;
1961
- } else {
1962
- // It's not wrong to have declref context here; simply unimplemented
1963
- assert (context == nullptr || !isa<DeclRefExpr>(context));
1964
- }
1965
-
1966
- if (validAccess) {
1967
- // Check for non-concurrent types.
1968
- return diagnoseNonConcurrentTypesInReference (
1969
- memberRef, getDeclContext (), memberLoc,
1970
- ConcurrentReferenceKind::SynchronousAsAsyncCall);
1971
- }
1972
-
1973
- return None;
1974
- };
1975
-
1976
1973
// Must reference actor-isolated state on 'self'.
1977
1974
auto *selfVar = getReferencedSelf (base);
1978
1975
if (!selfVar) {
1979
1976
// Check for implicit async.
1980
- if (auto result = checkImplicitlyAsync ())
1981
- return *result;
1977
+ auto result = tryMarkImplicitlyAsync (memberLoc, memberRef, context);
1978
+ if (result == AsyncMarkingResult::FoundAsync)
1979
+ return false ; // no problems
1980
+ else if (result == AsyncMarkingResult::NotConcurrentValue)
1981
+ return true ;
1982
1982
1983
1983
auto useKind = static_cast <unsigned >(
1984
1984
kindOfUsage (member, context).getValueOr (VarRefUseEnv::Read));
@@ -2017,9 +2017,11 @@ namespace {
2017
2017
return false ;
2018
2018
2019
2019
case ActorIsolation::Independent: {
2020
- // Check for implicit async.
2021
- if (auto result = checkImplicitlyAsync ())
2022
- return *result;
2020
+ auto result = tryMarkImplicitlyAsync (memberLoc, memberRef, context);
2021
+ if (result == AsyncMarkingResult::FoundAsync)
2022
+ return false ; // no problems
2023
+ else if (result == AsyncMarkingResult::NotConcurrentValue)
2024
+ return true ;
2023
2025
2024
2026
// The 'self' is for an actor-independent member, which means
2025
2027
// we cannot refer to actor-isolated state.
@@ -2033,20 +2035,26 @@ namespace {
2033
2035
}
2034
2036
2035
2037
case ActorIsolation::GlobalActor:
2036
- case ActorIsolation::GlobalActorUnsafe:
2037
- // Check for implicit async.
2038
- if (auto result = checkImplicitlyAsync ())
2039
- return *result;
2038
+ case ActorIsolation::GlobalActorUnsafe: {
2039
+ auto result = tryMarkImplicitlyAsync (memberLoc, memberRef, context);
2040
+ if (result == AsyncMarkingResult::FoundAsync)
2041
+ return false ; // no problems
2042
+ else if (result == AsyncMarkingResult::NotConcurrentValue)
2043
+ return true ;
2040
2044
2041
2045
// The 'self' is for a member that's part of a global actor, which
2042
2046
// means we cannot refer to actor-isolated state.
2043
- ctx.Diags .diagnose (
2044
- memberLoc, diag::actor_isolated_global_actor_context,
2045
- member->getDescriptiveKind (),
2046
- member->getName (),
2047
- contextIsolation.getGlobalActor ());
2047
+ auto useKind = static_cast <unsigned >(
2048
+ kindOfUsage (member, context).getValueOr (VarRefUseEnv::Read));
2049
+ ctx.Diags .diagnose (memberLoc,
2050
+ diag::actor_isolated_global_actor_context,
2051
+ member->getDescriptiveKind (), member->getName (),
2052
+ contextIsolation.getGlobalActor (), useKind,
2053
+ result == AsyncMarkingResult::SyncContext
2054
+ );
2048
2055
noteIsolatedActorMember (member, context);
2049
2056
return true ;
2057
+ }
2050
2058
}
2051
2059
llvm_unreachable (" Unhandled actor isolation" );
2052
2060
}
0 commit comments