mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The present approach is not prudent because `@concurrent` synchronous
functions, a natural extension, are a likely-to-happen future direction,
whereas the current inference rule is entirely grounded on `@concurrent`
being exclusive to async functions.
If we were to ship this rule, we would have to keep the promise for
backwards compatibility when implementing the aforementioned future
direction, replacing one inconsistency with another, and possibly
introducing new bug-prone expression checking code.
```swift
func foo(_: () -> Void) {}
func foo(_: () async -> Void) {}
// In a future without this change and `@concurrent` synchronous
// functions accepted, the first call resolves to the first overload,
// and the second call resolves to the second, despite `@concurrent` no
// longer implying `async`.
foo { }
foo { @concurrent in }
```
This change also drops the fix-it for removing `@concurrent` when used
on a synchronous closure. With the inference rule gone, and the
diagnosis delayed until after solution application, this error raises a
fairly balanced choice between removing the attribute and being
explicit about the effect, where a unilateral suggestion is quite
possibly more harmful than useful.
(cherry picked from commit 58d5059617)
165 lines
12 KiB
Swift
165 lines
12 KiB
Swift
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple
|
|
|
|
// REQUIRES: concurrency
|
|
|
|
@globalActor
|
|
actor MyActor {
|
|
static let shared = MyActor()
|
|
}
|
|
|
|
@concurrent
|
|
func concurrentTest() async {
|
|
}
|
|
|
|
nonisolated(nonsending)
|
|
func callerTest() async {
|
|
}
|
|
|
|
@MainActor
|
|
func actorIsolated() async {}
|
|
|
|
let _: nonisolated(nonsending) () async -> Void = concurrentTest // Ok
|
|
let _: @concurrent () async -> Void = callerTest // Ok
|
|
|
|
let _: @MainActor () async -> Void = concurrentTest // Ok
|
|
let _: @MainActor () async -> Void = callerTest // Ok
|
|
|
|
let _: @isolated(any) () async -> Void = concurrentTest // Ok
|
|
let _: @isolated(any) () async -> Void = callerTest
|
|
// expected-error@-1 {{cannot convert value of type 'nonisolated(nonsending) () async -> ()' to specified type '@isolated(any) () async -> Void'}}
|
|
|
|
let _: nonisolated(nonsending) () async -> Void = actorIsolated // Ok
|
|
let _: @concurrent () async -> Void = actorIsolated // Ok
|
|
|
|
func testIsolationErasure(fn: @escaping @isolated(any) () async -> Void) {
|
|
let _: @concurrent () async -> Void = fn // Ok
|
|
let _: nonisolated(nonsending) () async -> Void = fn // Ok
|
|
}
|
|
|
|
func testUpcast(arr: [nonisolated(nonsending) () async -> Void]) {
|
|
let _: [() async -> Void] = arr // Ok - collection upcast
|
|
let _: [String: () async -> Void] = ["": arr]
|
|
// expected-error@-1 {{cannot convert value of type '[nonisolated(nonsending) () async -> Void]' to expected dictionary value type '() async -> Void'}}
|
|
}
|
|
|
|
// Isolated parameter
|
|
func testParameterIsolation(fn: @escaping (isolated (any Actor)?) async -> Void, caller: nonisolated(nonsending) @escaping (String) async -> Void) {
|
|
let _: nonisolated(nonsending) () async -> Void = fn
|
|
// expected-error@-1 {{cannot convert value of type '(isolated (any Actor)?) async -> Void' to specified type 'nonisolated(nonsending) () async -> Void'}}
|
|
let _: @concurrent () async -> Void = fn
|
|
// expected-error@-1 {{cannot convert value of type '(isolated (any Actor)?) async -> Void' to specified type '() async -> Void'}}
|
|
|
|
let _: (isolated (any Actor)?) async -> Void = callerTest // Ok
|
|
let _: (isolated (any Actor)?) -> Void = callerTest
|
|
// expected-error@-1 {{invalid conversion from 'async' function of type 'nonisolated(nonsending) () async -> ()' to synchronous function type '(isolated (any Actor)?) -> Void'}}
|
|
let _: (isolated (any Actor)?) async -> Void = concurrentTest
|
|
// expected-error@-1 {{cannot convert value of type '() async -> ()' to specified type '(isolated (any Actor)?) async -> Void'}}
|
|
let _: (isolated (any Actor)?, Int) async -> Void = callerTest
|
|
// expected-error@-1 {{cannot convert value of type 'nonisolated(nonsending) () async -> ()' to specified type '(isolated (any Actor)?, Int) async -> Void'}}
|
|
|
|
let _: (String, isolated any Actor) async -> Void = caller // Ok
|
|
let _: (isolated (any Actor)?, String) async -> Void = caller // Ok
|
|
}
|
|
|
|
// Non-conversion situations
|
|
do {
|
|
struct S<T> {
|
|
}
|
|
func test<T>(_: S<T>, _: T.Type) {}
|
|
|
|
test(S<() async -> Void>(), type(of: callerTest))
|
|
// expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '(() async -> Void).Type'}}
|
|
|
|
test(S<nonisolated(nonsending) () async -> Void>(), type(of: concurrentTest))
|
|
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(nonisolated(nonsending) () async -> Void).Type'}}
|
|
|
|
test(S<@MainActor () async -> Void>(), type(of: callerTest))
|
|
// expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '(@MainActor () async -> Void).Type'}}
|
|
|
|
test(S<@MainActor () async -> Void>(), type(of: concurrentTest))
|
|
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@MainActor () async -> Void).Type'}}
|
|
|
|
test(S<(isolated (any Actor)?) async -> Void>(), type(of: callerTest))
|
|
// expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '((isolated (any Actor)?) async -> Void).Type'}}
|
|
|
|
test(S<(isolated (any Actor)?) async -> Void>(), type(of: concurrentTest))
|
|
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '((isolated (any Actor)?) async -> Void).Type'}}
|
|
|
|
test(S<@isolated(any) () async -> Void>(), type(of: concurrentTest))
|
|
// expected-error@-1 {{cannot convert value of type '(() async -> ()).Type' to expected argument type '(@isolated(any) () async -> Void).Type'}}
|
|
test(S<@isolated(any) () async -> Void>(), type(of: callerTest))
|
|
// expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to expected argument type '(@isolated(any) () async -> Void).Type'}}
|
|
}
|
|
|
|
do {
|
|
let _: () -> Void = { @concurrent in
|
|
// expected-error@-1 {{cannot use @concurrent on non-async closure}}{{none}}
|
|
}
|
|
}
|
|
|
|
// Converting to `nonisolated(nonsending)` function
|
|
class NonSendable {}
|
|
|
|
func testNonSendableDiagnostics(
|
|
globalActor1: @escaping @Sendable @MainActor (NonSendable) async -> Void,
|
|
globalActor2: @escaping @Sendable @MainActor () async -> NonSendable,
|
|
erased1: @escaping @Sendable @isolated(any) (NonSendable) async -> Void,
|
|
erased2: @escaping @Sendable @isolated(any) () async -> NonSendable,
|
|
nonIsolated1: @escaping @Sendable (NonSendable) -> Void,
|
|
nonIsolated2: @escaping @Sendable @concurrent (NonSendable) async -> Void,
|
|
nonIsolated3: @escaping @Sendable () -> NonSendable,
|
|
nonIsolated4: @escaping @Sendable @concurrent () async -> NonSendable,
|
|
caller1: nonisolated(nonsending) @escaping @Sendable (NonSendable) async -> Void,
|
|
caller2: nonisolated(nonsending) @escaping @Sendable () async -> NonSendable
|
|
) {
|
|
let _: nonisolated(nonsending) (NonSendable) async -> Void = globalActor1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-error@-1 {{cannot convert '@MainActor @Sendable (NonSendable) async -> Void' to 'nonisolated(nonsending) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
let _: nonisolated(nonsending) () async -> NonSendable = globalActor2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-error@-1 {{cannot convert '@MainActor @Sendable () async -> NonSendable' to 'nonisolated(nonsending) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: @concurrent (NonSendable) async -> Void = globalActor1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert '@MainActor @Sendable (NonSendable) async -> Void' to '(NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
let _: @concurrent () async -> NonSendable = globalActor2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert '@MainActor @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: nonisolated(nonsending) (NonSendable) async -> Void = erased1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-error@-1 {{cannot convert '@isolated(any) @Sendable (NonSendable) async -> Void' to 'nonisolated(nonsending) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
let _: nonisolated(nonsending) () async -> NonSendable = erased2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-error@-1 {{cannot convert '@isolated(any) @Sendable () async -> NonSendable' to 'nonisolated(nonsending) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: nonisolated(nonsending) (NonSendable) async -> Void = nonIsolated1 // Ok
|
|
let _: nonisolated(nonsending) (NonSendable) async -> Void = nonIsolated2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-error@-1 {{cannot convert '@Sendable (NonSendable) async -> Void' to 'nonisolated(nonsending) (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: nonisolated(nonsending) () async -> NonSendable = nonIsolated3 // Ok
|
|
let _: nonisolated(nonsending) () async -> NonSendable = nonIsolated4 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-error@-1 {{cannot convert '@Sendable () async -> NonSendable' to 'nonisolated(nonsending) () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: @concurrent (NonSendable) async -> Void = erased1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert '@isolated(any) @Sendable (NonSendable) async -> Void' to '(NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
let _: @concurrent () async -> NonSendable = erased2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert '@isolated(any) @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
|
|
let _: @concurrent (NonSendable) async -> Void = caller1 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert 'nonisolated(nonsending) @Sendable (NonSendable) async -> Void' to '(NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
let _: @concurrent () async -> NonSendable = caller2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert 'nonisolated(nonsending) @Sendable () async -> NonSendable' to '() async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: @MainActor (NonSendable) async -> Void = nonIsolated1 // Ok
|
|
let _: @MainActor (NonSendable) async -> Void = nonIsolated2 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert '@Sendable (NonSendable) async -> Void' to '@MainActor (NonSendable) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: @MainActor () async -> NonSendable = nonIsolated3 // Ok
|
|
let _: @MainActor () async -> NonSendable = nonIsolated4 // expected-note {{type 'NonSendable' does not conform to 'Sendable' protocol}}
|
|
// expected-warning@-1 {{cannot convert '@Sendable () async -> NonSendable' to '@MainActor () async -> NonSendable' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol}}
|
|
|
|
let _: @MainActor (NonSendable) async -> Void = caller1 // Ok
|
|
let _: @MainActor () async -> NonSendable = caller2 // Ok
|
|
|
|
let _: @MyActor (NonSendable) async -> Void = globalActor1
|
|
// expected-error@-1 {{cannot convert value actor-isolated to 'MainActor' to specified type actor-isolated to 'MyActor'}}
|
|
let _: @MyActor () async -> NonSendable = globalActor2
|
|
// expected-error@-1 {{cannot convert value actor-isolated to 'MainActor' to specified type actor-isolated to 'MyActor'}}
|
|
}
|