# An unexpected \`Sendable\` pitfall

**URL:** <https://forums.swift.org/t/an-unexpected-sendable-pitfall/81012>\
**Category:** Using Swift\
**Created:** [July 9, 2025, 10:37am UTC](https://forums.swift.org/t/an-unexpected-sendable-pitfall/81012 "2025-07-09T10:37:39Z")\
**Posts on this page:** 4\
**Page:** 1

<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:** [July 9, 2025, 10:37am UTC](https://forums.swift.org/t/an-unexpected-sendable-pitfall/81012/1 "2025-07-09T10:37:39Z")

</div>

I just want to share a pitfall related to `Sendable`: currently any object can bypass `Sendable` checking if you want (without using any unsafe language features of course).

Normally, it is impossible to freely send non-`Sendable`s to another isolation domain:

```swift
class A {}

func foo(_ a: A) {
    Task { _ = a } // error
}

```

However, we can rely on the fact that `Sendable` is a marker protocol, and it does not have any runtime effects.

```swift
func runtimeCast<T, U>(_ t: T, _ u: U.Type) -> U? {
    t as? U
}

func transformed(_ a: A) -> (any Sendable)? {
    runtimeCast(a, (any Sendable).self) // always successfully
}

func bar(_ a: A) {
    guard let newA = transformed(a) else { return }
    Task { _ = newA } // this line is now possible, because newA is Sendable...
}

```

I learned this trick after reading [this discussion](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704/6) of `Sendable` casting checks related to isolated conformance.

---

<div class="post-metadata">

**Author:** ![Nickolas\_Pohilets](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nickolas_pohilets/32/10659_2.png) [@Nickolas\_Pohilets](https://forums.swift.org/u/Nickolas_Pohilets)\
**Post date:** [July 9, 2025, 5:00pm UTC](https://forums.swift.org/t/an-unexpected-sendable-pitfall/81012/2 "2025-07-09T17:00:48Z")

</div>

And to make things even more confusing, function types with and without `@Sendable` are differentiated in the runtime.

Which in turn means that value of `(@MainActor () -> Void).self` will differ depending on if code was compiled with [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) enabled or disabled. And if you mix code compiled with different compiler settings you can get very surprising runtime failures.

---

<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:** [July 12, 2025, 12:30pm UTC](https://forums.swift.org/t/an-unexpected-sendable-pitfall/81012/3 "2025-07-12T12:30:27Z")

</div>

why exactly does the runtime cast to Sendable succeed? do such casts work because the runtime has no knowledge of whether a type is or is not Sendable\[1\]?

per this comment from the referenced thread:

> [@SE-0470: Global-actor isolated conformances](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704/8):
>
> The prototype implementation does not implement it yet, but the [last paragraph on data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0470-isolated-conformances.md#data-race-safety-issues) in the proposal indicates that we will need to communicate the `Sendable`-ness of an existential type down to the runtime so it can refuse to use an isolated conformance for a cast to (e.g.)`any P & Sendable`.

is this 'just' something that remains to be implemented?

* * *

1. and presumably the casts should either always succeed or always fail in a deterministic manner if the true 'sendability' of the type cannot be recovered

---

<div class="post-metadata">

**Author:** ![Nickolas\_Pohilets](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nickolas_pohilets/32/10659_2.png) [@Nickolas\_Pohilets](https://forums.swift.org/u/Nickolas_Pohilets)\
**Post date:** [July 12, 2025, 3:46pm UTC](https://forums.swift.org/t/an-unexpected-sendable-pitfall/81012/4 "2025-07-12T15:46:07Z")

</div>

IIUC, there are no plans to implement this.

Down the thread @Douglas_Gregor mentions that he was thinking of a limited solution that wouldn't actually work in general case:

> [@SE-0470: Global-actor isolated conformances](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704/10):
>
> Oh, this is tricky. I was thinking of it as something we would do specifically for the casts themselves, because changing the identity of existential types (such as `any Sendable & P` is distinct from `any P` at runtime) can have far-ranging implications. But that means that my approach here _won't_ fix this existing issue with marker protocols.
