Skip to content

Commit 8439fe9

Browse files
authored
Merge pull request #350 from ijuma/scala-2.13.3
Upgrade Scala to 2.13.3 and Scala.js to 1.1.1
2 parents 3215ff3 + 0835d4b commit 8439fe9

File tree

5 files changed

+115
-18
lines changed

5 files changed

+115
-18
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ language: scala
77
scala:
88
- 2.11.12
99
- 2.12.12
10-
- 2.13.2
10+
- 2.13.3
1111

1212
env:
1313
- SCALAJS_VERSION= ADOPTOPENJDK=8
1414
- SCALAJS_VERSION=0.6.33 ADOPTOPENJDK=8
15-
- SCALAJS_VERSION=1.1.0 ADOPTOPENJDK=8
15+
- SCALAJS_VERSION=1.1.1 ADOPTOPENJDK=8
1616
- SCALAJS_VERSION= ADOPTOPENJDK=11
1717
- SCALAJS_VERSION=0.6.33 ADOPTOPENJDK=11
18-
- SCALAJS_VERSION=1.1.0 ADOPTOPENJDK=11
18+
- SCALAJS_VERSION=1.1.1 ADOPTOPENJDK=11
1919

2020
matrix:
2121

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ lazy val junit = libraryDependencies += "com.novocode" % "junit-interface" % "0.
4949

5050
lazy val scala211 = "2.11.12"
5151
lazy val scala212 = "2.12.12"
52-
lazy val scala213 = "2.13.2"
52+
lazy val scala213 = "2.13.3"
5353

5454
lazy val compat = MultiScalaCrossProject(JSPlatform, JVMPlatform, NativePlatform)(
5555
"compat",

compat/src/main/scala-2.11_2.12/scala/collection/compat/immutable/LazyList.scala

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,18 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St
231231

232232
@volatile private[this] var stateEvaluated: Boolean = false
233233
@inline private def stateDefined: Boolean = stateEvaluated
234+
private[this] var midEvaluation = false
234235

235236
private lazy val state: State[A] = {
236-
val res = lazyState()
237+
// if it's already mid-evaluation, we're stuck in an infinite
238+
// self-referential loop (also it's empty)
239+
if (midEvaluation) {
240+
throw new RuntimeException(
241+
"self-referential LazyList or a derivation thereof has no more elements")
242+
}
243+
midEvaluation = true
244+
val res = try lazyState()
245+
finally midEvaluation = false
237246
// if we set it to `true` before evaluating, we may infinite loop
238247
// if something expects `state` to already be evaluated
239248
stateEvaluated = true
@@ -358,7 +367,7 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St
358367
newLL {
359368
if (isEmpty) suffix match {
360369
case lazyList: LazyList[B] => lazyList.state // don't recompute the LazyList
361-
case _ => stateFromIterator(suffix.toIterator)
370+
case coll => stateFromIterator(coll.toIterator)
362371
} else sCons(head, tail lazyAppendedAll suffix)
363372
}
364373

@@ -1257,7 +1266,7 @@ object LazyList extends SeqFactory[LazyList] {
12571266
* @param hd The first element of the result lazy list
12581267
* @param tl The remaining elements of the result lazy list
12591268
*/
1260-
def apply[A](hd: => A, tl: => LazyList[A]): LazyList[A] = newLL(sCons(hd, tl))
1269+
def apply[A](hd: => A, tl: => LazyList[A]): LazyList[A] = newLL(sCons(hd, newLL(tl.state)))
12611270

12621271
/** Maps a lazy list to its head and tail */
12631272
def unapply[A](xs: LazyList[A]): Option[(A, LazyList[A])] = #::.unapply(xs)
@@ -1270,7 +1279,7 @@ object LazyList extends SeqFactory[LazyList] {
12701279
/** Construct a LazyList consisting of a given first element followed by elements
12711280
* from another LazyList.
12721281
*/
1273-
def #::[B >: A](elem: => B): LazyList[B] = newLL(sCons(elem, l()))
1282+
def #::[B >: A](elem: => B): LazyList[B] = newLL(sCons(elem, newLL(l().state)))
12741283

12751284
/** Construct a LazyList consisting of the concatenation of the given LazyList and
12761285
* another LazyList.

compat/src/test/scala/test/scala/collection/LazyListLazinessTest.scala

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package test.scala.collection
22

33
import org.junit.Test
44
import org.junit.Assert._
5+
import org.junit.Ignore
56

67
import scala.collection.compat.immutable.LazyList
78
import scala.collection.compat._
@@ -217,7 +218,7 @@ class LazyListLazinessTest {
217218
}
218219

219220
@Test
220-
def lazyAppendedAll_appendedAll_properlyLazy(): Unit = {
221+
def lazyAppendedAll_properlyLazy(): Unit = {
221222
genericAppendedColl_properlyLazy(_ lazyAppendedAll _)
222223
}
223224

@@ -715,6 +716,21 @@ class LazyListLazinessTest {
715716
// assertLazyAllSkipping(op, 4)
716717
// }
717718

719+
private def genericCons_unapply_properlyLazy(
720+
unapply: LazyList[Int] => Option[(Int, LazyList[Int])]): Unit = {
721+
assertLazyAllSkipping(unapply, 1)
722+
}
723+
724+
@Test
725+
def cons_unapply_properlyLazy(): Unit = {
726+
genericCons_unapply_properlyLazy(LazyList.cons.unapply)
727+
}
728+
729+
@Test
730+
def `#::_unapply_properlyLazy`(): Unit = {
731+
genericCons_unapply_properlyLazy(LazyList.#::.unapply)
732+
}
733+
718734
/* factory laziness tests */
719735

720736
@Test
@@ -776,24 +792,48 @@ class LazyListLazinessTest {
776792
assertRepeatedlyLazy(factory)
777793
}
778794

779-
@Test
780-
def `#:: properlyLazy`(): Unit = {
781-
val factory = lazyListFactory { init =>
795+
private def genericCons_properlyLazy(cons: (=> Int, => LazyList[Int]) => LazyList[Int]): Unit = {
796+
val headInitFactory = lazyListFactory { init =>
782797
def gen(index: Int): LazyList[Int] = {
783798
def elem(): Int = { init.evaluate(index); index }
784799
if (index >= LazinessChecker.count) LazyList.empty
785-
// else elem() #:: gen(index + 1)
786-
else gen(index + 1).#::(elem())
800+
else cons(elem(), gen(index + 1))
787801
}
788802

789803
gen(0)
790804
}
791-
assertRepeatedlyLazy(factory)
805+
assertRepeatedlyLazy(headInitFactory)
806+
807+
val tailInitFactory = lazyListFactory { init =>
808+
def gen(index: Int): LazyList[Int] = {
809+
if (index >= LazinessChecker.count) LazyList.empty
810+
else {
811+
init.evaluate(index)
812+
cons(index, gen(index + 1))
813+
}
814+
}
815+
816+
LazyList.empty lazyAppendedAll gen(0) // prevent initial state evaluation
817+
}
818+
assertRepeatedlyLazy(tailInitFactory)
819+
}
820+
821+
@Test
822+
@Ignore // TODO: enable after upgrading to 2.13.4+
823+
def cons_properlyLazy(): Unit = {
824+
genericCons_properlyLazy(LazyList.cons(_, _))
825+
}
826+
827+
@Test
828+
@Ignore // TODO: enable after upgrading Scala.js to 1.1.2+
829+
def `#::_properlyLazy`(): Unit = {
830+
// genericCons_properlyLazy(_ #:: _)
831+
genericCons_properlyLazy((hd, tl) => tl.#::(hd))
792832
}
793833

794834
@Test
795835
def `#::: properlyLazy`(): Unit = {
796-
val factory = lazyListFactory { init =>
836+
val headInitFactory = lazyListFactory { init =>
797837
def gen(index: Int): LazyList[Int] = {
798838
def elem(): LazyList[Int] = LazyList.fill(1) { init.evaluate(index); index }
799839
if (index >= LazinessChecker.count) LazyList.empty
@@ -802,7 +842,20 @@ class LazyListLazinessTest {
802842

803843
gen(0)
804844
}
805-
assertRepeatedlyLazy(factory)
845+
assertRepeatedlyLazy(headInitFactory)
846+
847+
val tailInitFactory = lazyListFactory { init =>
848+
def gen(index: Int): LazyList[Int] = {
849+
if (index >= LazinessChecker.count) LazyList.empty
850+
else {
851+
init.evaluate(index)
852+
LazyList.fill(1)(index) #::: gen(index + 1)
853+
}
854+
}
855+
856+
LazyList.empty lazyAppendedAll gen(0) // prevent initial state evaluation
857+
}
858+
assertRepeatedlyLazy(tailInitFactory)
806859
}
807860

808861
@Test

compat/src/test/scala/test/scala/collection/LazyListTest.scala

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package test.scala.collection
22

33
import org.junit.Test
44
import org.junit.Assert._
5+
import org.junit.Ignore
56

67
import scala.collection.compat.immutable.LazyList
78
import scala.collection.compat._
@@ -148,6 +149,7 @@ class LazyListTest {
148149
assertEquals("LazyList(1)", l.toString)
149150
}
150151

152+
@Ignore // TODO enable once Scala.js is upgraded to 1.1.2+
151153
@Test
152154
def testLazyListToStringWhenLazyListHasCyclicReference: Unit = {
153155
lazy val cyc: LazyList[Int] = 1 #:: 2 #:: 3 #:: 4 #:: cyc
@@ -161,7 +163,7 @@ class LazyListTest {
161163
cyc.tail.tail.head
162164
assertEquals("LazyList(1, 2, 3, <not computed>)", cyc.toString)
163165
cyc.tail.tail.tail.head
164-
assertEquals("LazyList(1, 2, 3, 4, <cycle>)", cyc.toString)
166+
assertEquals("LazyList(1, 2, 3, 4, <not computed>)", cyc.toString)
165167
cyc.tail.tail.tail.tail.head
166168
assertEquals("LazyList(1, 2, 3, 4, <cycle>)", cyc.toString)
167169
}
@@ -355,4 +357,37 @@ class LazyListTest {
355357
assertEquals(1 to 10, build(_ ++= LazyList.from(1).take(10)))
356358
assertEquals(1 to 10, build(_ ++= Iterator.from(1).take(10)))
357359
}
360+
361+
@Test
362+
def selfReferentialFailure(): Unit = {
363+
def assertNoStackOverflow[A](lazyList: LazyList[A]): Unit = {
364+
// don't hang the test if we've made a programming error in this test
365+
val finite = lazyList.take(1000)
366+
// AssertUtil.assertThrows[RuntimeException](finite.force, _ contains "self-referential")
367+
try {
368+
finite.force
369+
fail("Expected RuntimeException to be thrown")
370+
} catch { case e: RuntimeException => assertTrue(e.getMessage.contains("self-referential")) }
371+
}
372+
assertNoStackOverflow {
373+
class L { val ll: LazyList[Nothing] = LazyList.empty #::: ll }; (new L).ll
374+
}
375+
assertNoStackOverflow {
376+
class L { val ll: LazyList[Int] = 1 #:: ll.map(_ + 1).filter(_ % 2 == 0) }; (new L).ll
377+
}
378+
class L {
379+
lazy val a: LazyList[Nothing] = LazyList.empty #::: b
380+
lazy val b: LazyList[Nothing] = LazyList.empty #::: a
381+
}
382+
assertNoStackOverflow((new L).a)
383+
assertNoStackOverflow((new L).b)
384+
}
385+
386+
// scala/bug#11931
387+
@Test
388+
def lazyAppendedAllExecutesOnce(): Unit = {
389+
var count = 0
390+
LazyList(1).lazyAppendedAll({ count += 1; Seq(2) }).toList
391+
assertEquals(1, count)
392+
}
358393
}

0 commit comments

Comments
 (0)