Skip to content

Commit 872ecb0

Browse files
authored
Merge pull request #37711 from ahoppen/pr/global-actor-completion
[CodeComplete] Mark results as not recommended if global actor context doesn't match
2 parents 28bede4 + 6725e2b commit 872ecb0

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
@@ -2649,9 +2649,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26492649
break;
26502650
}
26512651
case ActorIsolation::GlobalActor:
2652-
case ActorIsolation::GlobalActorUnsafe:
2653-
// TODO: Implement.
2652+
case ActorIsolation::GlobalActorUnsafe: {
2653+
auto contextIsolation = getActorIsolationOfContext(
2654+
const_cast<DeclContext *>(CurrDeclContext));
2655+
if (contextIsolation != isolation) {
2656+
implicitlyAsync = true;
2657+
}
26542658
break;
2659+
}
26552660
case ActorIsolation::Unspecified:
26562661
case ActorIsolation::Independent:
26572662
return;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,7 @@ namespace {
23972397
return getExplicitGlobalActor(closure);
23982398
}
23992399

2400+
public:
24002401
/// Determine the isolation of a particular closure.
24012402
///
24022403
/// This function assumes that enclosing closures have already had their
@@ -2620,6 +2621,12 @@ void swift::checkPropertyWrapperActorIsolation(
26202621
expr->walk(checker);
26212622
}
26222623

2624+
ClosureActorIsolation
2625+
swift::determineClosureActorIsolation(AbstractClosureExpr *closure) {
2626+
ActorIsolationChecker checker(closure->getParent());
2627+
return checker.determineClosureIsolation(closure);
2628+
}
2629+
26232630
/// Determine actor isolation solely from attributes.
26242631
///
26252632
/// \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
@@ -30,6 +30,7 @@ class ActorIsolation;
3030
class AnyFunctionType;
3131
class ASTContext;
3232
class ClassDecl;
33+
class ClosureActorIsolation;
3334
class ClosureExpr;
3435
class ConcreteDeclRef;
3536
class CustomAttr;
@@ -59,6 +60,16 @@ void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr);
5960
void checkPropertyWrapperActorIsolation(
6061
PatternBindingDecl *binding, Expr *expr);
6162

63+
/// Determine the isolation of a particular closure.
64+
///
65+
/// This forwards to \c ActorIsolationChecker::determineClosureActorIsolation
66+
/// and thus assumes that enclosing closures have already had their isolation
67+
/// checked.
68+
///
69+
/// This does not set the closure's actor isolation
70+
ClosureActorIsolation
71+
determineClosureActorIsolation(AbstractClosureExpr *closure);
72+
6273
/// Describes the kind of operation that introduced the concurrent refernece.
6374
enum class ConcurrentReferenceKind {
6475
/// 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
@@ -1949,6 +1949,19 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator,
19491949
if (auto CE = dyn_cast<ClosureExpr>(DC)) {
19501950
if (CE->getBodyState() == ClosureExpr::BodyState::Parsed) {
19511951
swift::typeCheckASTNodeAtLoc(CE->getParent(), CE->getLoc());
1952+
// We need the actor isolation of the closure to be set so that we can
1953+
// annotate results that are on the same global actor.
1954+
// Since we are evaluating TypeCheckASTNodeAtLocRequest for every closure
1955+
// from outermost to innermost, we don't want to call checkActorIsolation,
1956+
// because that would cause actor isolation to be checked multiple times
1957+
// for nested closures. Instead, call determineClosureActorIsolation
1958+
// directly and set the closure's actor isolation manually. We can
1959+
// guarantee of that the actor isolation of enclosing closures have their
1960+
// isolation checked before nested ones are being checked by the way
1961+
// TypeCheckASTNodeAtLocRequest is called multiple times, as described
1962+
// above.
1963+
auto ActorIsolation = determineClosureActorIsolation(CE);
1964+
CE->setActorIsolation(ActorIsolation);
19521965
if (CE->getBodyState() != ClosureExpr::BodyState::ReadyForTypeChecking)
19531966
return false;
19541967
}
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)