Skip to content

Commit aa3d001

Browse files
committed
[Executors ]dont use loc validity to determine if we should diagnose
instead, error if all implementations are "default from stdlib" because end-user MUST implement at least one of them 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 35051fd commit aa3d001

File tree

4 files changed

+78
-23
lines changed

4 files changed

+78
-23
lines changed

include/swift/AST/DiagnosticsSema.def

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

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

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,12 +1283,13 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
12831283
auto &diags = C.Diags;
12841284
auto module = nominal->getParentModule();
12851285
Type nominalTy = nominal->getDeclaredInterfaceType();
1286+
NominalTypeDecl *executorDecl = C.getExecutorDecl();
12861287

12871288
// enqueue(_:)
12881289
auto enqueueDeclName = DeclName(C, DeclBaseName(C.Id_enqueue), { Identifier() });
12891290

12901291
FuncDecl *moveOnlyEnqueueRequirement = nullptr;
1291-
FuncDecl *legacyMoveOnlyEnqueueRequirement = nullptr; // TODO: preferably we'd want to remove handling of `enqueue(Job)` when able to
1292+
FuncDecl *legacyMoveOnlyEnqueueRequirement = nullptr;
12921293
FuncDecl *unownedEnqueueRequirement = nullptr;
12931294
for (auto req: proto->getProtocolRequirements()) {
12941295
auto *funcDecl = dyn_cast<FuncDecl>(req);
@@ -1331,18 +1332,17 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
13311332
assert(unownedEnqueueRequirement && "could not find the enqueue(UnownedJob) requirement, which should be always there");
13321333

13331334
// try to find at least a single implementations of enqueue(_:)
1334-
ConcreteDeclRef unownedEnqueueWitness = concreteConformance->getWitnessDeclRef(unownedEnqueueRequirement);
1335-
ValueDecl *unownedEnqueueWitnessDecl = unownedEnqueueWitness.getDecl();
1335+
ValueDecl *unownedEnqueueWitnessDecl = concreteConformance->getWitnessDecl(unownedEnqueueRequirement);
13361336
ValueDecl *moveOnlyEnqueueWitnessDecl = nullptr;
13371337
ValueDecl *legacyMoveOnlyEnqueueWitnessDecl = nullptr;
13381338

13391339
if (moveOnlyEnqueueRequirement) {
1340-
moveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDeclRef(
1341-
moveOnlyEnqueueRequirement).getDecl();
1340+
moveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDecl(
1341+
moveOnlyEnqueueRequirement);
13421342
}
13431343
if (legacyMoveOnlyEnqueueRequirement) {
1344-
legacyMoveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDeclRef(
1345-
legacyMoveOnlyEnqueueRequirement).getDecl();
1344+
legacyMoveOnlyEnqueueWitnessDecl = concreteConformance->getWitnessDecl(
1345+
legacyMoveOnlyEnqueueRequirement);
13461346
}
13471347

13481348
// --- Diagnose warnings and errors
@@ -1365,26 +1365,62 @@ void swift::tryDiagnoseExecutorConformance(ASTContext &C,
13651365
canRemoveOldDecls = declInfo.isContainedIn(requirementInfo);
13661366
}
13671367

1368+
auto concurrencyModule = C.getLoadedModule(C.Id_Concurrency);
1369+
auto isStdlibDefaultImplDecl = [executorDecl, concurrencyModule](ValueDecl *witness) -> bool {
1370+
if (auto declContext = witness->getDeclContext()) {
1371+
if (auto *extension = dyn_cast<ExtensionDecl>(declContext)) {
1372+
auto extensionModule = extension->getParentModule();
1373+
if (extensionModule != concurrencyModule) {
1374+
return false;
1375+
}
1376+
1377+
if (auto extendedNominal = extension->getExtendedNominal()) {
1378+
return extendedNominal->getDeclaredInterfaceType()->isEqual(
1379+
executorDecl->getDeclaredInterfaceType());
1380+
}
1381+
}
1382+
}
1383+
return false;
1384+
};
1385+
13681386
// If both old and new enqueue are implemented, but the old one cannot be removed,
13691387
// emit a warning that the new enqueue is unused.
1370-
if (!canRemoveOldDecls &&
1371-
unownedEnqueueWitnessDecl && unownedEnqueueWitnessDecl->getLoc().isValid() &&
1372-
moveOnlyEnqueueWitnessDecl && moveOnlyEnqueueWitnessDecl->getLoc().isValid()) {
1373-
diags.diagnose(moveOnlyEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_unused_implementation);
1388+
if (!canRemoveOldDecls && unownedEnqueueWitnessDecl && moveOnlyEnqueueWitnessDecl) {
1389+
if (!isStdlibDefaultImplDecl(moveOnlyEnqueueWitnessDecl)) {
1390+
diags.diagnose(moveOnlyEnqueueWitnessDecl->getLoc(),
1391+
diag::executor_enqueue_unused_implementation);
1392+
}
13741393
}
13751394

13761395
// Old UnownedJob based impl is present, warn about it suggesting the new protocol requirement.
1377-
if (canRemoveOldDecls && unownedEnqueueWitnessDecl && unownedEnqueueWitnessDecl->getLoc().isValid()) {
1378-
diags.diagnose(unownedEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_unowned_implementation, nominalTy);
1396+
if (canRemoveOldDecls && unownedEnqueueWitnessDecl) {
1397+
if (!isStdlibDefaultImplDecl(unownedEnqueueWitnessDecl)) {
1398+
diags.diagnose(unownedEnqueueWitnessDecl->getLoc(),
1399+
diag::executor_enqueue_deprecated_unowned_implementation,
1400+
nominalTy);
1401+
}
13791402
}
13801403
// Old Job based impl is present, warn about it suggesting the new protocol requirement.
1381-
if (legacyMoveOnlyEnqueueWitnessDecl && legacyMoveOnlyEnqueueWitnessDecl->getLoc().isValid()) {
1382-
diags.diagnose(legacyMoveOnlyEnqueueWitnessDecl->getLoc(), diag::executor_enqueue_deprecated_owned_job_implementation, nominalTy);
1404+
if (legacyMoveOnlyEnqueueWitnessDecl) {
1405+
if (!isStdlibDefaultImplDecl(legacyMoveOnlyEnqueueWitnessDecl)) {
1406+
diags.diagnose(legacyMoveOnlyEnqueueWitnessDecl->getLoc(),
1407+
diag::executor_enqueue_deprecated_owned_job_implementation,
1408+
nominalTy);
1409+
}
13831410
}
13841411

1385-
if ((!unownedEnqueueWitnessDecl || unownedEnqueueWitnessDecl->getLoc().isInvalid()) &&
1386-
(!moveOnlyEnqueueWitnessDecl || moveOnlyEnqueueWitnessDecl->getLoc().isInvalid()) &&
1387-
(!legacyMoveOnlyEnqueueWitnessDecl || legacyMoveOnlyEnqueueWitnessDecl->getLoc().isInvalid())) {
1412+
bool unownedEnqueueWitnessIsDefaultImpl = isStdlibDefaultImplDecl(unownedEnqueueWitnessDecl);
1413+
bool moveOnlyEnqueueWitnessIsDefaultImpl = isStdlibDefaultImplDecl(moveOnlyEnqueueWitnessDecl);
1414+
bool legacyMoveOnlyEnqueueWitnessDeclIsDefaultImpl = isStdlibDefaultImplDecl(legacyMoveOnlyEnqueueWitnessDecl);
1415+
1416+
auto missingWitness = !unownedEnqueueWitnessDecl &&
1417+
!moveOnlyEnqueueWitnessDecl &&
1418+
!legacyMoveOnlyEnqueueWitnessDecl;
1419+
auto allWitnessesAreDefaultImpls = unownedEnqueueWitnessIsDefaultImpl &&
1420+
moveOnlyEnqueueWitnessIsDefaultImpl &&
1421+
legacyMoveOnlyEnqueueWitnessDeclIsDefaultImpl;
1422+
if ((missingWitness) ||
1423+
(!missingWitness && allWitnessesAreDefaultImpls)) {
13881424
// Neither old nor new implementation have been found, but we provide default impls for them
13891425
// that are mutually recursive, so we must error and suggest implementing the right requirement.
13901426
//
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 -enable-experimental-move-only -disable-availability-checking
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)