consider the following code that is adapted from some of the examples in SE-414 (region based isolation) and SE-430 (sending
parameters & results):
class NS {} // non-sendable type
actor A {
func makeValue() -> sending NS {
NS()
}
@MainActor
func sendToMain(_ value: NS) {}
func makeAndSend() async {
let v = makeValue()
await sendToMain(v)
// |- error: sending 'v' risks causing data races
// `- note: sending 'self'-isolated 'v' to main actor-isolated instance method 'sendToMain' risks causing data races between main actor-isolated and 'self'-isolated uses
}
}
my interpretation of the error messages produced here suggests that the local variable v
has been merged into the isolation region of the actor-instance A
. thus, per the wording in the RBI doc:
When a region R_1 is merged into another region R_2 that is isolated to an actor, R_1 becomes protected by that isolation domain and cannot be passed or accessed across isolation boundaries again.
it can no longer be transferred across isolation boundaries. okay, that seems consistent with the model. however, i don't really follow precisely why v
is treated as part of the actor's region in this case. the RBI doc states that the merging rules which i think apply here are:
- All regions of [f's non-Sendable arguments] are merged into one larger region after f executes.
- If any of [f's arguments] are non-Sendable and y is non-Sendable, then y is in the same merged region as [f's non-Sendable arguments]. If all of [f's arguments] are Sendable, then y is within a new disconnected region that consists only of y.
given my current understanding of the model, my best explanation of the behavior thus far is that since the method makeValue()
is actor-isolated, then it implicitly has an isolated self
parameter, and so upon return, the v
variable is merged into the self-actor's region. this seems to be the situation outlined by this portion of the RBI doc:
The above rules are conservative; without any further annotations, we must assume:
- In the implementation of f, any [argument] could become reachable from [any other argument].
- [the return value] could be one of the [argument] values or alias contents of [any argument].
this seems somewhat in tension with the wording about what is described to occur when all of a function's arguments are Sendable
, which presumably should be the case in this instance (either there are no arguments, or just the implicit self
argument which should be Sendable
). to reiterate the relevant passage from above:
If all of [f's arguments] are Sendable, then y is within a new disconnected region that consists only of y.
additionally, since the return value of makeValue()
in the example has been marked sending
, it should be in a disconnected region upon return. this suggests the possible explanations:
- there is a bug in the
sending
implementation and the return value is not actually disconnected upon return - there is a bug in the region merging logic and
v
is being spuriously merged into the self-actor's region - the system is working as intended
does this analysis seem accurate? any insights on this matter would be appreciated!