Skip to content

Commit 6c4eaa1

Browse files
committed
[CodeCompletion] Handle "unsafe" global actor isolation
For 'unsafe' global actor isolation, implicit "async" happens only if * The context adopted concurrency feature * Not '-warn-concurrency' specified rdar://80907499
1 parent 7a3f7c6 commit 6c4eaa1

File tree

5 files changed

+225
-2
lines changed

5 files changed

+225
-2
lines changed

include/swift/Sema/IDETypeChecking.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ namespace swift {
305305
/// for a Fix-It that adds a new build* function to a result builder.
306306
std::tuple<SourceLoc, std::string, Type>
307307
determineResultBuilderBuildFixItInfo(NominalTypeDecl *builder);
308+
309+
/// Just a proxy to swift::contextUsesConcurrencyFeatures() from lib/IDE code.
310+
bool completionContextUsesConcurrencyFeatures(const DeclContext *dc);
308311
}
309312

310313
#endif

lib/IDE/CodeCompletion.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,6 +2598,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25982598
void analyzeActorIsolation(const ValueDecl *VD, Type T, bool &implicitlyAsync,
25992599
Optional<NotRecommendedReason> &NotRecommended) {
26002600
auto isolation = getActorIsolation(const_cast<ValueDecl *>(VD));
2601+
auto &ctx = VD->getASTContext();
26012602

26022603
switch (isolation.getKind()) {
26032604
case ActorIsolation::DistributedActorInstance: {
@@ -2611,8 +2612,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26112612
}
26122613
break;
26132614
}
2614-
case ActorIsolation::GlobalActor:
2615-
case ActorIsolation::GlobalActorUnsafe: {
2615+
case ActorIsolation::GlobalActorUnsafe:
2616+
// For "unsafe" global actor isolation, automatic 'async' only happens
2617+
// if the context has adopted concurrency.
2618+
if (!CanCurrDeclContextHandleAsync &&
2619+
!completionContextUsesConcurrencyFeatures(CurrDeclContext) &&
2620+
!ctx.LangOpts.WarnConcurrency) {
2621+
return;
2622+
}
2623+
LLVM_FALLTHROUGH;
2624+
case ActorIsolation::GlobalActor: {
26162625
auto contextIsolation = getActorIsolationOfContext(
26172626
const_cast<DeclContext *>(CurrDeclContext));
26182627
if (contextIsolation != isolation) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "swift/AST/TypeCheckRequests.h"
2828
#include "swift/AST/TypeVisitor.h"
2929
#include "swift/AST/ExistentialLayout.h"
30+
#include "swift/Sema/IDETypeChecking.h"
3031

3132
using namespace swift;
3233

@@ -3931,3 +3932,7 @@ AnyFunctionType *swift::applyGlobalActorType(
39313932
return FunctionType::get(
39323933
fnType->getParams(), Type(innerFnType), fnType->getExtInfo());
39333934
}
3935+
3936+
bool swift::completionContextUsesConcurrencyFeatures(const DeclContext *dc) {
3937+
return contextUsesConcurrencyFeatures(dc);
3938+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency
2+
3+
// SAFE_NOTREC: Begin completions, 2 items
4+
// SAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#];
5+
// SAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#];
6+
// SAFE_NOTREC: End completions
7+
8+
// UNSAFE_NOTREC: Begin completions, 2 items
9+
// UNSAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#];
10+
// UNSAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#];
11+
// UNSAFE_NOTREC: End completions
12+
13+
// SAFE_OK: Begin completions, 2 items
14+
// SAFE_OK-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#];
15+
// SAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#];
16+
// SAFE_OK: End completions
17+
18+
// UNSAFE_OK: Begin completions, 2 items
19+
// UNSAFE_OK-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#];
20+
// UNSAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#];
21+
// UNSAFE_OK: End completions
22+
23+
// UNSAFE_OK_SYNC: Begin completions, 2 items
24+
// UNSAFE_OK_SYNC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#];
25+
// UNSAFE_OK_SYNC-DAG: Decl[InstanceMethod]/CurrNominal: method()[#Void#];
26+
// UNSAFE_OK_SYNC: End completions
27+
28+
@globalActor
29+
actor MyGlobalActor {
30+
static let shared = MyGlobalActor()
31+
}
32+
33+
@MyGlobalActor(unsafe)
34+
class UnsafelyIsolatedCls {
35+
func method()
36+
}
37+
38+
@MyGlobalActor
39+
class SafelyIsolatedCls {
40+
func method()
41+
}
42+
43+
class TestNormal {
44+
func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
45+
s.#^IN_METHOD_SYNC_SAFE?check=SAFE_NOTREC^#
46+
u.#^IN_METHOD_SYNC_UNSAFE?check=UNSAFE_OK_SYNC^#
47+
}
48+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
49+
s.#^IN_METHOD_ASYNC_SAFE?check=SAFE_OK^#
50+
u.#^IN_METHOD_ASYNC_UNSAFE?check=UNSAFE_OK^#
51+
}
52+
}
53+
54+
func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
55+
s.#^IN_FUNC_SYNC_SAFE?check=SAFE_NOTREC^#
56+
u.#^IN_FUNC_SYNC_UNSAFE?check=UNSAFE_OK_SYNC^#
57+
}
58+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
59+
s.#^IN_FUNC_ASYNC_SAFE?check=SAFE_OK^#
60+
u.#^IN_FUNC_ASYNC_UNSAFE?check=UNSAFE_OK^#
61+
}
62+
63+
func testClosure(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
64+
func receiverSync(fn: () -> Void) {}
65+
func receiverAsync(fn: () async -> Void) {}
66+
67+
receiverSync {
68+
dummy;
69+
s.#^IN_CLOSURE_SYNC_SAFE?check=SAFE_NOTREC^#
70+
u.#^IN_CLOSURE_SYNC_UNSAFE?check=UNSAFE_OK_SYNC^#
71+
}
72+
73+
receiverAsync {
74+
dummy;
75+
s.#^IN_CLOSURE_ASYNC_SAFE?check=SAFE_OK^#
76+
u.#^IN_CLOSURE_ASYNC_UNSAFE?check=UNSAFE_OK^#
77+
}
78+
}
79+
80+
actor TestActor {
81+
func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
82+
s.#^IN_ACTOR_SYNC_SAFE?check=SAFE_NOTREC^#
83+
u.#^IN_ACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^#
84+
}
85+
86+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
87+
s.#^IN_ACTOR_ASYNC_SAFE?check=SAFE_OK^#
88+
u.#^IN_ACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^#
89+
}
90+
}
91+
92+
@MainActor
93+
class TestMainActorIsolated {
94+
func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
95+
s.#^IN_GLOBALACTOR_SYNC_SAFE?check=SAFE_NOTREC^#
96+
u.#^IN_GLOBALACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^#
97+
}
98+
99+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
100+
s.#^IN_GLOBALACTOR_ASYNC_SAFE?check=SAFE_OK^#
101+
u.#^IN_GLOBALACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^#
102+
}
103+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency -warn-concurrency
2+
3+
// SAFE_NOTREC: Begin completions, 2 items
4+
// SAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#];
5+
// SAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#];
6+
// SAFE_NOTREC: End completions
7+
8+
// UNSAFE_NOTREC: Begin completions, 2 items
9+
// UNSAFE_NOTREC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#];
10+
// UNSAFE_NOTREC-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: method()[' async'][#Void#];
11+
// UNSAFE_NOTREC: End completions
12+
13+
// SAFE_OK: Begin completions, 2 items
14+
// SAFE_OK-DAG: Keyword[self]/CurrNominal: self[#SafelyIsolatedCls#];
15+
// SAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#];
16+
// SAFE_OK: End completions
17+
18+
// UNSAFE_OK: Begin completions, 2 items
19+
// UNSAFE_OK-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#];
20+
// UNSAFE_OK-DAG: Decl[InstanceMethod]/CurrNominal: method()[' async'][#Void#];
21+
// UNSAFE_OK: End completions
22+
23+
// UNSAFE_OK_SYNC: Begin completions, 2 items
24+
// UNSAFE_OK_SYNC-DAG: Keyword[self]/CurrNominal: self[#UnsafelyIsolatedCls#];
25+
// UNSAFE_OK_SYNC-DAG: Decl[InstanceMethod]/CurrNominal: method()[#Void#];
26+
// UNSAFE_OK_SYNC: End completions
27+
28+
@globalActor
29+
actor MyGlobalActor {
30+
static let shared = MyGlobalActor()
31+
}
32+
33+
@MyGlobalActor(unsafe)
34+
class UnsafelyIsolatedCls {
35+
func method()
36+
}
37+
38+
@MyGlobalActor
39+
class SafelyIsolatedCls {
40+
func method()
41+
}
42+
43+
class TestNormal {
44+
func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
45+
s.#^IN_METHOD_SYNC_SAFE?check=SAFE_NOTREC^#
46+
u.#^IN_METHOD_SYNC_UNSAFE?check=UNSAFE_NOTREC^#
47+
}
48+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
49+
s.#^IN_METHOD_ASYNC_SAFE?check=SAFE_OK^#
50+
u.#^IN_METHOD_ASYNC_UNSAFE?check=UNSAFE_OK^#
51+
}
52+
}
53+
54+
func testSync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
55+
s.#^IN_FUNC_SYNC_SAFE?check=SAFE_NOTREC^#
56+
u.#^IN_FUNC_SYNC_UNSAFE?check=UNSAFE_NOTREC^#
57+
}
58+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
59+
s.#^IN_FUNC_ASYNC_SAFE?check=SAFE_OK^#
60+
u.#^IN_FUNC_ASYNC_UNSAFE?check=UNSAFE_OK^#
61+
}
62+
63+
func testClosure(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
64+
func receiverSync(fn: () -> Void) {}
65+
func receiverAsync(fn: () async -> Void) {}
66+
67+
receiverSync {
68+
dummy;
69+
s.#^IN_CLOSURE_SYNC_SAFE?check=SAFE_NOTREC^#
70+
u.#^IN_CLOSURE_SYNC_UNSAFE?check=UNSAFE_NOTREC^#
71+
}
72+
73+
receiverAsync {
74+
dummy;
75+
s.#^IN_CLOSURE_ASYNC_SAFE?check=SAFE_OK^#
76+
u.#^IN_CLOSURE_ASYNC_UNSAFE?check=UNSAFE_OK^#
77+
}
78+
}
79+
80+
actor TestActor {
81+
func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
82+
s.#^IN_ACTOR_SYNC_SAFE?check=SAFE_NOTREC^#
83+
u.#^IN_ACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^#
84+
}
85+
86+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
87+
s.#^IN_ACTOR_ASYNC_SAFE?check=SAFE_OK^#
88+
u.#^IN_ACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^#
89+
}
90+
}
91+
92+
@MainActor
93+
class TestMainActorIsolated {
94+
func test(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) {
95+
s.#^IN_GLOBALACTOR_SYNC_SAFE?check=SAFE_NOTREC^#
96+
u.#^IN_GLOBALACTOR_SYNC_UNSAFE?check=UNSAFE_NOTREC^#
97+
}
98+
99+
func testAsync(s: SafelyIsolatedCls, u: UnsafelyIsolatedCls) async {
100+
s.#^IN_GLOBALACTOR_ASYNC_SAFE?check=SAFE_OK^#
101+
u.#^IN_GLOBALACTOR_ASYNC_UNSAFE?check=UNSAFE_OK^#
102+
}
103+
}

0 commit comments

Comments
 (0)