diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index f81c23d2c72d6..faaf6d9d7a376 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -166,7 +166,22 @@ extension Task { /// have that error propagated here upon cancellation. /// /// - Returns: The task's result. + @_alwaysEmitIntoClient public var value: Success { + @_silgen_name("$sScT7valueTTxvg") // "_t" suffix for the typed throws version + get async throws(Failure) { + do { + return try await _taskFutureGetThrowing(_task) + } catch { + throw error as! Failure + } + } + } + + // Legacy non-typed throws computed property + @usableFromInline + internal var __abi_value: Success { + @_silgen_name("$sScT5valuexvg") get async throws { return try await _taskFutureGetThrowing(_task) } @@ -189,7 +204,7 @@ extension Task { do { return .success(try await value) } catch { - return .failure(error as! Failure) // as!-safe, guaranteed to be Failure + return .failure(error) } } } @@ -790,6 +805,104 @@ extension Task where Failure == Error { #endif } +// ==== Typed throws Task.init overloads --------------------------------------- + +@available(SwiftStdlib 6.0, *) +extension Task { +#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @discardableResult + @_alwaysEmitIntoClient + @_allowFeatureSuppression(IsolatedAny) + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") + public init( + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success + ) { + fatalError("Unavailable in task-to-thread concurrency model") + } +#elseif $Embedded + @discardableResult + @_alwaysEmitIntoClient + public init( + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success + ) { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + // Set up the task flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task future. + let (task, _) = Builtin.createAsyncTask(flags, operation) + + self._task = task +#else + fatalError("Unsupported Swift compiler") +#endif + } +#else // if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + /// Runs the given operation asynchronously + /// as part of a new top-level task on behalf of the current actor. + /// + /// Use this function when creating asynchronous work + /// that operates on behalf of the synchronous function that calls it. + /// Like `Task.detached(priority:operation:)`, + /// this function creates a separate, top-level task. + /// Unlike `detach(priority:operation:)`, + /// the task created by `Task.init(priority:operation:)` + /// inherits the priority and actor context of the caller, + /// so the operation is treated more like an asynchronous extension + /// to the synchronous operation. + /// + /// You need to keep a reference to the task + /// if you want to cancel it by calling the `Task.cancel()` method. + /// Discarding your reference to a detached task + /// doesn't implicitly cancel that task, + /// it only makes it impossible for you to explicitly cancel the task. + /// + /// - Parameters: + /// - priority: The priority of the task. + /// Pass `nil` to use the priority from `Task.currentPriority`. + /// - operation: The operation to perform. + @discardableResult + @_alwaysEmitIntoClient + @_allowFeatureSuppression(IsolatedAny) + public init( + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success + ) { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + // Set up the task flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task future. +#if $BuiltinCreateTask + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + let (task, _) = Builtin.createTask(flags: flags, + initialSerialExecutor: + builtinSerialExecutor, + operation: operation) +#else + let (task, _) = Builtin.createAsyncTask(flags, operation) +#endif + + self._task = task +#else + fatalError("Unsupported Swift compiler") +#endif + } +#endif +} + // ==== Detached Tasks --------------------------------------------------------- @available(SwiftStdlib 5.1, *) @@ -980,6 +1093,103 @@ extension Task where Failure == Error { #endif } +// ==== Typed throws Task.detached overloads ----------------------------------- + +@available(SwiftStdlib 6.0, *) +extension Task { +#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @discardableResult + @_alwaysEmitIntoClient + @_allowFeatureSuppression(IsolatedAny) + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") + public static func detached( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success + ) -> Task { + fatalError("Unavailable in task-to-thread concurrency model") + } +#elseif $Embedded + @discardableResult + @_alwaysEmitIntoClient + public static func detached( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws(Failure) -> Success + ) -> Task { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task future. + let (task, _) = Builtin.createAsyncTask(flags, operation) + + return Task(task) +#else + fatalError("Unsupported Swift compiler") +#endif + } +#else + /// Runs the given throwing operation asynchronously + /// as part of a new top-level task. + /// + /// If the operation throws an error, this method propagates that error. + /// + /// Don't use a detached task if it's possible + /// to model the operation using structured concurrency features like child tasks. + /// Child tasks inherit the parent task's priority and task-local storage, + /// and canceling a parent task automatically cancels all of its child tasks. + /// You need to handle these considerations manually with a detached task. + /// + /// You need to keep a reference to the detached task + /// if you want to cancel it by calling the `Task.cancel()` method. + /// Discarding your reference to a detached task + /// doesn't implicitly cancel that task, + /// it only makes it impossible for you to explicitly cancel the task. + /// + /// - Parameters: + /// - priority: The priority of the task. + /// - operation: The operation to perform. + /// + /// - Returns: A reference to the task. + @discardableResult + @_alwaysEmitIntoClient + @_allowFeatureSuppression(IsolatedAny) + public static func detached( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success + ) -> Task { +#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task future. +#if $BuiltinCreateTask + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + let (task, _) = Builtin.createTask(flags: flags, + initialSerialExecutor: + builtinSerialExecutor, + operation: operation) +#else + let (task, _) = Builtin.createAsyncTask(flags, operation) +#endif + + return Task(task) +#else + fatalError("Unsupported Swift compiler") +#endif + } +#endif +} + // ==== Voluntary Suspension ----------------------------------------------------- @available(SwiftStdlib 5.1, *) diff --git a/test/Concurrency/typed_throws.swift b/test/Concurrency/typed_throws.swift index 621d3b1f01e7c..91dbf91d71e0b 100644 --- a/test/Concurrency/typed_throws.swift +++ b/test/Concurrency/typed_throws.swift @@ -21,3 +21,21 @@ func testAsyncFor(seq: S) async throws(MyError) for try await _ in seq { } } + +@available(SwiftStdlib 6.0, *) +func testTask() async throws(MyError) { + let t: Task = Task { () throws(MyError) -> Int in + throw MyError.failed + } + + _ = try await t.value +} + +@available(SwiftStdlib 6.0, *) +func testTaskDetached() async throws(MyError) { + let t: Task = Task.detached { () throws(MyError) -> Int in + throw MyError.failed + } + + _ = try await t.value +} diff --git a/test/abi/macOS/arm64/concurrency.swift b/test/abi/macOS/arm64/concurrency.swift index 157ad11625b4d..a68bee073ed97 100644 --- a/test/abi/macOS/arm64/concurrency.swift +++ b/test/abi/macOS/arm64/concurrency.swift @@ -270,6 +270,10 @@ Added: _swift_task_getPreferredTaskExecutor Added: _swift_task_popTaskExecutorPreference Added: _swift_task_pushTaskExecutorPreference +// Typed throws Task +// property descriptor for Swift.Task.__abi_value : A +Added: _$sScT11__abi_valuexvpMV + // Adopt #isolation in with...Continuation APIs // Swift.withCheckedThrowingContinuation(isolation: isolated Swift.Actor?, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A Added: _$ss31withCheckedThrowingContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5Error_pGXEtYaKlF @@ -322,4 +326,3 @@ Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__y // Swift.TaskLocal.withValue(_: A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1 Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlF Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlFTu - diff --git a/test/abi/macOS/x86_64/concurrency.swift b/test/abi/macOS/x86_64/concurrency.swift index 330469a3c5af6..903f4a19d6e11 100644 --- a/test/abi/macOS/x86_64/concurrency.swift +++ b/test/abi/macOS/x86_64/concurrency.swift @@ -270,6 +270,10 @@ Added: _swift_task_getPreferredTaskExecutor Added: _swift_task_popTaskExecutorPreference Added: _swift_task_pushTaskExecutorPreference +// Typed throws Task +// property descriptor for Swift.Task.__abi_value : A +Added: _$sScT11__abi_valuexvpMV + // Adopt #isolation in with...Continuation APIs // Swift.withCheckedThrowingContinuation(isolation: isolated Swift.Actor?, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A Added: _$ss31withCheckedThrowingContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5Error_pGXEtYaKlF diff --git a/test/api-digester/stability-concurrency-abi.test b/test/api-digester/stability-concurrency-abi.test index a49455914ff8b..173eb66d833de 100644 --- a/test/api-digester/stability-concurrency-abi.test +++ b/test/api-digester/stability-concurrency-abi.test @@ -115,6 +115,12 @@ Func TaskLocal.withValue(_:operation:file:line:) has parameter 1 type change fro Func TaskLocal.withValue(_:operation:file:line:) has parameter 2 type change from Swift.String to (any _Concurrency.Actor)? Func TaskLocal.withValue(_:operation:file:line:) has parameter 3 type change from Swift.UInt to Swift.String +// Adopt typed throws in Task<> and Task::value +// (abi compat was handled but this test does not understand the silgen_name trickery) +Accessor Task.value.Get() has mangled name changing from 'Swift.Task.value.getter : A' to 'Swift.Task.__abi_value.getter : A' +Var Task.value has been renamed to Var __abi_value +Var Task.value has mangled name changing from 'Swift.Task.value : A' to 'Swift.Task.__abi_value : A' + // *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)