Incorrect overload behavior compiles invalid code

I have this extension:

public extension Drawable {
    @_disfavoredOverload func flatten() -> Image { .init(self) }
    func flatten<T: PrimitiveDrawable>() -> T { .init(self) }
}

where

/// A standalone `Drawable` representation which can be flattened into.
public protocol PrimitiveDrawable: Drawable {
    init(_ drawable: some Drawable)
}

fileprivate struct PanicDrawable: PrimitiveDrawable {
    public init(_ drawable: some Drawable) { fatalError() }
    public var width: Int { fatalError() }
    public var height: Int { fatalError() }
    public subscript(x: Int, y: Int) -> Color { fatalError() }
}

fileprivate func test() {
    let x = Rectangle(width: 10, height: 10).flatten<PanicDrawable>()
}

What I find odd is that while flatten can infer the result type if I add an explicit annotation to x, it completely ignores (yet allows specifying) the generics passed to flatten

In fact, this completely wrong expression works:

let x: Image = Rectangle(width: 10, height: 10).flatten<PanicDrawable>()

And it's result is an Image, yet the LSP shows the method signature to be the generic one, which according to the parameter should return a completely different type.


The intention was to have a default overload of flatten in case no type can be inferred, as Image is the most primitive Drawable and makes sense as the default — being a simple 2d color array.


edit:

Invalid syntax compiling aside, it is seemingly possible to achieve what I was after by adding a third overload:

func flatten<T>(into type: T.Type) -> T where T: PrimitiveDrawable { .init(self) }
1 Like

Huh, from a quick godbolt check it looks like we stopped diagnosing attempts to specialize non-generic functions back in Swift 5.8 and just silently allowed this syntax. Mind filing an issue?

2 Likes