Skip to content

Commit 03321b6

Browse files
author
Adam Cmiel
committed
[Sema] Permit dot reference with isolation change without call
A dot-reference of a method defined on `Self` (as well as through `self`) should be permitted to be made in a different actor isolation than the referenced function's actor isolation if a call is not yet made, as the DeclRefExpr can store the isolation of the referenced decl. That said, we currently can only express that known isolation with global actor annotations until the language adopts closure isolation control.
1 parent f042eb8 commit 03321b6

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,14 +3472,16 @@ namespace {
34723472
}
34733473
}
34743474

3475-
// Check for an 'async let' autoclosure.
34763475
if (auto autoclosure = dyn_cast<AutoClosureExpr>(dc)) {
34773476
switch (autoclosure->getThunkKind()) {
34783477
case AutoClosureExpr::Kind::AsyncLet:
3478+
// Check for an 'async let' autoclosure.
34793479
return ReferencedActor(var, isPotentiallyIsolated, ReferencedActor::AsyncLet);
3480-
34813480
case AutoClosureExpr::Kind::DoubleCurryThunk:
34823481
case AutoClosureExpr::Kind::SingleCurryThunk:
3482+
// Curried thunks inherit actor isolation but referencing the thunked
3483+
// fn shouldn't cause a data race until the fn is actually applied
3484+
return ReferencedActor(var, isPotentiallyIsolated, specificNonIsoClosureKind(dc));
34833485
case AutoClosureExpr::Kind::None:
34843486
break;
34853487
}

test/Concurrency/actor_isolation.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,3 +1723,44 @@ class InferIsolationViaOverride: SuperWithIsolatedMethod {
17231723
// expected-error@-1 {{call to main actor-isolated instance method 'isolatedMethod()' in a synchronous nonisolated context}}
17241724
}
17251725
}
1726+
1727+
struct ReferenceSelfDotMethods {
1728+
@MainActor
1729+
func mainActorAffinedFunction() {}
1730+
1731+
nonisolated
1732+
private func testCurry() -> (Self) -> (@MainActor () -> Void) {
1733+
let functionRef = Self.mainActorAffinedFunction
1734+
// warning goes away with InferSendableFromCaptures, see actor_isolation_swift6.swift
1735+
return functionRef // expected-warning {{converting non-sendable function value to '@MainActor @Sendable () -> Void' may introduce data races}}
1736+
}
1737+
1738+
@MainActor
1739+
private func callOnMainActorOk() {
1740+
let mainActorAffinedClosure = testCurry()(self)
1741+
mainActorAffinedClosure()
1742+
}
1743+
1744+
nonisolated
1745+
private func nonisolatedCallErrors() {
1746+
let mainActorAffinedClosure = testCurry()(self)
1747+
// expected-note@-1 {{calls to let 'mainActorAffinedClosure' from outside of its actor context are implicitly asynchronous}}
1748+
mainActorAffinedClosure()
1749+
// expected-error@-1 {{call to main actor-isolated let 'mainActorAffinedClosure' in a synchronous nonisolated context}}
1750+
}
1751+
}
1752+
1753+
actor UserDefinedActorSelfDotMethod {
1754+
func actorAffinedFunc() {} // expected-note {{calls to instance method 'actorAffinedFunc()' from outside of its actor context are implicitly asynchronous}}
1755+
1756+
// Unfortunately we can't express the desired isolation of this returned closure statically to
1757+
// be able to call it on the desired actor. This may be possible with the acceptance of
1758+
// https://forums.swift.org/t/closure-isolation-control/70378 but I think we need more expressivity
1759+
// in the type system to express this sort of curry.
1760+
nonisolated
1761+
private func testCurry() -> (UserDefinedActorSelfDotMethod) -> (@isolated(any) () -> Void) {
1762+
let functionRef = Self.actorAffinedFunc // expected-error {{call to actor-isolated instance method 'actorAffinedFunc()' in a synchronous nonisolated context}}
1763+
// error message changes with InferSendabaleFromCaptures - see actor_isolation_swift6.swift
1764+
return functionRef // expected-error {{cannot convert return expression of type '(isolated Self) -> () -> ()' to return type '(UserDefinedActorSelfDotMethod) -> @isolated(any) () -> Void'}}
1765+
}
1766+
}

test/Concurrency/actor_isolation_swift6.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,42 @@ nonisolated func accessAcrossActors() {
9595
// expected-error@+1 {{main actor-isolated static property 'shared' can not be referenced from a nonisolated context}}
9696
let _ = MainActorIsolated.shared
9797
}
98+
99+
struct ReferenceSelfDotMethods {
100+
@MainActor
101+
func mainActorAffinedFunction() {}
102+
103+
nonisolated
104+
private func testCurry() -> (Self) -> (@MainActor () -> Void) {
105+
let functionRef = Self.mainActorAffinedFunction
106+
return functionRef
107+
}
108+
109+
@MainActor
110+
private func callOnMainActorOk() {
111+
let mainActorAffinedClosure = testCurry()(self)
112+
mainActorAffinedClosure()
113+
}
114+
115+
nonisolated
116+
private func nonisolatedCallErrors() {
117+
let mainActorAffinedClosure = testCurry()(self)
118+
// expected-note@-1 {{calls to let 'mainActorAffinedClosure' from outside of its actor context are implicitly asynchronous}}
119+
mainActorAffinedClosure()
120+
// expected-error@-1 {{call to main actor-isolated let 'mainActorAffinedClosure' in a synchronous nonisolated context}}
121+
}
122+
}
123+
124+
actor UserDefinedActorSelfDotMethod {
125+
func actorAffinedFunc() {} // expected-note {{calls to instance method 'actorAffinedFunc()' from outside of its actor context are implicitly asynchronous}}
126+
127+
// Unfortunately we can't express the desired isolation of this returned closure statically to
128+
// be able to call it on the desired actor. This may be possible with the acceptance of
129+
// https://forums.swift.org/t/closure-isolation-control/70378 but I think we need more expressivity
130+
// in the type system to express this sort of curry.
131+
nonisolated
132+
private func testCurry() -> (UserDefinedActorSelfDotMethod) -> (@isolated(any) () -> Void) {
133+
let functionRef = Self.actorAffinedFunc // expected-error {{call to actor-isolated instance method 'actorAffinedFunc()' in a synchronous nonisolated context}}
134+
return functionRef // expected-error {{cannot convert return expression of type '@Sendable (isolated Self) -> @Sendable () -> ()' to return type '(UserDefinedActorSelfDotMethod) -> @isolated(any) () -> Void'}}
135+
}
136+
}

0 commit comments

Comments
 (0)