Limitations of type resolution in default parameter values

Can someone clarify what limitations are at play giving rise to these unexpected behaviours:
Consider:

extension View {
    func `if`<V>(
        `let` value: @autoclosure () -> V?,
        then ifTrue: (Self, V) -> some View,
        else ifFalse: (Self) -> some View = { $0 } // Cannot infer type of closure parameter '$0' without a type annotation
    )
    -> AnyView {
        if let value = value() {
            return AnyView(ifTrue(self, value))
        }
        else {
            return AnyView(ifFalse(self))
        }
    }
}

This error is lifted by explicitly declaring the type of $0 as Self:

        else ifFalse: (Self) -> some View = { (v: Self) in v }

Why is the compiler unable to make this inference from the parameter's function signature? It seems to me that if left undeclared, $0 should default to the type declaration from the signature?

Furthermore, when we use this utility:

struct ContentView: View {
    @State var text: String?
    var body: some View {
        Color.clear
            .if(let: self.text) {
                $0.overlay(Text($1))
            } else: {
                $0.overlay(Text("Empty"))
            }
    }
}

Things work fine, but if we comment out the else closure, falling back to its default value, suddenly Swift is unable to infer the ifFalse closure return value:

Generic parameter 'some View' could not be inferred

Try as hard as I may, I cannot get the compiler to derive the type successfully from the function signature or default value declaration:

    func `if`<V, X: View>(`let` value: @autoclosure () -> V?,
                 then ifTrue: (Self, V) -> some View,
                 else ifFalse: (Self) -> X = { (v: Self) -> AnyView in AnyView(v) } as ((Self) -> AnyView)
    )
    -> AnyView {

Why is the swift compiler refusing to accept the default value's declaration as a source of inference for its generic type parameter?

The compiler does just fine with things like this:

        @ViewBuilder control: @escaping () -> (some View)? = { nil as EmptyView? }

It seems that somehow the presence of the closure parameter is breaking the return value inference.

Are we seeing limitations in the implementation that should be considered bugs?
(Type inference for default value of closures that take parameters · Issue #68910 · apple/swift · GitHub)

1 Like