warning: non-sendable type 'NonSendable' exiting actor-isolated context
in call to non-isolated global function 'doSomethingAsyncWithNonSendable'
cannot cross actor boundary
is this a bug? or does @available(*, unavailable) actually mean something different from saying the type is merely not Sendable?
The extension marks the type as “explicitly non-Sendable”, so the compiler is more aggressive about telling you about it not being Sendable. Basically, it distinguishes between “we’ve looked at this type and it’s definitely not Sendable” and “we haven’t looked at this type so we don’t know if it’s Sendable”. In a 100% Swift 6 world, that difference won’t really matter anymore, but in a world full of Swift 5 code, it still does.
In settling upon a syntax for noncopyable types, it was contemplated that it should be generalizable to ~Sendable on the primary type declaration to suppress a presumption to the contrary. If we can do that, we shouldn’t need a macro to generate an unavailable extension in order to accomplish this.
Good point. However, is this message displayed at the point when a concurrency warning is displayed? Even if it were displayed, is it actionable information? If a type isn’t Sendable, the reason for that isn’t something that an end user can do anything about.
(Incidentally, types with reference semantics can nonetheless be Sendable, so that can’t be actually the full story.)
when i get a “Sendable” warning, my first thought is usually not “there is something wrong with this code”, my first thought is usually “whoever wrote this library (which is sometimes me) forgot to annotate this type with Sendable”. so it saves me time if i am told the lack of Sendable is intentional and not just a missing annotation.
but that’s all moot because (although i swear i’ve seen the message get printed before) i could not get it to show up on 5.8 when compiling a quick experiment that misuses the type. but maybe it should?
a MongoDB session is stateful, on both the client and server sides. even if it were made threadsafe on the client side, it would still be incorrect usage.
i thought about this some more and i think i finally understand why we can’t inline non-isolated stuff into isolated contexts, because when we declare a reference in an isolated context, it has the right to escape its original concurrency domain via assignment to shared actor state.
and the right to escape to the shared actor state is mutually exclusive with the right to escape to some external async task, because when the async call suspends, the actor doesn’t wait for it to return, it starts executing something else in its queue, and now session exists in two different concurrency timelines.
but if we declare a reference in a non-isolated context, it doesn’t have the right to escape to self, so it is fine to pass it to another async function.
let session:Mongo.Session = .init()
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ not allowed
i wonder if what we need is some kind of nonisolated do, like: