Compiler issue with Xcode 10.2 beta 4

Hello,

I recently downloaded the new Xcode 10.2 beta 4, and I'm having an issue with the following code:


func foo<A, B>(_ type: A.Type,
               arg: String? = nil,    
               _ fn: @escaping ((B))->A) {
  print("--> 2")
}

func foo<A, B, C>(_ type: A.Type,
                  arg: (String?, String)? = nil,
                  _ fn: @escaping ((B, C))->A) {
  print("--> 3")
}

func bar(arg1: String, arg2: Int) -> String {
  return "Bar"
}

foo(String.self, bar)

In this example, the last line won't compile, giving the error "Ambiguous use of 'foo'"

However, given the fact that bar's signature has two parameters, the compiler should determine that I want to use to second definition of

foo
, and print "--> 3".


NB: This example works properly with XCode 10.1. Is this an issue with the new beta?

Thanks

I'm taking a loose guess that these errors are w/swift and have been on~off again since at least v3.x.


Have you confirmed your work is swift 5 tight, via these release notes: https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_beta_4_release_notes/swift_5_release_notes_for_xcode_10_2_beta_4?language=objc

An iteresting change.


You are mistaking one thing:

given the fact that bar's signature has two parameters, the compiler should determine that I want to use to second definition of

foo
, and print "--> 3".

Your `bar` takes two parameters, but the function type of `fn` in your second foo does not.


In Swift (since SE-0066 Standardize function type argument syntax to require parentheses),


((B,C))->A is a function type taking a single argument of tuple type (B, C) and returning A

(B,C)->A is a function type taking two arguments of type A and type B and returning A


You can easily find the difference in a usual context:

You cannot assign your `bar` into a variable of type ((String, Int))->String.

var fn: ((String, Int))->String = bar //error: cannot convert value of type '(String, Int) -> String' to specified type '((String, Int)) -> String'

But you can into (String, Int)->String

var fn: (String, Int)->String = bar //compiles


A simple testing code:

func foo2<A, B>(_ type: A.Type,
               arg: String? = nil,
               _ fn: @escaping ((B))->A) {
    print("--> 2")
}

func foo3<A, B, C>(_ type: A.Type,
                  arg: (String?, String)? = nil,
                  _ fn: @escaping ((B, C))->A) {
    print("--> 3")
}

func foo2a<A, B>(_ type: A.Type,
                arg: String? = nil,
                _ fn: @escaping (B)->A) {
    print("--> 2a")
}

func foo3a<A, B, C>(_ type: A.Type,
                   arg: (String?, String)? = nil,
                   _ fn: @escaping (B, C)->A) {
    print("--> 3b")
}

foo2(String.self, bar) //10.1 error: cannot convert value of type '(String, Int) -> String' to expected argument type '((_)) -> _', compiles in Xcode 10.2
foo3(String.self, bar) //compiles both in 10.1 & 10.2
foo2a(String.self, bar) //compiles both in 10.1 & 10.2
foo3a(String.self, bar) //compiles both in 10.1 & 10.2

Only `foo2` shows different behaviors between 10.1 and 10.2.

Guessing from the error message, Swift 4.2 compiler (bundled with Xcode 10.1) seems to treat the function type `((B))->A` as:

a function type taking a single parameter of tuple type (B) and returning A.


But, as you know, there's no single element tuple in Swift. So, in my opinion, this is a bug of Swift 4.2.

With this bug, your `bar` is applicable only to `foo3`, so when you overload `foo` with `foo2` and `foo3`, the call cannot be ambiguous.


But in Swift 5 compiler, the bug is fixed and ((B))->A is treated as exactly the same as (B)->A. Your `bar` is applicable both to `foo2` and `foo3`.

So, overloading leads ambiguous call.


As you see, in some cases (though, not clearly documented), Swift converts a closure taking multiple parameters into a closure taking a single parameter of tuple type (or vice versa).

Please check SE-0110 Distinguish between single-tuple and multiple-argument function types, especially the linked article Additional Commentary.

Hello Ooper,
Thank you for your detailed answer.
My problem is that I don't really want to change the methods name, I would like to overload one method `foo` with different signatures. (i.e, in your example have the same name `foo` for `foo2a` and `foo3a`)
Indeed, ((B, C))->A is a method that takes a single argument, a tuple. I originally used (B, C)->A, but I changed it because of the following example:

func foo<a, b="">(_ type: A.Type,
                arg: String? = nil,
                 _ fn: @escaping ((B)) -> A) {
    print("--> 2")
}

func foo<a, b,="" c="">(_ type: A.Type,
                  arg: String? = nil,
                  _ fn: @escaping ((B, C)) -> A) {
    print("--> 3")
}

func fooBis<a, b="">(_ type: A.Type,
                  arg: String? = nil,
                  _ fn: @escaping ((B)) -> A) {
    print("--> 2")
}

func fooBis<a, b,="" c="">(_ type: A.Type,
                     arg: (String?, String?)? = nil,
                     _ fn: @escaping ((B, C)) -> A) {
    print("--> 3")
}

func bar(arg1: String, arg2: Int) -> String {
    return "Bar"
}

foo(String.self, bar) // Works properly
fooBis(String.self, bar) // Doesn't compile


I don't really understand why the call to `foo` works, but the one to `fooBis` doesn't compile (notice the difference in the `arg` parameter)

If I don't force using tuples in this example, as you mentioned, neither calls to `foo` nor `fooBis` compiles.

One more thing following what you said:
If I can write:

var fn: (String, Int) -> String = bar

But not

var fn: (Any) -> String = bar

Shouldn't it mean that `bar` signature can only be used with the second version of `foo`?
Thank you for having a look,
Manu

Shouldn't it mean that `bar` signature can only be used with the second version of `foo`?


No. Have you read the article I mentioned as Additional Commentary carefully? In older Swift, Swift converted a closure taking multiple parameters to a closure taking a single parameter of tuple type, and vice versa, in many cases. But that behavior was removed in some cases since SE-0110 was partially implemented.


You can write:

var fn: (String, Int)->String = bar //compiles

But not

var fn: ((String, Int))->String = bar


Don't you think this means your `bar` should not be used with the second version of `foo`, neither with the first version?


But it does work for your second, which means the conversion I noted above is working in this case.

If the conversion works here, `(B)->A` (and with the bug fixed Swift 5 compiler, it is exactly the same as `((B))->A`) can accept your `bar` with `B` intfered as tuple type `(String, Int)`.


My problem is that I don't really want to change the methods name, I would like to overload one method `foo` with different signatures.


My former comment has no intention to recommend you to rename your `foo`, but just illustrating that the signatures of overloaded functions need to be distinguishable with its actual arguments.


Your `bar` matches both `((B))->A` and `((B, C))->A`, so your `foo` is ambiguous. You need to write a distinguishable overloads.

All right, I get it, but I've been a little confused because of this:

func foo<A, B>(_ type: A.Type,
                arg: String? = nil,
                 _ fn: @escaping ((B)) -> A) {
    print("--> 2")
}

func foo<A, B, C>(_ type: A.Type,
                  arg: String? = nil,
                  _ fn: @escaping ((B, C)) -> A) {
    print("--> 3")
}

func bar(arg1: String, arg2: Int) -> String {
    return "Bar"
}

foo(String.self, bar) // Prints "---> 3"


Here, as you said, "my `bar` matches both `((B))->A` and `((B, C))->A`, so my `foo` is ambiguous"
So why does the call to `foo` prints "---> 3"?

You should better clarify the fact,

So why does the call to `foo` prints "---> 3"?


The call to `foo` prints "---> 3" in Xcode 10.1, right?

Because Swift 4.2 compiler has some bug treating a closure type like `((B)) -> A`, as I wrote in my first comment.

You should better not rely on a bug.

No, it does print 3 with Xcode 10.2 beta 4, swift 5.

All the code examples I gave above are compiled with Xcode 10.2 beta 4

Thanks for clarification.

So, your main concern now is...


This compiles and works expected in Xcode 10.2 beta 4:

func foo<A, B>(_ type: A.Type,
               arg: String? = nil,
               _ fn: @escaping (B) -> A) {
    print("--> 2")
}

func foo<A, B, C>(_ type: A.Type,
                  arg: String? = nil,
                  _ fn: @escaping ((B, C)) -> A) {
    print("--> 3")
}

But this does not, in the same version of Xcode:

func foo<A, B>(_ type: A.Type,
               arg: String? = nil,
               _ fn: @escaping ((B))->A) {
    print("--> 2", B.self)
}

func foo<A, B, C>(_ type: A.Type,
                  arg: (String?, Int)? = nil,
                  _ fn: @escaping ((B, C))->A) {
    print("--> 3")
}


What's the difference?


...right?

As I understand,

func foo<A, B>(_ type: A.Type,
               arg: String? = nil,
               _ fn: @escaping (B) -> A) {
    print("--> 2")
}

func foo<A, B, C>(_ type: A.Type,
                  arg: String? = nil,
                  _ fn: @escaping ((B, C)) -> A) {
    print("--> 3")
} 

These two methods are the same since (B,C) can be interpreted as a single argument, a tuple, so why isn't there any ambiguity?

And I would like to add a little modification to the second code example you gave above:
Why does the first one works, but not this one, with the same version of Xcode (10.2 beta 4)

func foo<A, B>(_ type: A.Type,
               arg: String? = nil,
               _ fn: @escaping (B) -> A) {
    print("--> 2")
}

func foo<A, B, C>(_ type: A.Type,
                  arg: (String?, String?)? = nil,
                  _ fn: @escaping ((B, C)) -> A) {
    print("--> 3")
}

, where the only difference is the definition of the arg parameter, for which I use the same default value (nil)

Thanks. But that is another issue than the bug of `((B))->A` in Xcode 10.1.


Even when multiple candidates found, some overloads can be resolved successfully:

func baz(_: B) {
    print("#1", B.self)
}
func baz<b,c>(_: (B,C)) {
    print("#2", B.self, C.self)
}
baz((1)) //->#1 Int
baz((1,2)) //->#2 Int Int
baz((1,2,3)) //->#1 (Int, Int, Int)

As the third example `baz((1,2,3))` is suggesting, `baz#1` can accept tuple type, so `baz((1,2))` can apply both to `baz#1` and `baz#2`.

But, Swift resolves successfully with applying `baz#2`.


This is because Swift can determine the specificity, `baz#2` is more specific than `baz#1`.

It is clear that a tuple type `(B, C)` is more specific than a type `B`.


This works in a little bit more complex arguments:

func baz(_: B, arg: String? = nil) {
    print("#1", B.self)
}
func baz<b,c>(_: (B,C), arg: String? = nil) {
    print("#2", B.self, C.self)
}
baz((1)) //->#1 Int
baz((1,2)) //->#2 Int Int
baz((1,2,3)) //->#1 (Int, Int, Int)


Seems `((B,C), String?)` is more specifc than `(B, String?)` for Swift. (<-fixed)


But, with a little more modification, Swift cannot determine which is more specific:

func baz(_: B, arg: String? = nil) {
    print("#1", B.self)
}
func baz<b,c>(_: (B,C), arg: (String?, Int)? = nil) {
    print("#2", B.self, C.self)
}
baz((1)) //->#1 Int
baz((1,2)) //error: ambiguous use of 'baz(_:arg:)'
baz((1,2,3)) //->#1 (Int, Int, Int)

Swift cannot detemine which is more specific, `(B, String?)` or `((B,C), (String?, Int)?)`.


The specificity comparison rule is not clearly documented, so this behavior would change at any time in the future, but anyway, overloads need to be clearly distinguishable or need to be specificity-ordered among the candidates, under the current implementation of specificity comparison.

Ok, I wasn't familiar with the specificity rule, I'll have a look.

Thanks for your answers!

Compiler issue with Xcode 10.2 beta 4
 
 
Q