mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
The `Task` type has oscillated somewhat from being purely a namespace,
to having instances that are used (albeit rarely), back to purely
being a namespace that isn't used for all that many names. Many of the
names that used to be on Task have already been moved out, e.g., for
creating new detached tasks, creating new task groups, adding
cancellation handlers, etc.
Collapse `Task.Handle<Success, Failure>` into `Task<Success, Failure>`.
`Task.Handle` is the type that is most frequently referenced in the
concurrency library, so giving it the short name `Task` is most
appropriate. Replace the top-level async/detach functions with a
`Task` initializer and `Task.detached`, respectively.
The `Task` type can still act as a namespace for static operations
such as, e.g., `Task.isCancelled`. Do this with an extension of the
form:
extension Task where Success == Never, Failure == Never { ... }
We've been accruing a number of compatibility shims. Move them all
into their own source file, deprecate them, and make them
always-emit-into-client so they don't have any ABI impact.
114 lines
3.8 KiB
Swift
114 lines
3.8 KiB
Swift
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
|
|
// REQUIRES: concurrency
|
|
|
|
@available(SwiftStdlib 5.5, *)
|
|
func someAsyncFunc() async -> String { "" }
|
|
|
|
struct MyError: Error {}
|
|
@available(SwiftStdlib 5.5, *)
|
|
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.5, *)
|
|
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.5, *)
|
|
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
|
|
let _: String = withUnsafeContinuation { continuation in // expected-error{{cannot pass function of type '(UnsafeContinuation<String, Never>) async -> Void' to parameter expecting synchronous function type}}
|
|
let s = await someAsyncFunc() // expected-note {{'async' inferred from asynchronous operation used here}}
|
|
continuation.resume(returning: s)
|
|
}
|
|
|
|
let _: String = await withUnsafeContinuation { continuation in
|
|
continuation.resume(returning: "")
|
|
}
|
|
|
|
// rdar://76475495 - suppress warnings for invalid expressions
|
|
func test_invalid_async_no_warnings() async -> Int {
|
|
return await withUnsafeContinuation {
|
|
$0.resume(throwing: 1) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Never'}}
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 5.5, *)
|
|
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?
|
|
}
|
|
|
|
// ==== Detached Tasks ---------------------------------------------------------
|
|
|
|
@available(SwiftStdlib 5.5, *)
|
|
func test_detached() async throws {
|
|
let handle = Task.detached {
|
|
await someAsyncFunc() // able to call async functions
|
|
}
|
|
|
|
let result: String = await handle.get()
|
|
_ = result
|
|
}
|
|
|
|
@available(SwiftStdlib 5.5, *)
|
|
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.get()
|
|
} catch {
|
|
print("caught: \(error)")
|
|
}
|
|
}
|