Skip to content

Commit 4ba396a

Browse files
committed
Refactor implementation of implicitly-async operation identification during type-checking.
1 parent 7caded3 commit 4ba396a

File tree

1 file changed

+96
-107
lines changed

1 file changed

+96
-107
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 96 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,70 +1558,83 @@ namespace {
15581558
return false;
15591559
}
15601560

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();
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;
1576-
1577-
bool asyncAccess = false;
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+
};
15781567

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-
}
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+
// If our current context isn't an asynchronous one, don't
1574+
if (!isInAsynchronousContext())
1575+
return AsyncMarkingResult::NotFound;
1576+
1577+
ValueDecl *decl = concDeclRef.getDecl();
1578+
AsyncMarkingResult result = AsyncMarkingResult::NotFound;
1579+
1580+
// is it an access to a property?
1581+
if (isPropOrSubscript(decl)) {
1582+
if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
1583+
if (usageEnv(declRef) == VarRefUseEnv::Read) {
1584+
declRef->setImplicitlyAsync(true);
1585+
result = AsyncMarkingResult::FoundAsync;
1586+
}
1587+
} else if (auto lookupExpr = dyn_cast_or_null<LookupExpr>(context)) {
1588+
if (usageEnv(lookupExpr) == VarRefUseEnv::Read) {
1589+
lookupExpr->setImplicitlyAsync(true);
1590+
result = AsyncMarkingResult::FoundAsync;
15911591
}
15921592
}
15931593

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-
}
1594+
} else if (llvm::isa_and_nonnull<SelfApplyExpr>(context) &&
1595+
isa<AbstractFunctionDecl>(decl)) {
1596+
// actor-isolated non-isolated-self calls are implicitly async
1597+
// and thus OK.
1598+
markNearestCallAsImplicitlyAsync();
1599+
result = AsyncMarkingResult::FoundAsync;
1600+
1601+
} else if (!applyStack.empty()) {
1602+
// Check our applyStack metadata from the traversal.
1603+
// Our goal is to identify whether the actor reference appears
1604+
// as the called value of the enclosing ApplyExpr. We cannot simply
1605+
// inspect Parent here because of expressions like (callee)()
1606+
// and the fact that the reference may be just an argument to an apply
1607+
ApplyExpr *apply = applyStack.back();
1608+
Expr *fn = apply->getFn()->getValueProvidingExpr();
1609+
if (auto memberRef = findMemberReference(fn)) {
1610+
auto concDecl = memberRef->first;
1611+
if (decl == concDecl.getDecl() && !apply->implicitlyAsync()) {
1612+
// then this ValueDecl appears as the called value of the ApplyExpr.
1613+
markNearestCallAsImplicitlyAsync();
1614+
result = AsyncMarkingResult::FoundAsync;
16101615
}
16111616
}
1617+
}
16121618

1613-
if (asyncAccess) {
1614-
// Check for non-concurrent types.
1615-
(void)diagnoseNonConcurrentTypesInReference(
1616-
valueRef, getDeclContext(), loc,
1619+
if (result == AsyncMarkingResult::FoundAsync) {
1620+
// Check for non-concurrent types.
1621+
bool problemFound =
1622+
diagnoseNonConcurrentTypesInReference(
1623+
concDeclRef, getDeclContext(), declLoc,
16171624
ConcurrentReferenceKind::SynchronousAsAsyncCall);
1625+
if (problemFound)
1626+
result = AsyncMarkingResult::NotConcurrentValue;
1627+
}
16181628

1619-
return true;
1620-
}
1621-
1622-
return false;
1623-
};
1629+
return result;
1630+
}
16241631

1632+
/// Check a reference to an entity within a global actor.
1633+
bool checkGlobalActorReference(
1634+
ConcreteDeclRef valueRef, SourceLoc loc, Type globalActor,
1635+
bool isCrossActor,
1636+
Expr *context) {
1637+
ValueDecl *value = valueRef.getDecl();
16251638
auto declContext = getDeclContext();
16261639

16271640
// Check whether we are within the same isolation context, in which
@@ -1641,7 +1654,8 @@ namespace {
16411654

16421655
switch (contextIsolation) {
16431656
case ActorIsolation::ActorInstance: {
1644-
if (inspectForImplicitlyAsync())
1657+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1658+
if (result == AsyncMarkingResult::FoundAsync)
16451659
return false;
16461660

16471661
auto useKind = static_cast<unsigned>(
@@ -1659,7 +1673,8 @@ namespace {
16591673
case ActorIsolation::GlobalActorUnsafe: {
16601674
// Check if this decl reference is the callee of the enclosing Apply,
16611675
// making it OK as an implicitly async call.
1662-
if (inspectForImplicitlyAsync())
1676+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1677+
if (result == AsyncMarkingResult::FoundAsync)
16631678
return false;
16641679

16651680
auto useKind = static_cast<unsigned>(
@@ -1679,7 +1694,8 @@ namespace {
16791694
return false;
16801695

16811696
case ActorIsolation::Independent: {
1682-
if (inspectForImplicitlyAsync())
1697+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1698+
if (result == AsyncMarkingResult::FoundAsync)
16831699
return false;
16841700

16851701
auto useKind = static_cast<unsigned>(
@@ -1695,7 +1711,8 @@ namespace {
16951711

16961712
case ActorIsolation::Unspecified: {
16971713
// NOTE: we must always inspect for implicitlyAsync
1698-
bool implicitlyAsyncExpr = inspectForImplicitlyAsync();
1714+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1715+
bool implicitlyAsyncExpr = (result == AsyncMarkingResult::FoundAsync);
16991716
bool didEmitDiagnostic = false;
17001717

17011718
auto emitError = [&](bool justNote = false) {
@@ -1938,47 +1955,15 @@ namespace {
19381955
}
19391956

19401957
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-
19761958
// Must reference actor-isolated state on 'self'.
19771959
auto *selfVar = getReferencedSelf(base);
19781960
if (!selfVar) {
19791961
// Check for implicit async.
1980-
if (auto result = checkImplicitlyAsync())
1981-
return *result;
1962+
auto result = tryMarkImplicitlyAsync(memberLoc, memberRef, context);
1963+
if (result == AsyncMarkingResult::FoundAsync)
1964+
return false; // no problems
1965+
else if (result == AsyncMarkingResult::NotConcurrentValue)
1966+
return true;
19821967

19831968
auto useKind = static_cast<unsigned>(
19841969
kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read));
@@ -2017,9 +2002,11 @@ namespace {
20172002
return false;
20182003

20192004
case ActorIsolation::Independent: {
2020-
// Check for implicit async.
2021-
if (auto result = checkImplicitlyAsync())
2022-
return *result;
2005+
auto result = tryMarkImplicitlyAsync(memberLoc, memberRef, context);
2006+
if (result == AsyncMarkingResult::FoundAsync)
2007+
return false; // no problems
2008+
else if (result == AsyncMarkingResult::NotConcurrentValue)
2009+
return true;
20232010

20242011
// The 'self' is for an actor-independent member, which means
20252012
// we cannot refer to actor-isolated state.
@@ -2033,20 +2020,22 @@ namespace {
20332020
}
20342021

20352022
case ActorIsolation::GlobalActor:
2036-
case ActorIsolation::GlobalActorUnsafe:
2037-
// Check for implicit async.
2038-
if (auto result = checkImplicitlyAsync())
2039-
return *result;
2023+
case ActorIsolation::GlobalActorUnsafe: {
2024+
auto result = tryMarkImplicitlyAsync(memberLoc, memberRef, context);
2025+
if (result == AsyncMarkingResult::FoundAsync)
2026+
return false; // no problems
2027+
else if (result == AsyncMarkingResult::NotConcurrentValue)
2028+
return true;
20402029

20412030
// The 'self' is for a member that's part of a global actor, which
20422031
// 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());
2032+
ctx.Diags.diagnose(memberLoc,
2033+
diag::actor_isolated_global_actor_context,
2034+
member->getDescriptiveKind(), member->getName(),
2035+
contextIsolation.getGlobalActor());
20482036
noteIsolatedActorMember(member, context);
20492037
return true;
2038+
}
20502039
}
20512040
llvm_unreachable("Unhandled actor isolation");
20522041
}

0 commit comments

Comments
 (0)