Skip to content

[Concurrency] @isolated(any) does not imply @Sendable. #73877

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 1 commit into from
May 24, 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
3 changes: 0 additions & 3 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3941,9 +3941,6 @@ NeverNullType TypeResolver::resolveASTFunctionType(
conventionAttr->getConventionName());
} else {
isolation = FunctionTypeIsolation::forErased();

// @isolated(any) implies @Sendable, unconditionally for now.
sendable = true;
}
break;
}
Expand Down
16 changes: 11 additions & 5 deletions test/Concurrency/isolated_any.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func testConvertIsolatedAnyToMainActor(fn: @Sendable @isolated(any) () -> ()) {
requireSendableGlobalActor(fn)
}

func extractFunctionIsolation(_ fn: @isolated(any) @escaping () async -> Void) {
func extractFunctionIsolation(_ fn: @isolated(any) @Sendable @escaping () async -> Void) {
let _: (any Actor)? = extractIsolation(fn)

let myActor = A()
Expand All @@ -85,8 +85,8 @@ func extractFunctionIsolation(_ fn: @isolated(any) @escaping () async -> Void) {
}

func extractFunctionIsolationExpr(
_ fn1: @isolated(any) @escaping () async -> Void,
_ fn2: @isolated(any) @escaping (Int, String) -> Bool
_ fn1: @isolated(any) @Sendable @escaping () async -> Void,
_ fn2: @isolated(any) @Sendable @escaping (Int, String) -> Bool
) {
let _: (any Actor)? = fn1.isolation
let _: (any Actor)? = fn2.isolation
Expand Down Expand Up @@ -118,9 +118,15 @@ func testFunctionIsolationExpr2(_ fn: Optional<(@isolated(any) () -> Void)>) ->
}

func testFunctionIsolationExprTuple(
_ fn1: (@isolated(any) () -> Void)?,
_ fn2: (@isolated(any) () -> Void)?
_ fn1: (@isolated(any) @Sendable () -> Void)?,
_ fn2: (@isolated(any) @Sendable () -> Void)?
) -> ((any Actor)?, (any Actor)?)
{
return (fn1?.isolation, fn2?.isolation)
}

func nonSendableIsolatedAny(
_ fn: @escaping @isolated(any) () -> Void // expected-note {{parameter 'fn' is implicitly non-sendable}}
) {
let _: @Sendable () -> Void = fn // expected-warning {{using non-sendable parameter 'fn' in a context expecting a @Sendable closure}}
}
2 changes: 1 addition & 1 deletion test/Distributed/distributed_actor_isolated_any.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import FakeDistributedActorSystems

typealias DefaultDistributedActorSystem = FakeActorSystem

func takeInheritingAsyncIsolatedAny(@_inheritActorContext fn: @escaping @isolated(any) () async -> ()) {}
func takeInheritingAsyncIsolatedAny(@_inheritActorContext fn: @escaping @isolated(any) @Sendable () async -> ()) {}

// CHECK-LABEL: sil hidden [distributed] [ossa] @$s4test2DAC0A20DistributedIsolationyyF
// CHECK: // function_ref closure #1
Expand Down
4 changes: 2 additions & 2 deletions test/IRGen/isolated_any_metadata.sil
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ sil_stage canonical
// CHECK-NEXT: ret ptr [[METADATA]]
sil @get_metadata : $() -> @thick Any.Type {
entry:
%type = metatype $@thick (@isolated(any) () -> ()).Type
%result = init_existential_metatype %type : $@thick (@isolated(any) () -> ()).Type, $@thick Any.Type
%type = metatype $@thick (@isolated(any) @Sendable () -> ()).Type
%result = init_existential_metatype %type : $@thick (@isolated(any) @Sendable () -> ()).Type, $@thick Any.Type
return %result : $@thick Any.Type
}

Expand Down
6 changes: 3 additions & 3 deletions test/ModuleInterface/isolated_any_suppression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
// CHECK: #if compiler(>=5.3) && $IsolatedAny
// CHECK-NEXT: {{^}}public func test1(fn: @isolated(any) @Sendable () -> ())
// CHECK-NEXT: #endif
public func test1(fn: @isolated(any) () -> ()) {}
public func test1(fn: @isolated(any) @Sendable () -> ()) {}

// CHECK-NEXT: #if compiler(>=5.3) && $IsolatedAny
// CHECK-NEXT: {{^}}public func test2(fn: @isolated(any) @Sendable () -> ())
// CHECK-NEXT: #endif
@_allowFeatureSuppression(XXX)
public func test2(fn: @isolated(any) () -> ()) {}
public func test2(fn: @isolated(any) @Sendable () -> ()) {}

// CHECK-NEXT: #if compiler(>=5.3) && $IsolatedAny
// CHECK-NEXT: {{^}}public func test3(fn: @isolated(any) @Sendable () -> ())
// CHECK-NEXT: #else
// CHECK-NEXT: {{^}}public func test3(fn: @Sendable () -> ())
// CHECK-NEXT: #endif
@_allowFeatureSuppression(IsolatedAny)
public func test3(fn: @isolated(any) () -> ()) {}
public func test3(fn: @isolated(any) @Sendable () -> ()) {}
28 changes: 14 additions & 14 deletions test/SILGen/isolated_any.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// CHECK-NEXT: end_borrow [[FN_BORROW2]]
// CHECK-NEXT: end_borrow [[FN_BORROW1]]
// CHECK-NEXT: hop_to_executor [[NIL_EXECUTOR]]
func callSync(fn: @isolated(any) () -> ()) async {
func callSync(fn: @isolated(any) @Sendable () -> ()) async {
await fn()
}

Expand All @@ -26,7 +26,7 @@ func callSync(fn: @isolated(any) () -> ()) async {
// CHECK-NEXT: apply [[FN_BORROW2]]()
// CHECK-NEXT: end_borrow [[FN_BORROW2]]
// CHECK-NEXT: hop_to_executor [[NIL_EXECUTOR]]
func callAsync(fn: @isolated(any) () async -> ()) async {
func callAsync(fn: @isolated(any) @Sendable () async -> ()) async {
await fn()
}

Expand All @@ -40,7 +40,7 @@ func callAsync(fn: @isolated(any) () async -> ()) async {
// CHECK-NEXT: [[THUNKED_FN:%.*]] = partial_apply [callee_guaranteed] [isolated_any] [[THUNK]]([[ISOLATION]], [[FN_COPY]])
// CHECK-NEXT: return [[THUNKED_FN]] : $@isolated(any) @Sendable @async @callee_guaranteed () -> ()
func convertFromNonIsolated(fn: @escaping @Sendable () async -> ())
-> @isolated(any) () async -> () {
-> @isolated(any) @Sendable () async -> () {
return fn
}

Expand Down Expand Up @@ -71,7 +71,7 @@ func convertFromNonIsolated(fn: @escaping @Sendable () async -> ())
// CHECK-NEXT: [[THUNKED_FN:%.*]] = partial_apply [callee_guaranteed] [isolated_any] [[THUNK]]([[ISOLATION]], [[FN_COPY]])
// CHECK-NEXT: return [[THUNKED_FN]] : $@isolated(any) @Sendable @async @callee_guaranteed () -> ()
func convertFromMainActor(fn: @escaping @Sendable @MainActor () async -> ())
-> @isolated(any) () async -> () {
-> @isolated(any) @Sendable () async -> () {
return fn
}

Expand All @@ -90,7 +90,7 @@ func convertFromMainActor(fn: @escaping @Sendable @MainActor () async -> ())
// CHECK-NEXT: [[THUNKED_FN:%.*]] = partial_apply [callee_guaranteed] [isolated_any] [[THUNK]]([[ISOLATION]], [[FN_COPY]])
// CHECK-NEXT: return [[THUNKED_FN]] : $@isolated(any) @Sendable @async @callee_guaranteed () -> Optional<Int>
func convertFromMainActorWithOtherChanges(fn: @escaping @Sendable @MainActor () async -> Int)
-> @isolated(any) () async -> Int? {
-> @isolated(any) @Sendable () async -> Int? {
return fn
}

Expand All @@ -107,7 +107,7 @@ func convertFromMainActorWithOtherChanges(fn: @escaping @Sendable @MainActor ()
// CHECK-NEXT: [[FN_COPY:%.*]] = copy_value %0 :
// CHECK-NEXT: [[FN_CONVERTED:%.*]] = convert_function [[FN_COPY]] : $@isolated(any) @Sendable @async @callee_guaranteed () -> () to $@Sendable @async @callee_guaranteed () -> ()
// CHECK-NEXT: return [[FN_CONVERTED]] :
func convertToNonIsolated(fn: @escaping @isolated(any) () async -> ())
func convertToNonIsolated(fn: @escaping @isolated(any) @Sendable () async -> ())
-> @Sendable () async -> () {
return fn
}
Expand All @@ -129,16 +129,16 @@ func convertToNonIsolated(fn: @escaping @isolated(any) () async -> ())
// CHECK-NEXT: [[SOME_INT:%.*]] = enum $Optional<Int>, #Optional.some!enumelt, [[INT]] : $Int
// CHECK-NEXT: return [[SOME_INT]] : $Optional<Int>

func convertToNonIsolatedWithOtherChanges(fn: @escaping @isolated(any) () async -> Int) -> @Sendable () async -> Int? {
func convertToNonIsolatedWithOtherChanges(fn: @escaping @isolated(any) @Sendable () async -> Int) -> @Sendable () async -> Int? {
return fn
}

/*-- Sync closures --*/

func syncAction() {}

func takeSyncIsolatedAny(fn: @escaping @isolated(any) () -> ()) {}
func takeInheritingSyncIsolatedAny(@_inheritActorContext fn: @escaping @isolated(any) () -> ()) {}
func takeSyncIsolatedAny(fn: @escaping @isolated(any) @Sendable () -> ()) {}
func takeInheritingSyncIsolatedAny(@_inheritActorContext fn: @escaping @isolated(any) @Sendable () -> ()) {}

// CHECK-LABEL: sil hidden [ossa] @$s4test0A27EraseSyncNonIsolatedClosureyyF
// CHECK: // function_ref closure #1
Expand Down Expand Up @@ -246,8 +246,8 @@ actor MyActor {

func asyncAction() async {}

func takeAsyncIsolatedAny(fn: @escaping @isolated(any) () async -> ()) {}
func takeInheritingAsyncIsolatedAny(@_inheritActorContext fn: @escaping @isolated(any) () async -> ()) {}
func takeAsyncIsolatedAny(fn: @escaping @isolated(any) @Sendable () async -> ()) {}
func takeInheritingAsyncIsolatedAny(@_inheritActorContext fn: @escaping @isolated(any) @Sendable () async -> ()) {}

// CHECK-LABEL: sil hidden [ossa] @$s4test0A28EraseAsyncNonIsolatedClosureyyF
// CHECK: // function_ref closure #1
Expand Down Expand Up @@ -383,7 +383,7 @@ extension MyActor {
func asyncAction() async {}
}

func takeInheritingOptionalAsyncIsolatedAny(@_inheritActorContext fn: Optional<@isolated(any) () async -> ()>) {}
func takeInheritingOptionalAsyncIsolatedAny(@_inheritActorContext fn: Optional<@isolated(any) @Sendable () async -> ()>) {}

// CHECK-LABEL: sil hidden [ossa] @$s4test7MyActorC0a20EraseInheritingAsyncC19ClosureIntoOptionalyyF
// CHECK: // function_ref closure #1
Expand All @@ -406,7 +406,7 @@ extension MyActor {
}
}

func takeInheritingAsyncIsolatedAny_optionalResult(@_inheritActorContext fn: @escaping @isolated(any) () async -> Int?) {}
func takeInheritingAsyncIsolatedAny_optionalResult(@_inheritActorContext fn: @escaping @isolated(any) @Sendable () async -> Int?) {}

// Test that we correctly handle isolation erasure from closures even when
// we can't completely apply the conversion as a peephole.
Expand Down Expand Up @@ -526,6 +526,6 @@ func testEraseAsyncActorIsolatedPartialApplication(a: MyActor) {
// CHECK-NEXT: end_borrow [[FN_BORROW]] : $@isolated(any) @Sendable @callee_guaranteed () -> ()
// CHECK-NEXT: destroy_value [[FN]] : $@isolated(any) @Sendable @callee_guaranteed () -> ()
// CHECK-NEXT: return [[RESULT]] : $Optional<any Actor>
func extractIsolation(fn: @escaping @isolated(any) () -> Void) -> (any Actor)? {
func extractIsolation(fn: @escaping @isolated(any) @Sendable () -> Void) -> (any Actor)? {
fn.isolation
}
2 changes: 1 addition & 1 deletion test/SILGen/isolated_any_conformance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// introduced a protocol for different task group types.

struct A<T> {
func enqueue(operation: @escaping @isolated(any) () async -> T) {}
func enqueue(operation: @escaping @isolated(any) @Sendable () async -> T) {}
}

protocol Enqueuer {
Expand Down
2 changes: 1 addition & 1 deletion test/decl/protocol/conforms/isolated_any.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ protocol AnnotatedEnqueuer {
associatedtype Result

// expected-note @+1 {{protocol requires function}}
func enqueue(operation: @escaping @isolated(any) () async -> Result)
func enqueue(operation: @escaping @isolated(any) @Sendable () async -> Result)
}

// expected-error @+1 {{type 'A<T>' does not conform to protocol 'AnnotatedEnqueuer'}}
Expand Down