Skip to content

Commit

Permalink
Fail early if self-referential LazyList runs out of elements
Browse files Browse the repository at this point in the history
Port of scala/scala#8635.

Co-authored-by: NthPortal <nthportal@gmail.com>
  • Loading branch information
ijuma and NthPortal committed Jul 5, 2020
1 parent 62da127 commit 223bdd9
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,16 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St

@volatile private[this] var stateEvaluated: Boolean = false
@inline private def stateDefined: Boolean = stateEvaluated
private[this] var midEvaluation = false

private lazy val state: State[A] = {
val res = lazyState()
// if it's already mid-evaluation, we're stuck in an infinite
// self-referential loop (also it's empty)
if (midEvaluation) {
throw new RuntimeException("self-referential LazyList or a derivation thereof has no more elements")
}
midEvaluation = true
val res = try lazyState() finally midEvaluation = false
// if we set it to `true` before evaluating, we may infinite loop
// if something expects `state` to already be evaluated
stateEvaluated = true
Expand Down
17 changes: 17 additions & 0 deletions compat/src/test/scala/test/scala/collection/LazyListTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,21 @@ class LazyListTest {
assertEquals(1 to 10, build(_ ++= LazyList.from(1).take(10)))
assertEquals(1 to 10, build(_ ++= Iterator.from(1).take(10)))
}

@Test
def selfReferentialFailure(): Unit = {
def assertNoStackOverflow[A](lazyList: LazyList[A]): Unit = {
// don't hang the test if we've made a programming error in this test
val finite = lazyList.take(1000)
AssertUtil.assertThrows[RuntimeException](finite.force, _ contains "self-referential")
}
assertNoStackOverflow { class L { val ll: LazyList[Nothing] = LazyList.empty #::: ll }; (new L).ll }
assertNoStackOverflow { class L { val ll: LazyList[Int] = 1 #:: ll.map(_ + 1).filter(_ % 2 == 0) }; (new L).ll }
class L {
lazy val a: LazyList[Nothing] = LazyList.empty #::: b
lazy val b: LazyList[Nothing] = LazyList.empty #::: a
}
assertNoStackOverflow((new L).a)
assertNoStackOverflow((new L).b)
}
}

0 comments on commit 223bdd9

Please sign in to comment.