Swift 3.0: erro do compilador ao chamar func min global (T, T) na extensão Array ou Dictionary

Após a conversão do Swift 2.2 para 3.0 minha extensão Array não compila mais, porque contém uma chamada para a function de biblioteca padrão global min(T,T) e mostra extra argument in call erro do compilador extra argument in call .

Aqui está uma maneira simples de reproduzir o erro:

 extension Array { func smallestInt(first: Int, second: Int) -> Int { return min(first, second) // compiler error: "Extra argument in call" } } 

Eu recebo o mesmo erro ao adicionar a mesma function a uma extensão do Dictionary , enquanto o mesmo código compila muito bem em uma extensão de outros tipos (por exemplo, String ou AudioBuffer ):

erros do compilador nas extensões Array e Dictionary

Olhando para a documentação do Array and Dictionary , eu acho que existem methods de instância em Sequence nomeados public func min() -> Element? e public func min(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element? . Enquanto ambos String e AudioBuffer não possuem nenhum tipo de function min(...) .

É possível que essa seja a razão pela qual não posso chamar a function global? O compilador não pode distinguir entre func min(T,T) global func min(T,T) e self.min(...) embora tenham assinaturas completamente diferentes?

isso é um erro ou uma característica? O que estou fazendo de errado? Como posso chamar min(T,T) corretamente dentro de uma extensão Array ?

Não vejo razão para que o compilador não consiga resolver essa chamada de function, portanto, eu consideraria um bug (ela já foi preenchida – consulte SR-2450 ).

Parece ocorrer sempre que se tenta chamar uma function de nível superior com o mesmo nome, mas assinatura inequivocamente diferente para um método ou propriedade acessível a partir do mesmo escopo em um determinado tipo (instância ou estática).

Um exemplo ainda mais simples seria:

 func foo(_ a: Int) {} struct Foo { func foo() {} // or static func foo() {}, var foo = 0, static var foo = 0 func bar() { foo(2) // error: argument passed to call that takes no arguments } } 

Até ser corrigido, uma solução simples seria prefixar a chamada com o nome do módulo no qual ela reside, a fim de desambiguar que você está se referindo à function de nível superior, em vez da instância um. Para a biblioteca padrão, isso é Swift :

 extension Array { func smallestInt(first: Int, second: Int) -> Int { return Swift.min(first, second) } } 

No Swift 4, o compilador tem um diagnóstico melhor para esse erro (embora o fato de ainda ser um erro seja um bug do IMO):

 extension Array { func smallestInt(first: Int, second: Int) -> Int { // Use of 'min' refers to instance method 'min(by:)' // rather than global function 'min' in module 'Swift' // - Use 'Swift.' to reference the global function in module 'Swift' return min(first, second) } } 

Embora o interessante seja que o compilador agora também avisará sobre a tentativa de chamar um método de biblioteca padrão com o mesmo nome de uma function de nível superior stdlib:

 extension Array where Element : Comparable { func smallest() -> Element? { // Use of 'min' treated as a reference to instance method in protocol 'Sequence' // - Use 'self.' to silence this warning // - Use 'Swift.' to reference the global function return min() } } 

Nesse caso, como o aviso diz, você pode silenciá-lo usando um self. explícito self. :

 extension Array where Element : Comparable { func smallest() -> Element? { return self.min() } } 

Embora o que é realmente curioso sobre este aviso é que ele não parece se estender para funções definidas não-stdlib:

 func foo(_ a: Int) {} struct Foo { func foo() {} func bar() { foo() // no warning... } }