Skip to content

Commit 6cc4a4d

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 250458f commit 6cc4a4d

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
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library -swift-version 5 -strict-concurrency=complete -enable-upcoming-feature NonisolatedNonsendingByDefault) | %FileCheck %s
38
// REQUIRES: swift_feature_NonisolatedNonsendingByDefault
@@ -15,7 +20,9 @@
1520
import StdlibUnittest
1621
import Synchronization
1722

23+
#if TOPLEVEL_FACTORY
1824
typealias DefaultExecutorFactory = SimpleExecutorFactory
25+
#endif
1926

2027
struct SimpleExecutorFactory: ExecutorFactory {
2128
public static var mainExecutor: any MainExecutor {
@@ -28,6 +35,15 @@ struct SimpleExecutorFactory: ExecutorFactory {
2835
}
2936
}
3037

38+
struct FatalExecutorFactory: ExecutorFactory {
39+
public static var mainExecutor: any MainExecutor {
40+
fatalError("mainExecutor called on FatalExecutorFactory")
41+
}
42+
public static var defaultExecutor: any TaskExecutor {
43+
fatalError("taskExecutor called on FatalExecutorFactory")
44+
}
45+
}
46+
3147
@available(SwiftStdlib 6.2, *)
3248
final class SimpleMainExecutor: MainExecutor, @unchecked Sendable {
3349
public var isRunning: Bool = false
@@ -76,8 +92,30 @@ final class SimpleTaskExecutor: TaskExecutor, @unchecked Sendable {
7692
print("Hello World")
7793
}
7894

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

0 commit comments

Comments
 (0)