Issue with Type Inference from default parameters

Since SE-0347 the following will compile as expected:

func compute<C: Collection>(_ values: C = [0, 1, 2]) { ... } 

So, the compiler can successfully infer the type of a generic from a default value expression for the function parameter.

Now I encountered an issue in a more complex case which involves associated type inference where the compiler fails to infer the type and emits an error:

Given a protocol which is later used as a type constraint for that function parameter:

protocol Subject<Value> {
    associatedtype Value
    func send(_ value: Value)
}

and another protocol P with an associated type and a static function where the above protocol is used as the parameter:

protocol P {
    associatedtype V
    
    static func foo<S: Subject<V>>(subject: S)
}

Now I want to have a default implementation of the above static function which also sports a default parameter:

struct DefaultSubject<Value>: Subject {
    func send(_ value: Value) {}
}

extension P {
    static func foo<S: Subject<V>>(subject: S = DefaultSubject<V>()) {}
}

Now, I want to define an Enum type for my use case:

enum E: P {
    typealias V = Int
}

So far so good. The compiler is happy.

But when I try to use it:

func test() async throws {    
    E.foo(subject: DefaultSubject()) // fine!
    E.foo()                          // Error: Generic parameter 'S' could not be inferred
}

The compiler cannot infer the type in the second line!

My understanding is, when the compiler applies the default parameter it would try something like this:

    let defaultParam = DefaultSubject<E.V>()
    E.foo(subject: defaultParam)
}

which does compile.

It's also my understanding (I could be wrong, though) that for the compiler everything needed to figure out the types should be there.

So, what's going on?
Do I miss something?
How can I fix it, or what would be a possible workaround?

Copy&Paste Test
import Testing

protocol Subject<Value> {
    associatedtype Value
    func send(_ value: Value)
}

protocol P {
    associatedtype V
    
    static func foo<S: Subject<V>>(subject: S)
}

struct DefaultSubject<Value>: Subject {
    func send(_ value: Value) {}
}

extension P {
    static func foo<S: Subject<V>>(subject: S = DefaultSubject<V>()) {}
}

enum E: P {
    typealias V = Int
}

@Test func test() async throws {
    
    E.foo(subject: DefaultSubject()) // fine!
    E.foo() // Generic parameter 'S' could not be inferred
    
    let defaultParam = DefaultSubject<E.V>()
    E.foo(subject: defaultParam)
}
1 Like

Default arguments are just shorthand for overloads.

extension P {
  static func foo(subject: some Subject<V>) { }
  static func foo() { foo(subject: DefaultSubject()) }
}

Interestingly, not only does this not work at all, but it makes you be explicit about DefaultSubject.Value.

extension P {
  static func foo(subject: some Subject<V> = DefaultSubject<V>()) { }
}

That's not necessary on a concrete type, where it does work.

extension E {
  static func foo(subject: some Subject<V> = DefaultSubject()) { }
}

Thank you Danny. I agree with your observation, that using a default expression does not work at all in this scenario.

Currently, I'm working around this issue with using an overload as you suggested. But it becomes quickly cumbersome to define all combinations when you have two or more default expressions, not to mention that the overload should have a documentation as well, resulting in a lot of "boiler plate" with potentially increased maintenance efforts.

1 Like