diff options
Diffstat (limited to 'test')
78 files changed, 1401 insertions, 294 deletions
diff --git a/test/files/neg/sammy_error_exist_no_crash.check b/test/files/neg/sammy_error_exist_no_crash.check new file mode 100644 index 0000000000..a0d2237ce0 --- /dev/null +++ b/test/files/neg/sammy_error_exist_no_crash.check @@ -0,0 +1,6 @@ +sammy_error_exist_no_crash.scala:5: error: Could not derive subclass of F[? >: String] + (with SAM `def method apply(s: String)Int`) + based on: ((x$1: String) => x$1.<parseInt: error>). + bar(_.parseInt) + ^ +one error found diff --git a/test/files/neg/sammy_error_exist_no_crash.flags b/test/files/neg/sammy_error_exist_no_crash.flags new file mode 100644 index 0000000000..e1b37447c9 --- /dev/null +++ b/test/files/neg/sammy_error_exist_no_crash.flags @@ -0,0 +1 @@ +-Xexperimental
\ No newline at end of file diff --git a/test/files/neg/sammy_error_exist_no_crash.scala b/test/files/neg/sammy_error_exist_no_crash.scala new file mode 100644 index 0000000000..da7e47206f --- /dev/null +++ b/test/files/neg/sammy_error_exist_no_crash.scala @@ -0,0 +1,6 @@ +abstract class F[T] { def apply(s: T): Int } + +object NeedsNiceError { + def bar(x: F[_ >: String]) = ??? + bar(_.parseInt) +}
\ No newline at end of file diff --git a/test/files/neg/sammy_restrictions.scala b/test/files/neg/sammy_restrictions.scala index 5f1a04cd20..d003cfaf36 100644 --- a/test/files/neg/sammy_restrictions.scala +++ b/test/files/neg/sammy_restrictions.scala @@ -1,28 +1,28 @@ -class NoAbstract +abstract class NoAbstract -class TwoAbstract { def ap(a: Int): Int; def pa(a: Int): Int } +abstract class TwoAbstract { def ap(a: Int): Int; def pa(a: Int): Int } -class Base // check that the super class constructor isn't considered. -class NoEmptyConstructor(a: Int) extends Base { def this(a: String) = this(0); def ap(a: Int): Int } +abstract class Base // check that the super class constructor isn't considered. +abstract class NoEmptyConstructor(a: Int) extends Base { def this(a: String) = this(0); def ap(a: Int): Int } -class OneEmptyConstructor() { def this(a: Int) = this(); def ap(a: Int): Int } +abstract class OneEmptyConstructor() { def this(a: Int) = this(); def ap(a: Int): Int } -class OneEmptySecondaryConstructor(a: Int) { def this() = this(0); def ap(a: Int): Int } +abstract class OneEmptySecondaryConstructor(a: Int) { def this() = this(0); def ap(a: Int): Int } -class MultipleConstructorLists()() { def ap(a: Int): Int } +abstract class MultipleConstructorLists()() { def ap(a: Int): Int } -class MultipleMethodLists()() { def ap(a: Int)(): Int } +abstract class MultipleMethodLists()() { def ap(a: Int)(): Int } -class ImplicitConstructorParam()(implicit a: String) { def ap(a: Int): Int } +abstract class ImplicitConstructorParam()(implicit a: String) { def ap(a: Int): Int } -class ImplicitMethodParam() { def ap(a: Int)(implicit b: String): Int } +abstract class ImplicitMethodParam() { def ap(a: Int)(implicit b: String): Int } -class PolyClass[T] { def ap(a: T): T } +abstract class PolyClass[T] { def ap(a: T): T } -class PolyMethod { def ap[T](a: T): T } +abstract class PolyMethod { def ap[T](a: T): T } -class OneAbstract { def ap(a: Any): Any } -class DerivedOneAbstract extends OneAbstract +abstract class OneAbstract { def ap(a: Int): Any } +abstract class DerivedOneAbstract extends OneAbstract object Test { implicit val s: String = "" diff --git a/test/files/neg/t2866.check b/test/files/neg/t2866.check new file mode 100644 index 0000000000..340fb8da22 --- /dev/null +++ b/test/files/neg/t2866.check @@ -0,0 +1,17 @@ +t2866.scala:30: warning: imported `one' is permanently hidden by definition of value one + import A.one // warning: imported `one' is permanently hidden by definition of value one. + ^ +t2866.scala:42: error: ambiguous implicit values: + both value two of type Int + and value one in object A of type => Int + match expected type Int + assert(implicitly[Int] == 2) // !!! Not ambiguous in 2.8.0. Ambigous in 2.7.6 + ^ +t2866.scala:50: error: ambiguous implicit values: + both value two of type Int + and value one in object A of type => Int + match expected type Int + assert(implicitly[Int] == 2) // !!! Not ambiguous in 2.8.0. Ambiguous in 2.7.6 + ^ +one warning found +two errors found diff --git a/test/files/neg/t2866.scala b/test/files/neg/t2866.scala new file mode 100644 index 0000000000..55ebff9710 --- /dev/null +++ b/test/files/neg/t2866.scala @@ -0,0 +1,59 @@ +// for 2.7.x compatibility + +object A { + implicit val one = 1 +} + +object Test { + + locally { + import A._ + locally { + // assert(implicitly[Int] == 1) // error: could not find implicit value for parameter e: Int. + // !!! Why one A.one? + // (I assume you mean: why _not_ A.one? A.one is shadowed by local one. + // but the local one cannot be used yet because it does not have an explicit type. + implicit val one = 2 + assert(implicitly[Int] == 2) + assert(one == 2) + } + } + + locally { + import A._ + implicit val one: Int = 2 + assert(implicitly[Int] == 2) + assert(one == 2) + } + + locally { + import A.one // warning: imported `one' is permanently hidden by definition of value one. + // !!! Really? + //assert(implicitly[Int] == 1) + implicit val one = 2 + assert(implicitly[Int] == 2) // !!! why not 2? + assert(one == 2) + } + + locally { + import A.one + assert(implicitly[Int] == 1) + implicit val two = 2 + assert(implicitly[Int] == 2) // !!! Not ambiguous in 2.8.0. Ambigous in 2.7.6 + } + + locally { + import A._ + assert(implicitly[Int] == 1) + implicit val two = 2 + import A.{one => _} + assert(implicitly[Int] == 2) // !!! Not ambiguous in 2.8.0. Ambiguous in 2.7.6 + } + + locally { + import A.{one => _, _} + implicit val two = 2 + assert(implicitly[Int] == 2) // not ambiguous in 2.8.0 nor im ambiguous in 2.7.6 + } + +} diff --git a/test/files/neg/t5639b.check b/test/files/neg/t5639b.check new file mode 100644 index 0000000000..faa1766660 --- /dev/null +++ b/test/files/neg/t5639b.check @@ -0,0 +1,4 @@ +A_2.scala:6: error: could not find implicit value for parameter e: Int + implicitly[Int] + ^ +one error found diff --git a/test/files/neg/t5639b/A_1.scala b/test/files/neg/t5639b/A_1.scala new file mode 100644 index 0000000000..c5da10eae4 --- /dev/null +++ b/test/files/neg/t5639b/A_1.scala @@ -0,0 +1,17 @@ +import Implicits._ + +class Baz + +object Test { + implicitly[Int] +} + +object Implicits { + implicit val Baz: Int = 0 + // This implicit was being ignored by `isQualifyingImplicit` + // if the classpath contained a class file for `class Baz`. + // This is because the package scope contains a speculative + // symbol for `object Baz` which is entered by `SymbolLoaders` + // before looking inside the class file. (A Java originated + // classfile results in the class/module symbol pair.) +} diff --git a/test/files/neg/t5639b/A_2.scala b/test/files/neg/t5639b/A_2.scala new file mode 100644 index 0000000000..2bb36273e0 --- /dev/null +++ b/test/files/neg/t5639b/A_2.scala @@ -0,0 +1,11 @@ +import Implicits._ + +class Baz + +object Test { + implicitly[Int] +} + +object Implicits { + implicit val Baz: Int = 0 +} diff --git a/test/files/neg/t8534.check b/test/files/neg/t8534.check new file mode 100644 index 0000000000..297e7c1beb --- /dev/null +++ b/test/files/neg/t8534.check @@ -0,0 +1,4 @@ +t8534.scala:6: error: MyTrait is not an enclosing class + class BugTest {def isTheBugHere(in: MyTrait.this.type#SomeData) = false} + ^ +one error found diff --git a/test/files/neg/t8534.scala b/test/files/neg/t8534.scala new file mode 100644 index 0000000000..f118d22b82 --- /dev/null +++ b/test/files/neg/t8534.scala @@ -0,0 +1,7 @@ +object line1 { + trait MyTrait +} +object line2 { + import line2._ + class BugTest {def isTheBugHere(in: MyTrait.this.type#SomeData) = false} +} diff --git a/test/files/neg/t8534b.check b/test/files/neg/t8534b.check new file mode 100644 index 0000000000..39ffa41194 --- /dev/null +++ b/test/files/neg/t8534b.check @@ -0,0 +1,4 @@ +t8534b.scala:3: error: stable identifier required, but foo.type found. + type T = foo.type#Foo + ^ +one error found diff --git a/test/files/neg/t8534b.scala b/test/files/neg/t8534b.scala new file mode 100644 index 0000000000..73b6703a9c --- /dev/null +++ b/test/files/neg/t8534b.scala @@ -0,0 +1,4 @@ +object Test { + def foo = "" + type T = foo.type#Foo +} diff --git a/test/files/neg/t963.check b/test/files/neg/t963.check index 4dc202c7bd..483e53c77d 100644 --- a/test/files/neg/t963.check +++ b/test/files/neg/t963.check @@ -1,9 +1,9 @@ -t963.scala:14: error: stable identifier required, but Test.this.y3.x found. +t963.scala:14: error: stable identifier required, but y3.x.type found. val w3 : y3.x.type = y3.x - ^ -t963.scala:17: error: stable identifier required, but Test.this.y4.x found. + ^ +t963.scala:17: error: stable identifier required, but y4.x.type found. val w4 : y4.x.type = y4.x - ^ + ^ t963.scala:10: error: type mismatch; found : AnyRef{def x: Integer} required: AnyRef{val x: Integer} diff --git a/test/files/pos/sammy_exist.flags b/test/files/pos/sammy_exist.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_exist.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_exist.scala b/test/files/pos/sammy_exist.scala new file mode 100644 index 0000000000..f05ae20463 --- /dev/null +++ b/test/files/pos/sammy_exist.scala @@ -0,0 +1,17 @@ +// scala> typeOf[java.util.stream.Stream[_]].nonPrivateMember(TermName("map")).info +// [R](x$1: java.util.function.Function[_ >: T, _ <: R])java.util.stream.Stream[R] + +// java.util.function.Function +trait Fun[A, B] { def apply(x: A): B } + +// java.util.stream.Stream +class S[T](x: T) { def map[R](f: Fun[_ >: T, _ <: R]): R = f(x) } + +class Bla { def foo: Bla = this } + +// NOTE: inferred types show unmoored skolems, should pack them to display properly as bounded wildcards +object T { + val aBlaSAM = (new S(new Bla)).map(_.foo) + val fun: Fun[Bla, Bla] = (x: Bla) => x + val aBlaSAMX = (new S(new Bla)).map(fun) +} diff --git a/test/files/pos/sammy_overload.flags b/test/files/pos/sammy_overload.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_overload.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_overload.scala b/test/files/pos/sammy_overload.scala new file mode 100644 index 0000000000..5472248f4d --- /dev/null +++ b/test/files/pos/sammy_overload.scala @@ -0,0 +1,9 @@ +trait Consumer[T] { + def consume(x: T): Unit +} + +object Test { + def foo(x: String): Unit = ??? + def foo(): Unit = ??? + val f: Consumer[_ >: String] = foo +}
\ No newline at end of file diff --git a/test/files/pos/sammy_override.flags b/test/files/pos/sammy_override.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_override.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_override.scala b/test/files/pos/sammy_override.scala new file mode 100644 index 0000000000..a1d0651c39 --- /dev/null +++ b/test/files/pos/sammy_override.scala @@ -0,0 +1,8 @@ +trait IntConsumer { + def consume(x: Int): Unit +} + +object Test { + def anyConsumer(x: Any): Unit = ??? + val f: IntConsumer = anyConsumer +}
\ No newline at end of file diff --git a/test/files/pos/t5413.scala b/test/files/pos/t5413.scala new file mode 100644 index 0000000000..47af514a14 --- /dev/null +++ b/test/files/pos/t5413.scala @@ -0,0 +1,9 @@ +object Fail { + def nom (guard : => Boolean) (something : => Unit) { } + def main(args: Array[String]) { + nom { + val i = 0 + (i != 3) + }() + } +} diff --git a/test/files/pos/t5639.flags b/test/files/pos/t5639.flags new file mode 100644 index 0000000000..0acce1e7ce --- /dev/null +++ b/test/files/pos/t5639.flags @@ -0,0 +1 @@ +-Xsource:2.12 diff --git a/test/files/pos/t5639/A_1.scala b/test/files/pos/t5639/A_1.scala new file mode 100644 index 0000000000..c5da10eae4 --- /dev/null +++ b/test/files/pos/t5639/A_1.scala @@ -0,0 +1,17 @@ +import Implicits._ + +class Baz + +object Test { + implicitly[Int] +} + +object Implicits { + implicit val Baz: Int = 0 + // This implicit was being ignored by `isQualifyingImplicit` + // if the classpath contained a class file for `class Baz`. + // This is because the package scope contains a speculative + // symbol for `object Baz` which is entered by `SymbolLoaders` + // before looking inside the class file. (A Java originated + // classfile results in the class/module symbol pair.) +} diff --git a/test/files/pos/t5639/A_2.scala b/test/files/pos/t5639/A_2.scala new file mode 100644 index 0000000000..2bb36273e0 --- /dev/null +++ b/test/files/pos/t5639/A_2.scala @@ -0,0 +1,11 @@ +import Implicits._ + +class Baz + +object Test { + implicitly[Int] +} + +object Implicits { + implicit val Baz: Int = 0 +} diff --git a/test/files/pos/t5639/Bar.scala b/test/files/pos/t5639/Bar.scala deleted file mode 100644 index f577500acd..0000000000 --- a/test/files/pos/t5639/Bar.scala +++ /dev/null @@ -1,7 +0,0 @@ -package pack.age - -import pack.age.Implicits._ - -object Quux { - def baz : Baz = 1 -} diff --git a/test/files/pos/t5639/Foo.scala b/test/files/pos/t5639/Foo.scala deleted file mode 100644 index 1a07734a8e..0000000000 --- a/test/files/pos/t5639/Foo.scala +++ /dev/null @@ -1,7 +0,0 @@ -package pack.age - -class Baz - -object Implicits { - implicit def Baz(n: Int): Baz = new Baz -} diff --git a/test/files/pos/t7750.flags b/test/files/pos/t7750.flags new file mode 100644 index 0000000000..b216e74c97 --- /dev/null +++ b/test/files/pos/t7750.flags @@ -0,0 +1 @@ +-Xfatal-warnings -feature diff --git a/test/files/pos/t7750.scala b/test/files/pos/t7750.scala new file mode 100644 index 0000000000..befec76949 --- /dev/null +++ b/test/files/pos/t7750.scala @@ -0,0 +1,8 @@ +trait LazyCombiner[Elem, +To, Buff <: Growable[Elem] with Sizing] +trait Growable[T] +trait Sizing + + +object Test { + null.isInstanceOf[LazyCombiner[_, _, _]] // issued an existential feature warning +} diff --git a/test/files/pos/t8310.flags b/test/files/pos/t8310.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/t8310.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/t8310.scala b/test/files/pos/t8310.scala new file mode 100644 index 0000000000..874caf4d3b --- /dev/null +++ b/test/files/pos/t8310.scala @@ -0,0 +1,22 @@ +trait Comparinator[T] { def compare(a: T, b: T): Int } + +object TestOkay { + def sort(x: Comparinator[_ >: String]) = () + sort((a: String, b: String) => a.compareToIgnoreCase(b)) +} + +object TestOkay2 { + def sort[T](x: Comparinator[_ >: T]) = () + sort((a: String, b: String) => a.compareToIgnoreCase(b)) +} + +object TestOkay3 { + def sort[T](xs: Option[T], x: Comparinator[_ >: T]) = () + sort(Some(""), (a: String, b: String) => a.compareToIgnoreCase(b)) +} + +object TestKoOverloaded { + def sort[T](xs: Option[T]) = () + def sort[T](xs: Option[T], x: Comparinator[_ >: T]) = () + sort(Some(""), (a: String, b: String) => a.compareToIgnoreCase(b)) +} diff --git a/test/files/pos/t8954.flags b/test/files/pos/t8954.flags new file mode 100644 index 0000000000..7de3c0f3ee --- /dev/null +++ b/test/files/pos/t8954.flags @@ -0,0 +1 @@ +-Xfatal-warnings -deprecation diff --git a/test/files/pos/t8954/t1.scala b/test/files/pos/t8954/t1.scala new file mode 100644 index 0000000000..3986d9f3b5 --- /dev/null +++ b/test/files/pos/t8954/t1.scala @@ -0,0 +1,13 @@ +package scala.foo + +// 1. a class about to be made final +@deprecatedInheritance class A { + def foo(): Unit = ??? +} + +// 1.1: +// - no inheritance warning because same file +// - no "override non-deprecated member" because @deprecatedInheritance +class B2 extends A { + @deprecated("","") override def foo(): Unit = ??? +} diff --git a/test/files/pos/t8954/t2.scala b/test/files/pos/t8954/t2.scala new file mode 100644 index 0000000000..4def127832 --- /dev/null +++ b/test/files/pos/t8954/t2.scala @@ -0,0 +1,39 @@ +package scala.foo + +// 1.2 deprecated children should be fine... +@deprecated("", "") class B extends A { + + // 1.3 and shouldn't trigger the + // "overriding non-deprecated parent" warning + override def foo(): Unit = ??? +} + +@deprecated("","") class F { + // 1.4 a class inside a deprecated class should work too + class G extends A +} + +// 2. a method about to be made final +class C { + @deprecatedOverriding def foo(): Unit = ??? +} + +// 2.1 overriding with a deprecated def should be fine +// and also shoudln't trigger the "deprecation is useless" +// warning +class D extends C { + @deprecated("","") override def foo(): Unit = ??? +} + +// 2.2 overriding from a deprecated class should be fine +@deprecated("","") class E extends C { + override def foo(): Unit = ??? +} + +// 2.3 overriding from deeper inside a deprecated class +// should work too +@deprecated("","") class H { + class I extends C { + override def foo(): Unit = ??? + } +} diff --git a/test/files/pos/t8965.flags b/test/files/pos/t8965.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/pos/t8965.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/pos/t8965.scala b/test/files/pos/t8965.scala new file mode 100644 index 0000000000..4f39330f4e --- /dev/null +++ b/test/files/pos/t8965.scala @@ -0,0 +1,7 @@ +class A { + def f(x: Any with AnyRef, y: Any with AnyRef) = x eq y + // a.scala:2: warning: Any and Any are unrelated: they will most likely never compare equal + // def f(x: Any with AnyRef, y: Any with AnyRef) = x eq y + // ^ + // one warning found +} diff --git a/test/files/run/iterator-concat.check b/test/files/run/iterator-concat.check deleted file mode 100644 index 23835b07ae..0000000000 --- a/test/files/run/iterator-concat.check +++ /dev/null @@ -1,4 +0,0 @@ -100 -1000 -10000 -100000 diff --git a/test/files/run/iterator-concat.scala b/test/files/run/iterator-concat.scala deleted file mode 100644 index f11363410f..0000000000 --- a/test/files/run/iterator-concat.scala +++ /dev/null @@ -1,15 +0,0 @@ -object Test { - // Create `size` Function0s, each of which evaluates to an Iterator - // which produces 1. Then fold them over ++ to get a single iterator, - // which should sum to "size". - def mk(size: Int): Iterator[Int] = { - val closures = (1 to size).toList.map(x => (() => Iterator(1))) - closures.foldLeft(Iterator.empty: Iterator[Int])((res, f) => res ++ f()) - } - def main(args: Array[String]): Unit = { - println(mk(100).sum) - println(mk(1000).sum) - println(mk(10000).sum) - println(mk(100000).sum) - } -} diff --git a/test/files/run/iterator-iterate-lazy.scala b/test/files/run/iterator-iterate-lazy.scala deleted file mode 100644 index 92b170062e..0000000000 --- a/test/files/run/iterator-iterate-lazy.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Test { - def main(args: Array[String]): Unit = { - Iterator.iterate((1 to 5).toList)(_.tail).takeWhile(_.nonEmpty).map(_.head).toList - } -} diff --git a/test/files/run/iterators.check b/test/files/run/iterators.check deleted file mode 100644 index bb139c1610..0000000000 --- a/test/files/run/iterators.check +++ /dev/null @@ -1,13 +0,0 @@ -test check_from was successful -test check_range was successful -test check_range2 was successful -test check_range3 was successful -test check_take was successful -test check_drop was successful -test check_foreach was successful -test check_forall was successful -test check_fromArray was successful -test check_toSeq was successful -test check_indexOf was successful -test check_findIndexOf was successful - diff --git a/test/files/run/iterators.scala b/test/files/run/iterators.scala deleted file mode 100644 index 57e05d3472..0000000000 --- a/test/files/run/iterators.scala +++ /dev/null @@ -1,136 +0,0 @@ -//############################################################################ -// Iterators -//############################################################################ - -//############################################################################ - -import scala.language.postfixOps - -object Test { - - def check_from: Int = { - val it1 = Iterator.from(-1) - val it2 = Iterator.from(0, -1) - it1.next + it2.next - } - - def check_range: Int = { - val xs1 = Iterator.range(0, 10, 2) toList; - val xs2 = Iterator.range(0, 10, -2) toList; - val xs3 = Iterator.range(10, 0, -2) toList; - val xs4 = Iterator.range(10, 0, 2) toList; - val xs5 = Iterator.range(0, 10, 11) toList; - xs1.length + xs2.length + xs3.length + xs4.length + xs5.length - } - - def check_range2: Int = { - val r1start = 0 - val r1end = 10 - val r1step = 1 - val r1 = Iterator.range(r1start, r1end, r1step) toList; - val r2 = Iterator.range(r1start, r1end, r1step + 1) toList; - val r3 = Iterator.range(r1end, r1start, -r1step) toList; - val r4 = Iterator.range(0, 10, 11) toList; - // 10 + 5 + 10 + 1 - r1.length + r2.length + r3.length + r4.length - } - - def check_range3: Int = { - def trues(xs: List[Boolean]) = xs.foldLeft(0)((a, b) => if (b) a+1 else a) - val r1 = Iterator.range(0, 10) - val xs1 = List(r1 contains 5, r1 contains 6) - val r2a = Iterator.range(0, 10, 2) - val r2b = Iterator.range(0, 10, 2) - val xs2 = List(r2a contains 5, r2b contains 6) - val r3 = Iterator.range(0, 10, 11) - val xs3 = List(r3 contains 5, r3 contains 6) - // 2 + 1 + 0 - trues(xs1) + trues(xs2) + trues(xs3) - } - - def check_take: Int = { - val it1 = Iterator.from(0) - val xs1 = it1 take 10 toList; - xs1.length - } - - def check_drop: Int = { - val it1 = Iterator.from(0) - val it2 = it1 map { 2 * _ } - val n1 = it1 drop 2 next - val n2 = it2 drop 2 next; - n1 + n2 - } - - def check_foreach: Int = { - val it1 = Iterator.from(0) take 20 - var n = 0 - it1 foreach { n += _ } - n - } - - def check_forall: Int = { - val it1 = Iterator.from(0) - val it2 = Iterator.from(1) - 0 - } - - def check_fromArray: Int = { // ticket #429 - val a = List(1, 2, 3, 4).toArray - var xs0 = a.iterator.toList; - var xs1 = a.slice(0, 1).iterator.toList; - var xs2 = a.slice(0, 2).iterator.toList; - var xs3 = a.slice(0, 3).iterator.toList; - var xs4 = a.slice(0, 4).iterator.toList; - xs0.length + xs1.length + xs2.length + xs3.length + xs4.length - } - - def check_toSeq: String = - List(1, 2, 3, 4, 5).iterator.toSeq.mkString("x") - - def check_indexOf: String = { - val i = List(1, 2, 3, 4, 5).indexOf(4) - val j = List(1, 2, 3, 4, 5).indexOf(16) - "" + i + "x" + j - } - - def check_findIndexOf: String = { - val i = List(1, 2, 3, 4, 5).indexWhere { x: Int => x >= 4 } - val j = List(1, 2, 3, 4, 5).indexWhere { x: Int => x >= 16 } - "" + i + "x" + j - } - - def check_success[A](name: String, closure: => A, expected: A) { - print("test " + name) - try { - val actual: A = closure - if (actual == expected) - print(" was successful") - else - print(" failed: expected "+ expected +", found "+ actual) - } - catch { - case exception: Throwable => - print(" raised exception " + exception) - } - println() - } - - def main(args: Array[String]) { - check_success("check_from", check_from, -1) - check_success("check_range", check_range, 11) - check_success("check_range2", check_range2, 26) - check_success("check_range3", check_range3, 3) - check_success("check_take", check_take, 10) - check_success("check_drop", check_drop, 12) - check_success("check_foreach", check_foreach, 190) - check_success("check_forall", check_forall, 0) - check_success("check_fromArray",check_fromArray, 14) - check_success("check_toSeq", check_toSeq, "1x2x3x4x5") - check_success("check_indexOf", check_indexOf, "3x-1") - check_success("check_findIndexOf", check_findIndexOf, "3x-1") - println() - } -} - -//############################################################################ diff --git a/test/files/run/sammy_repeated.check b/test/files/run/sammy_repeated.check new file mode 100644 index 0000000000..1cff0f067c --- /dev/null +++ b/test/files/run/sammy_repeated.check @@ -0,0 +1 @@ +WrappedArray(1) diff --git a/test/files/run/sammy_repeated.flags b/test/files/run/sammy_repeated.flags new file mode 100644 index 0000000000..e1b37447c9 --- /dev/null +++ b/test/files/run/sammy_repeated.flags @@ -0,0 +1 @@ +-Xexperimental
\ No newline at end of file diff --git a/test/files/run/sammy_repeated.scala b/test/files/run/sammy_repeated.scala new file mode 100644 index 0000000000..c24dc41909 --- /dev/null +++ b/test/files/run/sammy_repeated.scala @@ -0,0 +1,8 @@ +trait RepeatedSink { def accept(a: Any*): Unit } + +object Test { + def main(args: Array[String]): Unit = { + val f: RepeatedSink = (a) => println(a) + f.accept(1) + } +}
\ No newline at end of file diff --git a/test/files/run/t1994.scala b/test/files/run/t1994.scala new file mode 100644 index 0000000000..0b463e3444 --- /dev/null +++ b/test/files/run/t1994.scala @@ -0,0 +1,20 @@ +class A { + protected def x = 0 + protected[A] def y = 0 +} + +class B extends A { + override def x = 1 + def superY = super[A].y + override def y = 1 +} + + +object Test { + def main(args: Array[String]): Unit = { + val b = new B + assert(b.x == 1) + assert(b.y == 1) + assert(b.superY == 0) + } +} diff --git a/test/files/run/t2866.check b/test/files/run/t2866.check new file mode 100644 index 0000000000..7f52da85fb --- /dev/null +++ b/test/files/run/t2866.check @@ -0,0 +1,3 @@ +t2866.scala:30: warning: imported `one' is permanently hidden by definition of value one + import A.one // warning: imported `one' is permanently hidden by definition of value one. + ^ diff --git a/test/files/run/t2866.scala b/test/files/run/t2866.scala new file mode 100644 index 0000000000..8059107583 --- /dev/null +++ b/test/files/run/t2866.scala @@ -0,0 +1,44 @@ +// for 2.7.x compatibility + +object A { + implicit val one = 1 +} + +object Test extends App { + + locally { + import A._ + locally { + // assert(implicitly[Int] == 1) // error: could not find implicit value for parameter e: Int. + // !!! Why one A.one? + // (I assume you mean: why _not_ A.one? A.one is shadowed by local one. + // but the local one cannot be used yet because it does not have an explicit type. + implicit val one = 2 + assert(implicitly[Int] == 2) + assert(one == 2) + } + } + + locally { + import A._ + implicit val one: Int = 2 + assert(implicitly[Int] == 2) + assert(one == 2) + } + + locally { + import A.one // warning: imported `one' is permanently hidden by definition of value one. + // !!! Really? + //assert(implicitly[Int] == 1) + implicit val one = 2 + assert(implicitly[Int] == 2) // !!! why not 2? + assert(one == 2) + } + + locally { + import A.{one => _, _} + implicit val two = 2 + assert(implicitly[Int] == 2) // not ambiguous in 2.8.0 nor im ambiguous in 2.7.6 + } + +} diff --git a/test/files/run/t3516.check b/test/files/run/t3516.check deleted file mode 100644 index d0d10d82fa..0000000000 --- a/test/files/run/t3516.check +++ /dev/null @@ -1,3 +0,0 @@ -1 -1 -21 diff --git a/test/files/run/t3516.scala b/test/files/run/t3516.scala deleted file mode 100644 index aa302ce85a..0000000000 --- a/test/files/run/t3516.scala +++ /dev/null @@ -1,13 +0,0 @@ -object Test { - def mkIterator = (1 to 5).iterator map (x => { println(x) ; x }) - def mkInfinite = Iterator continually { println(1) ; 1 } - - def main(args: Array[String]): Unit = { - // Stream is strict in its head so we should see 1 from each of them. - val s1 = mkIterator.toStream - val s2 = mkInfinite.toStream - // back and forth without slipping into nontermination. - println((Stream from 1).toIterator.drop(10).toStream.drop(10).toIterator.next) - () - } -} diff --git a/test/files/run/t5665.scala b/test/files/run/t5665.scala new file mode 100644 index 0000000000..3ac498b5c0 --- /dev/null +++ b/test/files/run/t5665.scala @@ -0,0 +1,13 @@ +object O { + trait T { + private[this] val c: Int = 42 + def f = + { x: Int => c } + } +} + +object Test { + def main(args: Array[String]): Unit = { + assert(new O.T{}.f(0) == 42) + } +} diff --git a/test/files/run/t6502.check b/test/files/run/t6502.check new file mode 100644 index 0000000000..95d36ee221 --- /dev/null +++ b/test/files/run/t6502.check @@ -0,0 +1,8 @@ +test1 res1: true +test1 res2: true +test2 res1: true +test2 res2: true +test3 res1: true +test3 res2: true +test4 res1: true +test4 res2: true diff --git a/test/files/run/t6502.scala b/test/files/run/t6502.scala new file mode 100644 index 0000000000..ced1b5812d --- /dev/null +++ b/test/files/run/t6502.scala @@ -0,0 +1,101 @@ +import scala.tools.partest._ +import java.io.File +import scala.tools.nsc.interpreter.ILoop + +object Test extends StoreReporterDirectTest { + def code = ??? + + def compileCode(code: String, jarFileName: String) = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", s"${testOutput.path}/$jarFileName"))(code) + } + + def app1 = """ + package test + + object Test extends App { + def test(): Unit = { + println("testing...") + } + }""" + + def app2 = """ + package test + + object Test extends App { + def test(): Unit = { + println("testing differently...") + } + }""" + + def app3 = """ + package test + + object Test3 extends App { + def test(): Unit = { + println("new object in existing package") + } + }""" + + def test1(): Unit = { + val jar = "test1.jar" + compileCode(app1, jar) + + val output = ILoop.run(List(s":require ${testOutput.path}/$jar", "test.Test.test()")) + val lines = output.split("\n") + val res1 = lines(4).contains("Added") && lines(4).contains("test1.jar") + val res2 = lines(lines.length-3).contains("testing...") + + println(s"test1 res1: $res1") + println(s"test1 res2: $res2") + } + + def test2(): Unit = { + // should reject jars with conflicting entries + val jar1 = "test1.jar" + val jar2 = "test2.jar" + compileCode(app2, jar2) + + val output = ILoop.run(List(s":require ${testOutput.path}/$jar1", s":require ${testOutput.path}/$jar2")) + val lines = output.split("\n") + val res1 = lines(4).contains("Added") && lines(4).contains("test1.jar") + val res2 = lines(lines.length-3).contains("test2.jar") && lines(lines.length-3).contains("existing classpath entries conflict") + + println(s"test2 res1: $res1") + println(s"test2 res2: $res2") + } + + def test3(): Unit = { + // should accept jars with overlapping packages, but no conflicts + val jar1 = "test1.jar" + val jar3 = "test3.jar" + compileCode(app3, jar3) + + val output = ILoop.run(List(s":require ${testOutput.path}/$jar1", s":require ${testOutput.path}/$jar3", "test.Test3.test()")) + val lines = output.split("\n") + val res1 = lines(4).contains("Added") && lines(4).contains("test1.jar") + val res2 = lines(lines.length-3).contains("new object in existing package") + + println(s"test3 res1: $res1") + println(s"test3 res2: $res2") + } + + def test4(): Unit = { + // twice the same jar should be rejected + val jar1 = "test1.jar" + val output = ILoop.run(List(s":require ${testOutput.path}/$jar1", s":require ${testOutput.path}/$jar1")) + val lines = output.split("\n") + val res1 = lines(4).contains("Added") && lines(4).contains("test1.jar") + val res2 = lines(lines.length-3).contains("test1.jar") && lines(lines.length-3).contains("existing classpath entries conflict") + + println(s"test4 res1: $res1") + println(s"test4 res2: $res2") + } + + def show(): Unit = { + test1() + test2() + test3() + test4() + } +} diff --git a/test/files/run/t7407.flags b/test/files/run/t7407.flags index c8547a27dc..be4ef0798a 100644 --- a/test/files/run/t7407.flags +++ b/test/files/run/t7407.flags @@ -1 +1 @@ --Ynooptimise -Ybackend:GenBCode +-Ynooptimise -Yopt:l:none -Ybackend:GenBCode diff --git a/test/files/run/t8925.check b/test/files/run/t8925.check new file mode 100644 index 0000000000..112e7005df --- /dev/null +++ b/test/files/run/t8925.check @@ -0,0 +1,2 @@ +bar +abcd diff --git a/test/files/run/t8925.flags b/test/files/run/t8925.flags new file mode 100644 index 0000000000..be4ef0798a --- /dev/null +++ b/test/files/run/t8925.flags @@ -0,0 +1 @@ +-Ynooptimise -Yopt:l:none -Ybackend:GenBCode diff --git a/test/files/run/t8925.scala b/test/files/run/t8925.scala new file mode 100644 index 0000000000..33f4505f03 --- /dev/null +++ b/test/files/run/t8925.scala @@ -0,0 +1,31 @@ +object Ex { + def unapply(t: Throwable): Option[Throwable] = Some(t) +} + +class A { + var x = "" + + def bar = + try { + "bar" + } finally { + try { + x += "a" + } finally { + x += "b" + try { + x += "c" + throw null + } catch { + case Ex(_) => + x += "d" + } + } + } +} + +object Test extends App { + val a = new A + println(a.bar) + println(a.x) +} diff --git a/test/files/run/t8933.check b/test/files/run/t8933.check new file mode 100644 index 0000000000..d5ef468b98 --- /dev/null +++ b/test/files/run/t8933.check @@ -0,0 +1 @@ +'traitSymbol diff --git a/test/files/run/t8933/A_1.scala b/test/files/run/t8933/A_1.scala new file mode 100644 index 0000000000..996e3b4a2c --- /dev/null +++ b/test/files/run/t8933/A_1.scala @@ -0,0 +1,6 @@ +class MotherClass + +trait MixinWithSymbol { + self: MotherClass => + def symbolFromTrait: Symbol = 'traitSymbol +} diff --git a/test/files/run/t8933/Test_2.scala b/test/files/run/t8933/Test_2.scala new file mode 100644 index 0000000000..c506a7c51f --- /dev/null +++ b/test/files/run/t8933/Test_2.scala @@ -0,0 +1,10 @@ +class MotherClass extends MixinWithSymbol { + val classSymbol = 'classSymbol +} + +object Test { + def main(args: Array[String]) { + val symbol = (new MotherClass).symbolFromTrait + println(symbol) + } +} diff --git a/test/files/run/t8933b/A.scala b/test/files/run/t8933b/A.scala new file mode 100644 index 0000000000..d25d893c6f --- /dev/null +++ b/test/files/run/t8933b/A.scala @@ -0,0 +1,4 @@ +trait MixinWithSymbol { + self: MotherClass => + def symbolFromTrait: Any = 'traitSymbol +} diff --git a/test/files/run/t8933b/Test.scala b/test/files/run/t8933b/Test.scala new file mode 100644 index 0000000000..46eedd660f --- /dev/null +++ b/test/files/run/t8933b/Test.scala @@ -0,0 +1,9 @@ +class MotherClass extends MixinWithSymbol { + def foo = 'sym1 +} + +object Test { + def main(args: Array[String]) { + (new MotherClass).symbolFromTrait + } +} diff --git a/test/files/run/t8933c.scala b/test/files/run/t8933c.scala new file mode 100644 index 0000000000..22011bc323 --- /dev/null +++ b/test/files/run/t8933c.scala @@ -0,0 +1,14 @@ +object Test { + def main(args: Array[String]): Unit = { + try { + {throw T; Symbol}.apply("a") + assert(false, "exception not thrown") + } catch { + case T => // ok + case t: Throwable => + assert(false, "wrong not thrown: " + t) + } + } +} + +object T extends Throwable diff --git a/test/files/run/t8960.scala b/test/files/run/t8960.scala index c6bcd0770c..a58ac53d33 100644 --- a/test/files/run/t8960.scala +++ b/test/files/run/t8960.scala @@ -1,6 +1,12 @@ object Test extends App { def test(o: AnyRef, sp: Boolean = false) = { - if (sp) assert(o.getClass.getSuperclass.getName contains "$sp") + val isSpecialized = o.getClass.getSuperclass.getName contains "$sp" + val isDelambdafyMethod = o.getClass.getName contains "$lambda$" + assert( + // delambdafy:method doesn't currently emit specialized anonymous function classes + if (sp) (isSpecialized || isDelambdafyMethod) else !isSpecialized, + o.getClass.getName) + val Some(f) = o.getClass.getDeclaredFields.find(_.getName == "serialVersionUID") assert(f.getLong(null) == 0l) } diff --git a/test/files/t8449/Client.scala b/test/files/t8449/Client.scala new file mode 100644 index 0000000000..5d273f06b2 --- /dev/null +++ b/test/files/t8449/Client.scala @@ -0,0 +1,3 @@ +object Client { + def foo: Any = new Test().foo +} diff --git a/test/files/t8449/Test.java b/test/files/t8449/Test.java new file mode 100644 index 0000000000..ecb1711b24 --- /dev/null +++ b/test/files/t8449/Test.java @@ -0,0 +1,10 @@ +public class Test { + // Raw type over a Scala type constructor + public scala.Function1 foo() { return null; } + // scalac reported: + // % scalac-hash v2.11.2 -d /tmp sandbox/{Test.java,Client.scala} + // sandbox/Test.java:2: error: trait Function1 takes type parameters + // public scala.Function1 foo() { return null; } + // ^ + // one error found +} diff --git a/test/junit/scala/collection/IndexedSeqOptimizedTest.scala b/test/junit/scala/collection/IndexedSeqOptimizedTest.scala index e5382907af..419e1454cb 100644 --- a/test/junit/scala/collection/IndexedSeqOptimizedTest.scala +++ b/test/junit/scala/collection/IndexedSeqOptimizedTest.scala @@ -13,4 +13,17 @@ class IndexedSeqOptimizedTest { assertEquals(0, (Array(2): collection.mutable.WrappedArray[Int]).lastIndexWhere(_ => true, 1)) assertEquals(2, "abc123".lastIndexWhere(_.isLetter, 6)) } + + @Test + def hasCorrectDropAndTakeMethods() { + assertEquals("", "abc" take Int.MinValue) + assertEquals("", "abc" takeRight Int.MinValue) + assertEquals("abc", "abc" drop Int.MinValue) + assertEquals("abc", "abc" dropRight Int.MinValue) + + assertArrayEquals(Array.empty[Int], Array(1, 2, 3) take Int.MinValue) + assertArrayEquals(Array.empty[Int], Array(1, 2, 3) takeRight Int.MinValue) + assertArrayEquals(Array(1, 2, 3), Array(1, 2, 3) drop Int.MinValue) + assertArrayEquals(Array(1, 2, 3), Array(1, 2, 3) dropRight Int.MinValue) + } } diff --git a/test/junit/scala/collection/IterableViewLikeTest.scala b/test/junit/scala/collection/IterableViewLikeTest.scala new file mode 100644 index 0000000000..55da02744b --- /dev/null +++ b/test/junit/scala/collection/IterableViewLikeTest.scala @@ -0,0 +1,20 @@ +package scala.collection + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class IterableViewLikeTest { + + @Test + def hasCorrectDropAndTakeMethods() { + val iter = Iterable(1, 2, 3) + + assertEquals(Iterable.empty[Int], iter.view take Int.MinValue force) + assertEquals(Iterable.empty[Int], iter.view takeRight Int.MinValue force) + assertEquals(iter, iter.view drop Int.MinValue force) + assertEquals(iter, iter.view dropRight Int.MinValue force) + } +} diff --git a/test/junit/scala/collection/IteratorTest.scala b/test/junit/scala/collection/IteratorTest.scala index b7a9805c9f..d5389afd0c 100644 --- a/test/junit/scala/collection/IteratorTest.scala +++ b/test/junit/scala/collection/IteratorTest.scala @@ -6,11 +6,14 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import scala.tools.testing.AssertUtil._ + +import Seq.empty + @RunWith(classOf[JUnit4]) class IteratorTest { - @Test - def groupedIteratorShouldNotAskForUnneededElement(): Unit = { + @Test def groupedIteratorShouldNotAskForUnneededElement(): Unit = { var counter = 0 val it = new Iterator[Int] { var i = 0 ; def hasNext = { counter = i; true } ; def next = { i += 1; i } } val slidingIt = it sliding 2 @@ -25,4 +28,130 @@ class IteratorTest { slidingIt.next assertEquals("Counter should be one, that means we didn't look further than needed", 1, counter) } + + @Test def dropDoesNotGrowStack(): Unit = { + def it = new Iterator[Throwable] { def hasNext = true ; def next = new Throwable } + + assertEquals(it.drop(1).next.getStackTrace.length, it.drop(1).drop(1).next.getStackTrace.length) + } + + @Test def dropIsChainable(): Unit = { + assertSameElements(1 to 4, Iterator from 0 take 5 drop 1) + assertSameElements(3 to 4, Iterator from 0 take 5 drop 3) + assertSameElements(empty, Iterator from 0 take 5 drop 5) + assertSameElements(empty, Iterator from 0 take 5 drop 10) + assertSameElements(0 to 4, Iterator from 0 take 5 drop 0) + assertSameElements(0 to 4, Iterator from 0 take 5 drop -1) + assertSameElements(2 to 8 by 2, Iterator from 0 take 5 drop 1 map (2 * _)) + assertSameElements(2 to 8 by 2, Iterator from 0 take 5 map (2 * _) drop 1) + assertSameElements(3 to 4, Iterator from 0 take 5 drop 1 drop 2) + assertSameElements(3 to 4, Iterator from 0 take 5 drop 2 drop 1) + } + + @Test def sliceIsChainable(): Unit = { + assertSameElements(3 to 6, Iterator from 0 slice (3, 7)) + assertSameElements(empty, Iterator from 0 slice (3, 3)) + assertSameElements(0 to 2, Iterator from 0 slice (-1, 3)) + assertSameElements(empty, Iterator from 0 slice (3, -1)) + assertSameElements(6 to 12 by 2, Iterator from 0 slice (3, 7) map (2 * _)) + assertSameElements(6 to 12 by 2, Iterator from 0 map (2 * _) slice (3, 7)) + assertSameElements(4 to 6, Iterator from 0 slice (3, 7) drop 1) + assertSameElements(4 to 7, Iterator from 0 drop 1 slice (3, 7)) + assertSameElements(4 to 5, Iterator from 0 slice (3, 7) slice (1, 3)) + assertSameElements(4 to 6, Iterator from 0 slice (3, 7) slice (1, 10)) + } + + // test/files/run/iterator-concat.scala + @Test def concatIsStackFriendly(): Unit = { + // Create `size` Function0s, each of which evaluates to an Iterator + // which produces 1. Then fold them over ++ to get a single iterator, + // which should sum to "size". + def mk(size: Int): Iterator[Int] = { + //val closures = (1 to size).toList.map(x => (() => Iterator(1))) + //closures.foldLeft(Iterator.empty: Iterator[Int])((res, f) => res ++ f()) + List.fill(size)(() => Iterator(1)).foldLeft(Iterator.empty: Iterator[Int])((res, f) => res ++ f()) + } + assertEquals(100, mk(100).sum) + assertEquals(1000, mk(1000).sum) + assertEquals(10000, mk(10000).sum) + assertEquals(100000, mk(100000).sum) + } + + @Test def from(): Unit = { + val it1 = Iterator.from(-1) + val it2 = Iterator.from(0, -1) + assertEquals(-1, it1.next()) + assertEquals(0, it2.next()) + } + @Test def range(): Unit = { + assertEquals(5, Iterator.range(0, 10, 2).size) + assertEquals(0, Iterator.range(0, 10, -2).size) + assertEquals(5, Iterator.range(10, 0, -2).size) + assertEquals(0, Iterator.range(10, 0, 2).size) + assertEquals(1, Iterator.range(0, 10, 11).size) + assertEquals(10, Iterator.range(0, 10, 1).size) + assertEquals(10, Iterator.range(10, 0, -1).size) + } + @Test def range3(): Unit = { + val r1 = Iterator.range(0, 10) + assertTrue(r1 contains 5) + assertTrue(r1 contains 6) + assertFalse(r1 contains 4) + val r2a = Iterator.range(0, 10, 2) + assertFalse(r2a contains 5) + val r2b = Iterator.range(0, 10, 2) + assertTrue(r2b contains 6) + val r3 = Iterator.range(0, 10, 11) + assertFalse(r3 contains 5) + assertTrue(r3.isEmpty) + } + @Test def take(): Unit = { + assertEquals(10, (Iterator from 0 take 10).size) + } + @Test def foreach(): Unit = { + val it1 = Iterator.from(0) take 20 + var n = 0 + it1 foreach { n += _ } + assertEquals(190, n) + } + // ticket #429 + @Test def fromArray(): Unit = { + val a = List(1, 2, 3, 4).toArray + var xs0 = a.iterator.toList; + var xs1 = a.slice(0, 1).iterator + var xs2 = a.slice(0, 2).iterator + var xs3 = a.slice(0, 3).iterator + var xs4 = a.slice(0, 4).iterator + assertEquals(14, xs0.size + xs1.size + xs2.size + xs3.size + xs4.size) + } + @Test def toSeq(): Unit = { + assertEquals("1x2x3x4x5", List(1, 2, 3, 4, 5).iterator.mkString("x")) + } + @Test def indexOf(): Unit = { + assertEquals(3, List(1, 2, 3, 4, 5).iterator.indexOf(4)) + assertEquals(-1, List(1, 2, 3, 4, 5).iterator.indexOf(16)) + } + @Test def indexWhere(): Unit = { + assertEquals(3, List(1, 2, 3, 4, 5).iterator.indexWhere { x: Int => x >= 4 }) + assertEquals(-1, List(1, 2, 3, 4, 5).iterator.indexWhere { x: Int => x >= 16 }) + } + // iterator-iterate-lazy.scala + // was java.lang.UnsupportedOperationException: tail of empty list + @Test def iterateIsSufficientlyLazy(): Unit = { + //Iterator.iterate((1 to 5).toList)(_.tail).takeWhile(_.nonEmpty).toList // suffices + Iterator.iterate((1 to 5).toList)(_.tail).takeWhile(_.nonEmpty).map(_.head).toList + } + // SI-3516 + @Test def toStreamIsSufficientlyLazy(): Unit = { + val results = collection.mutable.ListBuffer.empty[Int] + def mkIterator = (1 to 5).iterator map (x => { results += x ; x }) + def mkInfinite = Iterator continually { results += 1 ; 1 } + + // Stream is strict in its head so we should see 1 from each of them. + val s1 = mkIterator.toStream + val s2 = mkInfinite.toStream + // back and forth without slipping into nontermination. + results += (Stream from 1).toIterator.drop(10).toStream.drop(10).toIterator.next() + assertSameElements(List(1,1,21), results) + } } diff --git a/test/junit/scala/collection/immutable/TreeMapTest.scala b/test/junit/scala/collection/immutable/TreeMapTest.scala new file mode 100644 index 0000000000..4c21b94b24 --- /dev/null +++ b/test/junit/scala/collection/immutable/TreeMapTest.scala @@ -0,0 +1,20 @@ +package scala.collection.immutable + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class TreeMapTest { + + @Test + def hasCorrectDropAndTakeMethods() { + val tree = TreeMap(1 -> "a", 2 -> "b", 3 -> "c") + + assertEquals(TreeMap.empty[Int, String], tree take Int.MinValue) + assertEquals(TreeMap.empty[Int, String], tree takeRight Int.MinValue) + assertEquals(tree, tree drop Int.MinValue) + assertEquals(tree, tree dropRight Int.MinValue) + } +} diff --git a/test/junit/scala/collection/immutable/TreeSetTest.scala b/test/junit/scala/collection/immutable/TreeSetTest.scala new file mode 100644 index 0000000000..8efe1bfeb8 --- /dev/null +++ b/test/junit/scala/collection/immutable/TreeSetTest.scala @@ -0,0 +1,20 @@ +package scala.collection.immutable + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class TreeSetTest { + + @Test + def hasCorrectDropAndTakeMethods() { + val set = TreeSet(1, 2, 3) + + assertEquals(TreeSet.empty[Int], set take Int.MinValue) + assertEquals(TreeSet.empty[Int], set takeRight Int.MinValue) + assertEquals(set, set drop Int.MinValue) + assertEquals(set, set dropRight Int.MinValue) + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala index b892eb36cf..c1c5a71b83 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala @@ -7,6 +7,8 @@ import scala.reflect.io.VirtualDirectory import scala.tools.asm.Opcodes import scala.tools.asm.tree.{AbstractInsnNode, LabelNode, ClassNode, MethodNode} import scala.tools.cmd.CommandLineParser +import scala.tools.nsc.backend.jvm.opt.LocalOpt +import scala.tools.nsc.settings.{MutableSettings, ScalaSettings} import scala.tools.nsc.{Settings, Global} import scala.tools.partest.ASMConverters import scala.collection.JavaConverters._ @@ -79,4 +81,23 @@ object CodeGenTools { def getSingleMethod(classNode: ClassNode, name: String): Method = convertMethod(classNode.methods.asScala.toList.find(_.name == name).get) + + def assertHandlerLabelPostions(h: ExceptionHandler, instructions: List[Instruction], startIndex: Int, endIndex: Int, handlerIndex: Int): Unit = { + val insVec = instructions.toVector + assertTrue(h.start == insVec(startIndex) && h.end == insVec(endIndex) && h.handler == insVec(handlerIndex)) + } + + val localOpt = { + val settings = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + settings.processArguments(List("-Yopt:l:method"), processAll = true) + new LocalOpt(settings) + } + + import scala.language.implicitConversions + + implicit def aliveInstruction(ins: Instruction): (Instruction, Boolean) = (ins, true) + + implicit class MortalInstruction(val ins: Instruction) extends AnyVal { + def dead: (Instruction, Boolean) = (ins, false) + } } diff --git a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala index 2fb5bb8052..89900291ca 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala @@ -10,13 +10,12 @@ import scala.tools.partest.ASMConverters._ @RunWith(classOf[JUnit4]) class DirectCompileTest { - val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode") + val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:method") @Test def testCompile(): Unit = { val List(("C.class", bytes)) = compile(compiler)( - """ - |class C { + """class C { | def f = 1 |} """.stripMargin) @@ -26,19 +25,12 @@ class DirectCompileTest { @Test def testCompileClasses(): Unit = { - val List(cClass, cModuleClass) = compileClasses(compiler)( - """ - |class C - |object C - """.stripMargin) + val List(cClass, cModuleClass) = compileClasses(compiler)("class C; object C") assertTrue(cClass.name == "C") assertTrue(cModuleClass.name == "C$") - val List(dMirror, dModuleClass) = compileClasses(compiler)( - """ - |object D - """.stripMargin) + val List(dMirror, dModuleClass) = compileClasses(compiler)("object D") assertTrue(dMirror.name == "D") assertTrue(dModuleClass.name == "D$") @@ -47,35 +39,35 @@ class DirectCompileTest { @Test def testCompileMethods(): Unit = { val List(f, g) = compileMethods(compiler)( - """ - |def f = 10 + """def f = 10 |def g = f """.stripMargin) assertTrue(f.name == "f") assertTrue(g.name == "g") - assertTrue(instructionsFromMethod(f).dropNonOp === + assertSameCode(instructionsFromMethod(f).dropNonOp, List(IntOp(BIPUSH, 10), Op(IRETURN))) - assertTrue(instructionsFromMethod(g).dropNonOp === - List(VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "f", "()I", false), Op(IRETURN))) + assertSameCode(instructionsFromMethod(g).dropNonOp, + List(VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "f", "()I", itf = false), Op(IRETURN))) } @Test def testDropNonOpAliveLabels(): Unit = { + // makes sure that dropNoOp doesn't drop labels that are being used val List(f) = compileMethods(compiler)("""def f(x: Int) = if (x == 0) "a" else "b"""") - assertTrue(instructionsFromMethod(f).dropNonOp === List( - VarOp(ILOAD, 1), - Op(ICONST_0), - Jump(IF_ICMPEQ, Label(6)), - Jump(GOTO, Label(10)), - Label(6), - Ldc(LDC, "a"), - Jump(GOTO, Label(13)), - Label(10), - Ldc(LDC, "b"), - Label(13), - Op(ARETURN) + assertSameCode(instructionsFromMethod(f).dropLinesFrames, List( + Label(0), + VarOp(ILOAD, 1), + Op(ICONST_0), + Jump(IF_ICMPNE, + Label(7)), + Ldc(LDC, "a"), + Op(ARETURN), + Label(7), + Ldc(LDC, "b"), + Op(ARETURN), + Label(11) )) } } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala new file mode 100644 index 0000000000..fc748196d0 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala @@ -0,0 +1,80 @@ +package scala.tools.nsc +package backend.jvm +package opt + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.tools.asm.Opcodes._ +import org.junit.Assert._ + +import CodeGenTools._ +import scala.tools.partest.ASMConverters +import ASMConverters._ + +@RunWith(classOf[JUnit4]) +class CompactLocalVariablesTest { + + // recurse-unreachable-jumps is required for eliminating catch blocks, in the first dce round they + // are still live.only after eliminating the empty handler the catch blocks become unreachable. + val methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code,recurse-unreachable-jumps,compact-locals") + val noCompactVarsCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code,recurse-unreachable-jumps") + + @Test + def compactUnused(): Unit = { + val code = + """def f: Double = { + | try { } + | catch { + | case _: Throwable => + | // eliminated by dce + | val i = 1 + | val d = 1d + | val f = 1f + | val l = 1l + | } + | + | val i = 1 // variable index 1 (it's an instance method, so at index 0 we have `this`) + | val d = 1d // 2,3 + | val f = 1f // 4 + | val l = 1l // 5,6 + | + | try { } + | catch { + | case _: Throwable => + | // eliminated by dce + | val i = 1 + | val d = 1d + | val f = 1f + | val l = 1l + | } + | + | val ii = 1 // 7 + | val dd = 1d // 8,9 + | val ff = 1f // 10 + | val ll = 1l // 11,12 + | + | i + ii + d + dd + f + ff + l + ll + |} + |""".stripMargin + + val List(noCompact) = compileMethods(noCompactVarsCompiler)(code) + val List(withCompact) = compileMethods(methodOptCompiler)(code) + + // code is the same, except for local var indices + assertTrue(noCompact.instructions.size == withCompact.instructions.size) + + val varOpSlots = convertMethod(withCompact).instructions collect { + case VarOp(_, v) => v + } + assertTrue(varOpSlots.toString, varOpSlots == List(1, 2, 4, 5, 7, 8, 10, 11, // stores + 1, 7, 2, 8, 4, 10, 5, 11)) // loads + + // the local variables descriptor table is cleaned up to remove stale entries after dce, + // also when the slots are not compacted + assertTrue(noCompact.localVariables.size == withCompact.localVariables.size) + + assertTrue(noCompact.maxLocals == 25) + assertTrue(withCompact.maxLocals == 13) + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala index 57fa1a7b66..7d83c54b5b 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala @@ -26,7 +26,7 @@ class EmptyExceptionHandlersTest { Op(RETURN) ) assertTrue(convertMethod(asmMethod).handlers.length == 1) - LocalOpt.removeEmptyExceptionHandlers(asmMethod) + localOpt.removeEmptyExceptionHandlers(asmMethod) assertTrue(convertMethod(asmMethod).handlers.isEmpty) } @@ -35,12 +35,8 @@ class EmptyExceptionHandlersTest { val handlers = List(ExceptionHandler(Label(1), Label(2), Label(2), Some(exceptionDescriptor))) val asmMethod = genMethod(handlers = handlers)( Label(1), // nops only - Op(NOP), - Op(NOP), Jump(GOTO, Label(3)), - Op(NOP), Label(3), - Op(NOP), Jump(GOTO, Label(4)), Label(2), // handler @@ -51,7 +47,7 @@ class EmptyExceptionHandlersTest { Op(RETURN) ) assertTrue(convertMethod(asmMethod).handlers.length == 1) - LocalOpt.removeEmptyExceptionHandlers(asmMethod) + localOpt.removeEmptyExceptionHandlers(asmMethod) assertTrue(convertMethod(asmMethod).handlers.isEmpty) } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyLabelsAndLineNumbersTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyLabelsAndLineNumbersTest.scala new file mode 100644 index 0000000000..8c0168826e --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyLabelsAndLineNumbersTest.scala @@ -0,0 +1,99 @@ +package scala.tools.nsc +package backend.jvm +package opt + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.tools.asm.Opcodes._ +import org.junit.Assert._ +import scala.tools.testing.AssertUtil._ + +import CodeGenTools._ +import scala.tools.partest.ASMConverters +import ASMConverters._ + +@RunWith(classOf[JUnit4]) +class EmptyLabelsAndLineNumbersTest { + @Test + def removeEmptyLineNumbers(): Unit = { + val ops = List[(Instruction, Boolean)]( + Label(1), + LineNumber(1, Label(1)), + Label(2), + Label(3), + Op(RETURN), + + Label(4), + LineNumber(4, Label(4)).dead, + LineNumber(5, Label(4)), + Op(RETURN), + + Label(5), + LineNumber(6, Label(5)).dead, + Label(6), + Label(7), + LineNumber(7, Label(7)), + Op(RETURN), + + Label(9), + LineNumber(8, Label(9)).dead, + Label(10) + ) + + val method = genMethod()(ops.map(_._1): _*) + assertTrue(localOpt.removeEmptyLineNumbers(method)) + assertSameCode(instructionsFromMethod(method), ops.filter(_._2).map(_._1)) + } + + @Test + def badlyLocatedLineNumbers(): Unit = { + def t(ops: Instruction*) = + assertThrows[AssertionError](localOpt.removeEmptyLineNumbers(genMethod()(ops: _*))) + + // line numbers have to be right after their referenced label node + t(LineNumber(0, Label(1)), Label(1)) + t(Label(0), Label(1), LineNumber(0, Label(0))) + } + + @Test + def removeEmptyLabels(): Unit = { + val handler = List(ExceptionHandler(Label(4), Label(5), Label(6), Some("java/lang/Throwable"))) + def ops(target1: Int, target2: Int, target3: Int, target4: Int, target5: Int, target6: Int) = List[(Instruction, Boolean)]( + Label(1), + Label(2).dead, + Label(3).dead, + LineNumber(3, Label(target1)), + VarOp(ILOAD, 1), + Jump(IFGE, Label(target2)), + + Label(4), + Label(5).dead, + Label(6).dead, + VarOp(ILOAD, 2), + Jump(IFGE, Label(target3)), + + Label(7), + Label(8).dead, + Label(9).dead, + Op(RETURN), + + LookupSwitch(LOOKUPSWITCH, Label(target4), List(1,2), List(Label(target4), Label(target5))), + TableSwitch(TABLESWITCH, 1, 2, Label(target4), List(Label(target4), Label(target5))), + + Label(10), + LineNumber(10, Label(10)), + Label(11).dead, + LineNumber(12, Label(target6)) + ) + + val method = genMethod(handlers = handler)(ops(2, 3, 8, 8, 9, 11).map(_._1): _*) + assertTrue(localOpt.removeEmptyLabelNodes(method)) + val m = convertMethod(method) + assertSameCode(m.instructions, ops(1, 1, 7, 7, 7, 10).filter(_._2).map(_._1)) + assertTrue(m.handlers match { + case List(ExceptionHandler(Label(4), Label(4), Label(4), _)) => true + case _ => false + }) + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala new file mode 100644 index 0000000000..5b0f0f238a --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala @@ -0,0 +1,83 @@ +package scala.tools.nsc +package backend.jvm +package opt + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.tools.asm.Opcodes._ +import org.junit.Assert._ + +import scala.tools.testing.AssertUtil._ + +import CodeGenTools._ +import scala.tools.partest.ASMConverters +import ASMConverters._ + +@RunWith(classOf[JUnit4]) +class MethodLevelOpts { + val methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method") + + def wrapInDefault(code: Instruction*) = List(Label(0), LineNumber(1, Label(0))) ::: code.toList ::: List(Label(1)) + + @Test + def eliminateEmptyTry(): Unit = { + val code = "def f = { try {} catch { case _: Throwable => 0; () }; 1 }" + assertSameCode(singleMethodInstructions(methodOptCompiler)(code), wrapInDefault(Op(ICONST_1), Op(IRETURN))) + } + + @Test + def cannotEliminateLoadBoxedUnit(): Unit = { + // the compiler inserts a boxed into the try block. it's therefore non-empty (and live) and not eliminated. + val code = "def f = { try {} catch { case _: Throwable => 0 }; 1 }" + val m = singleMethod(methodOptCompiler)(code) + assertTrue(m.handlers.length == 1) + assertSameCode(m.instructions.take(3), List(Label(0), LineNumber(1, Label(0)), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"))) + } + + @Test + def inlineThrowInCatchNotTry(): Unit = { + // the try block does not contain the `ATHROW` instruction, but in the catch block, `ATHROW` is inlined + val code = "def f(e: Exception) = throw { try e catch { case _: Throwable => e } }" + val m = singleMethod(methodOptCompiler)(code) + assertHandlerLabelPostions(m.handlers.head, m.instructions, 0, 3, 5) + assertSameCode(m.instructions, + wrapInDefault(VarOp(ALOAD, 1), Label(3), Op(ATHROW), Label(5), FrameEntry(4, List(), List("java/lang/Throwable")), Op(POP), VarOp(ALOAD, 1), Op(ATHROW)) + ) + } + + @Test + def inlineReturnInCachtNotTry(): Unit = { + val code = "def f: Int = return { try 1 catch { case _: Throwable => 2 } }" + // cannot inline the IRETURN into the try block (because RETURN may throw IllegalMonitorState) + val m = singleMethod(methodOptCompiler)(code) + assertHandlerLabelPostions(m.handlers.head, m.instructions, 0, 3, 5) + assertSameCode(m.instructions, + wrapInDefault(Op(ICONST_1), Label(3), Op(IRETURN), Label(5), FrameEntry(4, List(), List("java/lang/Throwable")), Op(POP), Op(ICONST_2), Op(IRETURN))) + } + + @Test + def simplifyJumpsInTryCatchFinally(): Unit = { + val code = + """def f: Int = + | try { + | return 1 + | } catch { + | case _: Throwable => + | return 2 + | } finally { + | return 2 + | // dead + | val x = try 10 catch { case _: Throwable => 11 } + | println(x) + | } + """.stripMargin + val m = singleMethod(methodOptCompiler)(code) + assertTrue(m.handlers.length == 2) + assertSameCode(m.instructions.dropNonOp, // drop line numbers and lables that are only used by line numbers + + // one single label left :-) + List(Op(ICONST_1), VarOp(ISTORE, 2), Jump(GOTO, Label(20)), Op(POP), Op(ICONST_2), VarOp(ISTORE, 2), Jump(GOTO, Label(20)), VarOp(ASTORE, 3), Op(ICONST_2), Op(IRETURN), Label(20), Op(ICONST_2), Op(IRETURN)) + ) + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala new file mode 100644 index 0000000000..360fa1d23d --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala @@ -0,0 +1,221 @@ +package scala.tools.nsc +package backend.jvm +package opt + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.tools.asm.Opcodes._ +import org.junit.Assert._ + +import CodeGenTools._ +import scala.tools.partest.ASMConverters +import ASMConverters._ + +@RunWith(classOf[JUnit4]) +class SimplifyJumpsTest { + @Test + def simpleGotoReturn(): Unit = { + val ops = List( + Jump(GOTO, Label(2)), // replaced by RETURN + Op(ICONST_1), // need some code, otherwise removeJumpToSuccessor kicks in + Op(POP), + Label(1), // multiple labels OK + Label(2), + Label(3), + Op(RETURN) + ) + val method = genMethod()(ops: _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), Op(RETURN) :: ops.tail) + } + + @Test + def simpleGotoThrow(): Unit = { + val rest = List( + Op(ICONST_1), // need some code, otherwise removeJumpToSuccessor kicks in + Op(POP), + Label(1), + Label(2), + Label(3), + Op(ATHROW) + ) + val method = genMethod()( + Op(ACONST_NULL) :: + Jump(GOTO, Label(2)) :: // replaced by ATHROW + rest: _* + ) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), Op(ACONST_NULL) :: Op(ATHROW) :: rest) + } + + @Test + def gotoThrowInTry(): Unit = { + val handler = List(ExceptionHandler(Label(1), Label(2), Label(4), Some("java/lang/Throwable"))) + val initialInstrs = List( + Label(1), + Op(ACONST_NULL), + Jump(GOTO, Label(3)), // not by ATHROW (would move the ATHROW into a try block) + Label(2), + Op(ICONST_1), // need some code, otherwise removeJumpToSuccessor kicks in + Op(POP), + Label(3), + Op(ATHROW), + Label(4), + Op(POP), + Op(RETURN) + ) + val method = genMethod(handlers = handler)(initialInstrs: _*) + assertFalse(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), initialInstrs) + + val optMethod = genMethod()(initialInstrs: _*) // no handler + assertTrue(localOpt.simplifyJumps(optMethod)) + assertSameCode(instructionsFromMethod(optMethod).take(3), List(Label(1), Op(ACONST_NULL), Op(ATHROW))) + } + + @Test + def simplifyBranchOverGoto(): Unit = { + val begin = List( + VarOp(ILOAD, 1), + Jump(IFGE, Label(2)) + ) + val rest = List( + Jump(GOTO, Label(3)), + Label(11), // other labels here are allowed + Label(2), + VarOp(ILOAD, 1), + Op(RETURN), + Label(3), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + val method = genMethod()(begin ::: rest: _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode( + instructionsFromMethod(method), + List(VarOp(ILOAD, 1), Jump(IFLT, Label(3))) ::: rest.tail ) + + // no label allowed between begin and rest. if there's another label, then there could be a + // branch that label. eliminating the GOTO would change the behavior. + val nonOptMethod = genMethod()(begin ::: Label(22) :: rest: _*) + assertFalse(localOpt.simplifyJumps(nonOptMethod)) + } + + @Test + def ensureGotoRemoved(): Unit = { + def code(jumps: Instruction*) = List( + VarOp(ILOAD, 1)) ::: jumps.toList ::: List( + Label(2), + + Op(RETURN), + Label(3), + Op(RETURN) + ) + + // ensures that the goto is safely removed. ASM supports removing while iterating, but not the + // next element of the current. Here, the current is the IFGE, the next is the GOTO. + val method = genMethod()(code(Jump(IFGE, Label(2)), Jump(GOTO, Label(3))): _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), code(Jump(IFLT, Label(3)))) + } + + @Test + def removeJumpToSuccessor(): Unit = { + val ops = List( + Jump(GOTO, Label(1)), + Label(11), + Label(1), + Label(2), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + val method = genMethod()(ops: _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops.tail) + } + + @Test + def collapseJumpChains(): Unit = { + def ops(target1: Int, target2: Int, target3: Int) = List( + VarOp(ILOAD, 1), + Jump(IFGE, Label(target1)), // initially 1, then 3 + VarOp(ILOAD, 1), + Op(IRETURN), + + Label(2), + Jump(GOTO, Label(target3)), + + Label(1), + Jump(GOTO, Label(target2)), // initially 2, then 3 + + VarOp(ILOAD, 1), // some code to prevent jumpToSuccessor optimization (once target2 is replaced by 3) + Op(RETURN), + + Label(3), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + val method = genMethod()(ops(1, 2, 3): _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(3, 3, 3)) + } + + @Test + def collapseJumpChainLoop(): Unit = { + def ops(target: Int) = List( + VarOp(ILOAD, 1), + Jump(IFGE, Label(target)), + + Label(4), + Jump(GOTO, Label(3)), + + VarOp(ILOAD, 1), // some code to prevent jumpToSuccessor (label 3) + Op(IRETURN), + + Label(3), + Jump(GOTO, Label(4)), + + Label(2), + Jump(GOTO, Label(3)) + ) + + val method = genMethod()(ops(2): _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(3)) + } + + @Test + def simplifyThenElseSameTarget(): Unit = { + def ops(jumpOp: Instruction) = List( + VarOp(ILOAD, 1), + jumpOp, + Label(2), + Jump(GOTO, Label(1)), + + VarOp(ILOAD, 1), // some code to prevent jumpToSuccessor (label 1) + Op(IRETURN), + + Label(1), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + + val method = genMethod()(ops(Jump(IFGE, Label(1))): _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(Op(POP))) + } + + @Test + def thenElseSameTargetLoop(): Unit = { + def ops(br: List[Instruction]) = List( + VarOp(ILOAD, 1), + VarOp(ILOAD, 2)) ::: br ::: List( + Label(1), + Jump(GOTO, Label(1)) + ) + val method = genMethod()(ops(List(Jump(IF_ICMPGE, Label(1)))): _*) + assertTrue(localOpt.simplifyJumps(method)) + assertSameCode(instructionsFromMethod(method), ops(List(Op(POP), Op(POP)))) + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala index a3bd7ae6fe..4a45dd9138 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala @@ -16,12 +16,20 @@ import ASMConverters._ @RunWith(classOf[JUnit4]) class UnreachableCodeTest { - import UnreachableCodeTest._ + + def assertEliminateDead(code: (Instruction, Boolean)*): Unit = { + val method = genMethod()(code.map(_._1): _*) + localOpt.removeUnreachableCodeImpl(method, "C") + val nonEliminated = instructionsFromMethod(method) + val expectedLive = code.filter(_._2).map(_._1).toList + assertSameCode(nonEliminated, expectedLive) + } // jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks, // see comment in BCodeBodyBuilder - val dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code") - val noOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none") + val methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method") + val dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code") + val noOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none") // jvm-1.5 disables computing stack map frames, and it emits dead code as-is. val noOptNoFramesCompiler = newCompiler(extraArgs = "-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none") @@ -48,8 +56,8 @@ class UnreachableCodeTest { @Test def eliminateNop(): Unit = { assertEliminateDead( - // not dead, since visited by data flow analysis. need a different opt to eliminate it. - Op(NOP), + // reachable, but removed anyway. + Op(NOP).dead, Op(RETURN), Op(NOP).dead ) @@ -136,28 +144,31 @@ class UnreachableCodeTest { @Test def eliminateDeadCatchBlocks(): Unit = { + // the Label(1) is live: it's used in the local variable descriptor table (local variable "this" has a range from 0 to 1). + def wrapInDefault(code: Instruction*) = List(Label(0), LineNumber(1, Label(0))) ::: code.toList ::: List(Label(1)) + val code = "def f: Int = { return 0; try { 1 } catch { case _: Exception => 2 } }" - assertSameCode(singleMethodInstructions(dceCompiler)(code).dropNonOp, - List(Op(ICONST_0), Op(IRETURN))) + val m = singleMethod(dceCompiler)(code) + assertTrue(m.handlers.isEmpty) // redundant (if code is gone, handler is gone), but done once here for extra safety + assertSameCode(m.instructions, + wrapInDefault(Op(ICONST_0), Op(IRETURN))) val code2 = "def f: Unit = { try { } catch { case _: Exception => () }; () }" - // DCE only removes dead basic blocks, but not NOPs, and also not useless jumps - assertSameCode(singleMethodInstructions(dceCompiler)(code2).dropNonOp, - List(Op(NOP), Jump(GOTO, Label(33)), Label(33), Op(RETURN))) + // requires fixpoint optimization of methodOptCompiler (dce alone is not enough): first the handler is eliminated, then it's dead catch block. + assertSameCode(singleMethodInstructions(methodOptCompiler)(code2), wrapInDefault(Op(RETURN))) val code3 = "def f: Unit = { try { } catch { case _: Exception => try { } catch { case _: Exception => () } }; () }" - assertSameCode(singleMethodInstructions(dceCompiler)(code3).dropNonOp, - List(Op(NOP), Jump(GOTO, Label(33)), Label(33), Op(RETURN))) + assertSameCode(singleMethodInstructions(methodOptCompiler)(code3), wrapInDefault(Op(RETURN))) + // this example requires two iterations to get rid of the outer handler. + // the first iteration of DCE cannot remove the inner handler. then the inner (empty) handler is removed. + // then the second iteration of DCE removes the inner catch block, and then the outer handler is removed. val code4 = "def f: Unit = { try { try { } catch { case _: Exception => () } } catch { case _: Exception => () }; () }" - assertSameCode(singleMethodInstructions(dceCompiler)(code4).dropNonOp, - List(Op(NOP), Jump(GOTO, Label(4)), Label(4), Jump(GOTO, Label(7)), Label(7), Op(RETURN))) + assertSameCode(singleMethodInstructions(methodOptCompiler)(code4), wrapInDefault(Op(RETURN))) } @Test // test the dce-testing tools def metaTest(): Unit = { - assertEliminateDead() // no instructions - assertThrows[AssertionError]( assertEliminateDead(Op(RETURN).dead), _.contains("Expected: List()\nActual : List(Op(RETURN))") @@ -198,20 +209,3 @@ class UnreachableCodeTest { List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(1)), List("java/lang/Object", Label(3))), Label(1), Label(3))) } } - -object UnreachableCodeTest { - import scala.language.implicitConversions - implicit def aliveInstruction(ins: Instruction): (Instruction, Boolean) = (ins, true) - - implicit class MortalInstruction(val ins: Instruction) extends AnyVal { - def dead: (Instruction, Boolean) = (ins, false) - } - - def assertEliminateDead(code: (Instruction, Boolean)*): Unit = { - val cls = wrapInClass(genMethod()(code.map(_._1): _*)) - LocalOpt.removeUnreachableCode(cls) - val nonEliminated = instructionsFromMethod(cls.methods.get(0)) - val expectedLive = code.filter(_._2).map(_._1).toList - assertSameCode(nonEliminated, expectedLive) - } -} diff --git a/test/junit/scala/tools/testing/AssertUtil.scala b/test/junit/scala/tools/testing/AssertUtil.scala index 9b4833d46b..83a637783f 100644 --- a/test/junit/scala/tools/testing/AssertUtil.scala +++ b/test/junit/scala/tools/testing/AssertUtil.scala @@ -1,6 +1,11 @@ package scala.tools package testing +import org.junit.Assert +import Assert.fail +import scala.runtime.ScalaRunTime.stringOf +import scala.collection.{ GenIterable, IterableLike } + /** This module contains additional higher-level assert statements * that are ultimately based on junit.Assert primitives. */ @@ -21,6 +26,19 @@ object AssertUtil { throw e else return } - throw new AssertionError("Expression did not throw!") + fail("Expression did not throw!") } + + /** JUnit-style assertion for `IterableLike.sameElements`. + */ + def assertSameElements[A, B >: A](expected: IterableLike[A, _], actual: GenIterable[B], message: String = ""): Unit = + if (!(expected sameElements actual)) + fail( + f"${ if (message.nonEmpty) s"$message " else "" }expected:<${ stringOf(expected) }> but was:<${ stringOf(actual) }>" + ) + + /** Convenient for testing iterators. + */ + def assertSameElements[A, B >: A](expected: IterableLike[A, _], actual: Iterator[B]): Unit = + assertSameElements(expected, actual.toList, "") } |