Skip to content

Commit 1eb810f

Browse files
authored
Merge pull request swiftlang#35048 from kavon/typechecking-unspecified-isolation-contexts-71548470
2 parents 04ec96e + e8dcc97 commit 1eb810f

12 files changed

+238
-47
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: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,15 @@ 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 also emit a fix-it that inserts 'async' in the right place for func.
122+
// It's possibly a bit tricky to get the right source location from
123+
// just the AbstractFunctionDecl, but it's important to circle-back
124+
// to this.
118125

119126
if (func->canBeAsyncHandler()) {
120127
func->diagnose(
@@ -942,8 +949,9 @@ namespace {
942949
return false;
943950
};
944951

952+
auto declContext = getDeclContext();
945953
switch (auto contextIsolation =
946-
getInnermostIsolatedContext(getDeclContext())) {
954+
getInnermostIsolatedContext(declContext)) {
947955
case ActorIsolation::ActorInstance:
948956
if (inspectForImplicitlyAsync())
949957
return false;
@@ -980,16 +988,66 @@ namespace {
980988
return false;
981989

982990
ctx.Diags.diagnose(
983-
loc, diag::global_actor_from_independent_context,
984-
value->getDescriptiveKind(), value->getName(), globalActor);
991+
loc, diag::global_actor_from_nonactor_context,
992+
value->getDescriptiveKind(), value->getName(), globalActor,
993+
/*actorIndependent=*/true);
985994
noteIsolatedActorMember(value);
986995
return true;
987996

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

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) {

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ func anotherAsyncFunc() async {
125125

126126
}
127127

128-
// expected-note@+2 {{add 'async' to function 'regularFunc()' to make it asynchronous}}
129-
// expected-note@+1 {{add '@asyncHandler' to function 'regularFunc()' to create an implicit asynchronous context}}
128+
// expected-note@+2 {{add 'async' to function 'regularFunc()' to make it asynchronous}} {{none}}
129+
// expected-note@+1 {{add '@asyncHandler' to function 'regularFunc()' to create an implicit asynchronous context}} {{1-1=@asyncHandler }}
130130
func regularFunc() {
131131
let a = BankAccount(initialDeposit: 34)
132132

test/Concurrency/actor_isolation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extension MyActor {
4444
set { }
4545
}
4646

47-
// expected-note@+1 {{add 'async' to function 'actorIndependentFunc(otherActor:)' to make it asynchronous}}
47+
// expected-note@+1 {{add 'async' to function 'actorIndependentFunc(otherActor:)' to make it asynchronous}} {{none}}
4848
@actorIndependent func actorIndependentFunc(otherActor: MyActor) -> Int {
4949
_ = immutable
5050
_ = text[0] // expected-error{{actor-isolated property 'text' can not be referenced from an '@actorIndependent' context}}
@@ -261,8 +261,8 @@ struct GenericStruct<T> {
261261
f() // okay
262262
}
263263

264-
// expected-note@+2 {{add '@asyncHandler' to function 'h()' to create an implicit asynchronous context}}
265-
// expected-note@+1 {{add 'async' to function 'h()' to make it asynchronous}}
264+
// expected-note@+2 {{add '@asyncHandler' to function 'h()' to create an implicit asynchronous context}} {{3-3=@asyncHandler }}
265+
// expected-note@+1 {{add 'async' to function 'h()' to make it asynchronous}} {{none}}
266266
@GenericGlobalActor<String> func h() {
267267
f() // expected-error{{'async' in a function that does not support concurrency}}
268268
_ = f // expected-error{{instance method 'f()' isolated to global actor 'GenericGlobalActor<T>' can not be referenced from different global actor 'GenericGlobalActor<String>'}}

test/Concurrency/async_throwing.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func throwingTask() async throws -> String {
4444
return "ok!"
4545
}
4646

47-
// expected-note@+2 7 {{add '@asyncHandler' to function 'syncTest()' to create an implicit asynchronous context}}
48-
// expected-note@+1 7 {{add 'async' to function 'syncTest()' to make it asynchronous}}
47+
// expected-note@+2 7 {{add '@asyncHandler' to function 'syncTest()' to create an implicit asynchronous context}} {{1-1=@asyncHandler }}
48+
// expected-note@+1 7 {{add 'async' to function 'syncTest()' to make it asynchronous}} {{none}}
4949
func syncTest() {
5050
let _ = invoke(fn: normalTask) // expected-error{{'async' in a function that does not support concurrency}}
5151
let _ = invokeAuto(42) // expected-error{{'async' in a function that does not support concurrency}}
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'}} {{1-1=@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'}} {{1-1=@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}} {{1-1=@asyncHandler }}
52+
// expected-note@+1 {{add 'async' to function 'referenceAsyncGlobalActor()' to make it asynchronous}} {{none}}
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}} {{1-1=@asyncHandler }}
60+
// expected-note@+2 {{add 'async' to function 'callGlobalActor()' to make it asynchronous}} {{none}}
61+
// expected-note@+1 {{add '@SomeGlobalActor' to make global function 'callGlobalActor()' part of global actor 'SomeGlobalActor'}} {{1-1=@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}} {{none}}
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'}} {{3-3=@SomeGlobalActor }}
94+
// expected-note@+2 {{add '@asyncHandler' to function 'method1()' to create an implicit asynchronous context}} {{3-3=@asyncHandler }}
95+
// expected-note@+1 {{add 'async' to function 'method1()' to make it asynchronous}} {{none}}
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'}} {{3-3=@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+
}

0 commit comments

Comments
 (0)