Skip to content

Commit 7477c88

Browse files
committed
[SE-0338] Diagnose Sendable when leaving an actor to call nonisolated async code
(cherry picked from commit 3faf8c3)
1 parent 7fe6cd8 commit 7477c88

File tree

5 files changed

+41
-11
lines changed

5 files changed

+41
-11
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4686,6 +4686,7 @@ ERROR(isolated_parameter_not_actor,none,
46864686

46874687
WARNING(non_sendable_param_type,none,
46884688
"non-sendable type %0 %select{passed in call to %4 %2 %3|"
4689+
"exiting %4 context in call to non-isolated %2 %3|"
46894690
"passed in implicitly asynchronous call to %4 %2 %3|"
46904691
"in parameter of %4 %2 %3 satisfying protocol requirement|"
46914692
"in parameter of %4 overriding %2 %3|"
@@ -4697,6 +4698,7 @@ WARNING(non_sendable_call_param_type,none,
46974698
(Type, bool, ActorIsolation))
46984699
WARNING(non_sendable_result_type,none,
46994700
"non-sendable type %0 returned by %select{call to %4 %2 %3|"
4701+
"call from %4 context to non-isolated %2 %3|"
47004702
"implicitly asynchronous call to %4 %2 %3|"
47014703
"%4 %2 %3 satisfying protocol requirement|"
47024704
"%4 overriding %2 %3|"
@@ -4709,6 +4711,7 @@ WARNING(non_sendable_call_result_type,none,
47094711
WARNING(non_sendable_property_type,none,
47104712
"non-sendable type %0 in %select{"
47114713
"%select{asynchronous access to %5 %1 %2|"
4714+
"asynchronous access from %5 context to non-isolated %1 %2|"
47124715
"implicitly asynchronous access to %5 %1 %2|"
47134716
"conformance of %5 %1 %2 to protocol requirement|"
47144717
"%5 overriding %1 %2|"

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,16 @@ bool swift::diagnoseNonSendableTypes(
913913

914914
bool swift::diagnoseNonSendableTypesInReference(
915915
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
916-
SendableCheckReason reason) {
916+
SendableCheckReason reason, Optional<ActorIsolation> knownIsolation) {
917+
918+
// Retrieve the actor isolation to use in diagnostics.
919+
auto getActorIsolation = [&] {
920+
if (knownIsolation)
921+
return *knownIsolation;
922+
923+
return swift::getActorIsolation(declRef.getDecl());
924+
};
925+
917926
// For functions, check the parameter and result types.
918927
SubstitutionMap subs = declRef.getSubstitutions();
919928
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
@@ -922,7 +931,7 @@ bool swift::diagnoseNonSendableTypesInReference(
922931
if (diagnoseNonSendableTypes(
923932
paramType, fromDC, loc, diag::non_sendable_param_type,
924933
(unsigned)reason, function->getDescriptiveKind(),
925-
function->getName(), getActorIsolation(function)))
934+
function->getName(), getActorIsolation()))
926935
return true;
927936
}
928937

@@ -932,7 +941,7 @@ bool swift::diagnoseNonSendableTypesInReference(
932941
if (diagnoseNonSendableTypes(
933942
resultType, fromDC, loc, diag::non_sendable_result_type,
934943
(unsigned)reason, func->getDescriptiveKind(), func->getName(),
935-
getActorIsolation(func)))
944+
getActorIsolation()))
936945
return true;
937946
}
938947

@@ -949,7 +958,7 @@ bool swift::diagnoseNonSendableTypesInReference(
949958
var->getDescriptiveKind(), var->getName(),
950959
var->isLocalCapture(),
951960
(unsigned)reason,
952-
getActorIsolation(var)))
961+
getActorIsolation()))
953962
return true;
954963
}
955964

@@ -959,7 +968,7 @@ bool swift::diagnoseNonSendableTypesInReference(
959968
if (diagnoseNonSendableTypes(
960969
paramType, fromDC, loc, diag::non_sendable_param_type,
961970
(unsigned)reason, subscript->getDescriptiveKind(),
962-
subscript->getName(), getActorIsolation(subscript)))
971+
subscript->getName(), getActorIsolation()))
963972
return true;
964973
}
965974

@@ -968,7 +977,7 @@ bool swift::diagnoseNonSendableTypesInReference(
968977
if (diagnoseNonSendableTypes(
969978
resultType, fromDC, loc, diag::non_sendable_result_type,
970979
(unsigned)reason, subscript->getDescriptiveKind(),
971-
subscript->getName(), getActorIsolation(subscript)))
980+
subscript->getName(), getActorIsolation()))
972981
return true;
973982

974983
return false;
@@ -2722,8 +2731,10 @@ namespace {
27222731
if (diagnoseReferenceToUnsafeGlobal(decl, loc))
27232732
return true;
27242733

2725-
// FIXME: SE-0338 would trigger Sendable checks here.
2726-
return false;
2734+
return diagnoseNonSendableTypesInReference(
2735+
declRef, getDeclContext(), loc,
2736+
SendableCheckReason::ExitingActor,
2737+
result.isolation);
27272738

27282739
case ActorReferenceResult::EntersActor:
27292740
// Handle all of the checking below.

lib/Sema/TypeCheckConcurrency.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ enum class SendableCheckReason {
7575
/// A reference to an actor from outside that actor.
7676
CrossActor,
7777

78+
/// Exiting an actor to non-isolated async code.
79+
ExitingActor,
80+
7881
/// A synchronous operation that was "promoted" to an asynchronous one
7982
/// because it was out of the actor's domain.
8083
SynchronousAsAsync,
@@ -271,7 +274,8 @@ struct ActorReferenceResult {
271274
/// \returns true if an problem was detected, false otherwise.
272275
bool diagnoseNonSendableTypesInReference(
273276
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
274-
SendableCheckReason refKind);
277+
SendableCheckReason refKind,
278+
Optional<ActorIsolation> knownIsolation = None);
275279

276280
/// Produce a diagnostic for a missing conformance to Sendable.
277281
void diagnoseMissingSendableConformance(

test/Concurrency/sendable_checking.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
@available(SwiftStdlib 5.1, *)
66
struct NS1 { }
7+
// expected-note@-1 2{{consider making struct 'NS1' conform to the 'Sendable' protocol}}
78

89
@available(SwiftStdlib 5.1, *)
910
@available(*, unavailable)
@@ -68,10 +69,21 @@ public protocol MyProto {
6869
func foo<F>(aFoo: F) async where F: Sendable
6970
}
7071

72+
@available(SwiftStdlib 5.1, *)
73+
func nonisolatedAsyncFunc1(_: NS1) async { }
74+
75+
@available(SwiftStdlib 5.1, *)
76+
func nonisolatedAsyncFunc2() async -> NS1 { NS1() }
77+
7178
@available(SwiftStdlib 5.1, *)
7279
public actor MyActor: MyProto {
7380
public func foo<F>(aFoo: F) async where F: Sendable { }
7481
public func bar<B>(aBar: B) async where B: Sendable { }
82+
83+
func g(ns1: NS1) async {
84+
await nonisolatedAsyncFunc1(ns1) // expected-warning{{non-sendable type 'NS1' exiting actor-isolated context in call to non-isolated global function 'nonisolatedAsyncFunc1' cannot cross actor boundary}}
85+
_ = await nonisolatedAsyncFunc2() // expected-warning{{non-sendable type 'NS1' returned by call from actor-isolated context to non-isolated global function 'nonisolatedAsyncFunc2()' cannot cross actor boundary}}
86+
}
7587
}
7688

7789
// rdar://82452688 - make sure sendable checking doesn't fire for a capture

test/Concurrency/unsafe_inherit_executor.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct A {
2121
}
2222

2323

24-
class NonSendableObject {
24+
class NonSendableObject { // expected-note{{class 'NonSendableObject' does not conform to the 'Sendable' protocol}}
2525
var property = 0
2626
}
2727

@@ -31,7 +31,7 @@ func useNonSendable(object: NonSendableObject) async {}
3131
actor MyActor {
3232
var object = NonSendableObject()
3333
func foo() async {
34-
// This should not be diagnosed when we implement SE-0338 checking.
34+
// expected-warning@+1{{non-sendable type 'NonSendableObject' exiting actor-isolated context in call to non-isolated global function 'useNonSendable(object:)' cannot cross actor boundary}}
3535
await useNonSendable(object: self.object)
3636
}
3737
}

0 commit comments

Comments
 (0)