Skip to content

Commit 3d1c592

Browse files
authored
Merge pull request swiftlang#82324 from xedin/SE-0449-fixes-6.2
[6.2][Concurrency] SE-0449: Fix a few issues related to `nonisolated` type declarations
2 parents c224505 + 06d1224 commit 3d1c592

File tree

2 files changed

+171
-4
lines changed

2 files changed

+171
-4
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5148,6 +5148,13 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) {
51485148
if (dc->getSelfProtocolDecl())
51495149
return std::nullopt;
51505150

5151+
// Prevent isolation inference from requirements if the conforming type
5152+
// has an explicit `nonisolated` attribute.
5153+
if (auto *NTD = dc->getSelfNominalTypeDecl()) {
5154+
if (NTD->getAttrs().hasAttribute<NonisolatedAttr>())
5155+
return std::nullopt;
5156+
}
5157+
51515158
// Walk through each of the conformances in this context, collecting any
51525159
// requirements that have actor isolation.
51535160
auto conformances = idc->getLocalConformances( // note this
@@ -5156,6 +5163,11 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) {
51565163
std::tuple<ProtocolConformance *, ActorIsolation, ValueDecl *>;
51575164
SmallVector<IsolatedRequirement, 2> isolatedRequirements;
51585165
for (auto conformance : conformances) {
5166+
auto *implied =
5167+
conformance->getSourceKind() == ConformanceEntryKind::Implied
5168+
? conformance->getImplyingConformance()
5169+
: nullptr;
5170+
51595171
auto protocol = conformance->getProtocol();
51605172
for (auto found : protocol->lookupDirect(value->getName())) {
51615173
if (!isa<ProtocolDecl>(found->getDeclContext()))
@@ -5165,6 +5177,19 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) {
51655177
if (!requirement || isa<TypeDecl>(requirement))
51665178
continue;
51675179

5180+
// The conformance implied by an explicitly stated nonisolated protocol
5181+
// makes all of the requirements nonisolated.
5182+
if (implied &&
5183+
implied->getSourceKind() == ConformanceEntryKind::Explicit) {
5184+
auto protocol = implied->getProtocol();
5185+
if (protocol->getAttrs().hasAttribute<NonisolatedAttr>()) {
5186+
isolatedRequirements.push_back(IsolatedRequirement{
5187+
implied, ActorIsolation::forNonisolated(/*unsafe=*/false),
5188+
requirement});
5189+
continue;
5190+
}
5191+
}
5192+
51685193
auto requirementIsolation = getActorIsolation(requirement);
51695194
switch (requirementIsolation) {
51705195
case ActorIsolation::ActorInstance:
@@ -5276,19 +5301,35 @@ getIsolationFromConformances(NominalTypeDecl *nominal) {
52765301
}
52775302

52785303
auto *proto = conformance->getProtocol();
5279-
switch (auto protoIsolation = getActorIsolation(proto)) {
5304+
auto inferredIsolation = getInferredActorIsolation(proto);
5305+
auto protoIsolation = inferredIsolation.isolation;
5306+
switch (protoIsolation) {
52805307
case ActorIsolation::ActorInstance:
52815308
case ActorIsolation::Unspecified:
5282-
case ActorIsolation::Nonisolated:
52835309
case ActorIsolation::CallerIsolationInheriting:
52845310
case ActorIsolation::NonisolatedUnsafe:
52855311
break;
5312+
case ActorIsolation::Nonisolated:
5313+
if (inferredIsolation.source.kind == IsolationSource::Kind::Explicit) {
5314+
if (!foundIsolation) {
5315+
// We found an explicitly 'nonisolated' protocol.
5316+
foundIsolation = {
5317+
protoIsolation,
5318+
IsolationSource(proto, IsolationSource::Conformance)};
5319+
}
5320+
continue;
5321+
} else {
5322+
break;
5323+
}
52865324

52875325
case ActorIsolation::Erased:
52885326
llvm_unreachable("protocol cannot have erased isolation");
52895327

52905328
case ActorIsolation::GlobalActor:
5291-
if (!foundIsolation) {
5329+
// If we encountered an explicit globally isolated conformance, allow it
5330+
// to override the nonisolated isolation kind.
5331+
if (!foundIsolation ||
5332+
conformance->getSourceKind() == ConformanceEntryKind::Explicit) {
52925333
foundIsolation = {
52935334
protoIsolation,
52945335
IsolationSource(proto, IsolationSource::Conformance)

test/Concurrency/nonisolated_rules.swift

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public struct PublicNonSendable {
6363
}
6464

6565

66-
nonisolated struct NonisolatedStruct: GloballyIsolated {
66+
nonisolated struct StructRemovesGlobalActor: GloballyIsolated {
6767
var x: NonSendable
6868
var y: Int = 1
6969

@@ -100,12 +100,110 @@ nonisolated struct S1: GloballyIsolated {
100100
// MARK: - Protocols
101101

102102
nonisolated protocol Refined: GloballyIsolated {}
103+
nonisolated protocol WhyNot {}
104+
105+
nonisolated protocol NonisolatedWithMembers {
106+
func test()
107+
}
103108

104109
struct A: Refined {
105110
var x: NonSendable
106111
init(x: NonSendable) {
107112
self.x = x // okay
108113
}
114+
115+
init() {
116+
self.x = NonSendable()
117+
}
118+
119+
func f() {}
120+
}
121+
122+
@MainActor protocol ExplicitGlobalActor: Refined {}
123+
124+
struct IsolatedA: ExplicitGlobalActor {
125+
// expected-note@+2 {{main actor isolation inferred from conformance to protocol 'ExplicitGlobalActor'}}
126+
// expected-note@+1 {{calls to instance method 'g()' from outside of its actor context are implicitly asynchronous}}
127+
func g() {}
128+
}
129+
130+
struct IsolatedB: Refined, ExplicitGlobalActor {
131+
// expected-note@+2 {{calls to instance method 'h()' from outside of its actor context are implicitly asynchronous}}
132+
// expected-note@+1 {{main actor isolation inferred from conformance to protocol 'ExplicitGlobalActor'}}
133+
func h() {}
134+
}
135+
136+
struct IsolatedC: WhyNot, GloballyIsolated {
137+
// expected-note@+2 {{calls to instance method 'k()' from outside of its actor context are implicitly asynchronous}}
138+
// expected-note@+1 {{main actor isolation inferred from conformance to protocol 'GloballyIsolated'}}
139+
func k() {}
140+
}
141+
142+
struct IsolatedCFlipped: GloballyIsolated, WhyNot {
143+
// expected-note@+2 {{calls to instance method 'k2()' from outside of its actor context are implicitly asynchronous}}
144+
// expected-note@+1 {{main actor isolation inferred from conformance to protocol 'GloballyIsolated'}}
145+
func k2() {}
146+
}
147+
148+
struct NonisolatedStruct {
149+
func callF() {
150+
return A().f() // okay, 'A' is non-isolated.
151+
}
152+
153+
// expected-note@+1 {{add '@MainActor' to make instance method 'callG()' part of global actor 'MainActor'}}
154+
func callG() {
155+
// expected-error@+1{{call to main actor-isolated instance method 'g()' in a synchronous nonisolated context}}
156+
return IsolatedA().g()
157+
}
158+
159+
// expected-note@+1 {{add '@MainActor' to make instance method 'callH()' part of global actor 'MainActor'}}
160+
func callH() {
161+
// expected-error@+1 {{call to main actor-isolated instance method 'h()' in a synchronous nonisolated context}}
162+
return IsolatedB().h()
163+
}
164+
165+
// expected-note@+1 {{add '@MainActor' to make instance method 'callK()' part of global actor 'MainActor'}}
166+
func callK() {
167+
// expected-error@+1 {{call to main actor-isolated instance method 'k()' in a synchronous nonisolated context}}
168+
return IsolatedC().k()
169+
}
170+
171+
// expected-note@+1 {{add '@MainActor' to make instance method 'callK2()' part of global actor 'MainActor'}}
172+
func callK2() {
173+
// expected-error@+1 {{call to main actor-isolated instance method 'k2()' in a synchronous nonisolated context}}
174+
return IsolatedCFlipped().k2()
175+
}
176+
}
177+
178+
@MainActor
179+
struct TestIsolated : NonisolatedWithMembers {
180+
var x: NonSendable // expected-note {{property declared here}}
181+
182+
// requirement behaves as if it's explicitly `nonisolated` which gets inferred onto the witness
183+
func test() {
184+
_ = x // expected-error {{main actor-isolated property 'x' can not be referenced from a nonisolated context}}
185+
}
186+
}
187+
188+
@MainActor
189+
protocol Root {
190+
func testRoot()
191+
}
192+
193+
nonisolated protocol Child : Root {
194+
func testChild()
195+
}
196+
197+
struct TestDifferentLevels : Child {
198+
func testRoot() {}
199+
func testChild() {}
200+
func testNonWitness() {}
201+
}
202+
203+
nonisolated func testRequirementsOnMultipleNestingLevels(t: TestDifferentLevels) {
204+
t.testRoot() // okay
205+
t.testChild() // okay
206+
t.testNonWitness() // okay
109207
}
110208

111209
// MARK: - Extensions
@@ -144,6 +242,34 @@ nonisolated class K: GloballyIsolated {
144242
}
145243
}
146244

245+
@MainActor
246+
protocol GloballyIsolatedWithRequirements {
247+
var x: NonSendable { get set } // expected-note {{property declared here}}
248+
func test() // expected-note {{calls to instance method 'test()' from outside of its actor context are implicitly asynchronous}}
249+
}
250+
251+
nonisolated class K2: GloballyIsolatedWithRequirements {
252+
var x: NonSendable
253+
254+
func test() {}
255+
256+
func testNonWitness() {}
257+
258+
init(x: NonSendable) {
259+
self.x = x // okay
260+
test() // okay
261+
testNonWitness() // okay
262+
}
263+
264+
func test<T: GloballyIsolatedWithRequirements>(t: T, s: K2) {
265+
_ = s.x // okay
266+
_ = t.x // expected-error {{main actor-isolated property 'x' can not be referenced from a nonisolated context}}
267+
268+
s.test() // okay
269+
t.test() // expected-error {{call to main actor-isolated instance method 'test()' in a synchronous nonisolated context}}
270+
}
271+
}
272+
147273
// MARK: - Storage of non-Sendable
148274

149275
class KlassA {

0 commit comments

Comments
 (0)