bug
bug copied to clipboard
Type inference failure while picking among conversions
Reproduction steps
Scala version: 2.13.16
//> using scala 2.13.16
import scala.language.implicitConversions
trait LowPriorityConversions {
implicit def convertIterable1[A, I[T] <: Iterable[T]](iterable: I[A]): String = {
"convertIterable1 wins"
}
// implicit def convertIterable2[A, I[_]](iterable: I[A])(implicit ev: I[A] <:< Iterable[A]): String = {
// "convertIterable2 wins"
// }
}
object Conversions extends LowPriorityConversions {
implicit def convertMap[K, V, M[A, B] <: Map[A, B]](map: M[K, V]): String = {
"convertMap wins"
}
}
object OverloadTest {
// force conversion to String
def printStr(str: String): Unit = {
println(str)
}
def main(args: Array[String]): Unit = {
import Conversions._
printStr(Map("key" -> "value")) // ok: got "convertMap wins"
printStr(Map("key" -> "value").map(identity)) // expect "convertMap wins", but got "convertIterable1 wins"
}
}
Problem
Map#maphas 2 alternatives:map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2]andmap[B](f: ((K, V)) => B): Iterable[B]. The first one is more specific so I expectMap("key" -> "value").map(identity)to return aMap, not anIterable- It works as expected when we replace
convertIterable1withconvertIterable2 - This doesn't happen on Scala 3.3.5
I don't understand the details yet but -Vlog:typer suggests that it (spuriously) can't infer type args for convertMap but can for convertIterable1.
[log typer] infer method inst Conversions.convertMap[K, V, M], tparams = List(type K, type V, type M), args = List(immutable.this.Map[?K2,scala.this.Nothing]), pt = String, lobounds = List(scala.this.Nothing, scala.this.Nothing, scala.this.Nothing), parambounds = List(, , [A, B] <: Map[A,B])
[log typer] error tree = <argument>
[log typer] implicit adapt failed: no type parameters for method convertMap: (map: M[K,V]): String exist so that it can be applied to arguments (immutable.this.Map[?K2,scala.this.Nothing])
--- because ---
no unique instantiation of type variable K2 could be found
test/files/run/t13102.scala:22: convertMap is not a valid implicit value for scala.this.Function1[immutable.this.Map[?K2,scala.this.Nothing],String] because:
no type parameters for method convertMap: (map: M[K,V]): String exist so that it can be applied to arguments (immutable.this.Map[?K2,scala.this.Nothing])
--- because ---
no unique instantiation of type variable K2 could be found
import Conversions._
^
I also don't understand why convertIterable2 fails with
[log typer] implicit adapt failed: Cannot prove that scala.collection.immutable.Iterable[A] <:< Iterable[A].
Then convertMap eventually succeeds (after failing to infer type args) because it tries picking the overload first, and that allows it to proceed successfully.
I'll tweak the title to reflect that it's a problem with type inference and not overload resolution per se.
for reference, the Map construction is not involved (but produces noisy debug)
//> using scala 2.13.16
//> using options -Xsource:3-cross -Vlog:typer -Vdebug -Vtyper
import scala.language.implicitConversions
trait LowPriorityConversions {
implicit def convertIterable1[A, I[T] <: Iterable[T]](iterable: I[A]): String = "convertIterable1 wins"
//implicit def convertIterable2[A, I[_]](iterable: I[A])(implicit ev: I[A] <:< Iterable[A]): String = "convertIterable2 wins"
}
object Conversions extends LowPriorityConversions {
implicit def convertMap[K, V, M[A, B] <: Map[A, B]](map: M[K, V]): String = "convertMap wins"
}
object OverloadTest {
// force conversion to String
def printStr(str: String): Unit = println(str)
def sut = Map("key" -> "value")
def main(args: Array[String]): Unit = {
import Conversions._
//printStr(Map("key" -> "value")) // ok: got "convertMap wins"
//printStr(Map("key" -> "value").map(identity)) // expect "convertMap wins", but got "convertIterable1 wins"
printStr(sut.map(identity)) // expect "convertMap wins", but got "convertIterable1 wins"
}
}