Skip to content

Commit e9f6226

Browse files
authored
Merge pull request #40662 from etcwilde/ewilde/concurrency/update-main-namelookup-resolution
Update main namelookup resolution
2 parents c9c0794 + 8730d01 commit e9f6226

15 files changed

+252
-44
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3180,6 +3180,7 @@ ERROR(nscopying_doesnt_conform,none,
31803180
#define SELECT_APPLICATION_DELEGATE "select{'UIApplicationDelegate'|'NSApplicationDelegate'}"
31813181
#define SELECT_APPLICATION_TYPE "select{class|class|type}"
31823182
#define SELECT_APPLICATION_TYPES "select{classes|classes|types}"
3183+
#define SELECT_APPLICATION_MAIN_TYPES "select{() -> Void or () throws -> Void|() -> Void, () throws -> Void, () async -> Void, or () async throws -> Void}"
31833184

31843185
ERROR(attr_ApplicationMain_not_ApplicationDelegate,none,
31853186
"%" SELECT_APPLICATION_MAIN "0 class must conform to the %" SELECT_APPLICATION_DELEGATE "0 protocol",
@@ -3199,9 +3200,11 @@ NOTE(attr_ApplicationMain_script_here,none,
31993200
())
32003201

32013202
ERROR(attr_MainType_without_main,none,
3202-
"%0 is annotated with @main and must provide a main static function of type () -> Void or () throws -> Void.",
3203-
(DeclName))
3203+
"%0 is annotated with @main and must provide a main static function of type %"
3204+
SELECT_APPLICATION_MAIN_TYPES "1",
3205+
(DeclName, bool))
32043206

3207+
#undef SELECT_APPLICATION_MAIN_TYPES
32053208
#undef SELECT_APPLICATION_MAIN
32063209
#undef SELECT_APPLICATION_DELEGATE
32073210

lib/Sema/TypeCheckAttr.cpp

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,70 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) {
19941994
return std::make_pair(body, /*typechecked=*/false);
19951995
}
19961996

1997+
static FuncDecl *resolveMainFunctionDecl(DeclContext *declContext,
1998+
ResolvedMemberResult &resolution,
1999+
ASTContext &ctx) {
2000+
// The normal resolution mechanism won't choose the asynchronous main function
2001+
// unless no other options are available because extensions and generic types
2002+
// (structs/classes) are not considered asynchronous contexts.
2003+
// We want them to be promoted to a viable entrypoint if the deployment target
2004+
// is high enough.
2005+
SmallVector<FuncDecl *, 4> viableCandidates;
2006+
for (ValueDecl *candidate : resolution.getMemberDecls(Viable)) {
2007+
if (FuncDecl *function = dyn_cast<FuncDecl>(candidate)) {
2008+
if (function->isMainTypeMainMethod())
2009+
viableCandidates.push_back(function);
2010+
}
2011+
}
2012+
if (viableCandidates.empty()) {
2013+
return nullptr;
2014+
}
2015+
2016+
AvailabilityContext contextAvailability =
2017+
AvailabilityContext::forDeploymentTarget(ctx);
2018+
const bool hasAsyncSupport = contextAvailability.isContainedIn(
2019+
ctx.getBackDeployedConcurrencyAvailability());
2020+
2021+
FuncDecl *best = nullptr;
2022+
for (FuncDecl *candidate : viableCandidates) {
2023+
// The candidate will work if it's synchronous, or if we support concurrency
2024+
// and it is async, or if we are in YOLO mode
2025+
const bool candidateWorks = !candidate->hasAsync() ||
2026+
(hasAsyncSupport && candidate->hasAsync()) ||
2027+
ctx.LangOpts.DisableAvailabilityChecking;
2028+
2029+
// Skip it if it won't work
2030+
if (!candidateWorks)
2031+
continue;
2032+
2033+
// If we don't have a best, the candidate is the best so far
2034+
if (!best) {
2035+
best = candidate;
2036+
continue;
2037+
}
2038+
2039+
// If the candidate is better and it's synchronous, just swap it right in.
2040+
// If the candidate is better and it's async, make sure we support async
2041+
// before selecting it.
2042+
//
2043+
// If it's unordered (equally bestest), use the async version if we support
2044+
// it or use the sync version if we don't.
2045+
const Comparison rank =
2046+
TypeChecker::compareDeclarations(declContext, candidate, best);
2047+
const bool isBetter = rank == Comparison::Better;
2048+
const bool isUnordered = rank == Comparison::Unordered;
2049+
const bool swapForAsync =
2050+
hasAsyncSupport && candidate->hasAsync() && !best->hasAsync();
2051+
const bool swapForSync =
2052+
!hasAsyncSupport && !candidate->hasAsync() && best->hasAsync();
2053+
const bool selectCandidate =
2054+
isBetter || (isUnordered && (swapForAsync || swapForSync));
2055+
if (selectCandidate)
2056+
best = candidate;
2057+
}
2058+
return best;
2059+
}
2060+
19972061
FuncDecl *
19982062
SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
19992063
Decl *D) const {
@@ -2049,37 +2113,17 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
20492113

20502114
auto resolution = resolveValueMember(
20512115
*declContext, nominal->getInterfaceType(), context.Id_main);
2052-
2053-
FuncDecl *mainFunction = nullptr;
2054-
2055-
if (resolution.hasBestOverload()) {
2056-
auto best = resolution.getBestOverload();
2057-
if (auto function = dyn_cast<FuncDecl>(best)) {
2058-
if (function->isMainTypeMainMethod()) {
2059-
mainFunction = function;
2060-
}
2061-
}
2062-
}
2063-
2064-
if (mainFunction == nullptr) {
2065-
SmallVector<FuncDecl *, 4> viableCandidates;
2066-
2067-
for (auto *candidate : resolution.getMemberDecls(Viable)) {
2068-
if (auto func = dyn_cast<FuncDecl>(candidate)) {
2069-
if (func->isMainTypeMainMethod()) {
2070-
viableCandidates.push_back(func);
2071-
}
2072-
}
2073-
}
2074-
2075-
if (viableCandidates.size() != 1) {
2076-
context.Diags.diagnose(attr->getLocation(),
2077-
diag::attr_MainType_without_main,
2078-
nominal->getBaseName());
2079-
attr->setInvalid();
2080-
return nullptr;
2081-
}
2082-
mainFunction = viableCandidates[0];
2116+
FuncDecl *mainFunction =
2117+
resolveMainFunctionDecl(declContext, resolution, context);
2118+
if (!mainFunction) {
2119+
const bool hasAsyncSupport =
2120+
AvailabilityContext::forDeploymentTarget(context).isContainedIn(
2121+
context.getBackDeployedConcurrencyAvailability());
2122+
context.Diags.diagnose(attr->getLocation(),
2123+
diag::attr_MainType_without_main,
2124+
nominal->getBaseName(), hasAsyncSupport);
2125+
attr->setInvalid();
2126+
return nullptr;
20832127
}
20842128

20852129
auto where = ExportContext::forDeclSignature(D);

test/Concurrency/Runtime/actor_init.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(%import-libdispatch -parse-as-library) | %FileCheck %s
1+
// RUN: %target-run-simple-swift(%import-libdispatch -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Concurrency/Runtime/effectful_properties.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(-parse-as-library %import-libdispatch) | %FileCheck %s
1+
// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking %import-libdispatch) | %FileCheck %s
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Concurrency/Runtime/exclusivity.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift( -parse-as-library)
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library)
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Concurrency/Runtime/exclusivity_custom_executors.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(-parse-as-library)
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library)
22

33
// REQUIRES: concurrency
44
// REQUIRES: executable_test
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Non-apple platforms don't need to worry about the version number as much
2+
// because they can pull in the concurrency libraries with the swift
3+
// installation.
4+
5+
// async main is nested deeper in protocols than sync, use sync
6+
// sync main is nested deeper in protocols than async, use async
7+
// async and sync are same level, use async
8+
9+
// REQUIRES: concurrency
10+
// UNSUPPORTED: VENDOR=apple
11+
12+
// Async is deeper in the protocol chain from `MyMain`, use sync
13+
// RUN: %target-swift-frontend -DASYNC_NESTED -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
14+
15+
// Sync is deeper in the protocol chain from `MyMain`, use async
16+
// RUN: %target-swift-frontend -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
17+
18+
// Async and sync are the same level, use async
19+
// RUN: %target-swift-frontend -DBOTH -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
20+
21+
#if ASYNC_NESTED
22+
protocol AsyncMainProtocol { }
23+
protocol MainProtocol : AsyncMainProtocol { }
24+
#else
25+
protocol MainProtocol { }
26+
protocol AsyncMainProtocol : MainProtocol { }
27+
#endif
28+
29+
#if NO_SYNC
30+
#else
31+
extension MainProtocol {
32+
static func main() { }
33+
}
34+
#endif
35+
36+
#if NO_ASYNC
37+
#else
38+
extension AsyncMainProtocol {
39+
static func main() async { }
40+
}
41+
#endif
42+
43+
#if BOTH
44+
extension MainProtocol {
45+
static func main() async { }
46+
}
47+
#endif
48+
49+
50+
#if INHERIT_SYNC
51+
@main struct MyMain : MainProtocol {}
52+
#else
53+
@main struct MyMain : AsyncMainProtocol {}
54+
#endif
55+
56+
57+
58+
// CHECK-IS-SYNC-LABEL: "MyMain" interface type='MyMain.Type'
59+
// CHECK-IS-SYNC: (func_decl implicit "$main()" interface type='(MyMain.Type) -> () -> ()'
60+
// CHECK-IS-SYNC: (declref_expr implicit type='(MyMain.Type) -> () -> ()'
61+
62+
// CHECK-IS-ASYNC-LABEL: "MyMain" interface type='MyMain.Type'
63+
// CHECK-IS-ASYNC: (func_decl implicit "$main()" interface type='(MyMain.Type) -> () async -> ()'
64+
// CHECK-IS-ASYNC: (declref_expr implicit type='(MyMain.Type) -> () async -> ()'
65+
66+
// CHECK-IS-ERROR: error: 'MyMain' is annotated with @main and must provide a main static function of type () -> Void or () throws -> Void
67+
68+
// CHECK-IS-ERROR-ASYNC: error: 'MyMain' is annotated with @main and must provide a main static function of type () -> Void, () throws -> Void, () async -> Void, or () async throws -> Void
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// async main is nested deeper in protocols than sync, should use sync (always)
2+
// sync main is nested deeper in protocols than async, use async if supported
3+
// async and sync are same level, use async if supported
4+
5+
// async main is nested in the protocol chain from `MyMain`
6+
// Always choose Sync overload
7+
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.9 -DASYNC_NESTED -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
8+
// RUN: %target-swift-frontend -target x86_64-apple-macosx11.0 -DASYNC_NESTED -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
9+
10+
// sync main is deeper in the protocol chain from `MyMain`
11+
// Choose async when available
12+
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.9 -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
13+
// RUN: %target-swift-frontend -target x86_64-apple-macosx11.0 -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
14+
15+
// sync and async main are at same level (In MainProtocol) to `MyMain`.
16+
// Choose async when available
17+
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.9 -DBOTH -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
18+
// RUN: %target-swift-frontend -target x86_64-apple-macosx11.9 -DBOTH -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
19+
20+
// async main is the only option on the protocol chain
21+
// Choose async if we support it, error otherwise
22+
// RUN: not %target-swift-frontend -target x86_64-apple-macosx10.9 -DASYNC_NESTED -typecheck -dump-ast -parse-as-library %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR
23+
// RUN: %target-swift-frontend -target x86_64-apple-macosx11.9 -DASYNC_NESTED -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
24+
25+
// sync main is the only option on the protocol chain
26+
// Always choose sync
27+
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.9 -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
28+
// RUN: %target-swift-frontend -target x86_64-apple-macosx11.9 -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
29+
30+
// No synchronous, choose async if we support it, error otherwise
31+
// RUN: not %target-swift-frontend -target x86_64-apple-macosx10.9 -DNO_SYNC -typecheck -dump-ast -parse-as-library %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR
32+
// RUN: %target-swift-frontend -target x86_64-apple-macosx11.9 -DNO_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
33+
34+
// No asynchronous, choose sync
35+
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.9 -DNO_ASYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
36+
// RUN: %target-swift-frontend -target x86_64-apple-macosx11.9 -DNO_ASYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
37+
38+
// No main functions
39+
// RUN: not %target-swift-frontend -target x86_64-apple-macosx10.9 -DNO_SYNC -DNO_ASYNC -typecheck -dump-ast -parse-as-library %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR
40+
// RUN: not %target-swift-frontend -target x86_64-apple-macosx11.9 -DNO_SYNC -DNO_ASYNC -typecheck -dump-ast -parse-as-library %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR-ASYNC
41+
42+
// REQUIRES: concurrency
43+
// REQUIRES: OS=macosx
44+
45+
#if ASYNC_NESTED
46+
protocol AsyncMainProtocol { }
47+
protocol MainProtocol : AsyncMainProtocol { }
48+
#else
49+
protocol MainProtocol { }
50+
protocol AsyncMainProtocol : MainProtocol { }
51+
#endif
52+
53+
#if NO_SYNC
54+
#else
55+
extension MainProtocol {
56+
static func main() { }
57+
}
58+
#endif
59+
60+
#if NO_ASYNC
61+
#else
62+
extension AsyncMainProtocol {
63+
@available(macOS 10.15, *)
64+
static func main() async { }
65+
}
66+
#endif
67+
68+
#if BOTH
69+
extension MainProtocol {
70+
@available(macOS 10.15, *)
71+
static func main() async { }
72+
}
73+
#endif
74+
75+
76+
#if INHERIT_SYNC
77+
@main struct MyMain : MainProtocol {}
78+
#else
79+
@main struct MyMain : AsyncMainProtocol {}
80+
#endif
81+
82+
83+
// CHECK-IS-SYNC-LABEL: "MyMain" interface type='MyMain.Type'
84+
// CHECK-IS-SYNC: (func_decl implicit "$main()" interface type='(MyMain.Type) -> () -> ()'
85+
// CHECK-IS-SYNC: (declref_expr implicit type='(MyMain.Type) -> () -> ()'
86+
87+
// CHECK-IS-ASYNC-LABEL: "MyMain" interface type='MyMain.Type'
88+
// CHECK-IS-ASYNC: (func_decl implicit "$main()" interface type='(MyMain.Type) -> () async -> ()'
89+
// CHECK-IS-ASYNC: (declref_expr implicit type='(MyMain.Type) -> () async -> ()'
90+
91+
// CHECK-IS-ERROR: error: 'MyMain' is annotated with @main and must provide a main static function of type () -> Void or () throws -> Void
92+
93+
// CHECK-IS-ERROR-ASYNC: error: 'MyMain' is annotated with @main and must provide a main static function of type () -> Void, () throws -> Void, () async -> Void, or () async throws -> Void

test/Distributed/Runtime/distributed_actor_deinit.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Distributed/Runtime/distributed_actor_isRemote.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(-Onone -Xfrontend -g -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -Onone -Xfrontend -g -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Distributed/Runtime/distributed_actor_local.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift(-Onone -Xfrontend -g -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -Onone -Xfrontend -g -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Distributed/Runtime/distributed_no_transport_boom.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-fail-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library %import-libdispatch) 2>&1 | %FileCheck %s
1+
// RUN: %target-fail-simple-swift(-Xfrontend -disable-availability-checking -Xfrontend -enable-experimental-distributed -parse-as-library %import-libdispatch) 2>&1 | %FileCheck %s
22
//
33
// // TODO: could not figure out how to use 'not --crash' it never is used with target-run-simple-swift
44
// This test is intended to *crash*, so we're using target-fail-simple-swift

test/Sanitizers/tsan/actor_counters.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift( %import-libdispatch -parse-as-library -sanitize=thread)
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library -sanitize=thread)
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

test/Sanitizers/tsan/basic_future.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift( %import-libdispatch -parse-as-library -sanitize=thread)
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library -sanitize=thread)
22

33
// REQUIRES: executable_test
44
// REQUIRES: concurrency

0 commit comments

Comments
 (0)