From 13c453b9c0683032f8ed0fcc62db48330b6a0f7b Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Tue, 21 Oct 2025 16:46:15 +0900 Subject: [PATCH 1/6] [Concurrency] Prevent task re-enqueues in continuation APIs This explicitly adopts nonisolated nonsending funcs for the with continuation APIs. This guarantees there are not spurious enqueues between the caller and the closure executing, which is a requirement for these methods. This takes a risk on removing the bad #isolation based API from source, however explicitly passing the isolation as something else than the actual isolation would always have been incorrect, so we should try to get this through. There are further re-enqueue fixes to be done, but this specific API should adopt the nonisolated nonsending approach. Resolves rdar://162192512 --- .../Concurrency/CheckedContinuation.swift | 47 ++++- .../public/Concurrency/PartialAsyncTask.swift | 6 +- .../Sources/Observation/Observations.swift | 2 +- .../must_not_hop_withContinuation.swift | 160 ++++++++++++++++++ 4 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 test/Concurrency/Runtime/must_not_hop_withContinuation.swift diff --git a/stdlib/public/Concurrency/CheckedContinuation.swift b/stdlib/public/Concurrency/CheckedContinuation.swift index a4f0f1553d050..2d821f4c3a587 100644 --- a/stdlib/public/Concurrency/CheckedContinuation.swift +++ b/stdlib/public/Concurrency/CheckedContinuation.swift @@ -290,12 +290,35 @@ extension CheckedContinuation { /// - SeeAlso: `withCheckedThrowingContinuation(function:_:)` /// - SeeAlso: `withUnsafeContinuation(function:_:)` /// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)` +@inlinable +@_alwaysEmitIntoClient +@available(SwiftStdlib 5.1, *) +// ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy +// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit +@abi( + nonisolated(nonsending) func withCheckedContinuationNonisolatedNonsending( + function: String, + _ body: (CheckedContinuation) -> Void + ) async -> sending T +) +public nonisolated(nonsending) func withCheckedContinuation( + function: String = #function, +_ body: (CheckedContinuation) -> Void +) async -> sending T { + return await Builtin.withUnsafeContinuation { + let unsafeContinuation = unsafe UnsafeContinuation($0) + return body(unsafe CheckedContinuation(continuation: unsafeContinuation, + function: function)) + } +} + @inlinable @available(SwiftStdlib 5.1, *) #if !$Embedded @backDeployed(before: SwiftStdlib 6.0) #endif -public func withCheckedContinuation( +@available(*, deprecated, message: "Replaced by nonisolated(nonsending) overload") +func withCheckedContinuation( isolation: isolated (any Actor)? = #isolation, function: String = #function, _ body: (CheckedContinuation) -> Void @@ -354,6 +377,28 @@ public func _unsafeInheritExecutor_withCheckedContinuation( /// - SeeAlso: `withCheckedContinuation(function:_:)` /// - SeeAlso: `withUnsafeContinuation(function:_:)` /// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)` +@inlinable +@_alwaysEmitIntoClient +@available(SwiftStdlib 5.1, *) +// ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy +// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit +@abi( + nonisolated(nonsending) func withCheckedThrowingContinuationNonisolatedNonsending( + function: String, + _ body: (CheckedContinuation) -> Void + ) async throws -> sending T +) +public nonisolated(nonsending) func withCheckedThrowingContinuation( + function: String = #function, + _ body: (CheckedContinuation) -> Void +) async throws -> sending T { + return try await Builtin.withUnsafeThrowingContinuation { + let unsafeContinuation = unsafe UnsafeContinuation($0) + return body(unsafe CheckedContinuation(continuation: unsafeContinuation, + function: function)) + } +} + @inlinable @available(SwiftStdlib 5.1, *) #if !$Embedded diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index a255d34d3454a..5ab540e9332cf 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -889,8 +889,7 @@ internal func _resumeUnsafeThrowingContinuationWithError( @available(SwiftStdlib 5.1, *) @_alwaysEmitIntoClient @unsafe -public func withUnsafeContinuation( - isolation: isolated (any Actor)? = #isolation, +public nonisolated(nonsending) func withUnsafeContinuation( _ fn: (UnsafeContinuation) -> Void ) async -> sending T { return await Builtin.withUnsafeContinuation { @@ -926,8 +925,7 @@ public func withUnsafeContinuation( @available(SwiftStdlib 5.1, *) @_alwaysEmitIntoClient @unsafe -public func withUnsafeThrowingContinuation( - isolation: isolated (any Actor)? = #isolation, +public nonisolated(nonsending) func withUnsafeThrowingContinuation( _ fn: (UnsafeContinuation) -> Void ) async throws -> sending T { return try await Builtin.withUnsafeThrowingContinuation { diff --git a/stdlib/public/Observation/Sources/Observation/Observations.swift b/stdlib/public/Observation/Sources/Observation/Observations.swift index f87cc24eaf72f..7ba7af4f0bf50 100644 --- a/stdlib/public/Observation/Sources/Observation/Observations.swift +++ b/stdlib/public/Observation/Sources/Observation/Observations.swift @@ -114,7 +114,7 @@ public struct Observations: AsyncSequence, Se // install a willChange continuation into the set of continuations // this must take a locally unique id (to the active calls of next) static func willChange(isolation iterationIsolation: isolated (any Actor)? = #isolation, state: _ManagedCriticalState, id: Int) async { - return await withUnsafeContinuation(isolation: iterationIsolation) { continuation in + return await withUnsafeContinuation { continuation in state.withCriticalRegion { state in defer { state.dirty = false } switch state.continuations[id] { diff --git a/test/Concurrency/Runtime/must_not_hop_withContinuation.swift b/test/Concurrency/Runtime/must_not_hop_withContinuation.swift new file mode 100644 index 0000000000000..e200cbc2d61b5 --- /dev/null +++ b/test/Concurrency/Runtime/must_not_hop_withContinuation.swift @@ -0,0 +1,160 @@ +// RUN: %target-run-simple-swift( -target %target-swift-5.1-abi-triple %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift( -O -target %target-swift-5.1-abi-triple %import-libdispatch) | %FileCheck %s +// REQUIRES: concurrency +// REQUIRES: executable_test + +// REQUIRES: concurrency_runtime +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: freestanding +// REQUIRES: libdispatch +// REQUIRES: synchronization + +import Synchronization + + +if #available(SwiftStdlib 6.0, *) { + print("=== foo() async") + print("---------------------------------------") + await foo() +} + +// CHECK: === foo() async +// CHECK-NEXT: --------------------------------------- +// We hop to the task executor: +// CHECK-NEXT: [executor][task-executor] Enqueue (1) + +// CHECK-NEXT: foo - withTaskExecutorPreference + +// CHECK: foo - withTaskExecutorPreference - withCheckedContinuation +// CHECK-NEXT: foo - withTaskExecutorPreference - withCheckedContinuation done + +// CHECK: foo - withTaskExecutorPreference - withUnsafeContinuation +// CHECK-NEXT: foo - withTaskExecutorPreference - withUnsafeContinuation done + +// CHECK: foo - withTaskExecutorPreference - withCheckedThrowingContinuation +// CHECK-NEXT: foo - withTaskExecutorPreference - withCheckedThrowingContinuation done + +// CHECK: foo - withTaskExecutorPreference - withUnsafeThrowingContinuation +// CHECK-NEXT: foo - withTaskExecutorPreference - withUnsafeThrowingContinuation done + +// By checking that this is the second enqueue here, +// we check that there was no stray enqueues between with... invocations: +// CHECK-NEXT: [executor][task-executor] Enqueue (2) + +// CHECK-NEXT: foo - withTaskExecutorPreference done + +// CHECK-NEXT: == Make: actor Foo +// CHECK-NEXT: --------------------------------------- +// CHECK-NEXT: [executor][actor-executor] Enqueue (1) +// CHECK-NEXT: actor.foo + +// CHECK: actor.foo - withCheckedContinuation +// CHECK-NEXT: actor.foo - withCheckedContinuation done + +// CHECK: actor.foo - withUnsafeContinuation +// CHECK-NEXT: actor.foo - withUnsafeContinuation done + +// CHECK: actor.foo - withCheckedThrowingContinuation +// CHECK-NEXT: actor.foo - withCheckedThrowingContinuation done + +// CHECK: actor.foo - withUnsafeThrowingContinuation +// CHECK-NEXT: actor.foo - withUnsafeThrowingContinuation done +// CHECK-NEXT: actor.foo done + +// No more enqueues are expected afterwards +// CHECK-NOT: [executor] + +@available(SwiftStdlib 6.0, *) +@concurrent +func foo() async { + await withTaskExecutorPreference(EnqueueCountExecutor(name: "task-executor")) { + print("foo - withTaskExecutorPreference") + await withCheckedContinuation { cont in + print("foo - withTaskExecutorPreference - withCheckedContinuation") + cont.resume() + } + print("foo - withTaskExecutorPreference - withCheckedContinuation done") + + await withUnsafeContinuation { cont in + print("foo - withTaskExecutorPreference - withUnsafeContinuation") + cont.resume() + } + print("foo - withTaskExecutorPreference - withUnsafeContinuation done") + + try! await withCheckedThrowingContinuation { cont in + print("foo - withTaskExecutorPreference - withCheckedThrowingContinuation") + cont.resume() + } + print("foo - withTaskExecutorPreference - withCheckedThrowingContinuation done") + + try! await withUnsafeThrowingContinuation { cont in + print("foo - withTaskExecutorPreference - withUnsafeThrowingContinuation") + cont.resume() + } + print("foo - withTaskExecutorPreference - withUnsafeThrowingContinuation done") + } + print("foo - withTaskExecutorPreference done") + + print("== Make: actor Foo") + print("---------------------------------------") + await Foo().foo() +} + +@available(SwiftStdlib 6.0, *) +actor Foo { + let exec = EnqueueCountExecutor(name: "actor-executor") + + nonisolated var unownedExecutor: UnownedSerialExecutor { + self.exec.asUnownedSerialExecutor() + } + + func foo() async { + print("actor.foo") + + await withCheckedContinuation { cont in + print("actor.foo - withCheckedContinuation") + cont.resume() + } + print("actor.foo - withCheckedContinuation done") + + await withUnsafeContinuation { cont in + print("actor.foo - withUnsafeContinuation") + cont.resume() + } + print("actor.foo - withUnsafeContinuation done") + + try! await withCheckedThrowingContinuation { cont in + print("actor.foo - withCheckedThrowingContinuation") + cont.resume() + } + print("actor.foo - withCheckedThrowingContinuation done") + + try! await withUnsafeThrowingContinuation { cont in + print("actor.foo - withUnsafeThrowingContinuation") + cont.resume() + } + print("actor.foo - withUnsafeThrowingContinuation done") + + print("actor.foo done") + } +} + +@available(SwiftStdlib 6.0, *) +final class EnqueueCountExecutor: TaskExecutor, SerialExecutor { + let enqueueCount: Atomic + + let name: String + + init(name: String) { + self.enqueueCount = .init(0) + self.name = name + } + + public func enqueue(_ job: consuming ExecutorJob) { + let newEnqueueValue = self.enqueueCount.add(1, ordering: .relaxed).newValue + print("[executor][\(self.name)] Enqueue (\(newEnqueueValue))") + job.runSynchronously(on: self.asUnownedSerialExecutor()) + } +} + +print("done") // CHECK: done \ No newline at end of file From 0c58cc4fbc9a102e62073ef8cd41061fe20ff971 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Tue, 21 Oct 2025 21:28:56 +0900 Subject: [PATCH 2/6] workaround for @abi issue in test, use silgen_name again --- .../Concurrency/CheckedContinuation.swift | 34 +++++++++++-------- .../stability-concurrency-abi.test | 10 +++--- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/stdlib/public/Concurrency/CheckedContinuation.swift b/stdlib/public/Concurrency/CheckedContinuation.swift index 2d821f4c3a587..7795ce12e4741 100644 --- a/stdlib/public/Concurrency/CheckedContinuation.swift +++ b/stdlib/public/Concurrency/CheckedContinuation.swift @@ -294,13 +294,16 @@ extension CheckedContinuation { @_alwaysEmitIntoClient @available(SwiftStdlib 5.1, *) // ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy -// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit -@abi( - nonisolated(nonsending) func withCheckedContinuationNonisolatedNonsending( - function: String, - _ body: (CheckedContinuation) -> Void - ) async -> sending T -) +// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit. We add the extra `N` +// in the mangled name to avoid the clash. +// // FIXME: We cannot use @abi since it causes issues with IDE/complete_diagnostics_concurrency.swift +//@abi( +// nonisolated(nonsending) func withCheckedContinuationNonisolatedNonsending( +// function: String, +// _ body: (CheckedContinuation) -> Void +// ) async -> sending T +//) +@_silgen_name("$ss24withCheckedContinuationN8function_xSS_yScCyxs5NeverOGXEtYalF") public nonisolated(nonsending) func withCheckedContinuation( function: String = #function, _ body: (CheckedContinuation) -> Void @@ -381,13 +384,16 @@ public func _unsafeInheritExecutor_withCheckedContinuation( @_alwaysEmitIntoClient @available(SwiftStdlib 5.1, *) // ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy -// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit -@abi( - nonisolated(nonsending) func withCheckedThrowingContinuationNonisolatedNonsending( - function: String, - _ body: (CheckedContinuation) -> Void - ) async throws -> sending T -) +// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit. We add the extra `N` +// in the mangled name to avoid the clash. +// // FIXME: We cannot use @abi since it causes issues with IDE/complete_diagnostics_concurrency.swift +//@abi( +// nonisolated(nonsending) func withCheckedThrowingContinuationNonisolatedNonsending( +// function: String, +// _ body: (CheckedContinuation) -> Void +// ) async throws -> sending T +//) +@_silgen_name("$ss32withCheckedThrowingContinuationN8function_xSS_yScCyxs5Error_pGXEtYaKlF") public nonisolated(nonsending) func withCheckedThrowingContinuation( function: String = #function, _ body: (CheckedContinuation) -> Void diff --git a/test/api-digester/stability-concurrency-abi.test b/test/api-digester/stability-concurrency-abi.test index 8e8e24505f24f..4676e6f24a7cf 100644 --- a/test/api-digester/stability-concurrency-abi.test +++ b/test/api-digester/stability-concurrency-abi.test @@ -81,8 +81,6 @@ Protocol SerialExecutor has added inherited protocol SendableMetatype // #isolated adoption in with...Continuation // api-digester is not aware of silgen_name trickery we do to keep this ABI compatible -Func withCheckedContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)? -Func withCheckedContinuation(function:_:) has parameter 1 type change from (_Concurrency.CheckedContinuation<τ_0_0, Swift.Never>) -> () to Swift.String Func withCheckedThrowingContinuation(function:_:) has been renamed to Func withCheckedThrowingContinuation(isolation:function:_:) Func withCheckedThrowingContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedThrowingContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A' to '_Concurrency.withCheckedThrowingContinuation(isolation: isolated Swift.Optional, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A' Func withCheckedThrowingContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)? @@ -92,10 +90,6 @@ Func withCheckedThrowingContinuation(function:_:) has parameter 1 type change fr Func withTaskCancellationHandler(operation:onCancel:) has been renamed to Func withTaskCancellationHandler(operation:onCancel:isolation:) Func withTaskCancellationHandler(operation:onCancel:) has mangled name changing from '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> ()) async throws -> A' to '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> (), isolation: isolated Swift.Optional) async throws -> A' -// #isolated was adopted and the old methods kept: $ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF -Func withCheckedContinuation(function:_:) has been renamed to Func withCheckedContinuation(isolation:function:_:) -Func withCheckedContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' to '_Concurrency.withCheckedContinuation(isolation: isolated Swift.Optional, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' - // AsyncStream.init(unfolding:onCancel:) uses @_silgen_name to preserve mangling after adding @preconcurrency. Constructor AsyncStream.init(unfolding:onCancel:) has mangled name changing from 'Swift.AsyncStream.init(unfolding: () async -> Swift.Optional, onCancel: Swift.Optional<@Sendable () -> ()>) -> Swift.AsyncStream' to 'Swift.AsyncStream.init(unfolding: () async -> Swift.Optional, onCancel: Swift.Optional<() -> ()>) -> Swift.AsyncStream' @@ -128,6 +122,10 @@ Func withThrowingTaskGroup(of:returning:body:) has parameter 2 type change from Func withTaskGroup(of:returning:body:) has been renamed to Func withTaskGroup(of:returning:isolation:body:) Func withTaskGroup(of:returning:body:) has mangled name changing from '_Concurrency.withTaskGroup(of: A.Type, returning: B.Type, body: (inout Swift.TaskGroup) async -> B) async -> B' to '_Concurrency.withTaskGroup(of: A.Type, returning: B.Type, isolation: isolated Swift.Optional, body: (inout Swift.TaskGroup) async -> B) async -> B' +// withCheckedContinuation now is using nonisolated(nonsending) +Func withCheckedContinuation(function:_:) has been renamed to Func _unsafeInheritExecutor_withCheckedContinuation(function:_:) +Func withCheckedContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' to '_Concurrency._unsafeInheritExecutor_withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' + Func pthread_main_np() is a new API without '@available' // *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.) From c7ffe998bb7f3338acb6182a738f52fefe4d05aa Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Tue, 21 Oct 2025 22:52:11 +0900 Subject: [PATCH 3/6] disable verifyUSRToDeclReconstruction on @abi for now --- lib/IDE/CodeCompletionResultBuilder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/IDE/CodeCompletionResultBuilder.cpp b/lib/IDE/CodeCompletionResultBuilder.cpp index 2aae021605fb3..0ea60e5074a24 100644 --- a/lib/IDE/CodeCompletionResultBuilder.cpp +++ b/lib/IDE/CodeCompletionResultBuilder.cpp @@ -110,6 +110,10 @@ static void verifyUSRToDeclReconstruction(const Decl *D) { if (!shouldCopyAssociatedUSRForDecl(VD)) return; + // FIXME(#): We don't currently handle `@abi` correctly. + if (D->getAttrs().hasAttribute()) + return; + SmallString<128> SwiftUSR; llvm::raw_svector_ostream OS(SwiftUSR); From 58bafbaf36273d975a650ca9b52a2978849b6f05 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Tue, 21 Oct 2025 22:52:48 +0900 Subject: [PATCH 4/6] Revert "workaround for @abi issue in test, use silgen_name again" This reverts commit 0c58cc4fbc9a102e62073ef8cd41061fe20ff971. --- .../Concurrency/CheckedContinuation.swift | 34 ++++++++----------- .../stability-concurrency-abi.test | 10 +++--- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/stdlib/public/Concurrency/CheckedContinuation.swift b/stdlib/public/Concurrency/CheckedContinuation.swift index 7795ce12e4741..2d821f4c3a587 100644 --- a/stdlib/public/Concurrency/CheckedContinuation.swift +++ b/stdlib/public/Concurrency/CheckedContinuation.swift @@ -294,16 +294,13 @@ extension CheckedContinuation { @_alwaysEmitIntoClient @available(SwiftStdlib 5.1, *) // ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy -// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit. We add the extra `N` -// in the mangled name to avoid the clash. -// // FIXME: We cannot use @abi since it causes issues with IDE/complete_diagnostics_concurrency.swift -//@abi( -// nonisolated(nonsending) func withCheckedContinuationNonisolatedNonsending( -// function: String, -// _ body: (CheckedContinuation) -> Void -// ) async -> sending T -//) -@_silgen_name("$ss24withCheckedContinuationN8function_xSS_yScCyxs5NeverOGXEtYalF") +// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit +@abi( + nonisolated(nonsending) func withCheckedContinuationNonisolatedNonsending( + function: String, + _ body: (CheckedContinuation) -> Void + ) async -> sending T +) public nonisolated(nonsending) func withCheckedContinuation( function: String = #function, _ body: (CheckedContinuation) -> Void @@ -384,16 +381,13 @@ public func _unsafeInheritExecutor_withCheckedContinuation( @_alwaysEmitIntoClient @available(SwiftStdlib 5.1, *) // ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy -// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit. We add the extra `N` -// in the mangled name to avoid the clash. -// // FIXME: We cannot use @abi since it causes issues with IDE/complete_diagnostics_concurrency.swift -//@abi( -// nonisolated(nonsending) func withCheckedThrowingContinuationNonisolatedNonsending( -// function: String, -// _ body: (CheckedContinuation) -> Void -// ) async throws -> sending T -//) -@_silgen_name("$ss32withCheckedThrowingContinuationN8function_xSS_yScCyxs5Error_pGXEtYaKlF") +// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit +@abi( + nonisolated(nonsending) func withCheckedThrowingContinuationNonisolatedNonsending( + function: String, + _ body: (CheckedContinuation) -> Void + ) async throws -> sending T +) public nonisolated(nonsending) func withCheckedThrowingContinuation( function: String = #function, _ body: (CheckedContinuation) -> Void diff --git a/test/api-digester/stability-concurrency-abi.test b/test/api-digester/stability-concurrency-abi.test index 4676e6f24a7cf..8e8e24505f24f 100644 --- a/test/api-digester/stability-concurrency-abi.test +++ b/test/api-digester/stability-concurrency-abi.test @@ -81,6 +81,8 @@ Protocol SerialExecutor has added inherited protocol SendableMetatype // #isolated adoption in with...Continuation // api-digester is not aware of silgen_name trickery we do to keep this ABI compatible +Func withCheckedContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)? +Func withCheckedContinuation(function:_:) has parameter 1 type change from (_Concurrency.CheckedContinuation<τ_0_0, Swift.Never>) -> () to Swift.String Func withCheckedThrowingContinuation(function:_:) has been renamed to Func withCheckedThrowingContinuation(isolation:function:_:) Func withCheckedThrowingContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedThrowingContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A' to '_Concurrency.withCheckedThrowingContinuation(isolation: isolated Swift.Optional, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A' Func withCheckedThrowingContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)? @@ -90,6 +92,10 @@ Func withCheckedThrowingContinuation(function:_:) has parameter 1 type change fr Func withTaskCancellationHandler(operation:onCancel:) has been renamed to Func withTaskCancellationHandler(operation:onCancel:isolation:) Func withTaskCancellationHandler(operation:onCancel:) has mangled name changing from '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> ()) async throws -> A' to '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> (), isolation: isolated Swift.Optional) async throws -> A' +// #isolated was adopted and the old methods kept: $ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF +Func withCheckedContinuation(function:_:) has been renamed to Func withCheckedContinuation(isolation:function:_:) +Func withCheckedContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' to '_Concurrency.withCheckedContinuation(isolation: isolated Swift.Optional, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' + // AsyncStream.init(unfolding:onCancel:) uses @_silgen_name to preserve mangling after adding @preconcurrency. Constructor AsyncStream.init(unfolding:onCancel:) has mangled name changing from 'Swift.AsyncStream.init(unfolding: () async -> Swift.Optional, onCancel: Swift.Optional<@Sendable () -> ()>) -> Swift.AsyncStream' to 'Swift.AsyncStream.init(unfolding: () async -> Swift.Optional, onCancel: Swift.Optional<() -> ()>) -> Swift.AsyncStream' @@ -122,10 +128,6 @@ Func withThrowingTaskGroup(of:returning:body:) has parameter 2 type change from Func withTaskGroup(of:returning:body:) has been renamed to Func withTaskGroup(of:returning:isolation:body:) Func withTaskGroup(of:returning:body:) has mangled name changing from '_Concurrency.withTaskGroup(of: A.Type, returning: B.Type, body: (inout Swift.TaskGroup) async -> B) async -> B' to '_Concurrency.withTaskGroup(of: A.Type, returning: B.Type, isolation: isolated Swift.Optional, body: (inout Swift.TaskGroup) async -> B) async -> B' -// withCheckedContinuation now is using nonisolated(nonsending) -Func withCheckedContinuation(function:_:) has been renamed to Func _unsafeInheritExecutor_withCheckedContinuation(function:_:) -Func withCheckedContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' to '_Concurrency._unsafeInheritExecutor_withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' - Func pthread_main_np() is a new API without '@available' // *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.) From 13cc716bbe4048168c0ffd610bd643deab20e3df Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Tue, 21 Oct 2025 22:56:23 +0900 Subject: [PATCH 5/6] add github issue to fixme --- lib/IDE/CodeCompletionResultBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IDE/CodeCompletionResultBuilder.cpp b/lib/IDE/CodeCompletionResultBuilder.cpp index 0ea60e5074a24..308f32e00bdeb 100644 --- a/lib/IDE/CodeCompletionResultBuilder.cpp +++ b/lib/IDE/CodeCompletionResultBuilder.cpp @@ -110,7 +110,7 @@ static void verifyUSRToDeclReconstruction(const Decl *D) { if (!shouldCopyAssociatedUSRForDecl(VD)) return; - // FIXME(#): We don't currently handle `@abi` correctly. + // FIXME(gh#85030): We don't currently handle `@abi` correctly. if (D->getAttrs().hasAttribute()) return; From f2eb567c8ac11bb92d2190d5c660d256b10999e4 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Wed, 22 Oct 2025 09:18:43 +0900 Subject: [PATCH 6/6] test fixup, also handle @abi issue in SwiftDocSupport until issue https://github.com/swiftlang/swift/issues/85030 is fixed --- test/api-digester/stability-concurrency-abi.test | 10 ++++------ tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/api-digester/stability-concurrency-abi.test b/test/api-digester/stability-concurrency-abi.test index 8e8e24505f24f..4f4f25162d6c4 100644 --- a/test/api-digester/stability-concurrency-abi.test +++ b/test/api-digester/stability-concurrency-abi.test @@ -81,8 +81,6 @@ Protocol SerialExecutor has added inherited protocol SendableMetatype // #isolated adoption in with...Continuation // api-digester is not aware of silgen_name trickery we do to keep this ABI compatible -Func withCheckedContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)? -Func withCheckedContinuation(function:_:) has parameter 1 type change from (_Concurrency.CheckedContinuation<τ_0_0, Swift.Never>) -> () to Swift.String Func withCheckedThrowingContinuation(function:_:) has been renamed to Func withCheckedThrowingContinuation(isolation:function:_:) Func withCheckedThrowingContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedThrowingContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A' to '_Concurrency.withCheckedThrowingContinuation(isolation: isolated Swift.Optional, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async throws -> A' Func withCheckedThrowingContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)? @@ -92,10 +90,6 @@ Func withCheckedThrowingContinuation(function:_:) has parameter 1 type change fr Func withTaskCancellationHandler(operation:onCancel:) has been renamed to Func withTaskCancellationHandler(operation:onCancel:isolation:) Func withTaskCancellationHandler(operation:onCancel:) has mangled name changing from '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> ()) async throws -> A' to '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> (), isolation: isolated Swift.Optional) async throws -> A' -// #isolated was adopted and the old methods kept: $ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF -Func withCheckedContinuation(function:_:) has been renamed to Func withCheckedContinuation(isolation:function:_:) -Func withCheckedContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' to '_Concurrency.withCheckedContinuation(isolation: isolated Swift.Optional, function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' - // AsyncStream.init(unfolding:onCancel:) uses @_silgen_name to preserve mangling after adding @preconcurrency. Constructor AsyncStream.init(unfolding:onCancel:) has mangled name changing from 'Swift.AsyncStream.init(unfolding: () async -> Swift.Optional, onCancel: Swift.Optional<@Sendable () -> ()>) -> Swift.AsyncStream' to 'Swift.AsyncStream.init(unfolding: () async -> Swift.Optional, onCancel: Swift.Optional<() -> ()>) -> Swift.AsyncStream' @@ -128,6 +122,10 @@ Func withThrowingTaskGroup(of:returning:body:) has parameter 2 type change from Func withTaskGroup(of:returning:body:) has been renamed to Func withTaskGroup(of:returning:isolation:body:) Func withTaskGroup(of:returning:body:) has mangled name changing from '_Concurrency.withTaskGroup(of: A.Type, returning: B.Type, body: (inout Swift.TaskGroup) async -> B) async -> B' to '_Concurrency.withTaskGroup(of: A.Type, returning: B.Type, isolation: isolated Swift.Optional, body: (inout Swift.TaskGroup) async -> B) async -> B' +// Adopt nonisolated(nonsending) in withCheckedContinuation +Func withCheckedContinuation(function:_:) has been renamed to Func _unsafeInheritExecutor_withCheckedContinuation(function:_:) +Func withCheckedContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' to '_Concurrency._unsafeInheritExecutor_withCheckedContinuation(function: Swift.String, _: (Swift.CheckedContinuation) -> ()) async -> A' + Func pthread_main_np() is a new API without '@available' // *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.) diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index 211f88ff38bf1..723602ab266da 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -196,9 +196,13 @@ class AnnotatingPrinter : public StreamPrinter { initDefaultMapToUse(D); // If D is declared in the extension, then the synthesized target is valid. TypeOrExtensionDecl SynthesizedTarget; - assert(D->getDeclContext()->isModuleScopeContext() == EntitiesStack.empty()); - if (D->getDeclContext() == SynthesizedExtensionInfo.first) - SynthesizedTarget = SynthesizedExtensionInfo.second; + // FIXME: the if-below used to be: assert(D->getDeclContext()->isModuleScopeContext() == EntitiesStack.empty()); + // Resolve once https://github.com/swiftlang/swift/issues/85030 is fixed. + if (D->getDeclContext()->isModuleScopeContext() == EntitiesStack.empty()) { + if (D->getDeclContext() == SynthesizedExtensionInfo.first) { + SynthesizedTarget = SynthesizedExtensionInfo.second; + } + } EntitiesStack.emplace_back(D, SynthesizedTarget, getDefaultImplementation(D), StartOffset, false); }