Skip to content

Commit daf8d97

Browse files
authored
Merge pull request #80484 from DougGregor/isolated-conformances-fix-and-docs
[SE-0470] Ensure that one cannot form an isolated conformance when Self: Sendable
2 parents 0c5fd6a + 1c53728 commit daf8d97

File tree

7 files changed

+112
-47
lines changed

7 files changed

+112
-47
lines changed

include/swift/AST/DiagnosticGroups.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ GROUP(DynamicCallable, "dynamic-callable-requirements.md")
3030
GROUP(ErrorInFutureSwiftVersion, "error-in-future-swift-version.md")
3131
GROUP(ExistentialAny, "existential-any.md")
3232
GROUP(ExistentialMemberAccess, "existential-member-access-limitations.md")
33+
GROUP(IsolatedConformances, "isolated-conformances.md")
3334
GROUP(MultipleInheritance, "multiple-inheritance.md")
3435
GROUP(MutableGlobalVariable, "mutable-global-variable.md")
3536
GROUP(NominalTypes, "nominal-types.md")

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8483,7 +8483,8 @@ ERROR(attr_abi_failable_mismatch,none,
84838483
//===----------------------------------------------------------------------===//
84848484
// MARK: Isolated conformances
84858485
//===----------------------------------------------------------------------===//
8486-
ERROR(isolated_conformance_experimental_feature,none,
8486+
GROUPED_ERROR(isolated_conformance_experimental_feature,IsolatedConformances,
8487+
none,
84878488
"isolated conformances require experimental feature "
84888489
" 'IsolatedConformances'", ())
84898490
NOTE(note_isolate_conformance_to_global_actor,none,
@@ -8492,15 +8493,16 @@ NOTE(note_isolate_conformance_to_global_actor,none,
84928493
NOTE(note_depends_on_isolated_conformance,none,
84938494
"conformance depends on %0 conformance of %1 to %kind2",
84948495
(ActorIsolation, Type, const ValueDecl *))
8495-
ERROR(isolated_conformance_with_sendable,none,
8496+
GROUPED_ERROR(isolated_conformance_with_sendable,IsolatedConformances,none,
84968497
"%4 conformance of %0 to %1 cannot satisfy conformance "
8497-
"requirement for a %select{`Sendable`|`SendableMetatype`}2 type "
8498+
"requirement for a %select{'Sendable'|'SendableMetatype'}2 type "
84988499
"parameter %3", (Type, DeclName, bool, Type, ActorIsolation))
8499-
ERROR(isolated_conformance_with_sendable_simple,none,
8500+
GROUPED_ERROR(isolated_conformance_with_sendable_simple,IsolatedConformances,
8501+
none,
85008502
"%2 conformance of %0 to %1 cannot satisfy "
8501-
"conformance requirement for a `Sendable` type parameter ",
8503+
"conformance requirement for a 'Sendable' type parameter ",
85028504
(Type, DeclName, ActorIsolation))
8503-
ERROR(isolated_conformance_wrong_domain,none,
8505+
GROUPED_ERROR(isolated_conformance_wrong_domain,IsolatedConformances,none,
85048506
"%0 conformance of %1 to %2 cannot be used in %3 context",
85058507
(ActorIsolation, Type, DeclName, ActorIsolation))
85068508

include/swift/AST/ProtocolConformance.h

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -739,22 +739,7 @@ class NormalProtocolConformance : public RootProtocolConformance,
739739

740740
void setSourceKindAndImplyingConformance(
741741
ConformanceEntryKind sourceKind,
742-
NormalProtocolConformance *implyingConformance) {
743-
assert(sourceKind != ConformanceEntryKind::Inherited &&
744-
"a normal conformance cannot be inherited");
745-
assert((sourceKind == ConformanceEntryKind::Implied) ==
746-
(bool)implyingConformance &&
747-
"an implied conformance needs something that implies it");
748-
assert(sourceKind != ConformanceEntryKind::PreMacroExpansion &&
749-
"cannot create conformance pre-macro-expansion");
750-
Bits.NormalProtocolConformance.SourceKind = unsigned(sourceKind);
751-
if (auto implying = implyingConformance) {
752-
ImplyingConformance = implying;
753-
PreconcurrencyLoc = implying->getPreconcurrencyLoc();
754-
Bits.NormalProtocolConformance.Options =
755-
implyingConformance->getOptions().toRaw();
756-
}
757-
}
742+
NormalProtocolConformance *implyingConformance);
758743

759744
/// Determine whether this conformance is lazily loaded.
760745
///

lib/AST/ProtocolConformance.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,31 @@ usesDefaultDefinition(AssociatedTypeDecl *requirement) const {
203203
CONFORMANCE_SUBCLASS_DISPATCH(usesDefaultDefinition, (requirement))
204204
}
205205

206+
void NormalProtocolConformance::setSourceKindAndImplyingConformance(
207+
ConformanceEntryKind sourceKind,
208+
NormalProtocolConformance *implyingConformance) {
209+
assert(sourceKind != ConformanceEntryKind::Inherited &&
210+
"a normal conformance cannot be inherited");
211+
assert((sourceKind == ConformanceEntryKind::Implied) ==
212+
(bool)implyingConformance &&
213+
"an implied conformance needs something that implies it");
214+
assert(sourceKind != ConformanceEntryKind::PreMacroExpansion &&
215+
"cannot create conformance pre-macro-expansion");
216+
Bits.NormalProtocolConformance.SourceKind = unsigned(sourceKind);
217+
if (auto implying = implyingConformance) {
218+
ImplyingConformance = implying;
219+
PreconcurrencyLoc = implying->getPreconcurrencyLoc();
220+
Bits.NormalProtocolConformance.Options =
221+
implyingConformance->getOptions().toRaw();
222+
if (getProtocol()->isMarkerProtocol()) {
223+
setExplicitGlobalActorIsolation(nullptr);
224+
} else if (auto globalActorIsolationType =
225+
implyingConformance->getExplicitGlobalActorIsolation()) {
226+
setExplicitGlobalActorIsolation(globalActorIsolationType);
227+
}
228+
}
229+
}
230+
206231
bool ProtocolConformance::isRetroactive() const {
207232
auto extensionModule = getDeclContext()->getParentModule();
208233
auto protocolModule = getProtocol()->getParentModule();
@@ -494,10 +519,14 @@ void
494519
NormalProtocolConformance::setExplicitGlobalActorIsolation(TypeExpr *typeExpr) {
495520
if (!typeExpr) {
496521
Bits.NormalProtocolConformance.HasExplicitGlobalActor = false;
522+
Bits.NormalProtocolConformance.Options &=
523+
~(unsigned)ProtocolConformanceFlags::GlobalActorIsolated;
497524
return;
498525
}
499526

500527
Bits.NormalProtocolConformance.HasExplicitGlobalActor = true;
528+
Bits.NormalProtocolConformance.Options |=
529+
(unsigned)ProtocolConformanceFlags::GlobalActorIsolated;
501530
ASTContext &ctx = getDeclContext()->getASTContext();
502531
ctx.getGlobalCache().conformanceExplicitGlobalActorIsolation[this] = typeExpr;
503532
}

test/Concurrency/isolated_conformance.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ struct SMismatchedActors: @MainActor Q {
9191
typealias A = C2
9292
}
9393

94+
protocol PSendable: P, Sendable { }
95+
96+
// expected-error@+2{{type 'PSendableS' does not conform to protocol 'PSendable'}}
97+
// expected-error@+1{{main actor-isolated conformance of 'PSendableS' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter 'Self'}}
98+
struct PSendableS: @MainActor PSendable { // expected-note{{requirement specified as 'Self' : 'P' [with Self = PSendableS]}}
99+
func f() { }
100+
}
101+
94102
// ----------------------------------------------------------------------------
95103
// Use checking of isolated conformances.
96104
// ----------------------------------------------------------------------------
@@ -108,8 +116,8 @@ struct PSendableMetaWrapper<T: P & SendableMetatype>: P {
108116
@MainActor
109117
func testIsolationConformancesInTypes() {
110118
typealias A1 = PWrapper<C>
111-
typealias A2 = PSendableWrapper<C> // expected-error{{isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a `Sendable` type parameter 'T'}}
112-
typealias A3 = PSendableMetaWrapper<C> // expected-error{{isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a `SendableMetatype` type parameter 'T'}}
119+
typealias A2 = PSendableWrapper<C> // expected-error{{isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter 'T'}}
120+
typealias A3 = PSendableMetaWrapper<C> // expected-error{{isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a 'SendableMetatype' type parameter 'T'}}
113121
}
114122

115123
func acceptP<T: P>(_: T) { }
@@ -124,20 +132,20 @@ func acceptSendableMetaP<T: SendableMetatype & P>(_: T) { }
124132
func testIsolationConformancesInCall(c: C) {
125133
acceptP(c) // okay
126134

127-
acceptSendableP(c) // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a `Sendable` type parameter}}
128-
acceptSendableMetaP(c) // expected-error{{isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a `Sendable` type parameter}}
135+
acceptSendableP(c) // expected-error{{main actor-isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter}}
136+
acceptSendableMetaP(c) // expected-error{{isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter}}
129137
}
130138

131139
@MainActor
132140
func testIsolatedConformancesOfActor(a: SomeActor) {
133141
acceptP(a)
134-
acceptSendableMetaP(a) // expected-error{{main actor-isolated conformance of 'SomeActor' to 'P' cannot satisfy conformance requirement for a `Sendable` type parameter}}
142+
acceptSendableMetaP(a) // expected-error{{main actor-isolated conformance of 'SomeActor' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter}}
135143
}
136144

137145
@SomeGlobalActor
138146
func testIsolatedConformancesOfOtherGlobalActor(c: CMismatchedIsolation) {
139147
acceptP(c)
140-
acceptSendableMetaP(c) // expected-error{{global actor 'SomeGlobalActor'-isolated conformance of 'CMismatchedIsolation' to 'P' cannot satisfy conformance requirement for a `Sendable` type parameter}}
148+
acceptSendableMetaP(c) // expected-error{{global actor 'SomeGlobalActor'-isolated conformance of 'CMismatchedIsolation' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter}}
141149
}
142150

143151
func testIsolationConformancesFromOutside(c: C) {

userdocs/diagnostics/conformance-isolation.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,26 @@ This code will produce an error similar to:
2929

3030
There are several options for resolving this error, as indicated by the notes:
3131

32-
* If all of the operations used to satisfy the protocol's requirements are on the same global actor (such as the main actor), the conformance itself can be isolated to that global actor. This allows the conformance to be used inside code running on that actor, but it cannot be used concurrency. A conformance can be isolated to a global actor the same way as anything else in the language, e.g.,
33-
```swift
34-
@MainActor
35-
struct MyData: @MainActor P {
36-
func f() { }
37-
}
38-
```
32+
* If all of the operations used to satisfy the protocol's requirements are on the same global actor (such as the main actor), the conformance itself can be isolated to that global actor. This allows the conformance to be used inside code running on that actor, but it cannot be used concurrently. A conformance can be isolated to a global actor the same way as anything else in the language, e.g.,
33+
```swift
34+
@MainActor
35+
struct MyData: @MainActor P {
36+
func f() { }
37+
}
38+
```
3939

4040
* If the conformance needs to be usable anywhere, then each of the operations used to satisfy its requirements must be marked `nonisolated`. This means that they will not have access to any actor-specific operations or state, because these operations can be called concurrently from anywhere. The result would look like this:
41-
```swift
42-
@MainActor
43-
struct MyData: P {
44-
nonisolated func f() { }
45-
}
46-
```
41+
```swift
42+
@MainActor
43+
struct MyData: P {
44+
nonisolated func f() { }
45+
}
46+
```
4747

4848
* If the protocol requirements themselves are meant to always be used from the correct isolation domain (for example, the main actor) but the protocol itself did not describe that requirement, the conformance can be marked with `@preconcurrency`. This approach moves isolation checking into a run-time assertion, which will produce a fatal error if an operation is called without already being on the right actor. A `@preconcurrency` conformance can be written as follows:
49-
```swift
50-
@MainActor
51-
struct MyData: @preconcurrency P {
52-
func f() { }
53-
}
54-
```
49+
```swift
50+
@MainActor
51+
struct MyData: @preconcurrency P {
52+
func f() { }
53+
}
54+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Isolated conformances
2+
3+
A protocol conformance can be isolated to a specific global actor, meaning that the conformance can only be used by code running on that actor. Isolated conformances are expressed by specifying the global actor on the conformance itself:
4+
5+
```swift
6+
protocol P {
7+
func f()
8+
}
9+
10+
@MainActor
11+
class MyType: @MainActor P {
12+
/*@MainActor*/ func f() {
13+
// must be called on the main actor
14+
}
15+
}
16+
```
17+
18+
Swift will produce diagnostics if the conformance is directly accessed in code that isn't guaranteed to execute in the same global actor. For example:
19+
20+
```swift
21+
func acceptP<T: P>(_ value: T) { }
22+
23+
/*nonisolated*/ func useIsolatedConformance(myType: MyType) {
24+
acceptP(myType) // error: main actor-isolated conformance of 'MyType' to 'P' cannot be used in nonisolated context
25+
}
26+
```
27+
28+
To address this issue, mark the code as having the same global actor as the conformance it is trying to use. In this case, mark `useIsolatedConformance` as `@MainActor` so that the code is guaranteed to execute on the main actor.
29+
30+
An isolated conformance cannot be used together with a `Sendable` requirement, because doing so would allow the conformance to cross isolation boundaries and be used from outside the global actor. For example:
31+
32+
```swift
33+
func acceptSendableP<T: P & Sendable>(_ value: T) { }
34+
35+
@MainActor func useIsolatedConformanceOnMainActor(myType: MyType) {
36+
acceptSendableP(myType) // error: main-actor-isolated conformance of 'MyType' to 'P' cannot satisfy conformance requirement for 'Sendable' type parameter 'T'
37+
}
38+
```
39+
40+
These errors can be addressed by either making the conformance itself `nonisolated` or making the generic function not require `Sendable`.

0 commit comments

Comments
 (0)