Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/IDE/CodeCompletionResultBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ABIAttr>())
return;

SmallString<128> SwiftUSR;

llvm::raw_svector_ostream OS(SwiftUSR);
Expand Down
47 changes: 46 additions & 1 deletion stdlib/public/Concurrency/CheckedContinuation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(
function: String,
_ body: (CheckedContinuation<T, Never>) -> Void
) async -> sending T
)
public nonisolated(nonsending) func withCheckedContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Never>) -> Void
) async -> sending T {
return await Builtin.withUnsafeContinuation {
let unsafeContinuation = unsafe UnsafeContinuation<T, Never>($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<T>(
@available(*, deprecated, message: "Replaced by nonisolated(nonsending) overload")
func withCheckedContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
function: String = #function,
_ body: (CheckedContinuation<T, Never>) -> Void
Expand Down Expand Up @@ -354,6 +377,28 @@ public func _unsafeInheritExecutor_withCheckedContinuation<T>(
/// - 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<T>(
function: String,
_ body: (CheckedContinuation<T, Error>) -> Void
) async throws -> sending T
)
public nonisolated(nonsending) func withCheckedThrowingContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Error>) -> Void
) async throws -> sending T {
return try await Builtin.withUnsafeThrowingContinuation {
let unsafeContinuation = unsafe UnsafeContinuation<T, Error>($0)
return body(unsafe CheckedContinuation(continuation: unsafeContinuation,
function: function))
}
}

@inlinable
@available(SwiftStdlib 5.1, *)
#if !$Embedded
Expand Down
6 changes: 2 additions & 4 deletions stdlib/public/Concurrency/PartialAsyncTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,7 @@ internal func _resumeUnsafeThrowingContinuationWithError<T>(
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@unsafe
public func withUnsafeContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
public nonisolated(nonsending) func withUnsafeContinuation<T>(
_ fn: (UnsafeContinuation<T, Never>) -> Void
) async -> sending T {
return await Builtin.withUnsafeContinuation {
Expand Down Expand Up @@ -926,8 +925,7 @@ public func withUnsafeContinuation<T>(
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@unsafe
public func withUnsafeThrowingContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
public nonisolated(nonsending) func withUnsafeThrowingContinuation<T>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope we can get away with this "technically" source break... Let's see what the source compat suite says.

_ fn: (UnsafeContinuation<T, Error>) -> Void
) async throws -> sending T {
return try await Builtin.withUnsafeThrowingContinuation {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public struct Observations<Element: Sendable, Failure: Error>: 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<State>, 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] {
Expand Down
160 changes: 160 additions & 0 deletions test/Concurrency/Runtime/must_not_hop_withContinuation.swift
Original file line number Diff line number Diff line change
@@ -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<Int>

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
10 changes: 4 additions & 6 deletions test/api-digester/stability-concurrency-abi.test
Original file line number Diff line number Diff line change
Expand Up @@ -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<A>(function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Error>) -> ()) async throws -> A' to '_Concurrency.withCheckedThrowingContinuation<A>(isolation: isolated Swift.Optional<Swift.Actor>, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Error>) -> ()) async throws -> A'
Func withCheckedThrowingContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)?
Expand All @@ -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<A>(operation: () async throws -> A, onCancel: @Sendable () -> ()) async throws -> A' to '_Concurrency.withTaskCancellationHandler<A>(operation: () async throws -> A, onCancel: @Sendable () -> (), isolation: isolated Swift.Optional<Swift.Actor>) 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<A>(function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Never>) -> ()) async -> A' to '_Concurrency.withCheckedContinuation<A>(isolation: isolated Swift.Optional<Swift.Actor>, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Never>) -> ()) 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<A>, onCancel: Swift.Optional<@Sendable () -> ()>) -> Swift.AsyncStream<A>' to 'Swift.AsyncStream.init(unfolding: () async -> Swift.Optional<A>, onCancel: Swift.Optional<() -> ()>) -> Swift.AsyncStream<A>'

Expand Down Expand Up @@ -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<A, B where A: Swift.Sendable>(of: A.Type, returning: B.Type, body: (inout Swift.TaskGroup<A>) async -> B) async -> B' to '_Concurrency.withTaskGroup<A, B where A: Swift.Sendable>(of: A.Type, returning: B.Type, isolation: isolated Swift.Optional<Swift.Actor>, body: (inout Swift.TaskGroup<A>) 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<A>(function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Never>) -> ()) async -> A' to '_Concurrency._unsafeInheritExecutor_withCheckedContinuation<A>(function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Never>) -> ()) async -> A'

Func pthread_main_np() is a new API without '@available'

// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)
Expand Down
10 changes: 7 additions & 3 deletions tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down