Skip to content

Commit 441b8f6

Browse files
committed
[CodeComplete] Mark results as not recommended if global actor context doesn't match
Annotate cross-actor references to global actors as `async` and mark them as not-recommended if they must be executed on a different actor than the current context is on. Resolves rdar://75902598
1 parent 4934207 commit 441b8f6

File tree

5 files changed

+214
-2
lines changed

5 files changed

+214
-2
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2645,9 +2645,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26452645
break;
26462646
}
26472647
case ActorIsolation::GlobalActor:
2648-
case ActorIsolation::GlobalActorUnsafe:
2649-
// TODO: Implement.
2648+
case ActorIsolation::GlobalActorUnsafe: {
2649+
auto contextIsolation = getActorIsolationOfContext(
2650+
const_cast<DeclContext *>(CurrDeclContext));
2651+
if (contextIsolation != isolation) {
2652+
implicitlyAsync = true;
2653+
}
26502654
break;
2655+
}
26512656
case ActorIsolation::Unspecified:
26522657
case ActorIsolation::Independent:
26532658
return;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,6 +2406,7 @@ namespace {
24062406
return getExplicitGlobalActor(closure);
24072407
}
24082408

2409+
public:
24092410
/// Determine the isolation of a particular closure.
24102411
///
24112412
/// This function assumes that enclosing closures have already had their
@@ -2540,6 +2541,12 @@ void swift::checkPropertyWrapperActorIsolation(
25402541
expr->walk(checker);
25412542
}
25422543

2544+
ClosureActorIsolation
2545+
swift::determineClosureActorIsolation(AbstractClosureExpr *closure) {
2546+
ActorIsolationChecker checker(closure->getParent());
2547+
return checker.determineClosureIsolation(closure);
2548+
}
2549+
25432550
/// Determine actor isolation solely from attributes.
25442551
///
25452552
/// \returns the actor isolation determined from attributes alone (with no

lib/Sema/TypeCheckConcurrency.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class ActorIsolation;
2929
class AnyFunctionType;
3030
class ASTContext;
3131
class ClassDecl;
32+
class ClosureActorIsolation;
3233
class ClosureExpr;
3334
class ConcreteDeclRef;
3435
class CustomAttr;
@@ -56,6 +57,16 @@ void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr);
5657
void checkPropertyWrapperActorIsolation(
5758
PatternBindingDecl *binding, Expr *expr);
5859

60+
/// Determine the isolation of a particular closure.
61+
///
62+
/// This forwards to \c ActorIsolationChecker::determineClosureActorIsolation
63+
/// and thus assumes that enclosing closures have already had their isolation
64+
/// checked.
65+
///
66+
/// This does not set the closure's actor isolation
67+
ClosureActorIsolation
68+
determineClosureActorIsolation(AbstractClosureExpr *closure);
69+
5970
/// Describes the kind of operation that introduced the concurrent refernece.
6071
enum class ConcurrentReferenceKind {
6172
/// A synchronous operation that was "promoted" to an asynchronous call

lib/Sema/TypeCheckStmt.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,19 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator,
19461946
if (auto CE = dyn_cast<ClosureExpr>(DC)) {
19471947
if (CE->getBodyState() == ClosureExpr::BodyState::Parsed) {
19481948
swift::typeCheckASTNodeAtLoc(CE->getParent(), CE->getLoc());
1949+
// We need the actor isolation of the closure to be set so that we can
1950+
// annotate results that are on the same global actor.
1951+
// Since we are evaluating TypeCheckASTNodeAtLocRequest for every closure
1952+
// from outermost to innermost, we don't want to call checkActorIsolation,
1953+
// because that would cause actor isolation to be checked multiple times
1954+
// for nested closures. Instead, call determineClosureActorIsolation
1955+
// directly and set the closure's actor isolation manually. We can
1956+
// guarantee of that the actor isolation of enclosing closures have their
1957+
// isolation checked before nested ones are being checked by the way
1958+
// TypeCheckASTNodeAtLocRequest is called multiple times, as described
1959+
// above.
1960+
auto ActorIsolation = determineClosureActorIsolation(CE);
1961+
CE->setActorIsolation(ActorIsolation);
19491962
if (CE->getBodyState() != ClosureExpr::BodyState::ReadyForTypeChecking)
19501963
return false;
19511964
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// REQUIRES: concurrency
2+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency
3+
4+
class MyNonSendable {}
5+
struct MySendable {}
6+
7+
@globalActor
8+
actor MyGlobalActor {
9+
static var shared = MyGlobalActor()
10+
}
11+
12+
@globalActor
13+
actor MyOtherGlobalActor {
14+
static var shared = MyOtherGlobalActor()
15+
}
16+
17+
@MyGlobalActor func globalFuncOnGlobalActor() {}
18+
19+
func takeClosure<T>(_: () async -> T) {}
20+
21+
var otherInstanceOfMyClass = MyClass()
22+
23+
class MyClass {
24+
@MyGlobalActor func funcOnGlobalActor() -> Int { return 0 }
25+
@MyOtherGlobalActor func funcOnOtherGlobalActor() -> Int { return 0 }
26+
func funcSync() -> Int { return 0 }
27+
28+
@MyGlobalActor func nonSenableFuncOnGlobalActor(arg: MyNonSendable) -> Int { return 0 }
29+
@MyOtherGlobalActor func nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) -> Int { return 0 }
30+
31+
@MyGlobalActor var varOnGlobalActor: Int = 0
32+
@MyOtherGlobalActor var varOnOtherGlobalActor: Int = 0
33+
var varSync: Int = 0
34+
35+
@MyGlobalActor subscript(onGlobalActor onGlobalActor: Int) -> Int { get { 1 } set { } }
36+
@MyOtherGlobalActor subscript(onOtherGlobalActor onOtherGlobalActor: Int) -> Int { get { 1 } set { } }
37+
subscript(sync sync: Int) -> Int { get { 1 } set { } }
38+
}
39+
40+
extension MyClass {
41+
@MyGlobalActor func testOnGlobalActor() {
42+
let _ = #^IN_FUNC_ON_GLOBAL_ACTOR^#
43+
// IN_FUNC_ON_GLOBAL_ACTOR: Begin completions
44+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal: funcOnGlobalActor()[#Int#]; name=funcOnGlobalActor()
45+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() async
46+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync()
47+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[#Int#]; name=nonSenableFuncOnGlobalActor(arg: MyNonSendable)
48+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) async
49+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceVar]/CurrNominal: varOnGlobalActor[#Int#]; name=varOnGlobalActor
50+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor async
51+
// IN_FUNC_ON_GLOBAL_ACTOR-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync
52+
// IN_FUNC_ON_GLOBAL_ACTOR: End completions
53+
54+
let _ = self.#^IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
55+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT: Begin completions
56+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcOnGlobalActor()[#Int#]; name=funcOnGlobalActor()
57+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() async
58+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync()
59+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[#Int#]; name=nonSenableFuncOnGlobalActor(arg: MyNonSendable)
60+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) async
61+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: varOnGlobalActor[#Int#]; name=varOnGlobalActor
62+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor async
63+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync
64+
// IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT: End completions
65+
66+
let _ = self#^IN_FUNC_ON_GLOBAL_ACTOR_NODOT^#
67+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT: Begin completions
68+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .funcOnGlobalActor()[#Int#]; name=funcOnGlobalActor()
69+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() async
70+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .funcSync()[#Int#]; name=funcSync()
71+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[#Int#]; name=nonSenableFuncOnGlobalActor(arg: MyNonSendable)
72+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) async
73+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .varOnGlobalActor[#Int#]; name=varOnGlobalActor
74+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: .varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor async
75+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .varSync[#Int#]; name=varSync
76+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[Subscript]/CurrNominal: [{#onGlobalActor: Int#}][#Int#]; name=[onGlobalActor: Int]
77+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[Subscript]/CurrNominal/NotRecommended: [{#onOtherGlobalActor: Int#}][' async'][#Int#]; name=[onOtherGlobalActor: Int] async
78+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT-DAG: Decl[Subscript]/CurrNominal: [{#sync: Int#}][#Int#]; name=[sync: Int]
79+
// IN_FUNC_ON_GLOBAL_ACTOR_NODOT: End completions
80+
81+
let _ = otherInstanceOfMyClass.#^IN_FUNC_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
82+
let _ = otherInstanceOfMyClass#^IN_FUNC_ON_GLOBAL_ACTOR_OTHER_NODOT?check=IN_FUNC_ON_GLOBAL_ACTOR_NODOT^#
83+
}
84+
85+
func testInSyncFunc() {
86+
let _ = #^IN_SYNC_FUNC^#
87+
// IN_SYNC_FUNC: Begin completions
88+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnGlobalActor()[' async'][#Int#]; name=funcOnGlobalActor()
89+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() async
90+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync()
91+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnGlobalActor(arg: MyNonSendable)
92+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) async
93+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnGlobalActor[#Int#][' async']; name=varOnGlobalActor
94+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor async
95+
// IN_SYNC_FUNC_DOT-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync
96+
// IN_SYNC_FUNC: End completions
97+
98+
let _ = self.#^IN_SYNC_FUNC_SELF_DOT^#
99+
// IN_SYNC_FUNC_SELF_DOT: Begin completions
100+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnGlobalActor()[' async'][#Int#]; name=funcOnGlobalActor()
101+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() async
102+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: funcSync()[#Int#]; name=funcSync()
103+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnGlobalActor(arg: MyNonSendable)
104+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) async
105+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnGlobalActor[#Int#][' async']; name=varOnGlobalActor
106+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor async
107+
// IN_SYNC_FUNC_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: varSync[#Int#]; name=varSync
108+
// IN_SYNC_FUNC_SELF_DOT: End completions
109+
110+
let _ = self#^IN_SYNC_FUNC_NODOT^#
111+
// IN_SYNC_FUNC_NODOT: Begin completions
112+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .funcOnGlobalActor()[' async'][#Int#]; name=funcOnGlobalActor()
113+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .funcOnOtherGlobalActor()[' async'][#Int#]; name=funcOnOtherGlobalActor() async
114+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .funcSync()[#Int#]; name=funcSync()
115+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .nonSenableFuncOnGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnGlobalActor(arg: MyNonSendable)
116+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: .nonSenableFuncOnOtherGlobalActor({#arg: MyNonSendable#})[' async'][#Int#]; name=nonSenableFuncOnOtherGlobalActor(arg: MyNonSendable) async
117+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: .varOnGlobalActor[#Int#][' async']; name=varOnGlobalActor
118+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: .varOnOtherGlobalActor[#Int#][' async']; name=varOnOtherGlobalActor async
119+
// IN_SYNC_FUNC_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .varSync[#Int#]; name=varSync
120+
// IN_SYNC_FUNC_NODOT-DAG: Decl[Subscript]/CurrNominal/NotRecommended: [{#onGlobalActor: Int#}][' async'][#Int#]; name=[onGlobalActor: Int]
121+
// IN_SYNC_FUNC_NODOT-DAG: Decl[Subscript]/CurrNominal/NotRecommended: [{#onOtherGlobalActor: Int#}][' async'][#Int#]; name=[onOtherGlobalActor: Int] async
122+
// IN_SYNC_FUNC_NODOT-DAG: Decl[Subscript]/CurrNominal: [{#sync: Int#}][#Int#]; name=[sync: Int]
123+
// IN_SYNC_FUNC_NODOT: End completions
124+
125+
let _ = otherInstanceOfMyClass.#^IN_SYNC_FUNC_OTHER_DOT?check=IN_SYNC_FUNC_SELF_DOT^#
126+
let _ = otherInstanceOfMyClass#^IN_SYNC_FUNC_OTHER_NODOT?check=IN_SYNC_FUNC_NODOT^#
127+
}
128+
129+
func testInGlobalActorClosure() {
130+
_ = { @MyGlobalActor () -> Void in
131+
let _ = otherInstanceOfMyClass.#^IN_CLOSURE_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
132+
}
133+
}
134+
135+
func testInGlobalActorClosureWithoutExplicitAttribute() {
136+
let callback: @MyGlobalActor () -> Void
137+
callback = {
138+
let _ = otherInstanceOfMyClass.#^IN_CLOSURE_ON_GLOBAL_ACTOR_WITHOUT_EXPLICIT_LABEL_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
139+
}
140+
}
141+
142+
@MyGlobalActor func testInClosureInGlobalActorFunc() {
143+
_ = { () -> Void in
144+
let _ = otherInstanceOfMyClass.#^IN_CLOSURE_IN_FUNC_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
145+
}
146+
}
147+
148+
func testInClosureNestedInClosureOnGlobalActorFunc() {
149+
_ = { @MyGlobalActor () -> Void in
150+
_ = { () -> Void in
151+
let _ = otherInstanceOfMyClass.#^IN_CLOSURE_NESTED_IN_CLOSURE_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
152+
}
153+
}
154+
}
155+
156+
func testInLocalFunc() {
157+
@MyGlobalActor func localFunc() {
158+
let _ = otherInstanceOfMyClass.#^IN_LOCAL_FUNC_ON_GLOBAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
159+
}
160+
}
161+
162+
@MyGlobalActor func testInNestedSingleExpressionClosure() {
163+
takeClosure {
164+
takeClosure {
165+
otherInstanceOfMyClass.#^IN_NESTED_SINGLE_EXPRESSION_CLOSURE_ON_GLBOAL_ACTOR_OTHER_DOT?check=IN_FUNC_ON_GLOBAL_ACTOR_SELF_DOT^#
166+
}
167+
}
168+
}
169+
}
170+
171+
actor ActorTests {
172+
func testInActor() {
173+
let _ = otherInstanceOfMyClass.#^IN_ACTOR_OTHER_DOT?check=IN_SYNC_FUNC_SELF_DOT^#
174+
let _ = otherInstanceOfMyClass#^IN_ACTOR_OTHER_NODOT?check=IN_SYNC_FUNC_NODOT^#
175+
}
176+
}

0 commit comments

Comments
 (0)