Skip to content

Commit 5e43ba9

Browse files
committed
Don't trigger warnings about stdlib itself when user code might trigger them
The standard library's enqueue() does not play by the same rules -- we provide "deprecated" implementations in order to remain source/binary compatible, and showing warnings about this when users make mistake will only be misleading.
1 parent d0b6f4e commit 5e43ba9

File tree

4 files changed

+62
-26
lines changed

4 files changed

+62
-26
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6687,7 +6687,7 @@ WARNING(hashvalue_implementation,Deprecation,
66876687
"conform type %0 to 'Hashable' by implementing 'hash(into:)' instead",
66886688
(Type))
66896689

6690-
WARNING(executor_enqueue_unowned_implementation,Deprecation,
6690+
WARNING(executor_enqueue_deprecated_unowned_implementation,Deprecation,
66916691
"'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; "
66926692
"conform type %0 to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead",
66936693
(Type))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
12821282
auto enqueueDeclName = DeclName(C, DeclBaseName(C.Id_enqueue), { Identifier() });
12831283

12841284
FuncDecl *moveOnlyEnqueueRequirement = nullptr;
1285-
FuncDecl *legacyMoveOnlyEnqueueRequirement = nullptr; // TODO: preferably we'd want to remove handling of `enqueue(Job)` when able to
1285+
FuncDecl *legacyMoveOnlyEnqueueRequirement = nullptr;
12861286
FuncDecl *unownedEnqueueRequirement = nullptr;
12871287
for (auto req: proto->getProtocolRequirements()) {
12881288
auto *funcDecl = dyn_cast<FuncDecl>(req);
@@ -1325,8 +1325,7 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
13251325
assert(unownedEnqueueRequirement && "could not find the enqueue(UnownedJob) requirement, which should be always there");
13261326

13271327
// try to find at least a single implementations of enqueue(_:)
1328-
// auto unownedEnqueueWitness = concreteConformance->getWitnessDeclRef(unownedEnqueueRequirement);
1329-
ValueDecl *unownedEnqueueWitnessDecl = concreteConformance->getWitnessDecl(unownedEnqueueRequirement); // unownedEnqueueWitness.getDecl();
1328+
ValueDecl *unownedEnqueueWitnessDecl = concreteConformance->getWitnessDecl(unownedEnqueueRequirement);
13301329
ValueDecl *moveOnlyEnqueueWitnessDecl = nullptr;
13311330
ValueDecl *legacyMoveOnlyEnqueueWitnessDecl = nullptr;
13321331

@@ -1359,39 +1358,57 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
13591358
canRemoveOldDecls = declInfo.isContainedIn(requirementInfo);
13601359
}
13611360

1361+
auto concurrencyModule = C.getLoadedModule(C.Id_Concurrency);
1362+
auto isStdlibDefaultImplDecl = [executorDecl, concurrencyModule](ValueDecl *witness) -> bool {
1363+
if (auto declContext = witness->getDeclContext()) {
1364+
if (auto *extension = dyn_cast<ExtensionDecl>(declContext)) {
1365+
auto extensionModule = extension->getParentModule();
1366+
if (extensionModule != concurrencyModule) {
1367+
return false;
1368+
}
1369+
1370+
if (auto extendedNominal = extension->getExtendedNominal()) {
1371+
return extendedNominal->getDeclaredInterfaceType()->isEqual(
1372+
executorDecl->getDeclaredInterfaceType());
1373+
}
1374+
}
1375+
}
1376+
return false;
1377+
};
1378+
13621379
// If both old and new enqueue are implemented, but the old one cannot be removed,
13631380
// emit a warning that the new enqueue is unused.
13641381
if (!canRemoveOldDecls && unownedEnqueueWitnessDecl && moveOnlyEnqueueWitnessDecl) {
1365-
diags.diagnose(moveOnlyEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_unused_implementation);
1382+
if (!isStdlibDefaultImplDecl(moveOnlyEnqueueWitnessDecl)) {
1383+
diags.diagnose(moveOnlyEnqueueWitnessDecl->getLoc(),
1384+
diag::executor_enqueue_unused_implementation);
1385+
}
13661386
}
13671387

13681388
// Old UnownedJob based impl is present, warn about it suggesting the new protocol requirement.
13691389
if (canRemoveOldDecls && unownedEnqueueWitnessDecl) {
1370-
diags.diagnose(unownedEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_unowned_implementation, nominalTy);
1390+
if (!isStdlibDefaultImplDecl(unownedEnqueueWitnessDecl)) {
1391+
diags.diagnose(unownedEnqueueWitnessDecl->getLoc(),
1392+
diag::executor_enqueue_deprecated_unowned_implementation,
1393+
nominalTy);
1394+
}
13711395
}
13721396
// Old Job based impl is present, warn about it suggesting the new protocol requirement.
13731397
if (legacyMoveOnlyEnqueueWitnessDecl) {
1374-
diags.diagnose(legacyMoveOnlyEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_deprecated_owned_job_implementation, nominalTy);
1375-
}
1376-
1377-
auto isStdlibDefaultImplDecl = [executorDecl](ValueDecl *witness) -> bool {
1378-
if (auto declContext = witness->getDeclContext()) {
1379-
if (auto *extension = dyn_cast<ExtensionDecl>(declContext)) {
1380-
if (auto extendedNominal = extension->getExtendedNominal()) {
1381-
return extendedNominal->getDeclaredInterfaceType()->isEqual(
1382-
executorDecl->getDeclaredInterfaceType());
1383-
}
1384-
}
1398+
if (!isStdlibDefaultImplDecl(legacyMoveOnlyEnqueueWitnessDecl)) {
1399+
diags.diagnose(legacyMoveOnlyEnqueueWitnessDecl->getLoc(),
1400+
diag::executor_enqueue_deprecated_owned_job_implementation,
1401+
nominalTy);
13851402
}
1386-
return false;
1387-
};
1388-
auto missingWitness = !unownedEnqueueWitnessDecl &&
1389-
!moveOnlyEnqueueWitnessDecl &&
1390-
!legacyMoveOnlyEnqueueWitnessDecl;
1403+
}
13911404

13921405
bool unownedEnqueueWitnessIsDefaultImpl = isStdlibDefaultImplDecl(unownedEnqueueWitnessDecl);
13931406
bool moveOnlyEnqueueWitnessIsDefaultImpl = isStdlibDefaultImplDecl(moveOnlyEnqueueWitnessDecl);
13941407
bool legacyMoveOnlyEnqueueWitnessDeclIsDefaultImpl = isStdlibDefaultImplDecl(legacyMoveOnlyEnqueueWitnessDecl);
1408+
1409+
auto missingWitness = !unownedEnqueueWitnessDecl &&
1410+
!moveOnlyEnqueueWitnessDecl &&
1411+
!legacyMoveOnlyEnqueueWitnessDecl;
13951412
auto allWitnessesAreDefaultImpls = unownedEnqueueWitnessIsDefaultImpl &&
13961413
moveOnlyEnqueueWitnessIsDefaultImpl &&
13971414
legacyMoveOnlyEnqueueWitnessDeclIsDefaultImpl;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
2+
// REQUIRES: concurrency
3+
4+
// rdar://106849189 move-only types should be supported in freestanding mode
5+
// UNSUPPORTED: freestanding
6+
7+
8+
extension Executor {
9+
func enqueue(_ job: UnownedJob) { // expected-warning{{'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; conform type 'NoneExecutor' to 'Executor' by implementing 'func enqueue(ExecutorJob)' instead}}
10+
fatalError()
11+
}
12+
}
13+
14+
final class NoneExecutor: SerialExecutor {
15+
16+
// the enqueue from the extension is properly picked up,
17+
// even though we do warn about deprecation on it in the extension.
18+
19+
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
20+
UnownedSerialExecutor(ordinary: self)
21+
}
22+
}

test/Concurrency/custom_executor_enqueue_impls.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-ignore-unknown
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
22
// REQUIRES: concurrency
33

44
// rdar://106849189 move-only types should be supported in freestanding mode
55
// UNSUPPORTED: freestanding
66

7-
// FIXME: rdar://107112715 test failing on iOS simulator, investigating
8-
// UNSUPPORTED: OS=ios
9-
107
// Such type may be encountered since Swift 5.5 (5.1 backdeployed) if someone implemented the
118
// not documented, but public Executor types back then already.
129
//

0 commit comments

Comments
 (0)