Search code examples
kotlintype-inferenceoverload-resolution

Kotlin bad type inference gives overload resolution ambiguity


Consider the overloaded function foo:

fun foo(i: Int) { /* */ }
fun foo(i_s: Collection<Int>) { /* */ }

I get an overload resolution ambiguity error with the following code:

val bar = foo(Stream.empty<Int>().collect(Collectors.toList()))

Overload resolution ambiguity:
public fun foo(i: Int): Unit defined in ...
public fun foo(i_s: Collection): Unit defined in ...

As far as I can tell the resolution should be clear: I am collecting the stream to a list, so the foo(Collection<Int>) should be taken. Some more experiments suggest a failure to resolve the generics correctly, so:

  1. Why does the generics resolution fail in this case?
  2. Is this "flaw" documented somewhere? Is this a bug I should report?

I tested a few other things: foo(listOf()) does not error, neither does

val bar = Stream.empty<Int>().collect(Collectors.toList())
val baz = foo(bar)

Replacing toList() with toSet() does not alter the behavior, but toCollection { ArrayList<Int>() } compiles in all cases.

If I change foo to fun <T> foo(i_s: Collection<T>), the error changes to

Type inference failed. Expected type mismatch: inferred type is (Mutable)List! but Int was expected

This opens more questions for me:

  1. Why does the resolution work if I store the intermediate result in a variable? After all, its type is resolved automatically as well?
  2. What does the type inference error mean? Why does it occur?

Solution

  • Looks like this is a bug in the old inference algorithm, because it works correctly with the new inference. I filed an issue in the Kotlin bug tracker, at least for adding a regression test. You can follow it for updates.

    More information on new inference:

    https://youtrack.jetbrains.com/issue/KT-31507

    https://blog.jetbrains.com/kotlin/2019/06/kotlin-1-3-40-released/