Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler think this if statement is an expression?

Tags:

kotlin

I have this method that compares sorted lists and tells you which items from list 1 are missing from list 2, and vice versa, in O(N) time:

fun <T : Comparable<T>> compareSortedLists(
        list1: Iterable<T>,
        list2: Iterable<T>,
        onlyInList1: MutableCollection<T>,
        onlyInList2: MutableCollection<T>) {
    val it1 = PeekingIterator(list1.iterator())
    val it2 = PeekingIterator(list2.iterator())
    while (it1.hasNext() && it2.hasNext()) {
        val comp = it1.peek().compareTo(it2.peek())
        if (comp < 0)       // <-- ERROR: type inference failure
            onlyInList1.add(it1.next())
        else if (comp > 0)
            onlyInList2.add(it2.next())
        else {
            it1.next()
            it2.next()      // <---- Error: type mismatch
        }
    }
    it1.forEachRemaining { onlyInList1.add(it) }
    it2.forEachRemaining { onlyInList2.add(it) }
}

The Kotlin compiler (1.2.41) in IntelliJ IDEA 2018.1.4 gives me a compile-time error (as marked above). The error message says:

Type mismatch.
Required: Comparable<Boolean>!
Found: T!

But I didn't intend the if to be an expression. I meant it as a statement (EDIT: I mean an expression whose value is ignored, since all ifs are actually expressions). If I convert the if to a when then it compiles ok:

        when {                // <-- Look, no error! ;-)
            comp < 0 ->
                onlyInList1.add(it1.next())
            comp > 0 ->
                onlyInList2.add(it2.next())
            else -> {
                it1.next()
                it2.next()
            }
        }

Why does it think the if is an expression? And, of all things, why does it think a Comparable<Boolean>! is required? Where's the context that requires such a type?

like image 813
DodgyCodeException Avatar asked Jun 11 '18 15:06

DodgyCodeException


1 Answers

This seems to be a compiler bug. I reported it here:

https://youtrack.jetbrains.net/issue/KT-24886

This is a minimal reproducer:

fun <T : Comparable<T>> test(a: Iterable<T>) {
    val x = if (booleanExpr1())
        booleanExpr2()
    else if (booleanExpr3())
        booleanExpr4()
    else {
        a.iterator().next()
    }
}

fun booleanExpr1() = true
fun booleanExpr2() = true
fun booleanExpr3() = true
fun booleanExpr4() = true

Note that the issue isn't at all with the "if expression not being treated as a statement", but with type inference itself. The compiler should have worked out a valid upper bound of all the then-branches, but failed.

like image 157
Marko Topolnik Avatar answered Dec 16 '22 23:12

Marko Topolnik