Skip to content

Commit 933f8eb

Browse files
committed
[Concurrency] Split up the non-Sendable argument diagnostics and improve
wording. Splitting up the diagnostic into separate diagnostics based on the reference kind is easier for me to read. The wording of the error message now puts the problem -- crossing an isolation boundary -- at the center of the message, and attempts to clarify how the value crosses an isolation boundary. E.g. for the witness diagnostics, the value crosses an isolation boundary when calling the witness through the protocol requirement in generic code. This change does not add any additional information to the diagnostics, but it'd be valuable to show both the source and destination isolation.
1 parent 5fa35b5 commit 933f8eb

9 files changed

+72
-37
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5635,14 +5635,27 @@ NOTE(in_derived_conformance, none,
56355635
NOTE(in_derived_witness, none,
56365636
"in %0 %1 for derived conformance to %2",
56375637
(DescriptiveDeclKind, DeclName, Type))
5638-
ERROR(non_sendable_param_type,none,
5639-
"non-sendable type %0 %select{passed in call to %3 %kind2|"
5640-
"exiting %3 context in call to nonisolated %kind2|"
5641-
"passed in implicitly asynchronous call to %3 %kind2|"
5642-
"in parameter of the protocol requirement satisfied by %3 %kind2|"
5643-
"in parameter of superclass method overridden by %3 %kind2|"
5644-
"in parameter of %3 '@objc' %kind2}1 cannot cross actor boundary",
5645-
(Type, unsigned, const ValueDecl *, ActorIsolation))
5638+
5639+
ERROR(non_sendable_arg_into_actor,none,
5640+
"non-sendable type %0 cannot be sent into %2 context in call to %kind1",
5641+
(Type, const ValueDecl *, ActorIsolation))
5642+
ERROR(non_sendable_arg_exits_actor,none,
5643+
"non-sendable type %0 cannot exit %2 context in call to nonisolated "
5644+
"%kind1",
5645+
(Type, const ValueDecl *, ActorIsolation))
5646+
ERROR(non_sendable_param_in_witness,none,
5647+
"non-sendable parameter type %0 cannot be sent from protocol "
5648+
"requirement %1 into %2 implementation",
5649+
(Type, const ValueDecl *, ActorIsolation))
5650+
ERROR(non_sendable_param_in_override,none,
5651+
"non-sendable parameter type %0 cannot be sent from superclass "
5652+
"%kind1 into %2 override",
5653+
(Type, const ValueDecl *, ActorIsolation))
5654+
ERROR(non_sendable_param_in_objc,none,
5655+
"non-sendable parameter type %0 of %2 '@objc' %kind1 cannot cross actor "
5656+
"boundary",
5657+
(Type, const ValueDecl *, ActorIsolation))
5658+
56465659
ERROR(non_sendable_result_type,none,
56475660
"non-sendable type %0 returned by %select{call to %3 %kind2|"
56485661
"call from %4 context to nonisolated %kind2|"

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,28 @@ bool swift::diagnoseNonSendableTypes(
11211121
return anyMissing;
11221122
}
11231123

1124+
static
1125+
Diag<Type, const ValueDecl *, ActorIsolation>
1126+
getSendableParamDiag(SendableCheckReason refKind) {
1127+
switch (refKind) {
1128+
case SendableCheckReason::CrossActor:
1129+
case SendableCheckReason::SynchronousAsAsync:
1130+
return diag::non_sendable_arg_into_actor;
1131+
1132+
case SendableCheckReason::ExitingActor:
1133+
return diag::non_sendable_arg_exits_actor;
1134+
1135+
case SendableCheckReason::Conformance:
1136+
return diag::non_sendable_param_in_witness;
1137+
1138+
case SendableCheckReason::Override:
1139+
return diag::non_sendable_param_in_override;
1140+
1141+
case SendableCheckReason::ObjC:
1142+
return diag::non_sendable_param_in_objc;
1143+
}
1144+
}
1145+
11241146
bool swift::diagnoseNonSendableTypesInReference(
11251147
Expr *base, ConcreteDeclRef declRef, const DeclContext *fromDC,
11261148
SourceLoc refLoc, SendableCheckReason refKind,
@@ -1155,8 +1177,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11551177
base->getType(),
11561178
fromDC, derivedConformanceType,
11571179
base->getStartLoc(),
1158-
diag::non_sendable_param_type,
1159-
(unsigned)refKind, declRef.getDecl(),
1180+
getSendableParamDiag(refKind),
1181+
declRef.getDecl(),
11601182
getActorIsolation()))
11611183
return true;
11621184
}
@@ -1171,8 +1193,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11711193
if (diagnoseNonSendableTypes(
11721194
paramType, fromDC, derivedConformanceType,
11731195
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1174-
diag::non_sendable_param_type,
1175-
(unsigned)refKind, function, getActorIsolation()))
1196+
getSendableParamDiag(refKind),
1197+
function, getActorIsolation()))
11761198
return true;
11771199
}
11781200
}
@@ -1217,8 +1239,8 @@ bool swift::diagnoseNonSendableTypesInReference(
12171239
if (diagnoseNonSendableTypes(
12181240
paramType, fromDC, derivedConformanceType,
12191241
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1220-
diag::non_sendable_param_type,
1221-
(unsigned)refKind, subscript, getActorIsolation()))
1242+
getSendableParamDiag(refKind),
1243+
subscript, getActorIsolation()))
12221244
return true;
12231245
}
12241246
}

test/Concurrency/actor_inout_isolation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ extension TestActor {
119119
func passStateIntoDifferentClassMethod() async {
120120
let other = NonAsyncClass()
121121
let otherCurry = other.modifyOtherAsync
122-
// expected-targeted-complete-tns-warning @-1 {{non-sendable type 'NonAsyncClass' exiting actor-isolated context in call to nonisolated instance method 'modifyOtherAsync' cannot cross actor boundary}}
122+
// expected-targeted-complete-tns-warning @-1 {{non-sendable type 'NonAsyncClass' cannot exit actor-isolated context in call to nonisolated instance method 'modifyOtherAsync'}}
123123
await other.modifyOtherAsync(&value2)
124124
// expected-error @-1 {{actor-isolated property 'value2' cannot be passed 'inout' to 'async' function call}}
125125

@@ -288,11 +288,11 @@ actor ProtectArray {
288288
func test() async {
289289
// FIXME: this is invalid too!
290290
_ = await array.mutateAsynchronously
291-
// expected-targeted-complete-tns-warning@-1 {{non-sendable type '@lvalue [Int]' exiting actor-isolated context in call to nonisolated property 'mutateAsynchronously' cannot cross actor boundary}}
291+
// expected-targeted-complete-tns-warning@-1 {{non-sendable type '@lvalue [Int]' cannot exit actor-isolated context in call to nonisolated property 'mutateAsynchronously'}}
292292

293293
_ = await array[mutateAsynchronously: 0]
294294
// expected-error@-1 {{actor-isolated property 'array' cannot be passed 'inout' to 'async' function call}}
295-
// expected-targeted-complete-tns-warning@-2 {{non-sendable type 'inout Array<Int>' exiting actor-isolated context in call to nonisolated subscript 'subscript(mutateAsynchronously:)' cannot cross actor boundary}}
295+
// expected-targeted-complete-tns-warning@-2 {{non-sendable type 'inout Array<Int>' cannot exit actor-isolated context in call to nonisolated subscript 'subscript(mutateAsynchronously:)'}}
296296

297297
await passToAsync(array[0])
298298

test/Concurrency/actor_isolation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,13 @@ func checkIsolationValueType(_ formance: InferredFromConformance,
153153
// these still do need an await in Swift 5
154154
_ = await ext.point // expected-warning {{non-sendable type 'Point' in implicitly asynchronous access to main actor-isolated property 'point' cannot cross actor boundary}}
155155
_ = await anno.point // expected-warning {{non-sendable type 'Point' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated property 'point' cannot cross actor boundary}}
156-
// expected-warning@-1 {{non-sendable type 'NoGlobalActorValueType' passed in implicitly asynchronous call to global actor 'SomeGlobalActor'-isolated property 'point' cannot cross actor boundary}}
156+
// expected-warning@-1 {{non-sendable type 'NoGlobalActorValueType' cannot be sent into global actor 'SomeGlobalActor'-isolated context in call to property 'point'}}
157157

158158
_ = formance.counter
159159
_ = anno.counter
160160

161161
// these will always need an await
162-
_ = await (formance as MainCounter).counter // expected-warning {{non-sendable type 'any MainCounter' passed in implicitly asynchronous call to main actor-isolated property 'counter' cannot cross actor boundary}}
162+
_ = await (formance as MainCounter).counter // expected-warning {{non-sendable type 'any MainCounter' cannot be sent into main actor-isolated context in call to property 'counter'}}
163163
_ = await ext[1]
164164
_ = await formance.ticker
165165
_ = await ext.polygon // expected-warning {{non-sendable type '[Point]' in implicitly asynchronous access to main actor-isolated property 'polygon' cannot cross actor boundary}}
@@ -1662,7 +1662,7 @@ actor SafeMutatingCall {
16621662

16631663

16641664
@MainActor
1665-
func testLocalFunctoinIsolation() {
1665+
func testLocalFunctionIsolation() {
16661666
func isolatedLocalFn() {}
16671667
// expected-note@-1 {{calls to local function 'isolatedLocalFn()' from outside of its actor context are implicitly asynchronous}}
16681668
// expected-note@-2 {{main actor isolation inferred from enclosing context}}

test/Concurrency/actor_isolation_swift6.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -disable-availability-checking -swift-version 6 -emit-sil -o /dev/null -verify -enable-upcoming-feature GlobalActorIsolatedTypesUsability %s
1+
// RUN: %target-swift-frontend -disable-availability-checking -swift-version 6 -emit-sil -o /dev/null -verify %s
22

33
// REQUIRES: concurrency
44
// REQUIRES: asserts
@@ -65,7 +65,7 @@ func checkIsolationValueType(_ formance: InferredFromConformance,
6565

6666
// these do need await, regardless of reference or value type
6767
_ = await (formance as any MainCounter).counter
68-
// expected-error@-1 {{non-sendable type 'any MainCounter' passed in implicitly asynchronous call to main actor-isolated property 'counter' cannot cross actor boundary}}
68+
// expected-error@-1 {{non-sendable type 'any MainCounter' cannot be sent into main actor-isolated context in call to property 'counter'}}
6969
_ = await ext[1]
7070
_ = await formance.ticker
7171
_ = await ext.polygon

test/Concurrency/concurrent_value_checking.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ protocol AsyncProto {
270270
}
271271

272272
extension A1: AsyncProto {
273-
func asyncMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable type 'NotConcurrent' in parameter of the protocol requirement satisfied by actor-isolated instance method 'asyncMethod' cannot cross actor boundary}}
273+
func asyncMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable parameter type 'NotConcurrent' cannot be sent from protocol requirement 'asyncMethod' into actor-isolated implementation}}
274274
}
275275

276276
protocol MainActorProto {
@@ -279,7 +279,7 @@ protocol MainActorProto {
279279

280280
class SomeClass: MainActorProto {
281281
@SomeGlobalActor
282-
func asyncMainMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable type 'NotConcurrent' in parameter of the protocol requirement satisfied by global actor 'SomeGlobalActor'-isolated instance method 'asyncMainMethod' cannot cross actor boundary}}
282+
func asyncMainMethod(_: NotConcurrent) async { } // expected-warning{{non-sendable parameter type 'NotConcurrent' cannot be sent from protocol requirement 'asyncMainMethod' into global actor 'SomeGlobalActor'-isolated implementation}}
283283
}
284284

285285
// ----------------------------------------------------------------------

test/Concurrency/global_actor_serialized.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ import SerializedStruct // expected-warning {{add '@preconcurrency' to treat 'Se
1414
// use it to force the right checks happen.
1515
func test() async -> Int {
1616
let x = MySerializedStruct()
17-
return await x.counter // expected-error {{non-sendable type 'MySerializedStruct' passed in implicitly asynchronous call to main actor-isolated property 'counter' cannot cross actor boundary}}
17+
return await x.counter // expected-error {{non-sendable type 'MySerializedStruct' cannot be sent into main actor-isolated context in call to property 'counter'}}
1818
}

test/Concurrency/sendable_checking.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,12 @@ protocol P {
142142
func foo(x : () -> ()) -> () {}
143143

144144
func bar(x : () -> ()) -> () {}
145-
// expected-warning@-1 {{non-sendable type '() -> ()' in parameter of the protocol requirement satisfied by actor-isolated instance method 'bar(x:)' cannot cross actor boundary}}
145+
// expected-warning@-1 {{non-sendable parameter type '() -> ()' cannot be sent from protocol requirement 'bar(x:)' into actor-isolated implementation}}
146146

147147
func foo2<T>(x : T) -> () {}
148148

149149
func bar2<T>(x : T) -> () {}
150-
// expected-warning@-1 {{non-sendable type 'T' in parameter of the protocol requirement satisfied by actor-isolated instance method 'bar2(x:)' cannot cross actor boundary}}
150+
// expected-warning@-1 {{non-sendable parameter type 'T' cannot be sent from protocol requirement 'bar2(x:)' into actor-isolated implementation}}
151151
}
152152

153153
@available(SwiftStdlib 5.1, *)
@@ -174,12 +174,12 @@ class Sub : Super {
174174
override nonisolated func foo(x : () -> ()) async {}
175175

176176
override nonisolated func bar(x : () -> ()) async {}
177-
// expected-warning@-1 {{non-sendable type '() -> ()' in parameter of superclass method overridden by nonisolated instance method 'bar(x:)' cannot cross actor boundary}}
177+
// expected-warning@-1 {{non-sendable parameter type '() -> ()' cannot be sent from superclass instance method 'bar(x:)' into nonisolated override}}
178178

179179
override nonisolated func foo2<T>(x: T) async {}
180180

181181
override nonisolated func bar2<T>(x: T) async {}
182-
// expected-warning@-1 {{non-sendable type 'T' in parameter of superclass method overridden by nonisolated instance method 'bar2(x:)' cannot cross actor boundary}}
182+
// expected-warning@-1 {{non-sendable parameter type 'T' cannot be sent from superclass instance method 'bar2(x:)' into nonisolated override}}
183183
}
184184

185185
@available(SwiftStdlib 5.1, *)
@@ -216,8 +216,8 @@ class SuperWUnsafeSubscript {
216216
class SubWUnsafeSubscript : SuperWUnsafeSubscript {
217217
override nonisolated subscript<T>(x : T) -> Int {
218218
get async {
219-
// expected-warning@-2{{non-sendable type 'T' in parameter of superclass method overridden by nonisolated subscript 'subscript(_:)' cannot cross actor boundary}}
220-
// expected-warning@-2{{non-sendable type 'T' in parameter of superclass method overridden by nonisolated getter for subscript 'subscript(_:)' cannot cross actor boundary}}
219+
// expected-warning@-2{{non-sendable parameter type 'T' cannot be sent from superclass subscript 'subscript(_:)' into nonisolated override}}
220+
// expected-warning@-2{{non-sendable parameter type 'T' cannot be sent from superclass getter for subscript 'subscript(_:)' into nonisolated override}}
221221
// there really shouldn't be two warnings produced here, see rdar://110846040 (Sendable diagnostics reported twice for subscript getters)
222222
return 0
223223
}
@@ -269,10 +269,10 @@ final class NonSendable {
269269
// expected-tns-note @-2 {{sending task-isolated 'self' to main actor-isolated instance method 'update()' risks causing data races between main actor-isolated and task-isolated uses}}
270270

271271
_ = await x
272-
// expected-warning@-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
272+
// expected-warning@-1 {{non-sendable type 'NonSendable' cannot be sent into main actor-isolated context in call to property 'x'}}
273273

274274
_ = await self.x
275-
// expected-warning@-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
275+
// expected-warning@-1 {{non-sendable type 'NonSendable' cannot be sent into main actor-isolated context in call to property 'x'}}
276276
}
277277

278278
@MainActor
@@ -294,7 +294,7 @@ func testNonSendableBaseArg() async {
294294
// expected-tns-note @-2 {{sending 't' to main actor-isolated instance method 'update()' risks causing data races between main actor-isolated and local nonisolated uses}}
295295

296296
_ = await t.x
297-
// expected-warning @-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
297+
// expected-warning @-1 {{non-sendable type 'NonSendable' cannot be sent into main actor-isolated context in call to property 'x'}}
298298
// expected-tns-note @-2 {{access can happen concurrently}}
299299
}
300300

@@ -309,7 +309,7 @@ func testNonSendableBaseArg2() async {
309309
// expected-tns-note @-4 {{sending 't' to main actor-isolated instance method 'update()' risks causing data races between main actor-isolated and local nonisolated uses}}
310310

311311
_ = await t.y
312-
// expected-warning @-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to global actor 'CustomActor'-isolated property 'y' cannot cross actor boundary}}
312+
// expected-warning @-1 {{non-sendable type 'NonSendable' cannot be sent into global actor 'CustomActor'-isolated context in call to property 'y'}}
313313
// expected-tns-note @-2 {{access can happen concurrently}}
314314
}
315315

test/Concurrency/sendable_override_checking.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ class Super {
2424
@available(SwiftStdlib 5.1, *)
2525
class Sub: Super {
2626
@MainActor override func f(_: NotSendable) async { }
27-
// expected-warning@-1{{non-sendable type 'NotSendable' in parameter of superclass method overridden by main actor-isolated instance method 'f' cannot cross actor boundary}}
27+
// expected-warning@-1{{non-sendable parameter type 'NotSendable' cannot be sent from superclass instance method 'f' into main actor-isolated override}}
2828

2929
nonisolated override func g1(_: NotSendable) { } // okay, synchronous
3030

3131
nonisolated override func g2(_: NotSendable) async { }
32-
// expected-warning@-1{{non-sendable type 'NotSendable' in parameter of superclass method overridden by nonisolated instance method 'g2' cannot cross actor boundary}}
32+
// expected-warning@-1{{non-sendable parameter type 'NotSendable' cannot be sent from superclass instance method 'g2' into nonisolated override}}
3333
}

0 commit comments

Comments
 (0)