Skip to content

Commit 51941e9

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 773561c commit 51941e9

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3288,6 +3288,7 @@ namespace {
32883288

32893289
case AutoClosureExpr::Kind::DoubleCurryThunk:
32903290
case AutoClosureExpr::Kind::SingleCurryThunk:
3291+
return ReferencedActor(var, isPotentiallyIsolated, specificNonIsoClosureKind(dc));
32913292
case AutoClosureExpr::Kind::None:
32923293
break;
32933294
}

test/Concurrency/actor_isolation.swift

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

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)