Files
swift-mirror/test/attr/attr_concurrent.swift
Michael Gottesman 3ed4059a60 [sema] Change non-sendable -> non-Sendable in diagnostics.
This matches send non sendable but importantly also makes it clear that we are
talking about something that doesn't conform to the Sendable protocol which is
capitalized.

rdar://151802975
2025-05-22 11:37:58 -07:00

160 lines
5.8 KiB
Swift

// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-flow-sensitive-concurrent-captures
// REQUIRES: concurrency
// Concurrent attribute on a function type.
// expected-note@+1{{found this candidate}}
func f(_ fn: @Sendable (Int) -> Int) { }
// Okay to overload @Sendable vs. not concurrent
// expected-note@+1{{found this candidate}}
func f(_ fn: (Int) -> Int) { }
// Concurrent attribute with other function attributes.
func onEscaping(_ fn: @escaping @Sendable (Int) -> Int) { }
func onEscaping2(_ fn: @Sendable @escaping (Int) -> Int) { }
func onAutoclosure(_ fn: @autoclosure @Sendable () -> Int) { }
func onAutoclosure2(_ fn: @Sendable @autoclosure () -> Int) { }
func onEscapingAutoclosure(_ fn: @Sendable @autoclosure @escaping () -> Int) { }
func onEscapingAutoclosure2(_ fn: @escaping @autoclosure @Sendable () -> Int) { }
func acceptsConcurrent(_ fn: @Sendable (Int) -> Int) { }
func acceptsNonConcurrent(_ fn: (Int) -> Int) { }
@Sendable func negate(_ x: Int) -> Int { -x }
func passingConcurrentOrNot(
_ cfn: @Sendable (Int) -> Int,
ncfn: (Int) -> Int // expected-note{{parameter 'ncfn' is implicitly non-Sendable}}{{9-9=@Sendable }}
) {
// Ambiguous because preconcurrency code doesn't consider `@Sendable`.
f(cfn) // expected-error{{ambiguous use of 'f'}}
// Okay due to overloading
f(ncfn)
acceptsConcurrent(cfn) // okay
acceptsConcurrent(ncfn) // expected-warning{{passing non-Sendable parameter 'ncfn' to function expecting a '@Sendable' closure}}
acceptsNonConcurrent(cfn) // okay
acceptsNonConcurrent(ncfn) // okay
acceptsConcurrent(negate)
acceptsNonConcurrent(negate)
let _: Int = negate // expected-error{{cannot convert value of type '@Sendable (Int) -> Int' to specified type 'Int'}}
}
func closures() {
// Okay, inferring @Sendable
acceptsConcurrent { $0 }
acceptsConcurrent({ $0 })
acceptsConcurrent({ i in i })
acceptsConcurrent({ (i: Int) -> Int in
print(i)
return i
})
let closure1 = { $0 + 1 } // inferred to be non-Sendable
acceptsConcurrent(closure1) // expected-warning{{converting non-Sendable function value to '@Sendable (Int) -> Int' may introduce data races}}
}
// Mutation of captured locals from within @Sendable functions.
extension Int {
mutating func makeNegative() {
self = -self
}
func printMe() {
print(self)
}
}
func mutationOfLocal() {
var localInt = 17
acceptsConcurrent { i in
// Non-mutating accesses are okay
print(localInt + 17)
localInt.printMe()
// Mutations of locally-defined variables are fine.
var localResult = localInt + 1
print(localResult)
_ = {
localResult = localResult + 1
}()
// Mutations of captured variables executing concurrently are bad.
localInt = 17 // expected-warning{{mutation of captured var 'localInt' in concurrently-executing code}}
localInt += 1 // expected-warning{{mutation of captured var 'localInt' in concurrently-executing code}}
localInt.makeNegative() // expected-warning{{mutation of captured var 'localInt' in concurrently-executing code}}
_ = {
localInt = localInt + 12 // expected-warning{{mutation of captured var 'localInt' in concurrently-executing code}}
}()
return i + localInt
}
localInt = 20
}
struct NonTrivialValueType {
var int: Int = 0
var array: [Int] = []
var optArray: [Int]? = nil
}
func testCaseNonTrivialValue() {
var i = NonTrivialValueType()
var j = 0
acceptsConcurrent { value in
print(i.int)
print(i.array[0])
print(i.array[j])
print(i.optArray?[j] ?? 0)
print(i.optArray![j])
i.int = 5 // expected-warning{{mutation of captured var 'i' in concurrently-executing code}}
i.array[0] = 5 // expected-warning{{mutation of captured var 'i' in concurrently-executing code}}
return value
}
j = 17
}
func testExplicitConcurrentClosure() {
let fn = { @Sendable in
17
}
let _: String = fn // expected-error{{cannot convert value of type '@Sendable () -> Int' to specified type 'String'}}
}
class SuperSendable {
func runsInBackground(_: @Sendable () -> Void) {}
func runsInForeground(_: () -> Void) {} // expected-note {{overridden declaration is here}}
func runnableInBackground() -> @Sendable () -> Void { fatalError() } // expected-note {{overridden declaration is here}}
func runnableInForeground() -> () -> Void { fatalError() }
}
class SubSendable: SuperSendable {
override func runsInBackground(_: () -> Void) {}
override func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{declaration 'runsInForeground' has a type with different sendability from any potential overrides}}
override func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{declaration 'runnableInBackground()' has a type with different sendability from any potential overrides}}
override func runnableInForeground() -> @Sendable () -> Void { fatalError() }
}
protocol AbstractSendable {
func runsInBackground(_: @Sendable () -> Void)
func runsInForeground(_: () -> Void) // expected-note {{expected sendability to match requirement here}}
func runnableInBackground() -> @Sendable () -> Void // expected-note {{expected sendability to match requirement here}}
func runnableInForeground() -> () -> Void
}
struct ConcreteSendable: AbstractSendable {
func runsInBackground(_: () -> Void) {}
func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{sendability of function types in instance method 'runsInForeground' does not match requirement in protocol 'AbstractSendable'}}
func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{sendability of function types in instance method 'runnableInBackground()' does not match requirement in protocol 'AbstractSendable'}}
func runnableInForeground() -> @Sendable () -> Void { fatalError() }
}