Skip to content

Commit 658cf60

Browse files
committed
[SILGen][Concurrency] Improve DefaultExecutorFactory lookup.
Rather than just looking at top level in the module, start by searching the type marked as `@main`. This means that a library that provides a protocol or superclass that the `@main` type can conform to can specify an executor in a reasonable manner.
1 parent 006efce commit 658cf60

File tree

2 files changed

+69
-2
lines changed

2 files changed

+69
-2
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,36 @@ FuncDecl *SILGenModule::getExit() {
527527
Type SILGenModule::getConfiguredExecutorFactory() {
528528
auto &ctx = getASTContext();
529529

530-
// Look in the main module for a typealias
530+
// First look in the @main struct, if any
531+
NominalTypeDecl *mainType = ctx.MainModule->getMainTypeDecl();
532+
if (mainType) {
533+
SmallVector<ValueDecl *, 1> decls;
534+
auto identifier = ctx.getIdentifier("DefaultExecutorFactory");
535+
mainType->lookupQualified(mainType,
536+
DeclNameRef(identifier),
537+
SourceLoc(),
538+
NL_RemoveNonVisible | NL_RemoveOverridden
539+
| NL_OnlyTypes | NL_ProtocolMembers,
540+
decls);
541+
for (auto decl : decls) {
542+
TypeDecl *typeDecl = dyn_cast<TypeDecl>(decl);
543+
if (typeDecl) {
544+
if (auto *nominalDecl = dyn_cast<NominalTypeDecl>(typeDecl)) {
545+
return nominalDecl->getDeclaredType();
546+
}
547+
548+
if (isa<AssociatedTypeDecl>(typeDecl)) {
549+
// We ignore associatedtype declarations; those with a default will
550+
// turn into a `typealias` instead.
551+
continue;
552+
}
553+
554+
return typeDecl->getDeclaredInterfaceType();
555+
}
556+
}
557+
}
558+
559+
// Failing that, look at the top level
531560
Type factory = ctx.getNamedSwiftType(ctx.MainModule, "DefaultExecutorFactory");
532561

533562
// If we don't find it, fall back to _Concurrency.PlatformExecutorFactory

test/Concurrency/Runtime/custom_main_executor.swift

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// RUN: %target-run-simple-swift(-DTOPLEVEL_FACTORY -Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library) | %FileCheck %s
2+
// RUN: %target-run-simple-swift(-DPROTOCOL_FACTORY -Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library) | %FileCheck %s
3+
// RUN: %target-run-simple-swift(-DPROTOCOL_FACTORY_OVERRIDDEN -Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library) | %FileCheck %s
4+
// RUN: %target-run-simple-swift(-DPROTOCOL_FACTORY_DEFAULT -Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library) | %FileCheck %s
5+
// RUN: %target-run-simple-swift(-DPROTOCOL_FACTORY_DEFAULT2 -Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library) | %FileCheck %s
16
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library) | %FileCheck %s
27

38
// REQUIRES: concurrency
@@ -13,7 +18,9 @@
1318
import StdlibUnittest
1419
import Synchronization
1520

21+
#if TOPLEVEL_FACTORY
1622
typealias DefaultExecutorFactory = SimpleExecutorFactory
23+
#endif
1724

1825
struct SimpleExecutorFactory: ExecutorFactory {
1926
public static var mainExecutor: any MainExecutor {
@@ -26,6 +33,15 @@ struct SimpleExecutorFactory: ExecutorFactory {
2633
}
2734
}
2835

36+
struct FatalExecutorFactory: ExecutorFactory {
37+
public static var mainExecutor: any MainExecutor {
38+
fatalError("mainExecutor called on FatalExecutorFactory")
39+
}
40+
public static var defaultExecutor: any TaskExecutor {
41+
fatalError("taskExecutor called on FatalExecutorFactory")
42+
}
43+
}
44+
2945
@available(SwiftStdlib 6.2, *)
3046
final class SimpleMainExecutor: MainExecutor, @unchecked Sendable {
3147
public var isRunning: Bool = false
@@ -74,8 +90,30 @@ func myAsyncFunction() async {
7490
print("Hello World")
7591
}
7692

93+
protocol AppProtocol {
94+
#if PROTOCOL_FACTORY
95+
associatedtype DefaultExecutorFactory
96+
#endif
97+
#if PROTOCOL_FACTORY_DEFAULT
98+
associatedtype DefaultExecutorFactory = SimpleExecutorFactory
99+
#endif
100+
#if PROTOCOL_FACTORY_DEFAULT2 || PROTOCOL_FACTORY_OVERRIDDEN
101+
associatedtype DefaultExecutorFactory = FatalExecutorFactory
102+
#endif
103+
}
104+
105+
#if PROTOCOL_FACTORY || PROTOCOL_FACTORY_DEFAULT2
106+
extension AppProtocol {
107+
typealias DefaultExecutorFactory = SimpleExecutorFactory
108+
}
109+
#endif
110+
77111
@available(SwiftStdlib 6.2, *)
78-
@main struct Main {
112+
@main struct Main: AppProtocol {
113+
#if !TOPLEVEL_FACTORY && !PROTOCOL_FACTORY && !PROTOCOL_FACTORY_DEFAULT && !PROTOCOL_FACTORY_DEFAULT2
114+
typealias DefaultExecutorFactory = SimpleExecutorFactory
115+
#endif
116+
79117
static func main() async {
80118
print("Hello")
81119
await myAsyncFunction()

0 commit comments

Comments
 (0)