Skip to content

Commit 41499d0

Browse files
committed
Require MainActor isolation on main function
This patch forces the main function to be protected behind MainActor isolation. If no actor isolation is specified, the main function will implicitly have MainActor isolation.
1 parent b65b894 commit 41499d0

File tree

5 files changed

+104
-2
lines changed

5 files changed

+104
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4289,6 +4289,10 @@ NOTE(note_add_globalactor_to_function,none,
42894289
"add '@%0' to make %1 %2 part of global actor %3",
42904290
(StringRef, DescriptiveDeclKind, DeclName, Type))
42914291
FIXIT(insert_globalactor_attr, "@%0 ", (Type))
4292+
4293+
ERROR(main_function_must_be_mainActor,none,
4294+
"main() must be '@MainActor'", ())
4295+
42924296
ERROR(not_objc_function_async,none,
42934297
"'async' %0 cannot be represented in Objective-C", (DescriptiveDeclKind))
42944298
NOTE(not_objc_function_type_async,none,

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2875,6 +2875,41 @@ static ActorIsolation getActorIsolationFromWrappedProperty(VarDecl *var) {
28752875
return ActorIsolation::forUnspecified();
28762876
}
28772877

2878+
static Optional<ActorIsolation>
2879+
getActorIsolationForMainFuncDecl(FuncDecl *fnDecl) {
2880+
// Ensure that the base type that this function is declared in has @main
2881+
// attribute
2882+
NominalTypeDecl *declContext =
2883+
dyn_cast<NominalTypeDecl>(fnDecl->getDeclContext());
2884+
if (ExtensionDecl *exDecl =
2885+
dyn_cast<ExtensionDecl>(fnDecl->getDeclContext())) {
2886+
declContext = exDecl->getExtendedNominal();
2887+
}
2888+
2889+
// We're not even in a nominal decl type, this can't be the main function decl
2890+
if (!declContext)
2891+
return {};
2892+
const bool isMainDeclContext =
2893+
declContext->getAttrs().hasAttribute<MainTypeAttr>();
2894+
2895+
ASTContext &ctx = fnDecl->getASTContext();
2896+
2897+
const bool isMainMain = fnDecl->isMainTypeMainMethod();
2898+
const bool isMain$Main =
2899+
fnDecl->getBaseIdentifier() == ctx.getIdentifier("$main") &&
2900+
!fnDecl->isInstanceMember() &&
2901+
fnDecl->getResultInterfaceType()->isVoid() &&
2902+
fnDecl->getParameters()->size() == 0;
2903+
const bool isMainFunction = isMainDeclContext && (isMainMain || isMain$Main);
2904+
const bool hasMainActor = !ctx.getMainActorType().isNull();
2905+
2906+
return isMainFunction && hasMainActor
2907+
? ActorIsolation::forGlobalActor(
2908+
ctx.getMainActorType()->mapTypeOutOfContext(),
2909+
/*isUnsafe*/ false)
2910+
: Optional<ActorIsolation>();
2911+
}
2912+
28782913
/// Check rules related to global actor attributes on a class declaration.
28792914
///
28802915
/// \returns true if an error occurred.
@@ -2941,9 +2976,26 @@ ActorIsolation ActorIsolationRequest::evaluate(
29412976
return ActorIsolation::forActorInstance(actor);
29422977
}
29432978

2979+
auto isolationFromAttr = getIsolationFromAttributes(value);
2980+
if (FuncDecl *fd = dyn_cast<FuncDecl>(value)) {
2981+
// Main.main() and Main.$main are implicitly MainActor-protected.
2982+
// Any other isolation is an error.
2983+
Optional<ActorIsolation> mainIsolation =
2984+
getActorIsolationForMainFuncDecl(fd);
2985+
if (mainIsolation) {
2986+
if (isolationFromAttr && isolationFromAttr->isGlobalActor()) {
2987+
if (!areTypesEqual(isolationFromAttr->getGlobalActor(),
2988+
mainIsolation->getGlobalActor())) {
2989+
fd->getASTContext().Diags.diagnose(
2990+
fd->getLoc(), diag::main_function_must_be_mainActor);
2991+
}
2992+
}
2993+
return *mainIsolation;
2994+
}
2995+
}
29442996
// If this declaration has one of the actor isolation attributes, report
29452997
// that.
2946-
if (auto isolationFromAttr = getIsolationFromAttributes(value)) {
2998+
if (isolationFromAttr) {
29472999
// Nonisolated declarations must involve Sendable types.
29483000
if (*isolationFromAttr == ActorIsolation::Independent) {
29493001
SubstitutionMap subs;

test/Concurrency/async_main.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,25 @@
1010
// REQUIRES: concurrency_runtime
1111
// UNSUPPORTED: back_deployment_runtime
1212

13+
@MainActor
14+
var foo: Int = 42
15+
1316
func asyncFunc() async {
1417
print("Hello World!")
1518
}
1619

1720
@main struct MyProgram {
1821
static func main() async throws {
22+
print(foo)
23+
foo += 1
1924
await asyncFunc()
25+
print(foo)
2026
}
2127
}
2228

23-
// CHECK-EXEC: Hello World!
29+
// CHECK-EXEC: 42
30+
// CHECK-EXEC-NEXT: Hello World!
31+
// CHECK-EXEC-NEXT: 43
2432

2533
// static MyProgram.main()
2634
// CHECK-SIL-LABEL: sil hidden @$s10async_main9MyProgramV0B0yyYaKFZ : $@convention(method) @async (@thin MyProgram.Type) -> @error Error
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -parse-as-library
2+
3+
@globalActor
4+
actor Kat {
5+
static let shared = Kat()
6+
}
7+
8+
@Kat
9+
var poof: Int = 1337 // expected-note{{var declared here}}
10+
11+
@main struct Doggo {
12+
@Kat
13+
static func main() { // expected-error{{main() must be '@MainActor'}}
14+
// expected-error@+1{{var 'poof' isolated to global actor 'Kat' can not be referenced from different global actor 'MainActor' in a synchronous context}}
15+
print("Kat value: \(poof)")
16+
}
17+
}
18+
19+
struct Bunny {
20+
// Bunnies are not @main, so they can have a "main" function that is on
21+
// another actor. It's not actually the main function, so it's fine.
22+
@Kat
23+
static func main() {
24+
}
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -parse-as-library
2+
// This should pass without any warnings or errors
3+
4+
@MainActor
5+
var floofer: Int = 42
6+
7+
@main struct Doggo { }
8+
9+
extension Doggo {
10+
static func main() {
11+
print("Doggo value: \(floofer)")
12+
}
13+
}

0 commit comments

Comments
 (0)