[Pitch] Dynamic actor isolation enforcement

I mentioned this back in the previous thread about priorities for Swift 6:

It doesn't appear to have gotten any better. The example I gave back then, calling one @MainActor function from another:

@MainActor
func funcOne() async -> Int {
  funcTwo()
}

@MainActor
func funcTwo() -> Int {
  return 42
}

If I look at the Godbolt output, funcOne does a bunch of runtime calls to get MainActor.shared.unownedExecutor, and enqueues its continuation on it. That continuation does the same sequence of runtime calls to get MainActor.shared.unownedExecutor again, then calls swift_task_isCurrentExecutor to check that it has the correct isolation -- even though it's a hidden continuation that was already enqueued on that executor. And there's a bunch of retain/release traffic along the way.

In fact, if you make funcOne synchronous, the compiler will actually check the current executor twice, even though there are no suspension points between the checks.

So the implementation is in a fairly disappointing state right now. I really hoped that language integrated concurrency meant we wouldn't need these kinds of runtime checks everywhere. I still hope that things will improve because IMHO it's not ready to be enabled by default as things stand.

And if there are some source-breaking changes needed to eliminate more of these checks statically, I think it would be good to disclose and discuss them as part of the journey to Swift 6.

4 Likes