Skip to content

Iterator.toIndexedSeq calls hasNext() multiple times in Scala 2.13.0-RC1 (preview) #11455

Closed
@xerial

Description

@xerial

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.
  • 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions