Skip to content

Commit ef7e22c

Browse files
authored
Merge pull request #41923 from DougGregor/isolated-match-may-not-execute-concurrently-5.6
[SE-0338] Contexts with the same actor isolation never execute concurrently
2 parents b9f6709 + 04d62c5 commit ef7e22c

File tree

3 files changed

+61
-4
lines changed

3 files changed

+61
-4
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,20 @@ class ActorIsolation {
109109

110110
bool isIndependent() const { return kind == Independent; }
111111

112+
bool isActorIsolated() const {
113+
switch (getKind()) {
114+
case ActorInstance:
115+
case DistributedActorInstance:
116+
case GlobalActor:
117+
case GlobalActorUnsafe:
118+
return true;
119+
120+
case Unspecified:
121+
case Independent:
122+
return false;
123+
}
124+
}
125+
112126
NominalTypeDecl *getActor() const {
113127
assert(getKind() == ActorInstance || getKind() == DistributedActorInstance);
114128
return actor;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2860,6 +2860,21 @@ namespace {
28602860

28612861
bool ActorIsolationChecker::mayExecuteConcurrentlyWith(
28622862
const DeclContext *useContext, const DeclContext *defContext) {
2863+
// Fast path for when the use and definition contexts are the same.
2864+
if (useContext == defContext)
2865+
return false;
2866+
2867+
// If both contexts are isolated to the same actor, then they will not
2868+
// execute concurrently.
2869+
auto useIsolation = getActorIsolationOfContext(
2870+
const_cast<DeclContext *>(useContext));
2871+
if (useIsolation.isActorIsolated()) {
2872+
auto defIsolation = getActorIsolationOfContext(
2873+
const_cast<DeclContext *>(defContext));
2874+
if (useIsolation == defIsolation)
2875+
return false;
2876+
}
2877+
28632878
// Walk the context chain from the use to the definition.
28642879
while (useContext != defContext) {
28652880
// If we find a concurrent closure... it can be run concurrently.

test/Concurrency/actor_isolation.swift

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -979,31 +979,59 @@ actor MyServer : Server {
979979
// ----------------------------------------------------------------------
980980
// @_inheritActorContext
981981
// ----------------------------------------------------------------------
982+
@available(SwiftStdlib 5.1, *)
982983
func acceptAsyncSendableClosure<T>(_: @Sendable () async -> T) { }
984+
@available(SwiftStdlib 5.1, *)
983985
func acceptAsyncSendableClosureInheriting<T>(@_inheritActorContext _: @Sendable () async -> T) { }
984986

985987
@available(SwiftStdlib 5.1, *)
986988
extension MyActor {
987989
func testSendableAndInheriting() {
990+
var counter = 0
991+
988992
acceptAsyncSendableClosure {
989-
synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}
993+
_ = synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}
990994
// expected-note@-1{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
995+
996+
counter += 1 // expected-error{{mutation of captured var 'counter' in concurrently-executing code}}
991997
}
992998

993999
acceptAsyncSendableClosure {
994-
await synchronous() // ok
1000+
_ = await synchronous() // ok
1001+
counter += 1 // expected-error{{mutation of captured var 'counter' in concurrently-executing code}}
9951002
}
9961003

9971004
acceptAsyncSendableClosureInheriting {
998-
synchronous() // okay
1005+
_ = synchronous() // okay
1006+
counter += 1 // okay
9991007
}
10001008

10011009
acceptAsyncSendableClosureInheriting {
1002-
await synchronous() // expected-warning{{no 'async' operations occur within 'await' expression}}
1010+
_ = await synchronous() // expected-warning{{no 'async' operations occur within 'await' expression}}
1011+
counter += 1 // okay
10031012
}
10041013
}
10051014
}
10061015

1016+
@available(SwiftStdlib 5.1, *)
1017+
@SomeGlobalActor
1018+
func testGlobalActorInheritance() {
1019+
var counter = 0
1020+
1021+
acceptAsyncSendableClosure {
1022+
counter += 1 // expected-error{{mutation of captured var 'counter' in concurrently-executing code}}
1023+
}
1024+
1025+
acceptAsyncSendableClosure { @SomeGlobalActor in
1026+
counter += 1 // ok
1027+
}
1028+
1029+
1030+
acceptAsyncSendableClosureInheriting {
1031+
counter += 1 // ok
1032+
}
1033+
}
1034+
10071035
@available(SwiftStdlib 5.1, *)
10081036
@MainActor // expected-note {{'GloballyIsolatedProto' is isolated to global actor 'MainActor' here}}
10091037
protocol GloballyIsolatedProto {

0 commit comments

Comments
 (0)