Skip to content

Commit 558a455

Browse files
committed
[SE-0338] Diagnose Sendable when leaving an actor to call nonisolated async code
1 parent 0392f37 commit 558a455

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
@@ -4657,6 +4657,7 @@ ERROR(isolated_parameter_not_actor,none,
46574657

46584658
WARNING(non_sendable_param_type,none,
46594659
"non-sendable type %0 %select{passed in call to %4 %2 %3|"
4660+
"exiting %4 context in call to non-isolated %2 %3|"
46604661
"passed in implicitly asynchronous call to %4 %2 %3|"
46614662
"in parameter of %4 %2 %3 satisfying protocol requirement|"
46624663
"in parameter of %4 overriding %2 %3|"
@@ -4668,6 +4669,7 @@ WARNING(non_sendable_call_param_type,none,
46684669
(Type, bool, ActorIsolation))
46694670
WARNING(non_sendable_result_type,none,
46704671
"non-sendable type %0 returned by %select{call to %4 %2 %3|"
4672+
"call from %4 context to non-isolated %2 %3|"
46714673
"implicitly asynchronous call to %4 %2 %3|"
46724674
"%4 %2 %3 satisfying protocol requirement|"
46734675
"%4 overriding %2 %3|"
@@ -4680,6 +4682,7 @@ WARNING(non_sendable_call_result_type,none,
46804682
WARNING(non_sendable_property_type,none,
46814683
"non-sendable type %0 in %select{"
46824684
"%select{asynchronous access to %5 %1 %2|"
4685+
"asynchronous access from %5 context to non-isolated %1 %2|"
46834686
"implicitly asynchronous access to %5 %1 %2|"
46844687
"conformance of %5 %1 %2 to protocol requirement|"
46854688
"%5 overriding %1 %2|"

lib/Sema/TypeCheckConcurrency.cpp

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

935935
bool swift::diagnoseNonSendableTypesInReference(
936936
ConcreteDeclRef declRef, const DeclContext *fromDC, SourceLoc loc,
937-
SendableCheckReason reason) {
937+
SendableCheckReason reason, Optional<ActorIsolation> knownIsolation) {
938+
939+
// Retrieve the actor isolation to use in diagnostics.
940+
auto getActorIsolation = [&] {
941+
if (knownIsolation)
942+
return *knownIsolation;
943+
944+
return swift::getActorIsolation(declRef.getDecl());
945+
};
946+
938947
// For functions, check the parameter and result types.
939948
SubstitutionMap subs = declRef.getSubstitutions();
940949
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
@@ -943,7 +952,7 @@ bool swift::diagnoseNonSendableTypesInReference(
943952
if (diagnoseNonSendableTypes(
944953
paramType, fromDC, loc, diag::non_sendable_param_type,
945954
(unsigned)reason, function->getDescriptiveKind(),
946-
function->getName(), getActorIsolation(function)))
955+
function->getName(), getActorIsolation()))
947956
return true;
948957
}
949958

@@ -953,7 +962,7 @@ bool swift::diagnoseNonSendableTypesInReference(
953962
if (diagnoseNonSendableTypes(
954963
resultType, fromDC, loc, diag::non_sendable_result_type,
955964
(unsigned)reason, func->getDescriptiveKind(), func->getName(),
956-
getActorIsolation(func)))
965+
getActorIsolation()))
957966
return true;
958967
}
959968

@@ -970,7 +979,7 @@ bool swift::diagnoseNonSendableTypesInReference(
970979
var->getDescriptiveKind(), var->getName(),
971980
var->isLocalCapture(),
972981
(unsigned)reason,
973-
getActorIsolation(var)))
982+
getActorIsolation()))
974983
return true;
975984
}
976985

@@ -980,7 +989,7 @@ bool swift::diagnoseNonSendableTypesInReference(
980989
if (diagnoseNonSendableTypes(
981990
paramType, fromDC, loc, diag::non_sendable_param_type,
982991
(unsigned)reason, subscript->getDescriptiveKind(),
983-
subscript->getName(), getActorIsolation(subscript)))
992+
subscript->getName(), getActorIsolation()))
984993
return true;
985994
}
986995

@@ -989,7 +998,7 @@ bool swift::diagnoseNonSendableTypesInReference(
989998
if (diagnoseNonSendableTypes(
990999
resultType, fromDC, loc, diag::non_sendable_result_type,
9911000
(unsigned)reason, subscript->getDescriptiveKind(),
992-
subscript->getName(), getActorIsolation(subscript)))
1001+
subscript->getName(), getActorIsolation()))
9931002
return true;
9941003

9951004
return false;
@@ -2772,8 +2781,10 @@ namespace {
27722781
if (diagnoseReferenceToUnsafeGlobal(decl, loc))
27732782
return true;
27742783

2775-
// FIXME: SE-0338 would trigger Sendable checks here.
2776-
return false;
2784+
return diagnoseNonSendableTypesInReference(
2785+
declRef, getDeclContext(), loc,
2786+
SendableCheckReason::ExitingActor,
2787+
result.isolation);
27772788

27782789
case ActorReferenceResult::EntersActor:
27792790
// 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)