Skip to content

Commit 2dbffa2

Browse files
authored
Merge pull request swiftlang#78479 from gottesmm/pr-fadbda958eb1024880fd290cdb1b5b17ddc46b99
[rbi] Ensure that we infer isolation correctly for direct sending results
2 parents dd0e419 + c061ee7 commit 2dbffa2

File tree

3 files changed

+92
-2
lines changed

3 files changed

+92
-2
lines changed

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,15 @@ inferIsolationInfoForTempAllocStack(AllocStackInst *asi) {
358358

359359
SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
360360
if (auto fas = FullApplySite::isa(inst)) {
361+
// Before we do anything, see if we have a sending result. In such a case,
362+
// our full apply site result must be disconnected.
363+
//
364+
// NOTE: This handles the direct case of a sending result. The indirect case
365+
// is handled above.
366+
if (fas.getSubstCalleeType()->hasSendingResult())
367+
return SILIsolationInfo::getDisconnected(
368+
false /*is unsafe non isolated*/);
369+
361370
// Check if we have SIL based "faked" isolation crossings that we use for
362371
// testing purposes.
363372
//

test/Concurrency/transfernonsendable.sil

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,21 @@ import _Concurrency
2121

2222
class Klass {}
2323

24-
class NonSendableKlass { // expected-note 2{{}}
24+
class NonSendableKlass { // expected-note 4{{}}
2525
var klass: Klass
2626

2727
func asyncCall() async
28+
29+
@MainActor static func getValue() -> sending NonSendableKlass
2830
}
2931

3032
sil @transferNonSendableKlass : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
3133
sil @useNonSendableKlass : $@convention(thin) (@guaranteed NonSendableKlass) -> ()
34+
sil @useNonSendableKlassAsync : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
3235
sil @constructNonSendableKlass : $@convention(thin) () -> @owned NonSendableKlass
3336
sil @useUnmanagedNonSendableKlass : $@convention(thin) (@guaranteed @sil_unmanaged NonSendableKlass) -> ()
37+
sil @constructNonSendableKlassAsync : $@convention(thin) @async () -> @owned NonSendableKlass
38+
sil @constructNonSendableKlassAsyncSending : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
3439

3540
final class SendableKlass : Sendable {}
3641

@@ -376,11 +381,60 @@ bb0(%0 : @guaranteed $Self):
376381
return %9999 : $()
377382
}
378383

384+
sil [ossa] @sending_direct_result_from_callee : $@convention(thin) @async () -> () {
385+
bb0:
386+
%0 = function_ref @constructNonSendableKlassAsyncSending : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
387+
%1 = apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %0() : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
388+
389+
%useValue = function_ref @useNonSendableKlassAsync : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
390+
apply [caller_isolation=nonisolated] [callee_isolation=actor_instance] %useValue(%1) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
391+
destroy_value %1
392+
393+
// Double check we actually error without @sil_sending
394+
%2 = function_ref @constructNonSendableKlassAsync : $@convention(thin) @async () -> @owned NonSendableKlass
395+
%3 = apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %2() : $@convention(thin) @async () -> @owned NonSendableKlass
396+
// expected-warning @-1 {{non-Sendable 'NonSendableKlass'-typed result can not be returned from global actor '<null>'-isolated function to nonisolated context}}
397+
398+
apply [caller_isolation=nonisolated] [callee_isolation=actor_instance] %useValue(%3) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
399+
// expected-warning @-1 {{sending value of non-Sendable type 'NonSendableKlass' risks causing data races}}
400+
// expected-note @-2 {{}}
401+
402+
destroy_value %3
403+
404+
%9999 = tuple ()
405+
return %9999 : $()
406+
}
407+
408+
sil [ossa] @sending_direct_result_from_callee_2 : $@convention(thin) @async () -> () {
409+
bb0:
410+
%0 = function_ref @constructNonSendableKlassAsyncSending : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
411+
%1 = apply [caller_isolation=global_actor] [callee_isolation=global_actor] %0() : $@convention(thin) @async () -> @sil_sending @owned NonSendableKlass
412+
413+
%useValue = function_ref @useNonSendableKlassAsync : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
414+
apply [caller_isolation=global_actor] [callee_isolation=actor_instance] %useValue(%1) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
415+
destroy_value %1
416+
417+
// Double check we actually error without @sil_sending
418+
%2 = function_ref @constructNonSendableKlassAsync : $@convention(thin) @async () -> @owned NonSendableKlass
419+
%3 = apply [caller_isolation=global_actor] [callee_isolation=global_actor] %2() : $@convention(thin) @async () -> @owned NonSendableKlass
420+
// expected-warning @-1 {{non-Sendable 'NonSendableKlass'-typed result can not be returned from global actor '<null>'-isolated function to global actor '<null>'-isolated context}}
421+
422+
apply [caller_isolation=global_actor] [callee_isolation=actor_instance] %useValue(%3) : $@convention(thin) @async (@guaranteed NonSendableKlass) -> ()
423+
// expected-warning @-1 {{sending value of non-Sendable type 'NonSendableKlass' risks causing data races}}
424+
// expected-note @-2 {{}}
425+
426+
destroy_value %3
427+
428+
%9999 = tuple ()
429+
return %9999 : $()
430+
}
431+
432+
379433
sil @closureForCheckedContinuation : $@convention(thin) <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()
380434
sil @withCheckedContinuation : $@convention(thin) @async <τ_0_0> (@sil_isolated @guaranteed Optional<any Actor>, @guaranteed @noescape @callee_guaranteed <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()) -> @sil_sending @out τ_0_0
381435

382436
// We shouldn't emit any error here due to sil_sending.
383-
sil [ossa] @sending_result_from_callee : $@convention(thin) @async () -> () {
437+
sil [ossa] @sending_indirect_result_from_callee : $@convention(thin) @async () -> () {
384438
bb0:
385439
%0 = alloc_stack $NonSendableKlass
386440
%1 = function_ref @closureForCheckedContinuation : $@convention(thin) <τ_0_0> (@in_guaranteed CheckedContinuation<τ_0_0, Never>) -> ()

test/Concurrency/transfernonsendable.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,3 +1906,30 @@ func offByOneWithImplicitPartialApply() {
19061906
}
19071907
}
19081908
}
1909+
1910+
// We should not error in either of the cases below due to sending.
1911+
func testIndirectAndDirectSendingResultsWithGlobalActor() async {
1912+
@MainActor
1913+
struct S {
1914+
let ns = NonSendableKlass()
1915+
1916+
func getNonSendableKlassIndirect<T>() -> sending T {
1917+
fatalError()
1918+
}
1919+
func getNonSendableKlassDirect() -> sending NonSendableKlass {
1920+
fatalError()
1921+
}
1922+
}
1923+
1924+
let s = await S()
1925+
let ns: NonSendableKlass = await s.getNonSendableKlassDirect()
1926+
1927+
Task.detached {
1928+
_ = ns
1929+
}
1930+
1931+
let ns2: NonSendableKlass = await s.getNonSendableKlassIndirect()
1932+
Task.detached {
1933+
_ = ns2
1934+
}
1935+
}

0 commit comments

Comments
 (0)