Skip to content

Commit 3604f89

Browse files
authored
Merge pull request #81985 from DougGregor/non-sendable-metatype-capture-diag-fixes-6.2
[6.2] Minor fixes for the non-sendable meta type capture diagnostics
2 parents 1c1177e + 1a3ce94 commit 3604f89

File tree

5 files changed

+79
-2
lines changed

5 files changed

+79
-2
lines changed

include/swift/AST/DiagnosticGroups.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ GROUP(PropertyWrappers, "property-wrapper-requirements")
6060
GROUP(ProtocolTypeNonConformance, "protocol-type-non-conformance")
6161
GROUP(ResultBuilderMethods, "result-builder-methods")
6262
GROUP(SendableClosureCaptures, "sendable-closure-captures")
63+
GROUP(SendableMetatypes, "sendable-metatypes")
6364
GROUP(SendingRisksDataRace, "sending-risks-data-race")
6465
GROUP(StrictLanguageFeatures, "strict-language-features")
6566
GROUP(StrictMemorySafety, "strict-memory-safety")

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5662,7 +5662,7 @@ ERROR(non_sendable_isolated_capture,none,
56625662
"capture of %1 with non-Sendable type %0 in an isolated "
56635663
"%select{local function|closure}2",
56645664
(Type, DeclName, bool))
5665-
ERROR(non_sendable_metatype_capture,none,
5665+
GROUPED_ERROR(non_sendable_metatype_capture,SendableMetatypes,none,
56665666
"capture of non-Sendable type %0 in an isolated "
56675667
"%select{local function|closure}1",
56685668
(Type, bool))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3068,7 +3068,8 @@ namespace {
30683068
}
30693069
}
30703070

3071-
if (mayExecuteConcurrentlyWith(
3071+
if (ctx.LangOpts.hasFeature(Feature::RegionBasedIsolation) &&
3072+
mayExecuteConcurrentlyWith(
30723073
localFunc.getAsDeclContext(), getDeclContext(),
30733074
/*includeSending*/true)) {
30743075
auto innermostGenericDC = localFunc.getAsDeclContext();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
2+
3+
// REQUIRES: concurrency
4+
5+
protocol P {
6+
static func doSomething()
7+
}
8+
9+
func doSomethingStatic<T: P>(_: T.Type) {
10+
Task { @concurrent in
11+
T.doSomething()
12+
}
13+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Sendable metatypes
2+
3+
Types that are shared in concurrent code generally need to conform to `Sendable`. The same is true in generic code when sharing parameters of a generic parameter `T`. For example, the given code will produce an error under strict concurrency checking
4+
5+
```swift
6+
func doSomethingElsewhere<T>(_ value: T) {
7+
Task { @concurrent in
8+
print(value) // warning: capture of non-Sendable type 'T'
9+
}
10+
}
11+
```
12+
13+
because `value` can have a non-Sendable type that is not safe to share. To address this potential data race, the type `T` can be marked as `Sendable`:
14+
15+
```swift
16+
func doSomethingElsewhere<T: Sendable>(_ value: T) {
17+
Task { @concurrent in
18+
print(value)
19+
}
20+
}
21+
```
22+
23+
The same issue can occur when passing the type `T` itself, rather than a value of type `T`. The compiler will indicate such problems by noting that the metatype of `T`, spelled `T.Type`, is not `Sendable`:
24+
25+
```swift
26+
protocol P {
27+
static func doSomething()
28+
}
29+
30+
func doSomethingStatic<T: P>(_: T.Type) {
31+
Task { @concurrent in
32+
T.doSomething() // warning: capture of non-Sendable type 'T.Type' in an isolated closure
33+
}
34+
}
35+
```
36+
37+
In these cases, the type parameter should be required to conform to the `SendableMetatype` protocol, e.g.,
38+
39+
```swift
40+
func doSomethingStatic<T: P & SendableMetatype>(_: T.Type) {
41+
Task { @concurrent in
42+
T.doSomething()
43+
}
44+
}
45+
```
46+
47+
The `SendableMetatype` requirement allows the function to share the type `T` in concurrent code. To maintain data race safety, it prevents callers from using isolated conformances in the call. For example, the following code will be rejected due to a data race:
48+
49+
```swift
50+
@MainActor
51+
class C: @MainActor P {
52+
static func doSomething() { }
53+
}
54+
55+
@MainActor
56+
func test(c: C) {
57+
doSomethingStatic(C.self) // error: main actor-isolated conformance of 'C' to 'P' cannot satisfy conformance requirement for a 'Sendable' type parameter
58+
}
59+
60+
```
61+
62+
The conformance of `C` to `P` can only be used on the main actor, so it cannot be provided to `doSomethingStatic`, which calls the conformance from a different concurrent task.

0 commit comments

Comments
 (0)