diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index bafd715ca6cbe..2c86b5739aa8a 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3879,8 +3879,13 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Find the 'RemoteCallArgument(label:name:value:)' initializer function. ConstructorDecl *getDistributedRemoteCallArgumentInitFunction() const; - /// Get the move-only `enqueue(Job)` protocol requirement function on the `Executor` protocol. + /// Get the move-only `enqueue(ExecutorJob)` protocol requirement function on the `Executor` protocol. AbstractFunctionDecl *getExecutorOwnedEnqueueFunction() const; + /// This method should be deprecated and removed + /// Get the move-only `enqueue(Job)` protocol requirement function on the `Executor` protocol. + AbstractFunctionDecl *getExecutorLegacyOwnedEnqueueFunction() const; + /// Get the move-only `enqueue(UnownedJob)` protocol requirement function on the `Executor` protocol. + AbstractFunctionDecl *getExecutorLegacyUnownedEnqueueFunction() const; /// Collect the set of protocols to which this type should implicitly /// conform, such as AnyObject (for classes). diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6350629c9f839..52d903ad95b4b 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -6521,7 +6521,11 @@ WARNING(hashvalue_implementation,Deprecation, WARNING(executor_enqueue_unowned_implementation,Deprecation, "'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; " - "conform type %0 to 'Executor' by implementing 'func enqueue(Job)' instead", + "conform type %0 to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead", + (Type)) +WARNING(executor_enqueue_deprecated_owned_job_implementation,Deprecation, + "'Executor.enqueue(Job)' is deprecated as a protocol requirement; " + "conform type %0 to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead", (Type)) //------------------------------------------------------------------------------ diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4f6a5f278f7ec..10598265bc54f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5305,6 +5305,48 @@ VarDecl *NominalTypeDecl::getGlobalActorInstance() const { AbstractFunctionDecl * NominalTypeDecl::getExecutorOwnedEnqueueFunction() const { auto &C = getASTContext(); + StructDecl *executorJobDecl = C.getExecutorJobDecl(); + if (!executorJobDecl) + return nullptr; + + auto proto = dyn_cast(this); + if (!proto) + return nullptr; + + llvm::SmallVector results; + lookupQualified(getSelfNominalTypeDecl(), + DeclNameRef(C.Id_enqueue), + NL_ProtocolMembers, + results); + + for (auto candidate: results) { + // we're specifically looking for the Executor protocol requirement + if (!isa(candidate->getDeclContext())) + continue; + + if (auto *funcDecl = dyn_cast(candidate)) { + auto params = funcDecl->getParameters(); + + if (params->size() != 1) + continue; + + if ((params->get(0)->getSpecifier() == ParamSpecifier::LegacyOwned || + params->get(0)->getSpecifier() == ParamSpecifier::Consuming) && + params->get(0)->getInterfaceType()->isEqual(executorJobDecl->getDeclaredInterfaceType())) { + return funcDecl; + } + } + } + + return nullptr; +} + +AbstractFunctionDecl * +NominalTypeDecl::getExecutorLegacyOwnedEnqueueFunction() const { + auto &C = getASTContext(); + StructDecl *legacyJobDecl = C.getJobDecl(); + if (!legacyJobDecl) + return nullptr; auto proto = dyn_cast(this); if (!proto) @@ -5322,11 +5364,51 @@ NominalTypeDecl::getExecutorOwnedEnqueueFunction() const { continue; if (auto *funcDecl = dyn_cast(candidate)) { - if (funcDecl->getParameters()->size() != 1) + auto params = funcDecl->getParameters(); + + if (params->size() != 1) continue; + if ((params->get(0)->getSpecifier() == ParamSpecifier::LegacyOwned || + params->get(0)->getSpecifier() == ParamSpecifier::Consuming) && + params->get(0)->getType()->isEqual(legacyJobDecl->getDeclaredInterfaceType())) { + return funcDecl; + } + } + } + + return nullptr; +} + +AbstractFunctionDecl * +NominalTypeDecl::getExecutorLegacyUnownedEnqueueFunction() const { + auto &C = getASTContext(); + StructDecl *unownedJobDecl = C.getUnownedJobDecl(); + if (!unownedJobDecl) + return nullptr; + + auto proto = dyn_cast(this); + if (!proto) + return nullptr; + + llvm::SmallVector results; + lookupQualified(getSelfNominalTypeDecl(), + DeclNameRef(C.Id_enqueue), + NL_ProtocolMembers, + results); + + for (auto candidate: results) { + // we're specifically looking for the Executor protocol requirement + if (!isa(candidate->getDeclContext())) + continue; + + if (auto *funcDecl = dyn_cast(candidate)) { auto params = funcDecl->getParameters(); - if (params->get(0)->getSpecifier() == ParamSpecifier::LegacyOwned) { // TODO: make this Consuming + + if (params->size() != 1) + continue; + + if (params->get(0)->getType()->isEqual(unownedJobDecl->getDeclaredInterfaceType())) { return funcDecl; } } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index eaa5fe76d2137..eb566bcde9a3c 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1269,8 +1269,9 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C, // enqueue(_:) auto enqueueDeclName = DeclName(C, DeclBaseName(C.Id_enqueue), { Identifier() }); - FuncDecl *unownedEnqueueRequirement = nullptr; FuncDecl *moveOnlyEnqueueRequirement = nullptr; + FuncDecl *legacyMoveOnlyEnqueueRequirement = nullptr; // TODO: preferably we'd want to remove handling of `enqueue(Job)` when able to + FuncDecl *unownedEnqueueRequirement = nullptr; for (auto req: proto->getProtocolRequirements()) { auto *funcDecl = dyn_cast(req); if (!funcDecl) @@ -1282,61 +1283,83 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C, // look for the first parameter being a Job or UnownedJob if (funcDecl->getParameters()->size() != 1) continue; + if (auto param = funcDecl->getParameters()->front()) { + StructDecl *executorJobDecl = C.getExecutorJobDecl(); + StructDecl *legacyJobDecl = C.getJobDecl(); StructDecl *unownedJobDecl = C.getUnownedJobDecl(); - StructDecl *jobDecl = nullptr; - if (auto executorJobDecl = C.getExecutorJobDecl()) { - jobDecl = executorJobDecl; - } else if (auto plainJobDecl = C.getJobDecl()) { - // old standard library, before we introduced the `typealias Job = ExecutorJob` - jobDecl = plainJobDecl; - } - if (jobDecl && - param->getType()->isEqual(jobDecl->getDeclaredInterfaceType())) { + if (executorJobDecl && param->getType()->isEqual(executorJobDecl->getDeclaredInterfaceType())) { assert(moveOnlyEnqueueRequirement == nullptr); moveOnlyEnqueueRequirement = funcDecl; - } else if (unownedJobDecl && - param->getType()->isEqual(unownedJobDecl->getDeclaredInterfaceType())) { + } else if (legacyJobDecl && param->getType()->isEqual(legacyJobDecl->getDeclaredInterfaceType())) { + assert(legacyMoveOnlyEnqueueRequirement == nullptr); + legacyMoveOnlyEnqueueRequirement = funcDecl; + } else if (unownedJobDecl && param->getType()->isEqual(unownedJobDecl->getDeclaredInterfaceType())) { assert(unownedEnqueueRequirement == nullptr); unownedEnqueueRequirement = funcDecl; } } - // if we found both, we're done here and break out of the loop - if (unownedEnqueueRequirement && moveOnlyEnqueueRequirement) + // if we found all potential requirements, we're done here and break out of the loop + if (unownedEnqueueRequirement && + moveOnlyEnqueueRequirement && + legacyMoveOnlyEnqueueRequirement) break; // we're done looking for the requirements } auto conformance = module->lookupConformance(nominalTy, proto); auto concreteConformance = conformance.getConcrete(); assert(unownedEnqueueRequirement && "could not find the enqueue(UnownedJob) requirement, which should be always there"); + + // try to find at least a single implementations of enqueue(_:) ConcreteDeclRef unownedEnqueueWitness = concreteConformance->getWitnessDeclRef(unownedEnqueueRequirement); + ValueDecl *unownedEnqueueWitnessDecl = unownedEnqueueWitness.getDecl(); + ValueDecl *moveOnlyEnqueueWitnessDecl = nullptr; + ValueDecl *legacyMoveOnlyEnqueueWitnessDecl = nullptr; - if (auto enqueueUnownedDecl = unownedEnqueueWitness.getDecl()) { - // Old UnownedJob based impl is present, warn about it suggesting the new protocol requirement. - if (enqueueUnownedDecl->getLoc().isValid()) { - diags.diagnose(enqueueUnownedDecl->getLoc(), diag::executor_enqueue_unowned_implementation, nominalTy); - } + if (moveOnlyEnqueueRequirement) { + moveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDeclRef( + moveOnlyEnqueueRequirement).getDecl(); + } + if (legacyMoveOnlyEnqueueRequirement) { + legacyMoveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDeclRef( + legacyMoveOnlyEnqueueRequirement).getDecl(); } - if (auto unownedEnqueueDecl = unownedEnqueueWitness.getDecl()) { - if (moveOnlyEnqueueRequirement) { - ConcreteDeclRef moveOnlyEnqueueWitness = concreteConformance->getWitnessDeclRef(moveOnlyEnqueueRequirement); - if (auto moveOnlyEnqueueDecl = moveOnlyEnqueueWitness.getDecl()) { - if (unownedEnqueueDecl && unownedEnqueueDecl->getLoc().isInvalid() && - moveOnlyEnqueueDecl && moveOnlyEnqueueDecl->getLoc().isInvalid()) { - // Neither old nor new implementation have been found, but we provide default impls for them - // that are mutually recursive, so we must error and suggest implementing the right requirement. - auto ownedRequirement = C.getExecutorDecl()->getExecutorOwnedEnqueueFunction(); - nominal->diagnose(diag::type_does_not_conform, nominalTy, proto->getDeclaredInterfaceType()); - ownedRequirement->diagnose(diag::no_witnesses, - getProtocolRequirementKind(ownedRequirement), - ownedRequirement->getName(), - proto->getDeclaredInterfaceType(), - /*AddFixIt=*/true); - } - } + // --- Diagnose warnings and errors + + // Old UnownedJob based impl is present, warn about it suggesting the new protocol requirement. + if (unownedEnqueueWitnessDecl && unownedEnqueueWitnessDecl->getLoc().isValid()) { + diags.diagnose(unownedEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_unowned_implementation, nominalTy); + } + // Old Job based impl is present, warn about it suggesting the new protocol requirement. + if (legacyMoveOnlyEnqueueWitnessDecl && legacyMoveOnlyEnqueueWitnessDecl->getLoc().isValid()) { + diags.diagnose(legacyMoveOnlyEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_deprecated_owned_job_implementation, nominalTy); + } + + if ((!unownedEnqueueWitnessDecl || unownedEnqueueWitnessDecl->getLoc().isInvalid()) && + (!moveOnlyEnqueueWitnessDecl || moveOnlyEnqueueWitnessDecl->getLoc().isInvalid()) && + (!legacyMoveOnlyEnqueueWitnessDecl || legacyMoveOnlyEnqueueWitnessDecl->getLoc().isInvalid())) { + // Neither old nor new implementation have been found, but we provide default impls for them + // that are mutually recursive, so we must error and suggest implementing the right requirement. + // + // If we're running against an SDK that does not have the ExecutorJob enqueue function, + // try to diagnose using the next-best one available. + auto missingRequirement = C.getExecutorDecl()->getExecutorOwnedEnqueueFunction(); + if (!missingRequirement) + missingRequirement = C.getExecutorDecl()->getExecutorLegacyOwnedEnqueueFunction(); + if (!missingRequirement) + missingRequirement = C.getExecutorDecl()->getExecutorLegacyUnownedEnqueueFunction(); + + if (missingRequirement) { + nominal->diagnose(diag::type_does_not_conform, nominalTy, proto->getDeclaredInterfaceType()); + missingRequirement->diagnose(diag::no_witnesses, + getProtocolRequirementKind(missingRequirement), + missingRequirement->getName(), + missingRequirement->getParameters()->get(0)->getInterfaceType(), + /*AddFixIt=*/true); + return; } } } diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 55b9b843e93a3..ea501762b7790 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -24,6 +24,12 @@ public protocol Executor: AnyObject, Sendable { #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY func enqueue(_ job: UnownedJob) + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) + @available(*, deprecated, message: "Use enqueue(ExecutorJob) instead") + func enqueue(_ job: __owned Job) + #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.9, *) func enqueue(_ job: __owned ExecutorJob) @@ -46,6 +52,17 @@ public protocol SerialExecutor: Executor { #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY func enqueue(_ job: UnownedJob) + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + // This requirement is repeated here as a non-override so that we + // get a redundant witness-table entry for it. This allows us to + // avoid drilling down to the base conformance just for the basic + // work-scheduling operation. + @_nonoverride + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) + @available(*, deprecated, message: "Use enqueue(ExecutorJob) instead") + func enqueue(_ job: __owned Job) + #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY // This requirement is repeated here as a non-override so that we // get a redundant witness-table entry for it. This allows us to @@ -92,6 +109,10 @@ extension Executor { public func enqueue(_ job: __owned ExecutorJob) { self.enqueue(UnownedJob(job)) } + + public func enqueue(_ job: __owned Job) { + self.enqueue(UnownedJob(job)) + } } #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index 5f375b965af61..3ef290bd2bc06 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -50,6 +50,14 @@ public struct UnownedJob: Sendable { } #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + /// Create an `UnownedJob` whose lifetime must be managed carefully until it is run exactly once. + @available(SwiftStdlib 5.9, *) + public init(_ job: __owned ExecutorJob) { + self.context = job.context + } + #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + /// The priority of this job. @available(SwiftStdlib 5.9, *) public var priority: JobPriority { @@ -106,9 +114,76 @@ extension UnownedJob: CustomStringConvertible { #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +/// Deprecated equivalent of ``ExecutorJob``. +/// +/// A unit of scheduleable work. +/// +/// Unless you're implementing a scheduler, +/// you don't generally interact with jobs directly. @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) @available(*, deprecated, renamed: "ExecutorJob") -public typealias Job = ExecutorJob +@frozen +@_moveOnly +public struct Job: Sendable { + internal var context: Builtin.Job + + @usableFromInline + internal init(context: __owned Builtin.Job) { + self.context = context + } + + public init(_ job: UnownedJob) { + self.context = job._context + } + + public init(_ job: __owned ExecutorJob) { + self.context = job.context + } + + public var priority: JobPriority { + let raw = _swift_concurrency_jobPriority(UnownedJob(context: self.context)) + return JobPriority(rawValue: raw) + } + + // TODO: move only types cannot conform to protocols, so we can't conform to CustomStringConvertible; + // we can still offer a description to be called explicitly though. + public var description: String { + let id = _getJobTaskId(UnownedJob(context: self.context)) + /// Tasks are always assigned an unique ID, however some jobs may not have it set, + /// and it appearing as 0 for _different_ jobs may lead to misunderstanding it as + /// being "the same 0 id job", we specifically print 0 (id not set) as nil. + if (id > 0) { + return "Job(id: \(id))" + } else { + return "Job(id: nil)" + } + } +} + +@available(SwiftStdlib 5.9, *) +extension Job { + + /// Run this job on the passed in executor. + /// + /// This operation runs the job on the calling thread and *blocks* until the job completes. + /// The intended use of this method is for an executor to determine when and where it + /// wants to run the job and then call this method on it. + /// + /// The passed in executor reference is used to establish the executor context for the job, + /// and should be the same executor as the one semantically calling the `runSynchronously` method. + /// + /// This operation consumes the job, preventing it accidental use after it has ben run. + /// + /// Converting a `ExecutorJob` to an ``UnownedJob`` and invoking ``UnownedJob/runSynchronously(_:)` on it multiple times is undefined behavior, + /// as a job can only ever be run once, and must not be accessed after it has been run. + /// + /// - Parameter executor: the executor this job will be semantically running on. + @_alwaysEmitIntoClient + @inlinable + __consuming public func runSynchronously(on executor: UnownedSerialExecutor) { + _swiftJobRun(UnownedJob(self), executor) + } +} /// A unit of scheduleable work. /// @@ -129,6 +204,10 @@ public struct ExecutorJob: Sendable { self.context = job._context } + public init(_ job: __owned Job) { + self.context = job.context + } + public var priority: JobPriority { let raw = _swift_concurrency_jobPriority(UnownedJob(context: self.context)) return JobPriority(rawValue: raw) @@ -142,9 +221,9 @@ public struct ExecutorJob: Sendable { /// and it appearing as 0 for _different_ jobs may lead to misunderstanding it as /// being "the same 0 id job", we specifically print 0 (id not set) as nil. if (id > 0) { - return "\(Self.self)(id: \(id))" + return "ExecutorJob(id: \(id))" } else { - return "\(Self.self)(id: nil)" + return "ExecutorJob(id: nil)" } } } diff --git a/stdlib/public/Distributed/DistributedDefaultExecutor.swift b/stdlib/public/Distributed/DistributedDefaultExecutor.swift index c1deed3455005..1aaad52fd95fa 100644 --- a/stdlib/public/Distributed/DistributedDefaultExecutor.swift +++ b/stdlib/public/Distributed/DistributedDefaultExecutor.swift @@ -26,7 +26,7 @@ internal final class DistributedRemoteActorReferenceExecutor: SerialExecutor { @inlinable public func enqueue(_ job: __owned ExecutorJob) { let jobDescription = job.description - fatalError("Attempted to enqueue \(Job.self) (\(jobDescription)) on executor of remote distributed actor reference!") + fatalError("Attempted to enqueue ExecutorJob (\(jobDescription)) on executor of remote distributed actor reference!") } public func asUnownedSerialExecutor() -> UnownedSerialExecutor { diff --git a/test/Concurrency/Runtime/custom_executors_moveOnly_job.swift b/test/Concurrency/Runtime/custom_executors_moveOnly_job.swift index 36d8512c91bcf..e919f2998752f 100644 --- a/test/Concurrency/Runtime/custom_executors_moveOnly_job.swift +++ b/test/Concurrency/Runtime/custom_executors_moveOnly_job.swift @@ -10,6 +10,12 @@ // REQUIRES: concurrency_runtime final class InlineExecutor: SerialExecutor, CustomStringConvertible { + public func enqueue(_ job: UnownedJob) { + job.runSynchronously(on: self.asUnownedSerialExecutor()) + } + public func enqueue(_ job: __owned Job) { + job.runSynchronously(on: self.asUnownedSerialExecutor()) + } public func enqueue(_ job: __owned ExecutorJob) { job.runSynchronously(on: self.asUnownedSerialExecutor()) } diff --git a/test/Concurrency/Runtime/custom_executors_tryDiagnoseExecutorConformance_with_sdk_typealias_struct_job_types.swift b/test/Concurrency/Runtime/custom_executors_tryDiagnoseExecutorConformance_with_sdk_typealias_struct_job_types.swift new file mode 100644 index 0000000000000..033c9c25ced6f --- /dev/null +++ b/test/Concurrency/Runtime/custom_executors_tryDiagnoseExecutorConformance_with_sdk_typealias_struct_job_types.swift @@ -0,0 +1,20 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-concurrency-typealias-struct-job) -typecheck -parse-as-library %s -verify + +// REQUIRES: concurrency +// REQUIRES: libdispatch + +// rdar://106849189 move-only types should be supported in freestanding mode +// UNSUPPORTED: freestanding + +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: concurrency_runtime + +import _Concurrency + +final class FakeExecutor1: SerialExecutor { + func enqueue(_ job: __owned ExecutorJob) {} +} + +final class FakeExecutor2: SerialExecutor { + func enqueue(_ job: __owned Job) {} +} diff --git a/test/Concurrency/custom_executor_enqueue_impls.swift b/test/Concurrency/custom_executor_enqueue_impls.swift index 46db955cced2f..4d1943c92ec84 100644 --- a/test/Concurrency/custom_executor_enqueue_impls.swift +++ b/test/Concurrency/custom_executor_enqueue_impls.swift @@ -12,7 +12,7 @@ // // We keep support for them, but also log a deprecation warning that they should move to the new signature. final class OldExecutor: SerialExecutor { - func enqueue(_ job: UnownedJob) {} // expected-warning{{'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; conform type 'OldExecutor' to 'Executor' by implementing 'func enqueue(Job)' instead}} + func enqueue(_ job: UnownedJob) {} // expected-warning{{'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; conform type 'OldExecutor' to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead}} func asUnownedSerialExecutor() -> UnownedSerialExecutor { UnownedSerialExecutor(ordinary: self) @@ -24,7 +24,23 @@ final class OldExecutor: SerialExecutor { /// /// That's why we do log the deprecation warning, people should use the move-only version. final class BothExecutor: SerialExecutor { - func enqueue(_ job: UnownedJob) {} // expected-warning{{'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; conform type 'BothExecutor' to 'Executor' by implementing 'func enqueue(Job)' instead}} + func enqueue(_ job: UnownedJob) {} // expected-warning{{'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; conform type 'BothExecutor' to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead}} + + func enqueue(_ job: __owned ExecutorJob) {} + + func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } +} + +/// For now we must keep all 3 implementation kinds and warn about deprecated ones +final class TripleExecutor: SerialExecutor { + func enqueue(_ job: UnownedJob) {} // expected-warning{{'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; conform type 'TripleExecutor' to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead}} + + // expected-warning@+2{{'Job' is deprecated: renamed to 'ExecutorJob'}} + // expected-note@+1{{use 'ExecutorJob' instead}} + func enqueue(_ job: __owned Job) {} // expected-warning{{'Executor.enqueue(Job)' is deprecated as a protocol requirement; conform type 'TripleExecutor' to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead}} + func enqueue(_ job: __owned ExecutorJob) {} func asUnownedSerialExecutor() -> UnownedSerialExecutor { @@ -43,6 +59,17 @@ final class NoneExecutor: SerialExecutor { // expected-error{{type 'NoneExecutor } } +/// Job still is deprecated +final class StillDeprecated: SerialExecutor { + // expected-warning@+2{{'Job' is deprecated: renamed to 'ExecutorJob'}} + // expected-note@+1{{use 'ExecutorJob' instead}} + func enqueue(_ job: __owned Job) {} // expected-warning{{'Executor.enqueue(Job)' is deprecated as a protocol requirement; conform type 'StillDeprecated' to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead}} + + func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } +} + /// Just implementing the new signature causes no warnings, good. final class NewExecutor: SerialExecutor { func enqueue(_ job: __owned ExecutorJob) {} // no warnings diff --git a/test/Inputs/clang-importer-sdk/swift-modules-concurrency-typealias-struct-job/_Concurrency.swift b/test/Inputs/clang-importer-sdk/swift-modules-concurrency-typealias-struct-job/_Concurrency.swift new file mode 100644 index 0000000000000..b3318e0f73d40 --- /dev/null +++ b/test/Inputs/clang-importer-sdk/swift-modules-concurrency-typealias-struct-job/_Concurrency.swift @@ -0,0 +1,9 @@ + +@_moveOnly +public struct ExecutorJob {} + +public typealias Job = ExecutorJob + +public protocol SerialExecutor { + func enqueue(_ job: __owned ExecutorJob) +} \ No newline at end of file diff --git a/test/lit.cfg b/test/lit.cfg index bb7d6ca66ccb8..407f17ec0e2ec 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -611,15 +611,22 @@ config.substitutions.append(('%build-clang-importer-objc-overlays', # FIXME: BEGIN -enable-source-import hackaround config.substitutions.append(('%clang-importer-sdk-concurrency-without-job-path', '%r' % (make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk')))) - config.substitutions.append(('%clang-importer-sdk-concurrency-without-job-nosource', '-sdk %r' % (make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk')))) # FIXME: END -enable-source-import hackaround - config.substitutions.append(('%clang-importer-sdk-concurrency-without-job', '-enable-source-import -sdk %r -I %r ' % (make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk'), make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk', 'swift-modules-concurrency-without-job')))) +# FIXME: BEGIN -enable-source-import hackaround +config.substitutions.append(('%clang-importer-sdk-concurrency-typealias-struct-job-path', + '%r' % (make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk')))) +config.substitutions.append(('%clang-importer-sdk-concurrency-typealias-struct-job-nosource', + '-sdk %r' % (make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk')))) +# FIXME: END -enable-source-import hackaround +config.substitutions.append(('%clang-importer-sdk-concurrency-typealias-struct-job', + '-enable-source-import -sdk %r -I %r ' % (make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk'), + make_path(config.test_source_root, 'Inputs', 'clang-importer-sdk', 'swift-modules-concurrency-typealias-struct-job')))) # FIXME: BEGIN -enable-source-import hackaround config.substitutions.append(('%clang-importer-sdk-path',