Skip to content

Commit 75a9f4f

Browse files
authored
Merge pull request #36328 from kavon/actor-effectful-properties-pt2
2 parents a64c105 + 6a063a2 commit 75a9f4f

File tree

8 files changed

+173
-137
lines changed

8 files changed

+173
-137
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4298,20 +4298,21 @@ ERROR(actor_isolated_mutating_func,none,
42984298
"cannot call mutating async function %0 on actor-isolated %1 %2",
42994299
(DeclName, DescriptiveDeclKind, DeclName))
43004300
ERROR(actor_isolated_global_actor_context,none,
4301-
"actor-isolated %0 %1 can not be referenced from context of global "
4302-
"actor %2",
4303-
(DescriptiveDeclKind, DeclName, Type))
4301+
"actor-isolated %0 %1 can not be %select{referenced|mutated|used 'inout'}3 "
4302+
"from%select{| synchronous}4 context of global actor %2",
4303+
(DescriptiveDeclKind, DeclName, Type, unsigned, bool))
43044304
ERROR(global_actor_from_instance_actor_context,none,
4305-
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4 from actor %3",
4306-
(DescriptiveDeclKind, DeclName, Type, DeclName, unsigned))
4305+
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4306+
" from actor %3 %select{|in a synchronous context}5",
4307+
(DescriptiveDeclKind, DeclName, Type, DeclName, unsigned, bool))
43074308
ERROR(global_actor_from_other_global_actor_context,none,
43084309
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4309-
" from different global actor %3",
4310-
(DescriptiveDeclKind, DeclName, Type, Type, unsigned))
4310+
" from different global actor %3 %select{|in a synchronous context}5",
4311+
(DescriptiveDeclKind, DeclName, Type, Type, unsigned, bool))
43114312
ERROR(global_actor_from_nonactor_context,none,
43124313
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
4313-
" from %select{this|an '@actorIndependent'}3 context",
4314-
(DescriptiveDeclKind, DeclName, Type, bool, unsigned))
4314+
" from %select{this|an '@actorIndependent'}3%select{| synchronous}5 context",
4315+
(DescriptiveDeclKind, DeclName, Type, bool, unsigned, bool))
43154316
ERROR(actor_isolated_partial_apply,none,
43164317
"actor-isolated %0 %1 can not be partially applied",
43174318
(DescriptiveDeclKind, DeclName))

lib/SILGen/SILGenExpr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,7 +2198,7 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *e,
21982198

21992199
RValue RValueEmitter::visitDynamicMemberRefExpr(DynamicMemberRefExpr *E,
22002200
SGFContext C) {
2201-
assert(!E->isImplicitlyAsync() && "actors do not have @objc members");
2201+
assert(!E->isImplicitlyAsync() && "an actor-isolated @objc member?");
22022202
return SGF.emitDynamicMemberRefExpr(E, C);
22032203
}
22042204

@@ -2220,7 +2220,7 @@ RValue RValueEmitter::visitSubscriptExpr(SubscriptExpr *E, SGFContext C) {
22202220

22212221
RValue RValueEmitter::visitDynamicSubscriptExpr(
22222222
DynamicSubscriptExpr *E, SGFContext C) {
2223-
assert(!E->isImplicitlyAsync() && "actors do not have @objc members");
2223+
assert(!E->isImplicitlyAsync() && "an actor-isolated @objc member?");
22242224
return SGF.emitDynamicSubscriptExpr(E, C);
22252225
}
22262226

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 117 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,70 +1558,95 @@ 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();
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+
};
15671567

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;
15761575

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) {
15781580

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;
15911586
}
1592-
}
1587+
} else if (auto lookupExpr = dyn_cast_or_null<LookupExpr>(context)) {
1588+
if (usageEnv(lookupExpr) == VarRefUseEnv::Read) {
15931589

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;
16101595
}
16111596
}
16121597

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.
16181602

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+
}
16201628
}
1629+
}
16211630

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+
}
16241640

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();
16251650
auto declContext = getDeclContext();
16261651

16271652
// Check whether we are within the same isolation context, in which
@@ -1641,7 +1666,8 @@ namespace {
16411666

16421667
switch (contextIsolation) {
16431668
case ActorIsolation::ActorInstance: {
1644-
if (inspectForImplicitlyAsync())
1669+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1670+
if (result == AsyncMarkingResult::FoundAsync)
16451671
return false;
16461672

16471673
auto useKind = static_cast<unsigned>(
@@ -1650,7 +1676,7 @@ namespace {
16501676
ctx.Diags.diagnose(loc, diag::global_actor_from_instance_actor_context,
16511677
value->getDescriptiveKind(), value->getName(),
16521678
globalActor, contextIsolation.getActor()->getName(),
1653-
useKind);
1679+
useKind, result == AsyncMarkingResult::SyncContext);
16541680
noteIsolatedActorMember(value, context);
16551681
return true;
16561682
}
@@ -1659,7 +1685,8 @@ namespace {
16591685
case ActorIsolation::GlobalActorUnsafe: {
16601686
// Check if this decl reference is the callee of the enclosing Apply,
16611687
// making it OK as an implicitly async call.
1662-
if (inspectForImplicitlyAsync())
1688+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1689+
if (result == AsyncMarkingResult::FoundAsync)
16631690
return false;
16641691

16651692
auto useKind = static_cast<unsigned>(
@@ -1669,7 +1696,8 @@ namespace {
16691696
ctx.Diags.diagnose(
16701697
loc, diag::global_actor_from_other_global_actor_context,
16711698
value->getDescriptiveKind(), value->getName(), globalActor,
1672-
contextIsolation.getGlobalActor(), useKind);
1699+
contextIsolation.getGlobalActor(), useKind,
1700+
result == AsyncMarkingResult::SyncContext);
16731701
noteIsolatedActorMember(value, context);
16741702
return true;
16751703
}
@@ -1679,7 +1707,8 @@ namespace {
16791707
return false;
16801708

16811709
case ActorIsolation::Independent: {
1682-
if (inspectForImplicitlyAsync())
1710+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1711+
if (result == AsyncMarkingResult::FoundAsync)
16831712
return false;
16841713

16851714
auto useKind = static_cast<unsigned>(
@@ -1688,14 +1717,16 @@ namespace {
16881717
ctx.Diags.diagnose(loc, diag::global_actor_from_nonactor_context,
16891718
value->getDescriptiveKind(), value->getName(),
16901719
globalActor,
1691-
/*actorIndependent=*/true, useKind);
1720+
/*actorIndependent=*/true, useKind,
1721+
result == AsyncMarkingResult::SyncContext);
16921722
noteIsolatedActorMember(value, context);
16931723
return true;
16941724
}
16951725

16961726
case ActorIsolation::Unspecified: {
16971727
// NOTE: we must always inspect for implicitlyAsync
1698-
bool implicitlyAsyncExpr = inspectForImplicitlyAsync();
1728+
auto result = tryMarkImplicitlyAsync(loc, valueRef, context);
1729+
bool implicitlyAsyncExpr = (result == AsyncMarkingResult::FoundAsync);
16991730
bool didEmitDiagnostic = false;
17001731

17011732
auto emitError = [&](bool justNote = false) {
@@ -1706,7 +1737,8 @@ namespace {
17061737
ctx.Diags.diagnose(
17071738
loc, diag::global_actor_from_nonactor_context,
17081739
value->getDescriptiveKind(), value->getName(), globalActor,
1709-
/*actorIndependent=*/false, useKind);
1740+
/*actorIndependent=*/false, useKind,
1741+
result == AsyncMarkingResult::SyncContext);
17101742
}
17111743
noteIsolatedActorMember(value, context);
17121744
};
@@ -1938,47 +1970,15 @@ namespace {
19381970
}
19391971

19401972
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-
19761973
// Must reference actor-isolated state on 'self'.
19771974
auto *selfVar = getReferencedSelf(base);
19781975
if (!selfVar) {
19791976
// 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;
19821982

19831983
auto useKind = static_cast<unsigned>(
19841984
kindOfUsage(member, context).getValueOr(VarRefUseEnv::Read));
@@ -2017,9 +2017,11 @@ namespace {
20172017
return false;
20182018

20192019
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;
20232025

20242026
// The 'self' is for an actor-independent member, which means
20252027
// we cannot refer to actor-isolated state.
@@ -2033,20 +2035,26 @@ namespace {
20332035
}
20342036

20352037
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;
20402044

20412045
// The 'self' is for a member that's part of a global actor, which
20422046
// 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+
);
20482055
noteIsolatedActorMember(member, context);
20492056
return true;
2057+
}
20502058
}
20512059
llvm_unreachable("Unhandled actor isolation");
20522060
}

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,16 @@ func blender(_ peeler : () -> Void) {
203203
await (true ? wisk : {n in return})(1)
204204
}
205205

206+
actor Chain {
207+
var next : Chain?
208+
}
209+
210+
func walkChain(chain : Chain) async {
211+
_ = chain.next?.next?.next?.next // expected-error 4 {{property access is 'async' but is not marked with 'await'}}
212+
_ = (await chain.next)?.next?.next?.next // expected-error 3 {{property access is 'async' but is not marked with 'await'}}
213+
_ = (await chain.next?.next)?.next?.next // expected-error 2 {{property access is 'async' but is not marked with 'await'}}
214+
}
215+
206216

207217
// want to make sure there is no note about implicitly async on this func.
208218
@BananaActor func rice() async {}

0 commit comments

Comments
 (0)