Skip to content

Commit 1647cdd

Browse files
authored
Merge pull request #40602 from DougGregor/sendable-diagnostics
2 parents df563ba + a901892 commit 1647cdd

11 files changed

+133
-90
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4575,17 +4575,36 @@ NOTE(protocol_isolated_to_global_actor_here,none,
45754575

45764576
ERROR(isolated_parameter_not_actor,none,
45774577
"'isolated' parameter has non-actor type %0", (Type))
4578-
4578+
45794579
WARNING(non_sendable_param_type,none,
4580-
"cannot pass argument of non-sendable type %0 across actors",
4581-
(Type))
4580+
"non-sendable type %0 %select{passed in call to %4 %2 %3|"
4581+
"passed in implicitly asynchronous call to %4 %2 %3|"
4582+
"in parameter of %4 %2 %3 satisfying non-isolated protocol "
4583+
"requirement|"
4584+
"in parameter of %4 '@objc' %2 %3}1 cannot cross actor boundary",
4585+
(Type, unsigned, DescriptiveDeclKind, DeclName, ActorIsolation))
4586+
WARNING(non_sendable_call_param_type,none,
4587+
"non-sendable type %0 passed in %select{implicitly asynchronous |}1"
4588+
"call to %2 function cannot cross actor boundary",
4589+
(Type, bool, ActorIsolation))
45824590
WARNING(non_sendable_result_type,none,
4583-
"cannot call function returning non-sendable type %0 across "
4584-
"actors", (Type))
4591+
"non-sendable type %0 returned by %select{call to %4 %2 %3|"
4592+
"implicitly asynchronous call to %4 %2 %3|"
4593+
"%4 %2 %3 satisfying non-isolated protocol requirement|"
4594+
"%4 '@objc' %2 %3}1 cannot cross actor boundary",
4595+
(Type, unsigned, DescriptiveDeclKind, DeclName, ActorIsolation))
4596+
WARNING(non_sendable_call_result_type,none,
4597+
"non-sendable type %0 returned by %select{implicitly asynchronous |}1"
4598+
"call to %2 function cannot cross actor boundary",
4599+
(Type, bool, ActorIsolation))
45854600
WARNING(non_sendable_property_type,none,
4586-
"cannot use %1 %2 with a non-sendable type %0 "
4587-
"%select{across actors|from concurrently-executed code}3",
4588-
(Type, DescriptiveDeclKind, DeclName, bool))
4601+
"non-sendable type %0 in %select{"
4602+
"%select{asynchronous access to %5 %1 %2|"
4603+
"implicitly asynchronous access to %5 %1 %2|"
4604+
"conformance of %5 %1 %2 to non-isolated protocol requirement|"
4605+
"%5 '@objc' %1 %2}4|captured local %1 %2}3 cannot "
4606+
"cross %select{actor|task}3 boundary",
4607+
(Type, DescriptiveDeclKind, DeclName, bool, unsigned, ActorIsolation))
45894608
WARNING(non_sendable_keypath_capture,none,
45904609
"cannot form key path that captures non-sendable type %0",
45914610
(Type))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -781,22 +781,26 @@ 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())) {
788788
for (auto param : *function->getParameters()) {
789789
Type paramType = param->getInterfaceType().subst(subs);
790790
if (diagnoseNonSendableTypes(
791-
paramType, fromDC, loc, diag::non_sendable_param_type))
791+
paramType, fromDC, loc, diag::non_sendable_param_type,
792+
(unsigned)reason, function->getDescriptiveKind(),
793+
function->getName(), getActorIsolation(function)))
792794
return true;
793795
}
794796

795797
// Check the result type of a function.
796798
if (auto func = dyn_cast<FuncDecl>(function)) {
797799
Type resultType = func->getResultInterfaceType().subst(subs);
798800
if (diagnoseNonSendableTypes(
799-
resultType, fromDC, loc, diag::non_sendable_result_type))
801+
resultType, fromDC, loc, diag::non_sendable_result_type,
802+
(unsigned)reason, func->getDescriptiveKind(), func->getName(),
803+
getActorIsolation(func)))
800804
return true;
801805
}
802806

@@ -811,22 +815,28 @@ bool swift::diagnoseNonSendableTypesInReference(
811815
propertyType, fromDC, loc,
812816
diag::non_sendable_property_type,
813817
var->getDescriptiveKind(), var->getName(),
814-
var->isLocalCapture()))
818+
var->isLocalCapture(),
819+
(unsigned)reason,
820+
getActorIsolation(var)))
815821
return true;
816822
}
817823

818824
if (auto subscript = dyn_cast<SubscriptDecl>(declRef.getDecl())) {
819825
for (auto param : *subscript->getIndices()) {
820826
Type paramType = param->getInterfaceType().subst(subs);
821827
if (diagnoseNonSendableTypes(
822-
paramType, fromDC, loc, diag::non_sendable_param_type))
828+
paramType, fromDC, loc, diag::non_sendable_param_type,
829+
(unsigned)reason, subscript->getDescriptiveKind(),
830+
subscript->getName(), getActorIsolation(subscript)))
823831
return true;
824832
}
825833

826834
// Check the element type of a subscript.
827835
Type resultType = subscript->getElementInterfaceType().subst(subs);
828836
if (diagnoseNonSendableTypes(
829-
resultType, fromDC, loc, diag::non_sendable_result_type))
837+
resultType, fromDC, loc, diag::non_sendable_result_type,
838+
(unsigned)reason, subscript->getDescriptiveKind(),
839+
subscript->getName(), getActorIsolation(subscript)))
830840
return true;
831841

832842
return false;
@@ -1973,12 +1983,11 @@ namespace {
19731983
}
19741984

19751985
if (result == AsyncMarkingResult::FoundAsync) {
1976-
19771986
// Check for non-sendable types.
19781987
bool problemFound =
19791988
diagnoseNonSendableTypesInReference(
19801989
concDeclRef, getDeclContext(), declLoc,
1981-
ConcurrentReferenceKind::SynchronousAsAsyncCall);
1990+
SendableCheckReason::SynchronousAsAsync);
19821991
if (problemFound)
19831992
result = AsyncMarkingResult::NotSendable;
19841993
}
@@ -2104,18 +2113,32 @@ namespace {
21042113
}
21052114

21062115
// Check for sendability of the parameter types.
2107-
for (const auto &param : fnType->getParams()) {
2108-
// FIXME: Dig out the locations of the corresponding arguments.
2116+
auto params = fnType->getParams();
2117+
for (unsigned paramIdx : indices(params)) {
2118+
const auto &param = params[paramIdx];
2119+
2120+
// Dig out the location of the argument.
2121+
SourceLoc argLoc = apply->getLoc();
2122+
if (auto argList = apply->getArgs()) {
2123+
auto arg = argList->get(paramIdx);
2124+
if (arg.getStartLoc().isValid())
2125+
argLoc = arg.getStartLoc();
2126+
}
2127+
21092128
if (diagnoseNonSendableTypes(
2110-
param.getParameterType(), getDeclContext(), apply->getLoc(),
2111-
diag::non_sendable_param_type))
2129+
param.getParameterType(), getDeclContext(), argLoc,
2130+
diag::non_sendable_call_param_type,
2131+
apply->isImplicitlyAsync().hasValue(),
2132+
*unsatisfiedIsolation))
21122133
return true;
21132134
}
21142135

21152136
// Check for sendability of the result type.
21162137
if (diagnoseNonSendableTypes(
21172138
fnType->getResult(), getDeclContext(), apply->getLoc(),
2118-
diag::non_sendable_result_type))
2139+
diag::non_sendable_call_result_type,
2140+
apply->isImplicitlyAsync().hasValue(),
2141+
*unsatisfiedIsolation))
21192142
return true;
21202143

21212144
return false;
@@ -2141,7 +2164,7 @@ namespace {
21412164
if (isCrossActor) {
21422165
return diagnoseNonSendableTypesInReference(
21432166
valueRef, getDeclContext(), loc,
2144-
ConcurrentReferenceKind::CrossActor);
2167+
SendableCheckReason::CrossActor);
21452168
}
21462169

21472170
// Call is implicitly asynchronous.
@@ -2482,7 +2505,7 @@ namespace {
24822505

24832506
return diagnoseNonSendableTypesInReference(
24842507
memberRef, getDeclContext(), memberLoc,
2485-
ConcurrentReferenceKind::CrossActor);
2508+
SendableCheckReason::CrossActor);
24862509
}
24872510

24882511
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
@@ -3023,7 +3023,7 @@ bool ConformanceChecker::checkActorIsolation(
30233023
if (isValidImplicitAsync(witness, requirement)) {
30243024
diagnoseNonSendableTypesInReference(
30253025
getConcreteWitness(), DC, witness->getLoc(),
3026-
ConcurrentReferenceKind::CrossActor);
3026+
SendableCheckReason::Conformance);
30273027

30283028
return false;
30293029
}
@@ -3062,7 +3062,7 @@ bool ConformanceChecker::checkActorIsolation(
30623062
case ActorIsolationRestriction::CrossActorSelf: {
30633063
if (diagnoseNonSendableTypesInReference(
30643064
getConcreteWitness(), DC, witness->getLoc(),
3065-
ConcurrentReferenceKind::CrossActor)) {
3065+
SendableCheckReason::Conformance)) {
30663066
return true;
30673067
}
30683068

@@ -3193,7 +3193,7 @@ bool ConformanceChecker::checkActorIsolation(
31933193

31943194
return diagnoseNonSendableTypesInReference(
31953195
getConcreteWitness(), DC, witness->getLoc(),
3196-
ConcurrentReferenceKind::CrossActor);
3196+
SendableCheckReason::Conformance);
31973197
}
31983198

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

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,11 @@ func someAsyncFunc() async {
175175
////////////
176176
// effectful properties from outside the actor instance
177177

178-
// expected-warning@+2 {{cannot use property 'effPropA' with a non-sendable type 'Box' across actors}}
178+
// expected-warning@+2 {{non-sendable type 'Box' in asynchronous access to actor-isolated property 'effPropA' cannot cross actor boundary}}
179179
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}}
180180
_ = a.effPropA
181181

182-
// expected-warning@+3 {{cannot use property 'effPropT' with a non-sendable type 'Box' across actors}}
182+
// expected-warning@+3 {{non-sendable type 'Box' in implicitly asynchronous access to actor-isolated property 'effPropT' cannot cross actor boundary}}
183183
// expected-error@+2{{property access can throw, but it is not marked with 'try' and the error is not handled}}
184184
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}}
185185
_ = a.effPropT
@@ -189,8 +189,8 @@ func someAsyncFunc() async {
189189
_ = a.effPropAT
190190

191191
// (mostly) corrected ones
192-
_ = await a.effPropA // expected-warning {{cannot use property 'effPropA' with a non-sendable type 'Box' across actors}}
193-
_ = try! await a.effPropT // expected-warning {{cannot use property 'effPropT' with a non-sendable type 'Box' across actors}}
192+
_ = await a.effPropA // expected-warning {{non-sendable type 'Box' in asynchronous access to actor-isolated property 'effPropA' cannot cross actor boundary}}
193+
_ = try! await a.effPropT // expected-warning {{non-sendable type 'Box' in implicitly asynchronous access to actor-isolated property 'effPropT' cannot cross actor boundary}}
194194
_ = try? await a.effPropAT
195195

196196
print("ok!")
@@ -289,28 +289,28 @@ func blender(_ peeler : () -> Void) {
289289

290290

291291
await wisk({})
292-
// expected-warning@-1{{cannot pass argument of non-sendable type 'Any' across actors}}
292+
// expected-warning@-1{{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
293293
await wisk(1)
294-
// expected-warning@-1{{cannot pass argument of non-sendable type 'Any' across actors}}
294+
// expected-warning@-1{{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
295295
await (peelBanana)()
296296
await (((((peelBanana)))))()
297297
await (((wisk)))((wisk)((wisk)(1)))
298-
// expected-warning@-1 3{{cannot pass argument of non-sendable type 'Any' across actors}}
298+
// expected-warning@-1 3{{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
299299

300300
blender((peelBanana))
301301
// expected-error@-1{{converting function value of type '@BananaActor () -> ()' to '() -> Void' loses global actor 'BananaActor'}}
302302
await wisk(peelBanana)
303-
// expected-warning@-1{{cannot pass argument of non-sendable type 'Any' across actors}}
303+
// expected-warning@-1{{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
304304

305305
await wisk(wisk)
306-
// expected-warning@-1{{cannot pass argument of non-sendable type 'Any' across actors}}
306+
// expected-warning@-1{{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
307307
await (((wisk)))(((wisk)))
308-
// expected-warning@-1{{cannot pass argument of non-sendable type 'Any' across actors}}
308+
// expected-warning@-1{{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
309309

310-
// expected-warning@+1 {{cannot pass argument of non-sendable type 'Any' across actors}}
310+
// expected-warning@+1 {{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
311311
await {wisk}()(1)
312312

313-
// expected-warning@+1 {{cannot pass argument of non-sendable type 'Any' across actors}}
313+
// expected-warning@+1 {{non-sendable type 'Any' passed in call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
314314
await (true ? wisk : {n in return})(1)
315315
}
316316

@@ -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/actor_isolation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func checkAsyncPropertyAccess() async {
106106

107107
act.text[0] += "hello" // expected-error{{actor-isolated property 'text' can not be mutated from a non-isolated context}}
108108

109-
_ = act.point // expected-warning{{cannot use property 'point' with a non-sendable type 'Point' across actors}}
109+
_ = act.point // expected-warning{{non-sendable type 'Point' in asynchronous access to actor-isolated property 'point' cannot cross actor boundary}}
110110
}
111111

112112
@available(SwiftStdlib 5.1, *)
@@ -872,8 +872,8 @@ func testCrossModuleLets(actor: OtherModuleActor) async {
872872
_ = actor.b // okay
873873
_ = actor.c // expected-error{{expression is 'async' but is not marked with 'await'}}
874874
// expected-note@-1{{property access is 'async'}}
875-
// expected-warning@-2{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}}
876-
_ = await actor.c // expected-warning{{cannot use property 'c' with a non-sendable type 'SomeClass' across actors}}
875+
// expected-warning@-2{{non-sendable type 'SomeClass' in implicitly asynchronous access to actor-isolated property 'c' cannot cross actor boundary}}
876+
_ = await actor.c // expected-warning{{non-sendable type 'SomeClass' in implicitly asynchronous access to actor-isolated property 'c' cannot cross actor boundary}}
877877
_ = await actor.d // okay
878878
}
879879

0 commit comments

Comments
 (0)