Skip to content

Commit e23f0ab

Browse files
committed
[concurrency] patch hole in typechecking ordinary functions for global actor references
Non-actor isolated synchronous functions were previously allowed to call & reference global-actor isolated declarations. This patch puts a stop to that. Resolves rdar://71548470
1 parent c65a206 commit e23f0ab

File tree

6 files changed

+210
-21
lines changed

6 files changed

+210
-21
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4132,6 +4132,10 @@ NOTE(note_add_async_to_function,none,
41324132
"add 'async' to function %0 to make it asynchronous", (DeclName))
41334133
NOTE(note_add_asynchandler_to_function,none,
41344134
"add '@asyncHandler' to function %0 to create an implicit asynchronous context", (DeclName))
4135+
NOTE(note_add_globalactor_to_function,none,
4136+
"add '@%0' to make %1 %2 part of global actor %3",
4137+
(StringRef, DescriptiveDeclKind, DeclName, Type))
4138+
FIXIT(insert_globalactor_attr, "@%0 ", (Type))
41354139
ERROR(not_objc_function_async,none,
41364140
"'async' function cannot be represented in Objective-C", ())
41374141
NOTE(not_objc_function_type_async,none,
@@ -4217,10 +4221,10 @@ ERROR(global_actor_from_other_global_actor_context,none,
42174221
"%0 %1 isolated to global actor %2 can not be referenced from "
42184222
"different global actor %3",
42194223
(DescriptiveDeclKind, DeclName, Type, Type))
4220-
ERROR(global_actor_from_independent_context,none,
4221-
"%0 %1 isolated to global actor %2 can not be referenced from an "
4222-
"'@actorIndependent' context",
4223-
(DescriptiveDeclKind, DeclName, Type))
4224+
ERROR(global_actor_from_nonactor_context,none,
4225+
"%0 %1 isolated to global actor %2 can not be referenced from "
4226+
"%select{this|an '@actorIndependent'}3 context",
4227+
(DescriptiveDeclKind, DeclName, Type, bool))
42244228
ERROR(actor_isolated_partial_apply,none,
42254229
"actor-isolated %0 %1 can not be partially applied",
42264230
(DescriptiveDeclKind, DeclName))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,12 @@ static bool checkAsyncHandler(FuncDecl *func, bool diagnose) {
113113
return false;
114114
}
115115

116-
void swift::addAsyncNotes(FuncDecl *func) {
117-
func->diagnose(diag::note_add_async_to_function, func->getName());
116+
void swift::addAsyncNotes(AbstractFunctionDecl const* func) {
117+
assert(func);
118+
if (!isa<DestructorDecl>(func))
119+
func->diagnose(diag::note_add_async_to_function, func->getName());
120+
// TODO: we need a source location for effects attributes so that we
121+
// can emit a fix-it.
118122

119123
if (func->canBeAsyncHandler()) {
120124
func->diagnose(
@@ -942,8 +946,9 @@ namespace {
942946
return false;
943947
};
944948

949+
auto declContext = getDeclContext();
945950
switch (auto contextIsolation =
946-
getInnermostIsolatedContext(getDeclContext())) {
951+
getInnermostIsolatedContext(declContext)) {
947952
case ActorIsolation::ActorInstance:
948953
if (inspectForImplicitlyAsync())
949954
return false;
@@ -980,16 +985,66 @@ namespace {
980985
return false;
981986

982987
ctx.Diags.diagnose(
983-
loc, diag::global_actor_from_independent_context,
984-
value->getDescriptiveKind(), value->getName(), globalActor);
988+
loc, diag::global_actor_from_nonactor_context,
989+
value->getDescriptiveKind(), value->getName(), globalActor,
990+
/*actorIndependent=*/true);
985991
noteIsolatedActorMember(value);
986992
return true;
987993

988-
case ActorIsolation::Unspecified:
989-
// Okay no matter what, but still must inspect for implicitly async.
990-
inspectForImplicitlyAsync();
991-
return false;
992-
}
994+
case ActorIsolation::Unspecified: {
995+
// NOTE: we must always inspect for implicitlyAsync
996+
bool implicitlyAsyncCall = inspectForImplicitlyAsync();
997+
bool didEmitDiagnostic = false;
998+
999+
auto emitError = [&](bool justNote = false) {
1000+
didEmitDiagnostic = true;
1001+
if (!justNote) {
1002+
ctx.Diags.diagnose(
1003+
loc, diag::global_actor_from_nonactor_context,
1004+
value->getDescriptiveKind(), value->getName(), globalActor,
1005+
/*actorIndependent=*/false);
1006+
}
1007+
noteIsolatedActorMember(value);
1008+
};
1009+
1010+
if (AbstractFunctionDecl const* fn =
1011+
dyn_cast_or_null<AbstractFunctionDecl>(declContext->getAsDecl())) {
1012+
bool isAsyncContext = fn->isAsyncContext();
1013+
1014+
if (implicitlyAsyncCall && isAsyncContext)
1015+
return didEmitDiagnostic; // definitely an OK reference.
1016+
1017+
// otherwise, there's something wrong.
1018+
1019+
// if it's an implicitly-async call in a non-async context,
1020+
// then we know later type-checking will raise an error,
1021+
// so we just emit a note pointing out that callee of the call is
1022+
// implicitly async.
1023+
emitError(/*justNote=*/implicitlyAsyncCall);
1024+
1025+
// otherwise, if it's any kind of global-actor reference within
1026+
// this synchronous function, we'll additionally suggest becoming
1027+
// part of the global actor associated with the reference,
1028+
// since this function is not associated with an actor.
1029+
if (isa<FuncDecl>(fn) && !isAsyncContext) {
1030+
didEmitDiagnostic = true;
1031+
fn->diagnose(diag::note_add_globalactor_to_function,
1032+
globalActor->getWithoutParens().getString(),
1033+
fn->getDescriptiveKind(),
1034+
fn->getName(),
1035+
globalActor)
1036+
.fixItInsert(fn->getAttributeInsertionLoc(false),
1037+
diag::insert_globalactor_attr, globalActor);
1038+
}
1039+
1040+
} else {
1041+
// just the generic error with note.
1042+
emitError();
1043+
}
1044+
1045+
return didEmitDiagnostic;
1046+
} // end Unspecified case
1047+
} // end switch
9931048
llvm_unreachable("unhandled actor isolation kind!");
9941049
}
9951050

lib/Sema/TypeCheckConcurrency.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class ValueDecl;
4040

4141
/// Add notes suggesting the addition of 'async' or '@asyncHandler', as
4242
/// appropriate, to a diagnostic for a function that isn't an async context.
43-
void addAsyncNotes(FuncDecl *func);
43+
void addAsyncNotes(AbstractFunctionDecl const* func);
4444

4545
/// Check actor isolation rules.
4646
void checkTopLevelActorIsolation(TopLevelCodeDecl *decl);

lib/Sema/TypeCheckEffects.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,11 +1382,8 @@ class Context {
13821382
if (!Function)
13831383
return;
13841384

1385-
auto func = dyn_cast_or_null<FuncDecl>(Function->getAbstractFunctionDecl());
1386-
if (!func)
1387-
return;
1388-
1389-
addAsyncNotes(func);
1385+
if (auto func = Function->getAbstractFunctionDecl())
1386+
addAsyncNotes(func);
13901387
}
13911388

13921389
void diagnoseUnhandledAsyncSite(DiagnosticEngine &Diags, ASTNode node) {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
2+
// REQUIRES: concurrency
3+
4+
// provides coverage for rdar://71548470
5+
6+
actor class TestActor {}
7+
8+
@globalActor
9+
struct SomeGlobalActor {
10+
static var shared: TestActor { TestActor() }
11+
}
12+
13+
// expected-note@+1 13 {{calls to global function 'syncGlobActorFn()' from outside of its actor context are implicitly asynchronous}}
14+
@SomeGlobalActor func syncGlobActorFn() { }
15+
@SomeGlobalActor func asyncGlobalActFn() async { }
16+
17+
actor class Alex {
18+
@SomeGlobalActor let const_memb = 20
19+
@SomeGlobalActor var mut_memb = 30 // expected-note 2 {{mutable state is only available within the actor instance}}
20+
@SomeGlobalActor func method() {} // expected-note 2 {{calls to instance method 'method()' from outside of its actor context are implicitly asynchronous}}
21+
@SomeGlobalActor subscript(index : Int) -> Int { // expected-note 4 {{subscript declared here}}
22+
get {
23+
return index * 2
24+
}
25+
set {}
26+
}
27+
}
28+
29+
30+
// expected-note@+1 4 {{add '@SomeGlobalActor' to make global function 'referenceGlobalActor()' part of global actor 'SomeGlobalActor'}}
31+
func referenceGlobalActor() {
32+
let a = Alex()
33+
// expected-error@+1 {{instance method 'method()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
34+
_ = a.method
35+
_ = a.const_memb
36+
_ = a.mut_memb // expected-error{{property 'mut_memb' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
37+
38+
_ = a[1] // expected-error{{subscript 'subscript(_:)' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
39+
a[0] = 1 // expected-error{{subscript 'subscript(_:)' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
40+
}
41+
42+
43+
// expected-note@+1 {{add '@SomeGlobalActor' to make global function 'referenceGlobalActor2()' part of global actor 'SomeGlobalActor'}}
44+
func referenceGlobalActor2() {
45+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
46+
let x = syncGlobActorFn
47+
x()
48+
}
49+
50+
51+
// expected-note@+2 {{add '@asyncHandler' to function 'referenceAsyncGlobalActor()' to create an implicit asynchronous context}}
52+
// expected-note@+1 {{add 'async' to function 'referenceAsyncGlobalActor()' to make it asynchronous}}
53+
func referenceAsyncGlobalActor() {
54+
let y = asyncGlobalActFn
55+
y() // expected-error{{'async' in a function that does not support concurrency}}
56+
}
57+
58+
59+
// expected-note@+3 {{add '@asyncHandler' to function 'callGlobalActor()' to create an implicit asynchronous context}}
60+
// expected-note@+2 {{add 'async' to function 'callGlobalActor()' to make it asynchronous}}
61+
// expected-note@+1 {{add '@SomeGlobalActor' to make global function 'callGlobalActor()' part of global actor 'SomeGlobalActor'}}
62+
func callGlobalActor() {
63+
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
64+
}
65+
66+
func fromClosure() {
67+
{ () -> Void in
68+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
69+
let x = syncGlobActorFn
70+
x()
71+
}()
72+
73+
// expected-error@+2 {{'async' in a function that does not support concurrency}}
74+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
75+
let _ = { syncGlobActorFn() }()
76+
}
77+
78+
class Taylor {
79+
init() { // expected-note {{add 'async' to function 'init()' to make it asynchronous}}
80+
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
81+
82+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
83+
_ = syncGlobActorFn
84+
}
85+
86+
deinit {
87+
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
88+
89+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
90+
_ = syncGlobActorFn
91+
}
92+
93+
// expected-note@+3 2 {{add '@SomeGlobalActor' to make instance method 'method1()' part of global actor 'SomeGlobalActor'}}
94+
// expected-note@+2 {{add '@asyncHandler' to function 'method1()' to create an implicit asynchronous context}}
95+
// expected-note@+1 {{add 'async' to function 'method1()' to make it asynchronous}}
96+
func method1() {
97+
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
98+
99+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
100+
_ = syncGlobActorFn
101+
}
102+
103+
// expected-note@+2 2 {{add '@SomeGlobalActor' to make instance method 'cannotBeHandler()' part of global actor 'SomeGlobalActor'}}
104+
// expected-note@+1 {{add 'async' to function 'cannotBeHandler()' to make it asynchronous}}
105+
func cannotBeHandler() -> Int {
106+
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
107+
108+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
109+
_ = syncGlobActorFn
110+
return 0
111+
}
112+
}
113+
114+
115+
func fromAsync() async {
116+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
117+
let x = syncGlobActorFn
118+
x()
119+
120+
let y = asyncGlobalActFn
121+
y() // expected-error{{call is 'async' but is not marked with 'await'}}
122+
123+
let a = Alex()
124+
// expected-error@+1 {{instance method 'method()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
125+
_ = a.method
126+
_ = a.const_memb
127+
_ = a.mut_memb // expected-error{{property 'mut_memb' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
128+
129+
_ = a[1] // expected-error{{subscript 'subscript(_:)' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
130+
a[0] = 1 // expected-error{{subscript 'subscript(_:)' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
131+
}

test/Concurrency/global_actor_inference.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ struct OtherContainer<U> {
181181
// Global actor inference for unspecified contexts
182182
// ----------------------------------------------------------------------
183183

184+
// expected-note@+1 {{calls to global function 'foo()' from outside of its actor context are implicitly asynchronous}}
184185
@SomeGlobalActor func foo() { sibling() }
185186

186187
@SomeGlobalActor func sibling() { foo() }
@@ -189,8 +190,9 @@ func bar() async {
189190
foo() // expected-error{{call is 'async' but is not marked with 'await'}}
190191
}
191192

193+
// expected-note@+3 {{add '@SomeGlobalActor' to make global function 'barSync()' part of global actor 'SomeGlobalActor'}}
192194
// expected-note@+2 {{add '@asyncHandler' to function 'barSync()' to create an implicit asynchronous context}}
193195
// expected-note@+1 {{add 'async' to function 'barSync()' to make it asynchronous}}
194196
func barSync() {
195-
foo() // expected-error{{'async' in a function that does not support concurrency}}
197+
foo() // expected-error {{'async' in a function that does not support concurrency}}
196198
}

0 commit comments

Comments
 (0)