Skip to content

[Concurrency] Split up the non-Sendable diagnostics and improve wording. #75687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 66 additions & 29 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5635,36 +5635,73 @@ NOTE(in_derived_conformance, none,
NOTE(in_derived_witness, none,
"in %0 %1 for derived conformance to %2",
(DescriptiveDeclKind, DeclName, Type))
ERROR(non_sendable_param_type,none,
"non-sendable type %0 %select{passed in call to %3 %kind2|"
"exiting %3 context in call to nonisolated %kind2|"
"passed in implicitly asynchronous call to %3 %kind2|"
"in parameter of the protocol requirement satisfied by %3 %kind2|"
"in parameter of superclass method overridden by %3 %kind2|"
"in parameter of %3 '@objc' %kind2}1 cannot cross actor boundary",
(Type, unsigned, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_result_type,none,
"non-sendable type %0 returned by %select{call to %3 %kind2|"
"call from %4 context to nonisolated %kind2|"
"implicitly asynchronous call to %3 %kind2|"
"%3 %kind2 satisfying protocol requirement|"
"%3 overriding %kind2|"
"%3 '@objc' %kind2}1 cannot cross actor boundary",
(Type, unsigned, const ValueDecl *, ActorIsolation))

ERROR(non_sendable_arg_into_actor,none,
"non-sendable type %0 cannot be sent into %2 context in call to %kind1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_arg_exits_actor,none,
"non-sendable type %0 cannot exit %2 context in call to nonisolated "
"%kind1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_param_in_witness,none,
"non-sendable parameter type %0 cannot be sent from caller of "
"protocol requirement %1 into %2 implementation",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_param_in_override,none,
"non-sendable parameter type %0 cannot be sent from caller of "
"superclass %kind1 into %2 override",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_param_in_objc,none,
"non-sendable parameter type %0 of %2 '@objc' %kind1 cannot cross actor "
"boundary",
(Type, const ValueDecl *, ActorIsolation))

ERROR(non_sendable_result_into_actor,none,
"non-sendable result type %0 cannot be sent from %2 context in call "
"to %kind1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_result_exits_actor,none,
"non-sendable result type %0 cannot exit %2 context in call to "
"nonisolated %kind1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_result_in_witness,none,
"non-sendable type %0 cannot be returned from %2 implementation "
"to caller of protocol requirement %1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_result_in_override,none,
"non-sendable type %0 cannot be returned from %2 override to "
"caller of superclass %kind1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_result_in_objc,none,
"non-sendable type %0 returned by %2 '@objc' %kind1 cannot cross "
"actor boundary",
(Type, const ValueDecl *, ActorIsolation))

ERROR(non_sendable_call_result_type,none,
"non-sendable type %0 returned by %select{|implicitly async }1"
"call to %2 function cannot cross actor boundary",
(Type, bool, ActorIsolation))
ERROR(non_sendable_property_type,none,
"non-sendable type %0 in %select{"
"%select{asynchronous access to %4 %kind1|"
"asynchronous access from %4 context to nonisolated %kind1|"
"implicitly asynchronous access to %4 %kind1|"
"conformance of %4 %kind1 to protocol requirement|"
"%4 overriding %kind1|"
"%4 '@objc' %kind1}3|captured local %1}2 cannot "
"cross %select{actor|task}2 boundary",
(Type, const ValueDecl *, bool, unsigned, ActorIsolation))
"non-sendable result type %0 cannot be sent from %1 context in call "
"to async function",
(Type, ActorIsolation))

ERROR(non_sendable_property_exits_actor,none,
"non-sendable type %0 of %kind1 cannot exit %2 context",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_property_into_actor,none,
"non-sendable type %0 of nonisolated %kind1 cannot be sent to "
"%2 context",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_property_in_witness,none,
"non-sendable type %0 cannot be returned from %2 implementation "
"to caller of protocol requirement %1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_property_in_override,none,
"non-sendable type %0 cannot be returned from %2 override to "
"caller of superclass %kind1",
(Type, const ValueDecl *, ActorIsolation))
ERROR(non_sendable_property_in_objc,none,
"non-sendable type %0 returned by %2 '@objc' %kind1 cannot cross "
"actor boundary",
(Type, const ValueDecl *, ActorIsolation))

ERROR(non_sendable_keypath_capture,none,
"cannot form key path that captures non-sendable type %0",
(Type))
Expand Down
116 changes: 93 additions & 23 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,72 @@ bool swift::diagnoseNonSendableTypes(
return anyMissing;
}

static
Diag<Type, const ValueDecl *, ActorIsolation>
getSendableParamDiag(SendableCheckReason refKind) {
switch (refKind) {
case SendableCheckReason::CrossActor:
case SendableCheckReason::SynchronousAsAsync:
return diag::non_sendable_arg_into_actor;

case SendableCheckReason::ExitingActor:
return diag::non_sendable_arg_exits_actor;

case SendableCheckReason::Conformance:
return diag::non_sendable_param_in_witness;

case SendableCheckReason::Override:
return diag::non_sendable_param_in_override;

case SendableCheckReason::ObjC:
return diag::non_sendable_param_in_objc;
}
}

static
Diag<Type, const ValueDecl *, ActorIsolation>
getSendableResultDiag(SendableCheckReason refKind) {
switch (refKind) {
case SendableCheckReason::CrossActor:
case SendableCheckReason::SynchronousAsAsync:
return diag::non_sendable_result_into_actor;

case SendableCheckReason::ExitingActor:
return diag::non_sendable_result_exits_actor;

case SendableCheckReason::Conformance:
return diag::non_sendable_result_in_witness;

case SendableCheckReason::Override:
return diag::non_sendable_result_in_override;

case SendableCheckReason::ObjC:
return diag::non_sendable_result_in_objc;
}
}

static
Diag<Type, const ValueDecl *, ActorIsolation>
getSendablePropertyDiag(SendableCheckReason refKind) {
switch (refKind) {
case SendableCheckReason::CrossActor:
case SendableCheckReason::SynchronousAsAsync:
return diag::non_sendable_property_exits_actor;

case SendableCheckReason::ExitingActor:
return diag::non_sendable_property_into_actor;

case SendableCheckReason::Conformance:
return diag::non_sendable_property_in_witness;

case SendableCheckReason::Override:
return diag::non_sendable_property_in_override;

case SendableCheckReason::ObjC:
return diag::non_sendable_property_in_objc;
}
}

bool swift::diagnoseNonSendableTypesInReference(
Expr *base, ConcreteDeclRef declRef, const DeclContext *fromDC,
SourceLoc refLoc, SendableCheckReason refKind,
Expand Down Expand Up @@ -1155,8 +1221,8 @@ bool swift::diagnoseNonSendableTypesInReference(
base->getType(),
fromDC, derivedConformanceType,
base->getStartLoc(),
diag::non_sendable_param_type,
(unsigned)refKind, declRef.getDecl(),
getSendableParamDiag(refKind),
declRef.getDecl(),
getActorIsolation()))
return true;
}
Expand All @@ -1171,8 +1237,8 @@ bool swift::diagnoseNonSendableTypesInReference(
if (diagnoseNonSendableTypes(
paramType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
diag::non_sendable_param_type,
(unsigned)refKind, function, getActorIsolation()))
getSendableParamDiag(refKind),
function, getActorIsolation()))
return true;
}
}
Expand All @@ -1185,8 +1251,8 @@ bool swift::diagnoseNonSendableTypesInReference(
if (diagnoseNonSendableTypes(
resultType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
diag::non_sendable_result_type,
(unsigned)refKind, func, getActorIsolation()))
getSendableResultDiag(refKind),
func, getActorIsolation()))
return true;
}
}
Expand All @@ -1201,11 +1267,8 @@ bool swift::diagnoseNonSendableTypesInReference(
if (diagnoseNonSendableTypes(
propertyType, fromDC,
derivedConformanceType, refLoc,
diag::non_sendable_property_type,
var,
var->isLocalCapture(),
(unsigned)refKind,
getActorIsolation()))
getSendablePropertyDiag(refKind),
var, getActorIsolation()))
return true;
}

Expand All @@ -1217,8 +1280,8 @@ bool swift::diagnoseNonSendableTypesInReference(
if (diagnoseNonSendableTypes(
paramType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
diag::non_sendable_param_type,
(unsigned)refKind, subscript, getActorIsolation()))
getSendableParamDiag(refKind),
subscript, getActorIsolation()))
return true;
}
}
Expand All @@ -1229,8 +1292,8 @@ bool swift::diagnoseNonSendableTypesInReference(
if (diagnoseNonSendableTypes(
resultType, fromDC, derivedConformanceType,
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
diag::non_sendable_result_type,
(unsigned)refKind, subscript, getActorIsolation()))
getSendableResultDiag(refKind),
subscript, getActorIsolation()))
return true;
}

Expand Down Expand Up @@ -3849,14 +3912,21 @@ namespace {
fnType->getResult().getPointer();
};

if (!willDoubleError() &&
diagnoseNonSendableTypes(fnType->getResult(), getDeclContext(),
/*inDerivedConformance*/ Type(),
apply->getLoc(),
diag::non_sendable_call_result_type,
apply->isImplicitlyAsync().has_value(),
*unsatisfiedIsolation)) {
return true;
if (!willDoubleError()) {
if (calleeDecl) {
return diagnoseNonSendableTypes(fnType->getResult(), getDeclContext(),
/*inDerivedConformance*/ Type(),
apply->getLoc(),
diag::non_sendable_result_into_actor,
calleeDecl,
*unsatisfiedIsolation);
}

return diagnoseNonSendableTypes(fnType->getResult(), getDeclContext(),
/*inDerivedConformance*/ Type(),
apply->getLoc(),
diag::non_sendable_call_result_type,
*unsatisfiedIsolation);
}
}

Expand Down
4 changes: 2 additions & 2 deletions test/ClangImporter/objc_async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -322,12 +322,12 @@ func check() async {
_ = await BarFrame()
_ = await FooFrame()
_ = await BazFrame()
// expected-warning@-1 {{non-sendable type 'BazFrame' returned by implicitly async call to global actor 'SomeGlobalActor'-isolated function cannot cross actor boundary; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{non-sendable result type 'BazFrame' cannot be sent from global actor 'SomeGlobalActor'-isolated context in call to initializer 'init()'; this is an error in the Swift 6 language mode}}

_ = await BarFrame(size: 0)
_ = await FooFrame(size: 0)
_ = await BazFrame(size: 0)
// expected-warning@-1 {{non-sendable type 'BazFrame' returned by implicitly async call to global actor 'SomeGlobalActor'-isolated function cannot cross actor boundary; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{non-sendable result type 'BazFrame' cannot be sent from global actor 'SomeGlobalActor'-isolated context in call to initializer 'init(size:)'; this is an error in the Swift 6 language mode}}
}

@available(SwiftStdlib 5.5, *)
Expand Down
18 changes: 9 additions & 9 deletions test/Concurrency/actor_call_implicitly_async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,11 @@ func someAsyncFunc() async {
////////////
// effectful properties from outside the actor instance

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

// expected-warning@+3 {{non-sendable type 'Box' in implicitly asynchronous access to actor-isolated property 'effPropT' cannot cross actor boundary}}
// expected-warning@+3 {{non-sendable type 'Box' of property 'effPropT' cannot exit actor-isolated context}}
// expected-error@+2{{property access can throw, but it is not marked with 'try' and the error is not handled}}
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}}
_ = a.effPropT
Expand All @@ -190,8 +190,8 @@ func someAsyncFunc() async {
_ = a.effPropAT

// (mostly) corrected ones
_ = await a.effPropA // expected-warning {{non-sendable type 'Box' in asynchronous access to actor-isolated property 'effPropA' cannot cross actor boundary}}
_ = try! await a.effPropT // expected-warning {{non-sendable type 'Box' in implicitly asynchronous access to actor-isolated property 'effPropT' cannot cross actor boundary}}
_ = await a.effPropA // expected-warning {{non-sendable type 'Box' of property 'effPropA' cannot exit actor-isolated context}}
_ = try! await a.effPropT // expected-warning {{non-sendable type 'Box' of property 'effPropT' cannot exit actor-isolated context}}
_ = try? await a.effPropAT

print("ok!")
Expand Down Expand Up @@ -353,23 +353,23 @@ actor Calculator {

@OrangeActor func doSomething() async {
let _ = (await bananaAdd(1))(2)
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly async call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from global actor 'BananaActor'-isolated context in call to global function 'bananaAdd'}}
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
let _ = await (await bananaAdd(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}}
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly async call to global actor 'BananaActor'-isolated function cannot cross actor boundary}}
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from global actor 'BananaActor'-isolated context in call to global function 'bananaAdd'}}
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}

let calc = Calculator()

let _ = (await calc.addCurried(1))(2)
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly async call to actor-isolated function cannot cross actor boundary}}
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from actor-isolated context in call to instance method 'addCurried'}}
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
let _ = await (await calc.addCurried(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}}
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly async call to actor-isolated function cannot cross actor boundary}}
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from actor-isolated context in call to instance method 'addCurried'}}
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}

let plusOne = await calc.addCurried(await calc.add(0, 1))
// expected-warning@-1{{non-sendable type '(Int) -> Int' returned by implicitly async call to actor-isolated function cannot cross actor boundary}}
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from actor-isolated context in call to instance method 'addCurried'}}
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
let _ = plusOne(2)
}
Expand Down
6 changes: 3 additions & 3 deletions test/Concurrency/actor_inout_isolation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ extension TestActor {
func passStateIntoDifferentClassMethod() async {
let other = NonAsyncClass()
let otherCurry = other.modifyOtherAsync
// 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}}
// expected-targeted-complete-tns-warning @-1 {{non-sendable type 'NonAsyncClass' cannot exit actor-isolated context in call to nonisolated instance method 'modifyOtherAsync'}}
await other.modifyOtherAsync(&value2)
// expected-error @-1 {{actor-isolated property 'value2' cannot be passed 'inout' to 'async' function call}}

Expand Down Expand Up @@ -288,11 +288,11 @@ actor ProtectArray {
func test() async {
// FIXME: this is invalid too!
_ = await array.mutateAsynchronously
// 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}}
// expected-targeted-complete-tns-warning@-1 {{non-sendable type '@lvalue [Int]' cannot exit actor-isolated context in call to nonisolated property 'mutateAsynchronously'}}

_ = await array[mutateAsynchronously: 0]
// expected-error@-1 {{actor-isolated property 'array' cannot be passed 'inout' to 'async' function call}}
// 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}}
// expected-targeted-complete-tns-warning@-2 {{non-sendable type 'inout Array<Int>' cannot exit actor-isolated context in call to nonisolated subscript 'subscript(mutateAsynchronously:)'}}

await passToAsync(array[0])

Expand Down
Loading