Files
swift-mirror/test/Concurrency/sendable_methods.swift
Pavel Yaskevich 0a49d2868c Partially revert: "[TypeChecker] TypeChecker::isSubtypeOf should recognize @sendable subtyping"
Reverts code chagnes introduced by 5626881da1 but leaves (modified) test-cases

This approach regressed existing ternary expressions that join to `any Sendable`
and one branch is inferred from the ternary type variable.
2024-04-05 00:23:52 -07:00

263 lines
7.3 KiB
Swift

// RUN: %target-typecheck-verify-swift -enable-upcoming-feature InferSendableFromCaptures -disable-availability-checking
// RUN: %target-swift-emit-silgen %s -verify -enable-upcoming-feature InferSendableFromCaptures -disable-availability-checking -module-name sendable_methods | %FileCheck %s
// REQUIRES: concurrency
// REQUIRES: asserts
func outer() {
@Sendable func sendable() {}
func nonSendable() {}
let _ : @Sendable () -> Void = sendable
let _ : @Sendable () -> Void = nonSendable // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races}}
}
final class C : Sendable {
func f() {}
}
struct S : Sendable {
func f(){}
}
enum E : Sendable {
case a, b, c
func f() {}
}
protocol P : Sendable {
init()
}
final class InferredSendableC: P {
func f() { }
}
struct InferredSendableS: P {
func f() { }
}
enum InferredSendableE: P {
case a, b
func f() { }
}
final class GenericC<T>: P {
func f() { }
}
struct GenericS<T> : P {
init(_: T) { }
init() { }
func f() { }
func g() async { }
}
class NonSendable {
func f() {}
}
func g<T>(_ f: @escaping @Sendable (T) -> (@Sendable () -> Void)) where T: P {
Task {
let instance = T()
f(instance)()
}
}
// Unapplied Func Parameters
g(GenericS<NonSendable>.f) // ok because unapplied references don't capture state
g(GenericC<NonSendable>.f)
g(InferredSendableS.f)
g(InferredSendableC.f)
g(InferredSendableE.f)
g(GenericS<Int>.f)
g(GenericC<Int>.f)
struct GenericQ<T> {
func f() { }
}
func g2<T>(_ f: @Sendable (T) -> (@Sendable () -> Void)) { }
g2(GenericQ<NonSendable>.f) // ok because unapplied references don't capture state
// Conditional Conformances
//extension GenericS : Sendable where T : Sendable { }
//extension GenericC : Sendable where T : Sendable { }
extension E {
init(){
self = .a
}
}
extension InferredSendableE {
init(){
self = .a
}
}
// Partial Apply Parameters
func h(_ f: (@Sendable () -> Void)) { }
h(GenericQ<NonSendable>().f) // ok
h(GenericS(NonSendable()).f) // ok
h(GenericS<Int>().f)
h(GenericS(1).f)
h(NonSendable().f) // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races}}
func executeAsTask (_ f: @escaping @Sendable () -> Void) {
Task {
f()
}
}
executeAsTask(S().f)
executeAsTask(C().f)
executeAsTask(E().f)
executeAsTask(NonSendable().f) // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races}}
do {
let f = S.f
let _ : @Sendable () -> () = f(S())
}
// Declarations
let us: @Sendable (GenericS<Int>) -> (@Sendable () -> Void) = GenericS<Int>.f
let uc: @Sendable (GenericC<Int>) -> (@Sendable () -> Void) = GenericC<Int>.f
let unappliedStruct: @Sendable (S) -> (@Sendable () -> Void) = S.f
let unappliedClass: @Sendable (C) -> (@Sendable () -> Void) = C.f
let unappliedEnum: @Sendable (E) -> (@Sendable () -> Void) = E.f
var partialStruct : @Sendable () -> Void = S().f
var partialClass : @Sendable () -> Void = C().f
var partialEnum : @Sendable () -> Void = E().f
// Reassign
partialClass = NonSendable().f // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races}}
partialStruct = NonSendable().f // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races}}
partialEnum = NonSendable().f // expected-warning{{converting non-sendable function value to '@Sendable () -> Void' may introduce data races}}
// Static Functions
struct World {
static func greet () { print("hello") }
}
let helloworld: @Sendable () -> Void = World.greet
class NonSendableC {
var x: Int = 0
@Sendable func inc() { // expected-warning {{instance methods of non-Sendable types cannot be marked as '@Sendable'; this is an error in the Swift 6 language mode}}
x += 1
}
}
func doWork() -> Int {
Int.random(in: 1..<42)
}
// unapplied global func call
let work: @Sendable () -> Int = doWork
Task<Int, Never>.detached(priority: nil, operation: doWork)
Task<Int, Never>.detached(priority: nil, operation: work)
// generic argument for `T` should be inferred as `@escaping @Sendable`
func generic2<T>(_ f: T) { }
// CHECK-LABEL: sil hidden [ossa] @$s16sendable_methods12testGeneric2yyF : $@convention(thin) () -> ()
// CHECK: [[F:%.*]] = alloc_stack $@Sendable @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <GenericS<Int>, @Sendable () -> ()>
// CHECK: [[GENERIC_2_FN:%.*]] = function_ref @$s16sendable_methods8generic2yyxlF : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
// CHECK-NEXT: {{.*}} = apply [[GENERIC_2_FN]]<@Sendable (GenericS<Int>) -> @Sendable () -> ()>([[F]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
func testGeneric2() {
generic2(GenericS<Int>.f)
}
actor TestActor {}
@globalActor
struct SomeGlobalActor {
static var shared: TestActor { TestActor() }
}
@SomeGlobalActor
let globalValue: NonSendable = NonSendable()
@SomeGlobalActor
// CHECK-LABEL: sil hidden [ossa] @$s16sendable_methods8generic3yyxYalF : $@convention(thin) @async <T> (@in_guaranteed T) -> ()
//
// CHECK: [[F2:%.*]] = alloc_stack $@Sendable @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <GenericS<String>, @Sendable () -> ()>
// CHECK: [[GENERIC_3_FN:%.*]] = function_ref @$s16sendable_methods8generic3yyxYalF : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
// CHECK-NEXT: {{.*}} = apply [[GENERIC_3_FN]]<@Sendable (GenericS<String>) -> @Sendable () -> ()>([[F2]]) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
//
// CHECK: [[F3:%.*]] = alloc_stack $@Sendable @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <GenericS<NonSendable>, @Sendable () -> ()>
// CHECK: [[GENERIC_3_FN:%.*]] = function_ref @$s16sendable_methods8generic3yyxYalF : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
// CHECK-NEXT: {{.*}} = apply [[GENERIC_3_FN]]<@Sendable (GenericS<NonSendable>) -> @Sendable () -> ()>([[F3]]) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
func generic3<T>(_ x: T) async {
await generic3(GenericS<String>.f)
await generic3(GenericS<NonSendable>.f)
}
// Make sure that static members are handled properly
do {
struct X<T> {
init(_: T) {
}
static func test(_: T) {}
}
class Test<T> {
init(_: T) {
_ = X(self) // Ok
_ = X.init(self) // Ok
_ = Optional.some(self) // Ok
let _: @Sendable (Int) -> X<Int> = X.init // Ok
let _: @Sendable (Test<Int>) -> Void = X.test // Ok
let _ = X.test(self) // Ok
}
}
}
func test_initializer_ref() {
func test<T>(_: @Sendable (T, T) -> Array<T>) {
}
let initRef: @Sendable (Int, Int) -> Array<Int> = Array<Int>.init // Ok
test(initRef) // Ok
test(Array<Int>.init) // Ok
}
// rdar://119593407 - incorrect errors when partially applied member is accessed with InferSendableFromCaptures
do {
@MainActor struct ErrorHandler {
static func log(_ error: Error) {}
}
@MainActor final class Manager {
static var shared: Manager!
func test(_: @escaping @MainActor (Error) -> Void) {
}
}
@MainActor class Test {
func schedule() {
Task {
Manager.shared.test(ErrorHandler.log) // Ok (access is wrapped in an autoclosure)
}
}
}
}