diff --git a/lib/IDE/CodeCompletionResultBuilder.cpp b/lib/IDE/CodeCompletionResultBuilder.cpp index 2aae021605fb3..308f32e00bdeb 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(gh#85030): We don't currently handle `@abi` correctly. + if (D->getAttrs().hasAttribute()) + return; + SmallString<128> SwiftUSR; llvm::raw_svector_ostream OS(SwiftUSR); 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 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); }