This is not a viable option - throws/async combinations already require 4 overloads, each attribute doubles that, and custom global actors defined by downstream users make it impossible. Moreover, you can't even have both @MainActor and @MainActor @Sendable overloads - the compiler considers them a redeclaration.
Discussion
Has there been any discussion about parameterizing over type attributes? Something like:
This seems like a natural next step after parameter packs solved the arity problem - type attributes are the remaining axis of the overload explosion. Curious if anyone has run into this or if there are existing proposals/pitches I missed.
Thanks for the pointer to @isolated(any) — but I don't think it addresses the original post.
@isolated(any) preserves isolation dynamically, but erases it statically and changes the call site:
// What a generic decorator preserving the attribute would give back:
let f: @MainActor (Int) -> T = wrap(g)
f(42) // sync, from a MainActor context
// What @isolated(any) gives back:
let f: @isolated(any) (Int) -> T = wrap(g)
await f(42) // async, even when both sides are on MainActor
That await is the regression the original post was trying to avoid — a generic wrapper silently turning a sync API into an async one.
@isolated(any) is the right tool when you genuinely don't know the actor and want correct executor enqueueing (Task.init is the canonical example), and the proposal solves that well. But that's a different problem from preserving a static attribute through a generic wrapper, and the gap is still open.