Closed
Description
Problem:
- Iterator.toIndexedSeq skips the first element if Iterator.hasNext changes some internal state.
- Iterator's hasNext() will be called multiple times, so if the code is expecting that hasNext() will be called only once and changing some internal state, some elements will be skipped in the generated IndexedSeq.
- For example, JDBC ResultSet.next() will move the internal cursor, so Iterators for traversing JDBCResultSet will be broken.
- There has been no guarantee nor any specification that Iterator.hasNext() must return the same result for subsequent calls before calling
next()
https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#hasNext--
- This behavior is introduced after Scala 2.13.0-M5.
- Iterator.haxNext() implementation that are not idempotent will produce broken results with this change, even though ideally all of the implementations of Iterator.haxNext() should be idempotent.
Code Example
class MyIterator extends Iterator[Int] {
private var i = 0
def hasNext: Boolean = {
val flag = i < 2
i += 1
flag
}
def next:Int = i
}
object Example {
def main(args:Array[String]): Unit = {
val s = new MyIterator().toIndexedSeq
println(s.mkString(", ")) // Expected 1,2, but 2.13.0-pre-59975bb produces 2
}
}
This code is in https://github.com/xerial/scala-bug-repro
How to reproduce:
$ git clone https://github.com/xerial/scala-bug-repro
$ cd scala-bug-repo
$ sbt run
...
2
For the other Scala versions
$ sbt
> ++ 2.12.8!
> run
1, 2
This behavior is found at #11453
Cause
VectorBuilder.addAll calls the Iterator.hasNext to check whether it should advance to the next block or not:
while (it.hasNext) { // The first call is here
advanceToNextBlockIfNecessary()
lo += it.copyToArray(xs = display0, start = lo, len = display0.length - lo) // The next call happens here
}
Related change: scala/scala#7588