Skip to content

Commit c7e6470

Browse files
committed
Improve Sendable diagnostics for result types of functions.
(cherry picked from commit 11601c4)
1 parent 92fca9c commit c7e6470

9 files changed

+54
-40
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4578,8 +4578,15 @@ WARNING(non_sendable_param_type,none,
45784578
"cannot pass argument of non-sendable type %0 across actors",
45794579
(Type))
45804580
WARNING(non_sendable_result_type,none,
4581-
"cannot call function returning non-sendable type %0 across "
4582-
"actors", (Type))
4581+
"non-sendable type %0 returned by %select{call to %4 %2 %3|"
4582+
"implicitly asynchronous call to %4 %2 %3|"
4583+
"%4 %2 %3 satisfying non-isolated protocol requirement|"
4584+
"%4 '@objc' %2 %3}1 cannot cross actor boundary",
4585+
(Type, unsigned, DescriptiveDeclKind, DeclName, ActorIsolation))
4586+
WARNING(non_sendable_call_result_type,none,
4587+
"non-sendable type %0 returned by %select{implicitly asynchronous |}1"
4588+
"call to %2 function cannot cross actor boundary",
4589+
(Type, bool, ActorIsolation))
45834590
WARNING(non_sendable_property_type,none,
45844591
"cannot use %1 %2 with a non-sendable type %0 "
45854592
"%select{across actors|from concurrently-executed code}3",

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ bool swift::diagnoseNonSendableTypes(
781781

782782
bool swift::diagnoseNonSendableTypesInReference(
783783
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
784-
ConcurrentReferenceKind refKind) {
784+
SendableCheckReason reason) {
785785
// For functions, check the parameter and result types.
786786
SubstitutionMap subs = declRef.getSubstitutions();
787787
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
@@ -796,7 +796,9 @@ bool swift::diagnoseNonSendableTypesInReference(
796796
if (auto func = dyn_cast<FuncDecl>(function)) {
797797
Type resultType = func->getResultInterfaceType().subst(subs);
798798
if (diagnoseNonSendableTypes(
799-
resultType, fromDC, loc, diag::non_sendable_result_type))
799+
resultType, fromDC, loc, diag::non_sendable_result_type,
800+
(unsigned)reason, func->getDescriptiveKind(), func->getName(),
801+
getActorIsolation(func)))
800802
return true;
801803
}
802804

@@ -826,7 +828,9 @@ bool swift::diagnoseNonSendableTypesInReference(
826828
// Check the element type of a subscript.
827829
Type resultType = subscript->getElementInterfaceType().subst(subs);
828830
if (diagnoseNonSendableTypes(
829-
resultType, fromDC, loc, diag::non_sendable_result_type))
831+
resultType, fromDC, loc, diag::non_sendable_result_type,
832+
(unsigned)reason, subscript->getDescriptiveKind(),
833+
subscript->getName(), getActorIsolation(subscript)))
830834
return true;
831835

832836
return false;
@@ -1973,12 +1977,11 @@ namespace {
19731977
}
19741978

19751979
if (result == AsyncMarkingResult::FoundAsync) {
1976-
19771980
// Check for non-sendable types.
19781981
bool problemFound =
19791982
diagnoseNonSendableTypesInReference(
19801983
concDeclRef, getDeclContext(), declLoc,
1981-
ConcurrentReferenceKind::SynchronousAsAsyncCall);
1984+
SendableCheckReason::SynchronousAsAsync);
19821985
if (problemFound)
19831986
result = AsyncMarkingResult::NotSendable;
19841987
}
@@ -2115,7 +2118,9 @@ namespace {
21152118
// Check for sendability of the result type.
21162119
if (diagnoseNonSendableTypes(
21172120
fnType->getResult(), getDeclContext(), apply->getLoc(),
2118-
diag::non_sendable_result_type))
2121+
diag::non_sendable_call_result_type,
2122+
apply->isImplicitlyAsync().hasValue(),
2123+
*unsatisfiedIsolation))
21192124
return true;
21202125

21212126
return false;
@@ -2141,7 +2146,7 @@ namespace {
21412146
if (isCrossActor) {
21422147
return diagnoseNonSendableTypesInReference(
21432148
valueRef, getDeclContext(), loc,
2144-
ConcurrentReferenceKind::CrossActor);
2149+
SendableCheckReason::CrossActor);
21452150
}
21462151

21472152
// Call is implicitly asynchronous.
@@ -2482,7 +2487,7 @@ namespace {
24822487

24832488
return diagnoseNonSendableTypesInReference(
24842489
memberRef, getDeclContext(), memberLoc,
2485-
ConcurrentReferenceKind::CrossActor);
2490+
SendableCheckReason::CrossActor);
24862491
}
24872492

24882493
case ActorIsolationRestriction::ActorSelf: {

lib/Sema/TypeCheckConcurrency.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,21 @@ void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr);
7070
ClosureActorIsolation
7171
determineClosureActorIsolation(AbstractClosureExpr *closure);
7272

73-
/// Describes the kind of operation that introduced the concurrent refernece.
74-
enum class ConcurrentReferenceKind {
75-
/// A synchronous operation that was "promoted" to an asynchronous call
76-
/// because it was out of the actor's domain.
77-
SynchronousAsAsyncCall,
78-
/// A cross-actor reference.
73+
/// States the reason for checking the Sendability of a given declaration.
74+
enum class SendableCheckReason {
75+
/// A reference to an actor from outside that actor.
7976
CrossActor,
80-
/// A local capture referenced from concurrent code.
81-
LocalCapture,
82-
/// Concurrent function
83-
ConcurrentFunction,
84-
/// Nonisolated declaration.
85-
Nonisolated,
77+
78+
/// A synchronous operation that was "promoted" to an asynchronous one
79+
/// because it was out of the actor's domain.
80+
SynchronousAsAsync,
81+
82+
/// A protocol conformance where the witness/requirement have different
83+
/// actor isolation.
84+
Conformance,
85+
86+
/// The declaration is being exposed to Objective-C.
87+
ObjC,
8688
};
8789

8890
/// The isolation restriction in effect for a given declaration that is
@@ -241,7 +243,7 @@ bool contextRequiresStrictConcurrencyChecking(
241243
/// \returns true if an problem was detected, false otherwise.
242244
bool diagnoseNonSendableTypesInReference(
243245
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
244-
ConcurrentReferenceKind refKind);
246+
SendableCheckReason refKind);
245247

246248
/// Produce a diagnostic for a missing conformance to Sendable.
247249
void diagnoseMissingSendableConformance(

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ static bool checkObjCActorIsolation(const ValueDecl *VD,
439439
// FIXME: Substitution map?
440440
diagnoseNonSendableTypesInReference(
441441
const_cast<ValueDecl *>(VD), VD->getDeclContext(),
442-
VD->getLoc(), ConcurrentReferenceKind::CrossActor);
442+
VD->getLoc(), SendableCheckReason::ObjC);
443443
return false;
444444
case ActorIsolationRestriction::ActorSelf:
445445
// Actor-isolated functions cannot be @objc.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3018,7 +3018,7 @@ bool ConformanceChecker::checkActorIsolation(
30183018
if (isValidImplicitAsync(witness, requirement)) {
30193019
diagnoseNonSendableTypesInReference(
30203020
getConcreteWitness(), DC, witness->getLoc(),
3021-
ConcurrentReferenceKind::CrossActor);
3021+
SendableCheckReason::Conformance);
30223022

30233023
return false;
30243024
}
@@ -3057,7 +3057,7 @@ bool ConformanceChecker::checkActorIsolation(
30573057
case ActorIsolationRestriction::CrossActorSelf: {
30583058
if (diagnoseNonSendableTypesInReference(
30593059
getConcreteWitness(), DC, witness->getLoc(),
3060-
ConcurrentReferenceKind::CrossActor)) {
3060+
SendableCheckReason::Conformance)) {
30613061
return true;
30623062
}
30633063

@@ -3188,7 +3188,7 @@ bool ConformanceChecker::checkActorIsolation(
31883188

31893189
return diagnoseNonSendableTypesInReference(
31903190
getConcreteWitness(), DC, witness->getLoc(),
3191-
ConcurrentReferenceKind::CrossActor);
3191+
SendableCheckReason::Conformance);
31923192
}
31933193

31943194
// If the witness has a global actor but the requirement does not, we have

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,23 +358,23 @@ actor Calculator {
358358

359359
@OrangeActor func doSomething() async {
360360
let _ = (await bananaAdd(1))(2)
361-
// expected-warning@-1{{cannot call function returning non-sendable type}}
361+
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
362362
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
363363
let _ = await (await bananaAdd(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}}
364-
// expected-warning@-1{{cannot call function returning non-sendable type}}
364+
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
365365
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
366366

367367
let calc = Calculator()
368368

369369
let _ = (await calc.addCurried(1))(2)
370-
// expected-warning@-1{{cannot call function returning non-sendable type}}
370+
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly asynchronous call to actor-isolated instance method 'addCurried' cannot cross actor boundary}}
371371
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
372372
let _ = await (await calc.addCurried(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}}
373-
// expected-warning@-1{{cannot call function returning non-sendable type}}
373+
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly asynchronous call to actor-isolated instance method 'addCurried' cannot cross actor boundary}}
374374
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
375375

376376
let plusOne = await calc.addCurried(await calc.add(0, 1))
377-
// expected-warning@-1{{cannot call function returning non-sendable type}}
377+
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly asynchronous call to actor-isolated instance method 'addCurried' cannot cross actor boundary}}
378378
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
379379
let _ = plusOne(2)
380380
}

test/Concurrency/concurrent_value_checking.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ extension A1 {
5151

5252
// Across to a different actor, so Sendable restriction is enforced.
5353
_ = other.localLet // expected-warning{{cannot use property 'localLet' with a non-sendable type 'NotConcurrent' across actors}}
54-
_ = await other.synchronous() // expected-warning{{cannot call function returning non-sendable type 'NotConcurrent?' across actors}}
54+
_ = await other.synchronous() // expected-warning{{non-sendable type 'NotConcurrent?' returned by implicitly asynchronous call to actor-isolated instance method 'synchronous()' cannot cross actor boundary}}
5555
_ = await other.asynchronous(nil) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
5656
}
5757
}
@@ -105,8 +105,8 @@ func globalTestMain(nc: NotConcurrent) async {
105105
await globalAsync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
106106
await globalSync(a) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent?' across actors}}
107107
_ = await ClassWithGlobalActorInits(nc) // expected-warning{{cannot pass argument of non-sendable type 'NotConcurrent' across actors}}
108-
// expected-warning@-1{{cannot call function returning non-sendable type 'ClassWithGlobalActorInits' across actors}}
109-
_ = await ClassWithGlobalActorInits() // expected-warning{{cannot call function returning non-sendable type 'ClassWithGlobalActorInits' across actors}}
108+
// expected-warning@-1{{non-sendable type 'ClassWithGlobalActorInits' returned by call to global actor 'SomeGlobalActor'-isolated function cannot cross actor boundary}}
109+
_ = await ClassWithGlobalActorInits() // expected-warning{{non-sendable type 'ClassWithGlobalActorInits' returned by call to global actor 'SomeGlobalActor'-isolated function cannot cross actor boundary}}
110110
}
111111

112112
@SomeGlobalActor

test/Concurrency/sendable_conformance_checking.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ protocol AsyncProtocolWithNotSendable {
4040
// actor's domain.
4141
@available(SwiftStdlib 5.1, *)
4242
actor A3: AsyncProtocolWithNotSendable {
43-
func f() async -> NotSendable { NotSendable() } // expected-warning{{cannot call function returning non-sendable type 'NotSendable' across actors}}
43+
func f() async -> NotSendable { NotSendable() } // expected-warning{{non-sendable type 'NotSendable' returned by actor-isolated instance method 'f()' satisfying non-isolated protocol requirement cannot cross actor boundary}}
4444

4545
var prop: NotSendable { // expected-warning{{cannot use property 'prop' with a non-sendable type 'NotSendable' across actors}}
4646
get async {
@@ -53,7 +53,7 @@ actor A3: AsyncProtocolWithNotSendable {
5353
// actor's domain.
5454
@available(SwiftStdlib 5.1, *)
5555
actor A4: AsyncProtocolWithNotSendable {
56-
func f() -> NotSendable { NotSendable() } // expected-warning{{cannot call function returning non-sendable type 'NotSendable' across actors}}
56+
func f() -> NotSendable { NotSendable() } // expected-warning{{non-sendable type 'NotSendable' returned by actor-isolated instance method 'f()' satisfying non-isolated protocol requirement cannot cross actor boundary}}
5757

5858
var prop: NotSendable { // expected-warning{{cannot use property 'prop' with a non-sendable type 'NotSendable' across actors}}
5959
get {
@@ -98,7 +98,7 @@ protocol AsyncThrowingProtocolWithNotSendable {
9898
// actor's domain.
9999
@available(SwiftStdlib 5.1, *)
100100
actor A7: AsyncThrowingProtocolWithNotSendable {
101-
func f() async -> NotSendable { NotSendable() } // expected-warning{{cannot call function returning non-sendable type 'NotSendable' across actors}}
101+
func f() async -> NotSendable { NotSendable() } // expected-warning{{non-sendable type 'NotSendable' returned by actor-isolated instance method 'f()' satisfying non-isolated protocol requirement cannot cross actor boundary}}
102102

103103
var prop: NotSendable { // expected-warning{{cannot use property 'prop' with a non-sendable type 'NotSendable' across actors}}
104104
get async {
@@ -111,7 +111,7 @@ actor A7: AsyncThrowingProtocolWithNotSendable {
111111
// actor's domain.
112112
@available(SwiftStdlib 5.1, *)
113113
actor A8: AsyncThrowingProtocolWithNotSendable {
114-
func f() -> NotSendable { NotSendable() } // expected-warning{{cannot call function returning non-sendable type 'NotSendable' across actors}}
114+
func f() -> NotSendable { NotSendable() } // expected-warning{{non-sendable type 'NotSendable' returned by actor-isolated instance method 'f()' satisfying non-isolated protocol requirement cannot cross actor boundary}}
115115

116116
var prop: NotSendable { // expected-warning{{cannot use property 'prop' with a non-sendable type 'NotSendable' across actors}}
117117
get {

test/attr/attr_objc_async.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ actor MyActor {
3434
// CHECK: @objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int)
3535
// CHECK-DUMP: func_decl{{.*}}doBigJobOrFail{{.*}}foreign_async=@convention(block) (Optional<AnyObject>, Int, Optional<Error>) -> (),completion_handler_param=1,error_param=2
3636
@objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int) { return (self, 0) }
37-
// expected-warning@-1{{cannot call function returning non-sendable type '(AnyObject, Int)' across actors}}
37+
// expected-warning@-1{{non-sendable type '(AnyObject, Int)' returned by actor-isolated '@objc' instance method 'doBigJobOrFail' cannot cross actor boundary}}
3838

3939
// Actor-isolated entities cannot be exposed to Objective-C.
4040
@objc func synchronousBad() { } // expected-error{{actor-isolated instance method 'synchronousBad()' cannot be @objc}}

0 commit comments

Comments
 (0)