Skip to content

Commit 696bd30

Browse files
committed
[Concurrency] Warn about "old" Executor.enqueue impl and prefer new
move new enqueue method to be named enqueue and not enqueueOwned
1 parent eb9d699 commit 696bd30

20 files changed

+327
-54
lines changed

include/swift/AST/Decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,6 +3856,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
38563856
/// Find the 'RemoteCallArgument(label:name:value:)' initializer function.
38573857
ConstructorDecl *getDistributedRemoteCallArgumentInitFunction() const;
38583858

3859+
/// Get the move-only `enqueue(Job)` protocol requirement function on the `Executor` protocol.
3860+
AbstractFunctionDecl *getExecutorOwnedEnqueueFunction() const;
3861+
38593862
/// Collect the set of protocols to which this type should implicitly
38603863
/// conform, such as AnyObject (for classes).
38613864
void getImplicitProtocols(SmallVectorImpl<ProtocolDecl *> &protocols);

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6408,6 +6408,11 @@ WARNING(hashvalue_implementation,Deprecation,
64086408
"conform type %0 to 'Hashable' by implementing 'hash(into:)' instead",
64096409
(Type))
64106410

6411+
WARNING(executor_enqueue_unowned_implementation,Deprecation,
6412+
"'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; "
6413+
"conform type %0 to 'Executor' by implementing 'func enqueue(Job)' instead",
6414+
(Type))
6415+
64116416
//------------------------------------------------------------------------------
64126417
// MARK: property wrapper diagnostics
64136418
//------------------------------------------------------------------------------

include/swift/AST/KnownProtocols.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ PROTOCOL(SIMDScalar)
7676
PROTOCOL(BinaryInteger)
7777
PROTOCOL(FixedWidthInteger)
7878
PROTOCOL(RangeReplaceableCollection)
79+
PROTOCOL(Executor)
7980
PROTOCOL(SerialExecutor)
8081
PROTOCOL(GlobalActor)
8182

include/swift/AST/KnownSDKTypes.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
3939
// standardized
4040
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
4141
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
42+
KNOWN_SDK_TYPE_DECL(Concurrency, Job, StructDecl, 0)
43+
KNOWN_SDK_TYPE_DECL(Concurrency, UnownedJob, StructDecl, 0)
44+
KNOWN_SDK_TYPE_DECL(Concurrency, Executor, NominalTypeDecl, 0)
45+
KNOWN_SDK_TYPE_DECL(Concurrency, SerialExecutor, NominalTypeDecl, 0)
4246
KNOWN_SDK_TYPE_DECL(Concurrency, UnownedSerialExecutor, NominalTypeDecl, 0)
4347

4448
KNOWN_SDK_TYPE_DECL(Concurrency, TaskLocal, ClassDecl, 1)

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
11201120
case KnownProtocolKind::GlobalActor:
11211121
case KnownProtocolKind::AsyncSequence:
11221122
case KnownProtocolKind::AsyncIteratorProtocol:
1123+
case KnownProtocolKind::Executor:
11231124
case KnownProtocolKind::SerialExecutor:
11241125
M = getLoadedModule(Id_Concurrency);
11251126
break;

lib/AST/Decl.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5242,6 +5242,42 @@ VarDecl *NominalTypeDecl::getGlobalActorInstance() const {
52425242
nullptr);
52435243
}
52445244

5245+
AbstractFunctionDecl *
5246+
NominalTypeDecl::getExecutorOwnedEnqueueFunction() const {
5247+
auto &C = getASTContext();
5248+
5249+
auto proto = dyn_cast<ProtocolDecl>(this);
5250+
if (!proto)
5251+
return nullptr;
5252+
5253+
llvm::SmallVector<ValueDecl *, 2> results;
5254+
lookupQualified(getSelfNominalTypeDecl(),
5255+
DeclNameRef(C.Id_enqueue),
5256+
NL_ProtocolMembers,
5257+
results);
5258+
5259+
for (auto candidate: results) {
5260+
// we're specifically looking for the Executor protocol requirement
5261+
if (!isa<ProtocolDecl>(candidate->getDeclContext()))
5262+
continue;
5263+
5264+
fprintf(stderr, "[%s:%d](%s) candidate\n", __FILE_NAME__, __LINE__, __FUNCTION__);
5265+
candidate->dump();
5266+
5267+
if (auto *funcDecl = dyn_cast<AbstractFunctionDecl>(candidate)) {
5268+
if (funcDecl->getParameters()->size() != 1)
5269+
continue;
5270+
5271+
auto params = funcDecl->getParameters();
5272+
if (params->get(0)->getSpecifier() == ParamSpecifier::Owned) {
5273+
return funcDecl;
5274+
}
5275+
}
5276+
}
5277+
5278+
return nullptr;
5279+
}
5280+
52455281
ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
52465282
ArrayRef<InheritedEntry> Inherited,
52475283
GenericParamList *GenericParams, DeclContext *Parent,

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6178,6 +6178,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
61786178
case KnownProtocolKind::CxxSequence:
61796179
case KnownProtocolKind::UnsafeCxxInputIterator:
61806180
case KnownProtocolKind::UnsafeCxxRandomAccessIterator:
6181+
case KnownProtocolKind::Executor:
61816182
case KnownProtocolKind::SerialExecutor:
61826183
case KnownProtocolKind::Sendable:
61836184
case KnownProtocolKind::UnsafeSendable:

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,79 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
12251225
}
12261226
}
12271227

1228+
void swift::tryDiagnoseExecutorConformance(ASTContext &C,
1229+
const NominalTypeDecl *nominal,
1230+
ProtocolDecl *proto) {
1231+
assert(proto->isSpecificProtocol(KnownProtocolKind::Executor) ||
1232+
proto->isSpecificProtocol(KnownProtocolKind::SerialExecutor));
1233+
1234+
auto &diags = C.Diags;
1235+
auto module = nominal->getParentModule();
1236+
Type nominalTy = nominal->getDeclaredInterfaceType();
1237+
1238+
// enqueue(_: UnownedJob)
1239+
auto enqueueDeclName = DeclName(C, DeclBaseName(C.Id_enqueue), { Identifier() });
1240+
1241+
FuncDecl *unownedEnqueueRequirement = nullptr;
1242+
FuncDecl *moveOnlyEnqueueRequirement = nullptr;
1243+
for (auto req: proto->getProtocolRequirements()) {
1244+
auto *funcDecl = dyn_cast<FuncDecl>(req);
1245+
if (!funcDecl)
1246+
continue;
1247+
1248+
if (funcDecl->getName() != enqueueDeclName)
1249+
continue;
1250+
1251+
1252+
// look for the first parameter being a Job or UnownedJob
1253+
if (funcDecl->getParameters()->size() != 1)
1254+
continue;
1255+
if (auto param = funcDecl->getParameters()->front()) {
1256+
if (param->getType()->isEqual(C.getJobDecl()->getDeclaredInterfaceType())) {
1257+
assert(moveOnlyEnqueueRequirement == nullptr);
1258+
moveOnlyEnqueueRequirement = funcDecl;
1259+
} else if (param->getType()->isEqual(C.getUnownedJobDecl()->getDeclaredInterfaceType())) {
1260+
assert(unownedEnqueueRequirement == nullptr);
1261+
unownedEnqueueRequirement = funcDecl;
1262+
}
1263+
}
1264+
1265+
// if we found both, we're done here and break out of the loop
1266+
if (unownedEnqueueRequirement && moveOnlyEnqueueRequirement)
1267+
break; // we're done looking for the requirements
1268+
}
1269+
1270+
1271+
auto conformance = module->lookupConformance(nominalTy, proto);
1272+
auto concreteConformance = conformance.getConcrete();
1273+
auto unownedEnqueueWitness = concreteConformance->getWitnessDeclRef(unownedEnqueueRequirement);
1274+
auto moveOnlyEnqueueWitness = concreteConformance->getWitnessDeclRef(moveOnlyEnqueueRequirement);
1275+
1276+
if (auto enqueueUnownedDecl = unownedEnqueueWitness.getDecl()) {
1277+
// Old UnownedJob based impl is present, warn about it suggesting the new protocol requirement.
1278+
if (enqueueUnownedDecl->getLoc().isValid()) {
1279+
diags.diagnose(enqueueUnownedDecl->getLoc(), diag::executor_enqueue_unowned_implementation, nominalTy);
1280+
}
1281+
}
1282+
1283+
if (auto unownedEnqueueDecl = unownedEnqueueWitness.getDecl()) {
1284+
if (auto moveOnlyEnqueueDecl = moveOnlyEnqueueWitness.getDecl()) {
1285+
if (unownedEnqueueDecl && unownedEnqueueDecl->getLoc().isInvalid() &&
1286+
moveOnlyEnqueueDecl && moveOnlyEnqueueDecl->getLoc().isInvalid()) {
1287+
// Neither old nor new implementation have been found, but we provide default impls for them
1288+
// that are mutually recursive, so we must error and suggest implementing the right requirement.
1289+
auto ownedRequirement = C.getExecutorDecl()->getExecutorOwnedEnqueueFunction();
1290+
nominal->diagnose(diag::type_does_not_conform, nominalTy, proto->getDeclaredInterfaceType());
1291+
ownedRequirement->diagnose(diag::no_witnesses,
1292+
getProtocolRequirementKind(ownedRequirement),
1293+
ownedRequirement->getName(),
1294+
proto->getDeclaredInterfaceType(),
1295+
/*AddFixIt=*/true);
1296+
}
1297+
}
1298+
}
1299+
}
1300+
12281301
/// Determine whether this is the main actor type.
12291302
/// FIXME: the diagnostics engine has a copy of this.
12301303
static bool isMainActor(Type type) {

lib/Sema/TypeCheckConcurrency.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@ void diagnoseMissingSendableConformance(
280280
/// state whether it conforms to Sendable, provide a diagnostic.
281281
void diagnoseMissingExplicitSendable(NominalTypeDecl *nominal);
282282

283+
/// Warn about deprecated `Executor.enqueue` implementations.
284+
void tryDiagnoseExecutorConformance(ASTContext &C, const NominalTypeDecl *nominal, ProtocolDecl *proto);
285+
283286
/// How the Sendable check should be performed.
284287
enum class SendableCheck {
285288
/// Sendable conformance was explicitly stated and should be

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6496,6 +6496,8 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
64966496
} else if (proto->isSpecificProtocol(
64976497
KnownProtocolKind::UnsafeSendable)) {
64986498
sendableConformanceIsUnchecked = true;
6499+
} else if (proto->isSpecificProtocol(KnownProtocolKind::Executor)) {
6500+
tryDiagnoseExecutorConformance(Context, nominal, proto);
64996501
}
65006502
}
65016503

0 commit comments

Comments
 (0)