@@ -1269,8 +1269,9 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
12691269 // enqueue(_:)
12701270 auto enqueueDeclName = DeclName (C, DeclBaseName (C.Id_enqueue ), { Identifier () });
12711271
1272- FuncDecl *unownedEnqueueRequirement = nullptr ;
12731272 FuncDecl *moveOnlyEnqueueRequirement = nullptr ;
1273+ FuncDecl *legacyMoveOnlyEnqueueRequirement = nullptr ; // TODO: preferably we'd want to remove handling of `enqueue(Job)` when able to
1274+ FuncDecl *unownedEnqueueRequirement = nullptr ;
12741275 for (auto req: proto->getProtocolRequirements ()) {
12751276 auto *funcDecl = dyn_cast<FuncDecl>(req);
12761277 if (!funcDecl)
@@ -1282,61 +1283,83 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
12821283 // look for the first parameter being a Job or UnownedJob
12831284 if (funcDecl->getParameters ()->size () != 1 )
12841285 continue ;
1286+
12851287 if (auto param = funcDecl->getParameters ()->front ()) {
1288+ StructDecl *executorJobDecl = C.getExecutorJobDecl ();
1289+ StructDecl *legacyJobDecl = C.getJobDecl ();
12861290 StructDecl *unownedJobDecl = C.getUnownedJobDecl ();
1287- StructDecl *jobDecl = nullptr ;
1288- if (auto executorJobDecl = C.getExecutorJobDecl ()) {
1289- jobDecl = executorJobDecl;
1290- } else if (auto plainJobDecl = C.getJobDecl ()) {
1291- // old standard library, before we introduced the `typealias Job = ExecutorJob`
1292- jobDecl = plainJobDecl;
1293- }
12941291
1295- if (jobDecl &&
1296- param->getType ()->isEqual (jobDecl->getDeclaredInterfaceType ())) {
1292+ if (executorJobDecl && param->getType ()->isEqual (executorJobDecl->getDeclaredInterfaceType ())) {
12971293 assert (moveOnlyEnqueueRequirement == nullptr );
12981294 moveOnlyEnqueueRequirement = funcDecl;
1299- } else if (unownedJobDecl &&
1300- param->getType ()->isEqual (unownedJobDecl->getDeclaredInterfaceType ())) {
1295+ } else if (legacyJobDecl && param->getType ()->isEqual (legacyJobDecl->getDeclaredInterfaceType ())) {
1296+ assert (legacyMoveOnlyEnqueueRequirement == nullptr );
1297+ legacyMoveOnlyEnqueueRequirement = funcDecl;
1298+ } else if (unownedJobDecl && param->getType ()->isEqual (unownedJobDecl->getDeclaredInterfaceType ())) {
13011299 assert (unownedEnqueueRequirement == nullptr );
13021300 unownedEnqueueRequirement = funcDecl;
13031301 }
13041302 }
13051303
1306- // if we found both, we're done here and break out of the loop
1307- if (unownedEnqueueRequirement && moveOnlyEnqueueRequirement)
1304+ // if we found all potential requirements, we're done here and break out of the loop
1305+ if (unownedEnqueueRequirement &&
1306+ moveOnlyEnqueueRequirement &&
1307+ legacyMoveOnlyEnqueueRequirement)
13081308 break ; // we're done looking for the requirements
13091309 }
13101310
13111311 auto conformance = module ->lookupConformance (nominalTy, proto);
13121312 auto concreteConformance = conformance.getConcrete ();
13131313 assert (unownedEnqueueRequirement && " could not find the enqueue(UnownedJob) requirement, which should be always there" );
1314+
1315+ // try to find at least a single implementations of enqueue(_:)
13141316 ConcreteDeclRef unownedEnqueueWitness = concreteConformance->getWitnessDeclRef (unownedEnqueueRequirement);
1317+ ValueDecl *unownedEnqueueWitnessDecl = unownedEnqueueWitness.getDecl ();
1318+ ValueDecl *moveOnlyEnqueueWitnessDecl = nullptr ;
1319+ ValueDecl *legacyMoveOnlyEnqueueWitnessDecl = nullptr ;
13151320
1316- if (auto enqueueUnownedDecl = unownedEnqueueWitness.getDecl ()) {
1317- // Old UnownedJob based impl is present, warn about it suggesting the new protocol requirement.
1318- if (enqueueUnownedDecl->getLoc ().isValid ()) {
1319- diags.diagnose (enqueueUnownedDecl->getLoc (), diag::executor_enqueue_unowned_implementation, nominalTy);
1320- }
1321+ if (moveOnlyEnqueueRequirement) {
1322+ moveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDeclRef (
1323+ moveOnlyEnqueueRequirement).getDecl ();
1324+ }
1325+ if (legacyMoveOnlyEnqueueRequirement) {
1326+ legacyMoveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDeclRef (
1327+ legacyMoveOnlyEnqueueRequirement).getDecl ();
13211328 }
13221329
1323- if (auto unownedEnqueueDecl = unownedEnqueueWitness.getDecl ()) {
1324- if (moveOnlyEnqueueRequirement) {
1325- ConcreteDeclRef moveOnlyEnqueueWitness = concreteConformance->getWitnessDeclRef (moveOnlyEnqueueRequirement);
1326- if (auto moveOnlyEnqueueDecl = moveOnlyEnqueueWitness.getDecl ()) {
1327- if (unownedEnqueueDecl && unownedEnqueueDecl->getLoc ().isInvalid () &&
1328- moveOnlyEnqueueDecl && moveOnlyEnqueueDecl->getLoc ().isInvalid ()) {
1329- // Neither old nor new implementation have been found, but we provide default impls for them
1330- // that are mutually recursive, so we must error and suggest implementing the right requirement.
1331- auto ownedRequirement = C.getExecutorDecl ()->getExecutorOwnedEnqueueFunction ();
1332- nominal->diagnose (diag::type_does_not_conform, nominalTy, proto->getDeclaredInterfaceType ());
1333- ownedRequirement->diagnose (diag::no_witnesses,
1334- getProtocolRequirementKind (ownedRequirement),
1335- ownedRequirement->getName (),
1336- proto->getDeclaredInterfaceType (),
1337- /* AddFixIt=*/ true );
1338- }
1339- }
1330+ // --- Diagnose warnings and errors
1331+
1332+ // Old UnownedJob based impl is present, warn about it suggesting the new protocol requirement.
1333+ if (unownedEnqueueWitnessDecl && unownedEnqueueWitnessDecl->getLoc ().isValid ()) {
1334+ diags.diagnose (unownedEnqueueWitnessDecl->getLoc (), diag::executor_enqueue_unowned_implementation, nominalTy);
1335+ }
1336+ // Old Job based impl is present, warn about it suggesting the new protocol requirement.
1337+ if (legacyMoveOnlyEnqueueWitnessDecl && legacyMoveOnlyEnqueueWitnessDecl->getLoc ().isValid ()) {
1338+ diags.diagnose (legacyMoveOnlyEnqueueWitnessDecl->getLoc (), diag::executor_enqueue_deprecated_owned_job_implementation, nominalTy);
1339+ }
1340+
1341+ if ((!unownedEnqueueWitnessDecl || unownedEnqueueWitnessDecl->getLoc ().isInvalid ()) &&
1342+ (!moveOnlyEnqueueWitnessDecl || moveOnlyEnqueueWitnessDecl->getLoc ().isInvalid ()) &&
1343+ (!legacyMoveOnlyEnqueueWitnessDecl || legacyMoveOnlyEnqueueWitnessDecl->getLoc ().isInvalid ())) {
1344+ // Neither old nor new implementation have been found, but we provide default impls for them
1345+ // that are mutually recursive, so we must error and suggest implementing the right requirement.
1346+ //
1347+ // If we're running against an SDK that does not have the ExecutorJob enqueue function,
1348+ // try to diagnose using the next-best one available.
1349+ auto missingRequirement = C.getExecutorDecl ()->getExecutorOwnedEnqueueFunction ();
1350+ if (!missingRequirement)
1351+ missingRequirement = C.getExecutorDecl ()->getExecutorLegacyOwnedEnqueueFunction ();
1352+ if (!missingRequirement)
1353+ missingRequirement = C.getExecutorDecl ()->getExecutorLegacyUnownedEnqueueFunction ();
1354+
1355+ if (missingRequirement) {
1356+ nominal->diagnose (diag::type_does_not_conform, nominalTy, proto->getDeclaredInterfaceType ());
1357+ missingRequirement->diagnose (diag::no_witnesses,
1358+ getProtocolRequirementKind (missingRequirement),
1359+ missingRequirement->getName (),
1360+ missingRequirement->getParameters ()->get (0 )->getInterfaceType (),
1361+ /* AddFixIt=*/ true );
1362+ return ;
13401363 }
13411364 }
13421365}
0 commit comments