Swift resolution order

Hey everyone!
I've got a question for a situation related to this : Concerned about this protocol extension/conformance bug

To set the scene - normally we cannot have a property and a function with the same name (or two properties with the same name), but from above post we know that it's possible to have this via a "clever" use of protocols and protocol extensions.

So now let's take this code :

protocol P1 {
    func foo()
}

extension P1 {
    func foo() {
        print("P1.foo")
    }
}

protocol P2 {
    var foo: () -> Void { get }
}

extension P2 {
    var foo: () -> Void {
        {
            print("P2.foo")
        }
    }
}

struct Overloaded: P1, P2 {
    var foo: Int = 0
}

let overLoaded = Overloaded()
overLoaded.foo     // #1
overLoaded.foo()   // #2

foo is three things :

  • a property on Overloaded
  • a function on P1
  • a closure on P2

So while behaviour for #1 is obvious - we directly access the property on Overloaded struct - I was surprised that #2 didn't throw an "Ambiguous use of 'foo'" kind of error.

In my tests it always called the closure on P2 resulting in printing "P2.foo".

I know I can force P1.foo to be called by doing (overLoaded as P1).foo().

My question is - is this behaviour guaranteed and documented? Will Swift always choose a property / getter instead of a func when both are available?

1 Like

It could also be an unapplied method reference for P1, or a call to the getter on P2. This one is actually the most ambiguous of the three, barring type context.

1 Like

Checked one more thing that seems to confirm Swifts preference for using a variable first instead of a function.

If we modify P2 to have a property of a @dynamicCallable / callAsFunction type it also uses it over func P1.foo()

Full code :

struct Callable {
    func callAsFunction() {
        print("Callable.callAsFunction")
    }
}

protocol P1 {
    func foo()
}

extension P1 {
    func foo() {
        print("P1.foo")
    }
}

protocol P2 {
    var foo: Callable { get }
}

extension P2 {
    var foo: Callable { .init() }
}

struct Overloaded: P1, P2 {
    var foo: Int = 0
}

let overLoaded = Overloaded()
overLoaded.foo()    // P2.foo.callAsFunction() called here

Still - very much interested in getting confirmation that this is indeed guaranteed behaviour.

1 Like