Question about the behaviour of `@isolated(any)`

@globalActor
actor MyActor {
    static let shared = MyActor()
}

@MyActor
func foo(cl: @isolated(any) @Sendable () async -> Void) async {
    let actor = #isolation
    print("inside foo: \(actor)")
    print("isolation: \(cl.isolation)")
    await cl()
}

@MainActor
func f() async {
    let main = #isolation
    print("inside f: \(main)")
    await foo {
        let act = #isolation
        print("inside cl: \(act)")
    }
}

await f()

The code above will print:
inside f: Optional(Swift.MainActor)
inside foo: Optional(playground.MyActor)
isolation: nil
inside cl: nil

I was expecting:
isolation: Optional(Swift.MainActor)
inside cl: Optional(Swift.MainActor)

but the code above, I got nil.
Am I misunderstanding the behaviour of @isolated(any)?

IIUC, the @Sendable annotation on the parameter effectively means the closure will be inferred to be nonisolated in this case. @isolated(any) doesn't affect inference rules, it just allows storing the inferred isolation within the function reference. if you want to get the 'inherit the surrounding actor context' behavior on a @Sendable or sending closure parameter, you'll need to resort to using the unformalized @_inheritActorContext attribute. this thread has some more info on the distinction between these attributes. if you add the underscored attribute, i think the isolation inference will be more like what you expect (godbolt example here).

5 Likes

so, as long as I use @Sendable on the closure. the closure will become nonisolated?
What about I use @Sendable on a function?

the attribute won't necessarily cause that behavior, but in the specific given example it will have that effect. if, for example, you wanted the closure passed as the cl parameter to be isolated to a particular global actor, then the inclusion of @Sendable on the closure parameter would no longer cause the parameter to be inferred as nonisolated, e.g.

@MainActor
func f() async {
    let main = #isolation
    print("inside f: \(main)")
    await foo { @MainActor in // closure param in `foo()` will now be main-actor-isolated
        let act = #isolation
        print("inside cl: \(act)")
    }
}

sorry, i'm not entirely clear what you're asking about precisely. what aspects of @Sendable are you wondering about?

so, If I add @MainActor in the closure, it will run on MainActor. Which I know it will. But without adding it, it will run on nonisolated by default, right?

@Sending on function:

@Sendable
func foo() {}