Actor Protocol requirement Concurrency Warning

Hey there!
I have a question in regards to actors that are conforming to certain protocols. Lets say I have an actor A that has a function that is returning a non-Sendable type, like this:

actor A {
    func doThingA() -> sending NonSendable {
        NonSendable()
    }
}

In addition to that, I have a class B that uses actor A (in my codebase the class is also sendable, but I think that does not make a difference right now):

class B {
    let a = A()

    func doSomethingWithA() async {
        let nonSendable = await a.doThingA()
    }
}

This is all fine and thanks to the sending keyword the NonSendable class can be sent across actor boundaries just fine (or at least the compiler does not complain about it).
But now the thing that keeps me puzzling. If I introduce a protocol for the doThingA() function and conform the actor A to it, the compiler suddenly tells me that Task or actor isolated value cannot be sent.
So this does not compile without warnings, and the only change is the protocol conformance:

protocol Proto: Actor {
    func doThingA() -> sending NonSendable
}

actor A: Proto {
    func doThingA() -> sending NonSendable { /* warning: Task or actor isolated value cannot be sent */
        NonSendable()
    }
}

Now I am wondered, whats the difference between both of those versions that one can compile without warnings and the other not? Or is this a false positive in region based isolation?
And if the warning is correct, is there a way to make that whole thing safe?

Thanks,
Basti

looking at the generated SIL (via godbolt), the only difference that stands out to me is that when A conforms to Proto, there is an additional method generated, presumably for resolving calls through the Proto interface to A's conformance of the requirement method[1]. it seems in both the 6.0 and 6.1 compilers this generates a region based isolation warning (or error, depending on the compiler configuration).

given that this issue no longer seems to occur on the main branch 6.2 builds, i suspect this is just an issue with RBI analysis in the earlier compilers. i skimmed recent-ish PRs in GitHub to see if there were any changes that would explain why this no longer happens on main, but couldn't find anything particularly obvious that stood out. i can't think of a reason why your code would actually cause problems because the returned value doesn't interact at all with the actor's isolated state. cc @hborla or @Michael_Gottesman – does that seem accurate to you?


  1. i think this is known as the 'protocol witness thunk', or something like that. ↩︎

Thanks for the answer. Looking forward to check this in an official release of 6.2.

Maybe also @ktoso knows wether this is an issue in the RBI analysis or not?