Of course there isn't! There almost never is
I also think the tension is fundamental; actor isolation introduces another dimension for API authors to think about because there's a big semantic difference between the caller's context and the callee's context.
If you're introducing an API that can cross an isolation boundary when called, you need to think carefully about both actor isolation and Sendability of the arguments and the result value. If you want an argument value to always be evaluated in the caller's isolation, then you don't have to do anything and your default argument must always be nonisolated. If you want an argument value to always be evaluated in the callee's isolation, then the argument should be wrapped in a closure (and today that must be done explicitly due to the issues you mentioned with @autoclosure). There's also the possibility that you want the evaluation of an argument to be polymorphic over actor isolation. This is how the Task initializer behaves using @_inheritsActorContext with an async closure, and we'd need more sophisticated isolation polymorphism tools if we wanted to introduce a static func run method on the GlobalActor protocol (or a func run method on the Actor protocol) so that the given closure is isolated to self.
There is no straightforward answer. I think the most common use case will follow a pattern where the default argument and the explicit argument want the same isolation context. For example, if you have a family of @MainActor-isolated types that are composed together, an explicit argument to a @MainActor-isolated memberwise initializer is commonly some custom initialized MainActor-isolated type:
@MainActor struct MediaPlayer {
var state: PresentationState = PresentationState()
}
@MainActor struct PresentationState {
var currentlyPlaying: Video? = nil
}
// From off the MainActor
await MediaPlayer() // pretend this is allowed somehow
await MediaPlayer(state: .init(currentlyPlaying: /*existing Video here*/))
Basing the actor isolation of the argument on whether or not that argument is explicit doesn't help here; if you provide an explicit argument, you probably still want it to be evaluated in the initializer's isolation domain to avoid unnecessary actor hops.
I think the solution in the proposal right now privileges the case where the API is primarily called from within the same isolation domain. Using closures privileges the case where the API is primarily called from across isolation boundaries.
You're right about actor isolation, but not about Sendable; an argument that violates Sendable rules when captured in a closure will also violate Sendable rules when passed directly. However, wrapping an expressions in a closure can remove Sendable diagnostics in some cases; for example, if the expression is constructing a new non-Sendable value, delaying that evaluation until after you're already in the callee's isolation domain is totally fine. Evaluating it in the caller's context and then passing it across isolation boundaries is not.
EDIT: Sorry, you're not wrong about the Sendable implications. Of course, if your expression makes a call within the caller's isolation boundary that accepts non-Sendable arguments but returns a Sendable value, then that's okay to do directly but that would violate Sendable when capturing those arguments in a closure.
I was imagining that @MainActor @autoclosure () -> CGFloat only applies to the memberwise initializer. The same rules for using default stored property values in the proposal would apply for explicit initializers and the default initializer. Of course, this means that the memberwise initializer would not be able to initialize stored properties with conflicting actor isolation, but I think that's reasonable behavior.