Possible Compiler Bug with Static Resolution of Overloaded Generic Functions

So, the logic that explains this behavior can be found here, and the comments should make it pretty readable even for anyone unfamiliar with the compiler code. When comparing two overloads (such as write in the call to logWriter.write(self)) the first check we do is to see whether one of the overloads is a generic declaration and the other is not:

  // A non-generic declaration is more specialized than a generic declaration.

This is why your func write(_ logEvent: @autoClosure () -> DefaultLogEvent) gets called instead of func write<T: LogEvent>(_ logEvent: @autoclosure () -> T).

The very next check we do is whether one or both of the overloads come from a protocol extension. If exactly one of the overloads is from a protocol extension:

    // One member is in a protocol extension, the other is in a concrete type.
    // Prefer the member in the concrete type.

So this is why func write<T: LogEvent>(_ logEvent: @autoclosure () -> T) is preferred over public func write<T: DefaultEvent>(_ logEvent: @autoclosure () -> T) (since the former is declared in the protocol body), even though the latter has more strict constraints on the generic parameter.

When you remove the declaration from the protocol body, then both declarations come from a protocol extension, and so the check no longer applies.

6 Likes