diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 8562119e8c239..29553130c1170 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1394,9 +1394,13 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C, // If both old and new enqueue are implemented, but the old one cannot be removed, // emit a warning that the new enqueue is unused. if (!canRemoveOldDecls && unownedEnqueueWitnessDecl && moveOnlyEnqueueWitnessDecl) { - if (!isStdlibDefaultImplDecl(moveOnlyEnqueueWitnessDecl)) { + if (!isStdlibDefaultImplDecl(moveOnlyEnqueueWitnessDecl) && + !isStdlibDefaultImplDecl(unownedEnqueueWitnessDecl)) { diags.diagnose(moveOnlyEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_unused_implementation); + if (auto decl = unownedEnqueueWitnessDecl) { + decl->diagnose(diag::decl_declared_here, decl); + } } } diff --git a/test/Concurrency/custom_executor_enqueue_availability.swift b/test/Concurrency/custom_executor_enqueue_availability.swift index 37b150d7135f9..10f334daf0833 100644 --- a/test/Concurrency/custom_executor_enqueue_availability.swift +++ b/test/Concurrency/custom_executor_enqueue_availability.swift @@ -26,7 +26,7 @@ final class OldExecutorOldStdlib: SerialExecutor { /// availability, since in this case the UnownedJob version needs to exist. @available(SwiftStdlib 5.1, *) final class BothExecutorOldStdlib: SerialExecutor { - func enqueue(_ job: UnownedJob) {} + func enqueue(_ job: UnownedJob) {} // expected-note{{'enqueue' declared here}} @available(SwiftStdlib 5.9, *) func enqueue(_ job: __owned ExecutorJob) {} // expected-warning{{'Executor.enqueue(ExecutorJob)' will never be used, due to the presence of 'enqueue(UnownedJob)'}} diff --git a/test/Concurrency/radar_concurrency_nio.swift b/test/Concurrency/radar_concurrency_nio.swift new file mode 100644 index 0000000000000..9dd20290c4dba --- /dev/null +++ b/test/Concurrency/radar_concurrency_nio.swift @@ -0,0 +1,66 @@ +// RUN: %target-swift-frontend -enable-experimental-move-only %s -emit-sil -o /dev/null -verify +// RUN: %target-swift-frontend -enable-experimental-move-only %s -emit-sil -o /dev/null -verify -strict-concurrency=targeted +// RUN: %target-swift-frontend -enable-experimental-move-only %s -emit-sil -o /dev/null -verify -strict-concurrency=complete +// RUN: %target-swift-frontend -enable-experimental-move-only %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -enable-experimental-feature SendNonSendable + +// REQUIRES: concurrency + +public protocol EventLoop: Sendable {} + +#if compiler(>=5.9) +/// A helper protocol that can be mixed in to a NIO ``EventLoop`` to provide an +/// automatic conformance to `SerialExecutor`. +/// +/// Implementers of `EventLoop` should consider conforming to this protocol as +/// well on Swift 5.9 and later. +@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) +public protocol NIOSerialEventLoopExecutor: EventLoop, SerialExecutor { } + +@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) +extension NIOSerialEventLoopExecutor { + @inlinable + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("mock impl") + } + + @inlinable + public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } + + @inlinable + public var executor: any SerialExecutor { + self + } +} + +// EARLIER AVAILABILITY +final class NIODefaultSerialEventLoopExecutor { + @usableFromInline + let loop: EventLoop + + @inlinable + init(_ loop: EventLoop) { + self.loop = loop + } +} + +@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) +extension NIODefaultSerialEventLoopExecutor: SerialExecutor { + @inlinable + public func enqueue(_ job: consuming ExecutorJob) { // do NOT issue a warning here + fatalError("mock impl") + } + + @inlinable + public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(complexEquality: self) + + } + + @inlinable + public func isSameExclusiveExecutionContext(other: NIODefaultSerialEventLoopExecutor) -> Bool { + false + } +} +#endif