# Region-based isolation, sending return value & actor isolation – what is the expected behavior?

**URL:** <https://forums.swift.org/t/region-based-isolation-sending-return-value-actor-isolation-what-is-the-expected-behavior/75455>\
**Category:** Using Swift\
**Tags:** concurrency, question, sending\
**Created:** [October 18, 2024, 12:54pm UTC](https://forums.swift.org/t/region-based-isolation-sending-return-value-actor-isolation-what-is-the-expected-behavior/75455 "2024-10-18T12:54:02Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [October 18, 2024, 12:54pm UTC](https://forums.swift.org/t/region-based-isolation-sending-return-value-actor-isolation-what-is-the-expected-behavior/75455/1 "2024-10-18T12:54:02Z")

</div>

consider the following code that is adapted from some of the examples in [SE-414](https://github.com/swiftlang/swift-evolution/blob/ce025340fb7c46bde7ef4c95fd1f6c4bd4dcb234/proposals/0414-region-based-isolation.md) (region based isolation) and [SE-430](https://github.com/swiftlang/swift-evolution/blob/fcf50dcf89e673b47dc099caded9fe4f1e5a35d4/proposals/0430-transferring-parameters-and-results.md) (`sending` parameters & results):

```swift
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](https://github.com/swiftlang/swift-evolution/blob/ce025340fb7c46bde7ef4c95fd1f6c4bd4dcb234/proposals/0414-region-based-isolation.md#transferring-values-and-isolation-regions) 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:

> 1. All regions of [f's non-Sendable arguments] are merged into one larger region after f executes.
> 2. 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](https://github.com/swiftlang/swift-evolution/blob/fcf50dcf89e673b47dc099caded9fe4f1e5a35d4/proposals/0430-transferring-parameters-and-results.md#sending-parameters-and-results) in a disconnected region upon return. this suggests the possible explanations:

1. there is a bug in the `sending` implementation and the return value is not actually disconnected upon return
2. there is a bug in the region merging logic and `v` is being spuriously merged into the self-actor's region
3. the system is working as intended

does this analysis seem accurate? any insights on this matter would be appreciated!

#### Motivation

- [motivating recent issue](https://github.com/swiftlang/swift/issues/77090)
- [prior similar issue](https://github.com/swiftlang/swift/issues/76100)

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [November 21, 2024, 1:02pm UTC](https://forums.swift.org/t/region-based-isolation-sending-return-value-actor-isolation-what-is-the-expected-behavior/75455/2 "2024-11-21T13:02:44Z")

</div>

i was thinking about this topic again while looking at [this issue](https://github.com/swiftlang/swift/issues/77679), and i've now distilled the crux of the question here a bit more:

> can an actor-isolated method actually return a `sending` value?

the compiler supports the function signature, but the current implementation of the RBI logic appears to imply that if you have an actor-isolated method that has a `sending` return value, it ends up being merged into the actor's region, so is effectively _not_ `sending`.

here is an adaptation of [an example](https://github.com/swiftlang/swift-evolution/blob/e273da0a1b7414fae9aaceb439715493de54a472/proposals/0430-transferring-parameters-and-results.md#sending-parameters-and-results) from the `sending` proposal illustrating the issue:

```swift
// compiled using swift 6 mode on a 6.1 development snapshot
class NonSendable {}

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

@MyGlobalActor
struct S {
  func getNonSendable() -> sending NonSendable {
    return NonSendable() // okay
  }
}

@MainActor
func onMain(_: NonSendable) {}

nonisolated func f(s: S) async {
  let ns = await s.getNonSendable() // okay; 'ns' is in a disconnected region

  await onMain(ns) // 'ns' can be sent away to the main actor
// |- error: sending 'ns' risks causing data races
// `- note: sending global actor 'MyGlobalActor'-isolated 'ns' to main actor-isolated global function 'onMain' risks causing data races between main actor-isolated and global actor 'MyGlobalActor'-isolated uses
}

```

by altering the `getNonSendable` function that returns the `sending` value from being main actor-isolated to being isolated to a different global actor, the compiler produces an error indicating that the return value is actually considered to be part of the global actor's region. the example from the proposal does compile, but it is presumably doing so because the actor isolation incidentally 'lines up' correctly.

@hborla and/or @Michael_Gottesman – as the resident experts on this functionality, can you shed any light on what the expected behavior here is if & when you have a moment? thanks in advance!

---

<div class="post-metadata">

**Author:** ![CrystDragon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/crystdragon/32/10461_2.png) [@CrystDragon](https://forums.swift.org/u/CrystDragon)\
**Post date:** [December 12, 2024, 3:57am UTC](https://forums.swift.org/t/region-based-isolation-sending-return-value-actor-isolation-what-is-the-expected-behavior/75455/3 "2024-12-12T03:57:24Z")

</div>

It seems there's no way to combine "actor" and "sending return". But I believe it is acceptable, because, in your example, there's no drawbacks to make `makeValue` nonisolated.

After all, if you want to return a sending value in a method of `actor A`, the value cannot be created with any non-sendable state comming from `A`, right?
