1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
// RUN: %target-swift-frontend -strict-concurrency=targeted -disable-availability-checking %s -o /dev/null -verify -emit-sil -DALLOW_TYPECHECKER_ERRORS -verify-additional-prefix typechecker-
// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking %s -o /dev/null -verify -emit-sil -DALLOW_TYPECHECKER_ERRORS -verify-additional-prefix typechecker-
// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking %s -o /dev/null -verify -emit-sil -verify-additional-prefix tns-
// REQUIRES: concurrency
// REQUIRES: asserts
@available(SwiftStdlib 5.1, *)
func someAsyncFunc() async -> String { "" }
struct MyError: Error {}
@available(SwiftStdlib 5.1, *)
func someThrowingAsyncFunc() async throws -> String { throw MyError() }
// ==== Unsafe Continuations ---------------------------------------------------
struct Vegetable {}
func buyVegetables(
shoppingList: [String],
// a) if all veggies were in store, this is invoked *exactly-once*
onGotAllVegetables: ([Vegetable]) -> (),
// b) if not all veggies were in store, invoked one by one (one or more times)
onGotVegetable: (Vegetable) -> (),
// b) if at least one onGotVegetable was called *exactly-once*
// this is invoked once no more veggies will be emitted
onNoMoreVegetables: () -> (),
// c) if no veggies _at all_ were available, this is invoked *exactly once*
onNoVegetablesInStore: (Error) -> ()
) {}
// returns 1 or more vegetables or throws an error
@available(SwiftStdlib 5.1, *)
func buyVegetables(shoppingList: [String]) async throws -> [Vegetable] {
try await withUnsafeThrowingContinuation { continuation in
var veggies: [Vegetable] = []
buyVegetables(
shoppingList: shoppingList,
onGotAllVegetables: { veggies in continuation.resume(returning: veggies) },
onGotVegetable: { v in veggies.append(v) },
onNoMoreVegetables: { continuation.resume(returning: veggies) },
onNoVegetablesInStore: { error in continuation.resume(throwing: error) }
)
}
}
@available(SwiftStdlib 5.1, *)
func test_unsafeContinuations() async {
// the closure should not allow async operations;
// after all: if you have async code, just call it directly, without the unsafe continuation
#if ALLOW_TYPECHECKER_ERRORS
let _: String = withUnsafeContinuation { continuation in // expected-typechecker-error{{cannot pass function of type '(UnsafeContinuation<String, Never>) async -> Void' to parameter expecting synchronous function type}}
let s = await someAsyncFunc() // expected-typechecker-note {{'async' inferred from asynchronous operation used here}}
continuation.resume(returning: s)
}
#endif
let _: String = await withUnsafeContinuation { continuation in
continuation.resume(returning: "")
}
// rdar://76475495 - suppress warnings for invalid expressions
#if ALLOW_TYPECHECKER_ERRORS
func test_invalid_async_no_warnings() async -> Int {
return await withUnsafeContinuation {
$0.resume(throwing: 1) // expected-typechecker-error {{cannot convert value of type 'Int' to expected argument type 'Never'}}
}
}
#endif
}
@available(SwiftStdlib 5.1, *)
func test_unsafeThrowingContinuations() async throws {
let _: String = try await withUnsafeThrowingContinuation { continuation in
continuation.resume(returning: "")
}
let _: String = try await withUnsafeThrowingContinuation { continuation in
continuation.resume(throwing: MyError())
}
// using resume(with:)
let _: String = try await withUnsafeThrowingContinuation { continuation in
let result : Result<String, MyError> = .success("")
continuation.resume(with: result)
}
let _: String = try await withUnsafeThrowingContinuation { continuation in
continuation.resume(with: .failure(MyError()))
}
// TODO: Potentially could offer some warnings if we know that a continuation was resumed or escaped at all in a closure?
}
// ==== Sendability ------------------------------------------------------------
class NotSendable { }
@available(SwiftStdlib 5.1, *)
func test_nonsendableContinuation() async throws {
let _: NotSendable = try await withUnsafeThrowingContinuation { continuation in
continuation.resume(returning: NotSendable())
}
let _: NotSendable = try await withUnsafeThrowingContinuation { continuation in
Task {
continuation.resume(returning: NotSendable()) // okay
}
}
}
// ==== Detached Tasks ---------------------------------------------------------
@available(SwiftStdlib 5.1, *)
func test_detached() async throws {
let handle = Task.detached {
await someAsyncFunc() // able to call async functions
}
let result: String = await handle.value
_ = result
}
@available(SwiftStdlib 5.1, *)
func test_detached_throwing() async -> String {
let handle: Task<String, Error> = Task.detached {
try await someThrowingAsyncFunc() // able to call async functions
}
do {
return try await handle.value
} catch {
print("caught: \(error)")
}
return ""
}
// ==== Detached Tasks with inout Params---------------------------------------
@available(SwiftStdlib 5.1, *)
func printOrderNumber(n: inout Int) async { // expected-tns-note {{parameter 'n' is declared 'inout'}}
Task.detached { // expected-tns-error {{escaping closure captures 'inout' parameter 'n'}}
n+=1 // expected-tns-note {{captured here}}
print(n) // expected-tns-note {{captured here}}
}
}
|