@@ -1269,8 +1269,9 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
1269
1269
// enqueue(_:)
1270
1270
auto enqueueDeclName = DeclName (C, DeclBaseName (C.Id_enqueue ), { Identifier () });
1271
1271
1272
- FuncDecl *unownedEnqueueRequirement = nullptr ;
1273
1272
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 ;
1274
1275
for (auto req: proto->getProtocolRequirements ()) {
1275
1276
auto *funcDecl = dyn_cast<FuncDecl>(req);
1276
1277
if (!funcDecl)
@@ -1282,61 +1283,83 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
1282
1283
// look for the first parameter being a Job or UnownedJob
1283
1284
if (funcDecl->getParameters ()->size () != 1 )
1284
1285
continue ;
1286
+
1285
1287
if (auto param = funcDecl->getParameters ()->front ()) {
1288
+ StructDecl *executorJobDecl = C.getExecutorJobDecl ();
1289
+ StructDecl *legacyJobDecl = C.getJobDecl ();
1286
1290
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
- }
1294
1291
1295
- if (jobDecl &&
1296
- param->getType ()->isEqual (jobDecl->getDeclaredInterfaceType ())) {
1292
+ if (executorJobDecl && param->getType ()->isEqual (executorJobDecl->getDeclaredInterfaceType ())) {
1297
1293
assert (moveOnlyEnqueueRequirement == nullptr );
1298
1294
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 ())) {
1301
1299
assert (unownedEnqueueRequirement == nullptr );
1302
1300
unownedEnqueueRequirement = funcDecl;
1303
1301
}
1304
1302
}
1305
1303
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)
1308
1308
break ; // we're done looking for the requirements
1309
1309
}
1310
1310
1311
1311
auto conformance = module ->lookupConformance (nominalTy, proto);
1312
1312
auto concreteConformance = conformance.getConcrete ();
1313
1313
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(_:)
1314
1316
ConcreteDeclRef unownedEnqueueWitness = concreteConformance->getWitnessDeclRef (unownedEnqueueRequirement);
1317
+ ValueDecl *unownedEnqueueWitnessDecl = unownedEnqueueWitness.getDecl ();
1318
+ ValueDecl *moveOnlyEnqueueWitnessDecl = nullptr ;
1319
+ ValueDecl *legacyMoveOnlyEnqueueWitnessDecl = nullptr ;
1315
1320
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 ();
1321
1328
}
1322
1329
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 ;
1340
1363
}
1341
1364
}
1342
1365
}
0 commit comments