Files
swift-mirror/test/Concurrency/predates_concurrency.swift
Kavon Farvardin 3f6a0ccb90 Allow a global-actor to be dropped for some non-async functions.
It's ok to drop the global-actor qualifier `@G` from a function's type if:

- the cast is happening in a context isolated to global-actor `G`
- the function value will not be `@Sendable`
- the function value is not `async`

It's primarily safe to drop the attribute because we're already in the
same isolation domain. So it's OK to simply drop the global-actor
if we prevent the value from later leaving that isolation domain.
This means we no longer need to warn about code like this:

```
@MainActor func doIt(_ x: [Int], _ f: @MainActor (Int) -> ()) {
  x.forEach(f)
// warning: converting function value of type '@MainActor (Int) -> ()' to '(Int) throws -> Void' loses global actor 'MainActor'
}
```

NOTE: this implementation is a bit gross in that the constraint solver
might emit false warnings about casts it introduced that are actually
safe. This is mainly because closure isolation is only fully determined
after constraint solving. See the FIXME's for more details.

resolves rdar://94462333
2023-01-05 17:54:00 -08:00

182 lines
7.0 KiB
Swift

// RUN: %target-typecheck-verify-swift -disable-availability-checking
// REQUIRES: concurrency
@preconcurrency func unsafelySendableClosure(_ closure: @Sendable () -> Void) { }
@preconcurrency func unsafelyMainActorClosure(_ closure: @MainActor () -> Void) { }
@preconcurrency func unsafelyDoEverythingClosure(_ closure: @MainActor @Sendable () -> Void) { }
struct X {
@preconcurrency func unsafelyDoEverythingClosure(_ closure: @MainActor @Sendable () -> Void) { }
@preconcurrency var sendableVar: @Sendable () -> Void
@preconcurrency var mainActorVar: @MainActor () -> Void
@preconcurrency
subscript(_: @MainActor () -> Void) -> (@Sendable () -> Void) { {} }
@preconcurrency
static subscript(statically _: @MainActor () -> Void) -> (@Sendable () -> Void) { { } }
}
@MainActor func onMainActor() { }
func testInAsync(x: X, plainClosure: () -> Void) async { // expected-note 2{{parameter 'plainClosure' is implicitly non-sendable}}
let _: Int = unsafelySendableClosure // expected-error{{type '(@Sendable () -> Void) -> ()'}}
let _: Int = unsafelyMainActorClosure // expected-error{{type '(@MainActor () -> Void) -> ()'}}
let _: Int = unsafelyDoEverythingClosure // expected-error{{type '(@MainActor @Sendable () -> Void) -> ()'}}
let _: Int = x.unsafelyDoEverythingClosure // expected-error{{type '(@MainActor @Sendable () -> Void) -> ()'}}
let _: Int = X.unsafelyDoEverythingClosure // expected-error{{type '(X) -> (@MainActor @Sendable () -> Void) -> ()'}}
let _: Int = (X.unsafelyDoEverythingClosure)(x) // expected-error{{type '(@MainActor @Sendable () -> Void) -> ()'}}
let _: Int = x.sendableVar // expected-error{{type '@Sendable () -> Void'}}
let _: Int = x.mainActorVar // expected-error{{type '@MainActor () -> Void'}}
let _: Int = x[{ onMainActor() }] // expected-error{{type '@Sendable () -> Void'}}
let _: Int = X[statically: { onMainActor() }] // expected-error{{type '@Sendable () -> Void'}}
unsafelySendableClosure(plainClosure) // expected-warning {{passing non-sendable parameter 'plainClosure' to function expecting a @Sendable closure}}
unsafelyMainActorClosure(plainClosure)
unsafelyDoEverythingClosure(plainClosure) // expected-warning {{passing non-sendable parameter 'plainClosure' to function expecting a @Sendable closure}}
}
func testElsewhere(x: X) {
let _: Int = unsafelySendableClosure // expected-error{{type '(() -> Void) -> ()'}}
let _: Int = unsafelyMainActorClosure // expected-error{{type '(() -> Void) -> ()'}}
let _: Int = unsafelyDoEverythingClosure // expected-error{{type '(() -> Void) -> ()'}}
let _: Int = x.unsafelyDoEverythingClosure // expected-error{{type '(() -> Void) -> ()'}}
let _: Int = X.unsafelyDoEverythingClosure // expected-error{{type '(X) -> (() -> Void) -> ()'}}
let _: Int = (X.unsafelyDoEverythingClosure)(x) // expected-error{{type '(() -> Void) -> ()'}}
let _: Int = x.sendableVar // expected-error{{type '() -> Void'}}
let _: Int = x.mainActorVar // expected-error{{type '() -> Void'}}
let _: Int = x[{ onMainActor() }] // expected-error{{type '() -> Void'}}
let _: Int = X[statically: { onMainActor() }] // expected-error{{type '() -> Void'}}
}
@MainActor @preconcurrency func onMainActorAlways() { }
@preconcurrency @MainActor class MyModelClass {
func f() { }
}
func testCalls(x: X) {
unsafelyMainActorClosure {
onMainActor()
}
unsafelyDoEverythingClosure {
onMainActor()
}
x.unsafelyDoEverythingClosure {
onMainActor()
onMainActorAlways()
}
(X.unsafelyDoEverythingClosure)(x)( {
onMainActor()
})
onMainActorAlways() // okay, haven't adopted concurrency
let _: () -> Void = onMainActorAlways
// both okay
let c = MyModelClass()
c.f()
}
func testCallsWithAsync() async {
onMainActorAlways() // expected-error{{expression is 'async' but is not marked with 'await'}}
// expected-note@-1{{calls to global function 'onMainActorAlways()' from outside of its actor context are implicitly asynchronous}}
let _: () -> Void = onMainActorAlways // expected-warning 2{{converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'}}
let c = MyModelClass() // expected-error{{expression is 'async' but is not marked with 'await'}}
// expected-note@-1{{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}}
c.f() // expected-error{{expression is 'async' but is not marked with 'await'}}
// expected-note@-1{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}}
}
// ---------------------------------------------------------------------------
// Protocols that inherit Sendable and predate concurrency.
// ---------------------------------------------------------------------------
@preconcurrency protocol P: Sendable { }
protocol Q: P { }
class NS { } // expected-note{{class 'NS' does not conform to the 'Sendable' protocol}}
struct S1: P {
var ns: NS
}
struct S2: Q {
var ns: NS
}
struct S3: Q, Sendable {
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'S3' has non-sendable type 'NS'}}
}
// ---------------------------------------------------------------------------
// Historical attribute names do nothing (but are permitted)
// ---------------------------------------------------------------------------
func aFailedExperiment(@_unsafeSendable _ body: @escaping () -> Void) { }
// expected-warning@-1{{'_unsafeSendable' attribute has been removed in favor of @preconcurrency}}
func anothingFailedExperiment(@_unsafeMainActor _ body: @escaping () -> Void) { }
// expected-warning@-1{{'_unsafeMainActor' attribute has been removed in favor of @preconcurrency}}
// ---------------------------------------------------------------------------
// Random bugs
// ---------------------------------------------------------------------------
public enum StringPlacement : Sendable {
public typealias StringPosition = @Sendable (_: [String]) -> Int
@preconcurrency
public static func position(before string: String) -> StringPosition {
return { _ in 0 }
}
@preconcurrency
public static func position(after string: String) -> StringPosition {
return { _ in 0 }
}
}
func testStringPlacement() {
let fn1 = StringPlacement.position(before: "Test")
let _: Int = fn1 // expected-error{{cannot convert value of type '([String]) -> Int' to specified type 'Int'}}
let fn2 = StringPlacement.position(before:)
let _: Int = fn2 // expected-error{{cannot convert value of type '(String) -> ([String]) -> Int' to specified type 'Int'}}
}
// @preconcurrency in an outer closure
// (https://github.com/apple/swift/issues/59910)
struct Scheduled<T> { }
@preconcurrency
func doPreconcurrency(_: @Sendable () -> Void) { }
class EventLoop {
@discardableResult
@preconcurrency
func scheduleTask<T>(deadline: Int, _ task: @escaping @Sendable () throws -> T) -> Scheduled<T> { fatalError("") }
}
class C {
var ev: EventLoop? = nil
func test(i: Int) {
func doNext() {
doPreconcurrency {
self.ev?.scheduleTask(deadline: i, doNext)
return
}
}
}
}