summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/files/neg/inlineMaxSize.check4
-rw-r--r--test/files/neg/inlineMaxSize.scala2
-rw-r--r--test/files/neg/t7623.check24
-rw-r--r--test/files/neg/t7623.scala8
-rw-r--r--test/files/pos/t9356/Foo_2.scala6
-rw-r--r--test/files/pos/t9356/MyAnnotation.java12
-rw-r--r--test/files/pos/t9356/Test_3.scala3
-rw-r--r--test/files/run/range.scala18
-rw-r--r--test/files/run/repl-serialization.check25
-rw-r--r--test/files/run/repl-serialization.scala68
-rw-r--r--test/files/run/t7747-repl.check41
-rw-r--r--test/files/run/t7747-repl.scala6
-rw-r--r--test/files/run/toolbox-varargs/Test.scala13
-rw-r--r--test/files/run/toolbox-varargs/Varargs.java8
-rw-r--r--test/junit/scala/collection/IteratorTest.scala10
-rw-r--r--test/junit/scala/collection/immutable/RangeConsistencyTest.scala11
-rw-r--r--test/junit/scala/math/NumericTest.scala28
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala231
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala16
-rw-r--r--test/junit/scala/util/SortingTest.scala69
-rw-r--r--test/scaladoc/run/SI-8210.check1
-rw-r--r--test/scaladoc/run/SI-8210.scala24
22 files changed, 589 insertions, 39 deletions
diff --git a/test/files/neg/inlineMaxSize.check b/test/files/neg/inlineMaxSize.check
index d218a8b6e2..9d790e154c 100644
--- a/test/files/neg/inlineMaxSize.check
+++ b/test/files/neg/inlineMaxSize.check
@@ -2,8 +2,8 @@ inlineMaxSize.scala:7: warning: C::i()I is annotated @inline but could not be in
The size of the callsite method C::j()I
would exceed the JVM method size limit after inlining C::i()I.
- @inline final def j = i + i
- ^
+ @inline final def j = i + i + i
+ ^
error: No warnings can be incurred under -Xfatal-warnings.
one warning found
one error found
diff --git a/test/files/neg/inlineMaxSize.scala b/test/files/neg/inlineMaxSize.scala
index 16dc0d9538..9d2db1a357 100644
--- a/test/files/neg/inlineMaxSize.scala
+++ b/test/files/neg/inlineMaxSize.scala
@@ -4,5 +4,5 @@ class C {
@inline final def g = f + f + f + f + f + f + f + f + f + f
@inline final def h = g + g + g + g + g + g + g + g + g + g
@inline final def i = h + h + h + h + h + h + h + h + h + h
- @inline final def j = i + i
+ @inline final def j = i + i + i
}
diff --git a/test/files/neg/t7623.check b/test/files/neg/t7623.check
index db368dd369..de35023664 100644
--- a/test/files/neg/t7623.check
+++ b/test/files/neg/t7623.check
@@ -1,21 +1,15 @@
-t7623.scala:19: warning: A repeated case parameter or extracted sequence should be matched only by a sequence wildcard (_*).
- def f = "" match { case X(s) => }
+t7623.scala:21: warning: A repeated case parameter or extracted sequence is not matched by a sequence wildcard (_*), and may fail at runtime.
+ def g = "" match { case X(s, t) => } // warn
^
-t7623.scala:21: warning: A repeated case parameter or extracted sequence should be matched only by a sequence wildcard (_*).
- def g = "" match { case X(s, t) => }
+t7623.scala:23: warning: Sequence wildcard (_*) does not align with repeated case parameter or extracted sequence; the result may be unexpected.
+ def h = "" match { case X(s, t, u @ _*) => } // warn
^
-t7623.scala:23: warning: A repeated case parameter or extracted sequence should be matched only by a sequence wildcard (_*).
- def h = "" match { case X(s, t, u @ _*) => }
- ^
-t7623.scala:9: warning: A repeated case parameter or extracted sequence should be matched only by a sequence wildcard (_*).
- def f = C("") match { case C(s) => }
- ^
-t7623.scala:11: warning: A repeated case parameter or extracted sequence should be matched only by a sequence wildcard (_*).
- def g = C("") match { case C(s, t) => }
+t7623.scala:11: warning: A repeated case parameter or extracted sequence is not matched by a sequence wildcard (_*), and may fail at runtime.
+ def g = C("") match { case C(s, t) => } // warn
^
-t7623.scala:13: warning: A repeated case parameter or extracted sequence should be matched only by a sequence wildcard (_*).
- def h = C("") match { case C(s, t, u @ _*) => }
+t7623.scala:13: warning: Sequence wildcard (_*) does not align with repeated case parameter or extracted sequence; the result may be unexpected.
+ def h = C("") match { case C(s, t, u @ _*) => } // warn
^
error: No warnings can be incurred under -Xfatal-warnings.
-6 warnings found
+four warnings found
one error found
diff --git a/test/files/neg/t7623.scala b/test/files/neg/t7623.scala
index 5c40f37bc1..5334cc5f69 100644
--- a/test/files/neg/t7623.scala
+++ b/test/files/neg/t7623.scala
@@ -8,9 +8,9 @@ object X { def unapplySeq(a: Any): Option[(String, Seq[Int])] = Some("", List(1,
trait Ctest {
def f = C("") match { case C(s) => }
- def g = C("") match { case C(s, t) => }
+ def g = C("") match { case C(s, t) => } // warn
- def h = C("") match { case C(s, t, u @ _*) => }
+ def h = C("") match { case C(s, t, u @ _*) => } // warn
def ok = C("") match { case C(s, u @ _*) => }
}
@@ -18,9 +18,9 @@ trait Ctest {
trait Xtest {
def f = "" match { case X(s) => }
- def g = "" match { case X(s, t) => }
+ def g = "" match { case X(s, t) => } // warn
- def h = "" match { case X(s, t, u @ _*) => }
+ def h = "" match { case X(s, t, u @ _*) => } // warn
def ok = "" match { case X(s, u @ _*) => }
}
diff --git a/test/files/pos/t9356/Foo_2.scala b/test/files/pos/t9356/Foo_2.scala
new file mode 100644
index 0000000000..ab7bb44d0e
--- /dev/null
+++ b/test/files/pos/t9356/Foo_2.scala
@@ -0,0 +1,6 @@
+class C
+
+trait Foo {
+ @annot.MyAnnotation(cls = classOf[C])
+ def function: Any = ???
+}
diff --git a/test/files/pos/t9356/MyAnnotation.java b/test/files/pos/t9356/MyAnnotation.java
new file mode 100644
index 0000000000..b6c00e7356
--- /dev/null
+++ b/test/files/pos/t9356/MyAnnotation.java
@@ -0,0 +1,12 @@
+package annot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MyAnnotation {
+ Class<?> cls();
+}
diff --git a/test/files/pos/t9356/Test_3.scala b/test/files/pos/t9356/Test_3.scala
new file mode 100644
index 0000000000..fa1b76c9e1
--- /dev/null
+++ b/test/files/pos/t9356/Test_3.scala
@@ -0,0 +1,3 @@
+class Foo1 extends Foo
+
+class Foo2 extends Foo \ No newline at end of file
diff --git a/test/files/run/range.scala b/test/files/run/range.scala
index 4637ab874d..e50d0ac6a5 100644
--- a/test/files/run/range.scala
+++ b/test/files/run/range.scala
@@ -36,16 +36,19 @@ object Test {
def gr1 = NumericRange(x, x, x)
def gr2 = NumericRange.inclusive(x, x, x)
- def gr3 = NumericRange(x, x * fromInt(10), x)
- def gr4 = NumericRange.inclusive(x, x * fromInt(10), x)
- def gr5 = gr3.toList ::: negated.gr3.toList
+ def gr3 = NumericRange(x, x * fromInt(4), x * fromInt(2)) // SI-9348
+ def gr4 = NumericRange(x, x * fromInt(-2), x * fromInt(-2))
+ def gr5 = NumericRange(x, x * fromInt(10), x)
+ def gr6 = NumericRange.inclusive(x, x * fromInt(10), x)
+ def gr7 = gr3.toList ::: negated.gr3.toList
def check = {
assert(gr1.isEmpty && !gr2.isEmpty)
- assert(gr3.size == 9 && gr4.size == 10)
- assert(gr5.sum == num.zero, gr5.toString)
- assert(!(gr3 contains (x * fromInt(10))))
- assert((gr4 contains (x * fromInt(10))))
+ assert(gr3.size == 2 && gr4.size == 2)
+ assert(gr5.size == 9 && gr6.size == 10)
+ assert(gr7.sum == num.zero, gr7.toString)
+ assert(!(gr5 contains (x * fromInt(10))))
+ assert(gr6 contains (x * fromInt(10)))
}
}
@@ -55,6 +58,7 @@ object Test {
val _grs = List[GR[_]](
GR(BigDecimal(5.0)),
+ GR(BigDecimal(0.25)), // SI-9348
GR(BigInt(5)),
GR(5L),
GR(5.0d),
diff --git a/test/files/run/repl-serialization.check b/test/files/run/repl-serialization.check
new file mode 100644
index 0000000000..eb62729f5c
--- /dev/null
+++ b/test/files/run/repl-serialization.check
@@ -0,0 +1,25 @@
+== evaluating lines
+extract: AnyRef => Unit = <function1>
+ evaluating x
+x: Int = 0
+getX: ()Int
+defined class U
+y: Int = <lazy>
+ evaluating z
+ evaluating zz
+defined class D
+z: Int = 0
+zz: Int = 0
+defined object O
+defined class A
+defined type alias AA
+constructing U
+u: U = U
+== evaluating lambda
+ evaluating y
+ evaluating O
+ constructing A
+== reconstituting into a fresh classloader
+ evaluating O
+== evaluating reconstituted lambda
+ constructing A
diff --git a/test/files/run/repl-serialization.scala b/test/files/run/repl-serialization.scala
new file mode 100644
index 0000000000..55b7519631
--- /dev/null
+++ b/test/files/run/repl-serialization.scala
@@ -0,0 +1,68 @@
+import java.io._
+
+import scala.reflect.io.AbstractFile
+import scala.tools.nsc.Settings
+import scala.tools.nsc.interpreter.IMain
+import scala.tools.nsc.util._
+import scala.reflect.internal.util.AbstractFileClassLoader
+
+object Test {
+ def main(args: Array[String]) {
+ run()
+ }
+
+ def run(): Unit = {
+ val settings = new Settings()
+ settings.Yreplclassbased.value = true
+ settings.usejavacp.value = true
+
+ var imain: IMain = null
+ object extract extends ((AnyRef) => Unit) with Serializable {
+ var value: AnyRef = null
+
+ def apply(a: AnyRef) = value = a
+ }
+
+ val code =
+ """val x = {println(" evaluating x"); 0 }
+ |def getX() = x
+ |class U extends Serializable { println("constructing U"); val x = 0 ; override def toString = "U" }
+ |lazy val y = {println(" evaluating y"); 0 }
+ |class D; val z = {println(" evaluating z"); 0}; val zz = {println(" evaluating zz"); 0}
+ |object O extends Serializable { val apply = {println(" evaluating O"); 0} }
+ |class A(i: Int) { println(" constructing A") }
+ |type AA = A
+ |val u = new U()
+ |extract(() => new AA(x + getX() + y + z + zz + O.apply + u.x))
+ """.stripMargin
+
+ imain = new IMain(settings)
+ println("== evaluating lines")
+ imain.directBind("extract", "(AnyRef => Unit)", extract)
+ code.lines.foreach(imain.interpret)
+
+ val virtualFile: AbstractFile = extract.value.getClass.getClassLoader.asInstanceOf[AbstractFileClassLoader].root
+ val newLoader = new AbstractFileClassLoader(virtualFile, getClass.getClassLoader)
+
+ def deserializeInNewLoader(string: Array[Byte]): AnyRef = {
+ val bis = new ByteArrayInputStream(string)
+ val in = new ObjectInputStream(bis) {
+ override def resolveClass(desc: ObjectStreamClass) = Class.forName(desc.getName, false, newLoader)
+ }
+ in.readObject()
+ }
+ def serialize(o: AnyRef): Array[Byte] = {
+ val bos = new ByteArrayOutputStream()
+ val out = new ObjectOutputStream(bos)
+ out.writeObject(o)
+ out.close()
+ bos.toByteArray
+ }
+ println("== evaluating lambda")
+ extract.value.asInstanceOf[() => Any].apply()
+ println("== reconstituting into a fresh classloader")
+ val reconstituted = deserializeInNewLoader(serialize(extract.value)).asInstanceOf[() => Any]
+ println("== evaluating reconstituted lambda")
+ reconstituted.apply() // should not print("evaluating x") a second time
+ }
+}
diff --git a/test/files/run/t7747-repl.check b/test/files/run/t7747-repl.check
index 105b238d01..5f436ba6b1 100644
--- a/test/files/run/t7747-repl.check
+++ b/test/files/run/t7747-repl.check
@@ -112,7 +112,7 @@ scala> 55 ; ((2 + 2)) ; (1, 2, 3)
res15: (Int, Int, Int) = (1,2,3)
scala> 55 ; (x: Int) => x + 1 ; () => ((5))
-<console>:8: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
+<console>:9: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
55 ; (x: Int) => x + 1 ;;
^
res16: () => Int = <function0>
@@ -258,12 +258,12 @@ class $read extends Serializable {
super.<init>;
()
};
- import $line44.$read.$iw.$iw.BippyBups;
- import $line44.$read.$iw.$iw.BippyBups;
- import $line45.$read.$iw.$iw.PuppyPups;
- import $line45.$read.$iw.$iw.PuppyPups;
- import $line46.$read.$iw.$iw.Bingo;
- import $line46.$read.$iw.$iw.Bingo;
+ import $line44.$read.INSTANCE.$iw.$iw.BippyBups;
+ import $line44.$read.INSTANCE.$iw.$iw.BippyBups;
+ import $line45.$read.INSTANCE.$iw.$iw.PuppyPups;
+ import $line45.$read.INSTANCE.$iw.$iw.PuppyPups;
+ import $line46.$read.INSTANCE.$iw.$iw.Bingo;
+ import $line46.$read.INSTANCE.$iw.$iw.Bingo;
class $iw extends Serializable {
def <init>() = {
super.<init>;
@@ -275,12 +275,35 @@ class $read extends Serializable {
};
val $iw = new $iw.<init>
}
-object $read extends $read {
+object $read extends scala.AnyRef {
def <init>() = {
super.<init>;
()
- }
+ };
+ val INSTANCE = new $read.<init>
}
res3: List[Product with Serializable] = List(BippyBups(), PuppyPups(), Bingo())
+scala> case class Sum(exp: String, exp2: String)
+defined class Sum
+
+scala> val a = Sum("A", "B")
+a: Sum = Sum(A,B)
+
+scala> def b(a: Sum): String = a match { case Sum(_, _) => "Found Sum" }
+b: (a: Sum)String
+
+scala> b(a)
+res4: String = Found Sum
+
+scala> :power
+** Power User mode enabled - BEEP WHIR GYVE **
+** :phase has been set to 'typer'. **
+** scala.tools.nsc._ has been imported **
+** global._, definitions._ also imported **
+** Try :help, :vals, power.<tab> **
+
+scala> intp.lastRequest
+res5: $r.intp.Request = Request(line=def $ires3 = intp.global, 1 trees)
+
scala> :quit
diff --git a/test/files/run/t7747-repl.scala b/test/files/run/t7747-repl.scala
index 0e64210460..141c2d9844 100644
--- a/test/files/run/t7747-repl.scala
+++ b/test/files/run/t7747-repl.scala
@@ -65,5 +65,11 @@ object Test extends ReplTest {
|case class PuppyPups()
|case class Bingo()
|List(BippyBups(), PuppyPups(), Bingo()) // show
+ |case class Sum(exp: String, exp2: String)
+ |val a = Sum("A", "B")
+ |def b(a: Sum): String = a match { case Sum(_, _) => "Found Sum" }
+ |b(a)
+ |:power
+ |intp.lastRequest
|""".stripMargin
}
diff --git a/test/files/run/toolbox-varargs/Test.scala b/test/files/run/toolbox-varargs/Test.scala
new file mode 100644
index 0000000000..be5ab45768
--- /dev/null
+++ b/test/files/run/toolbox-varargs/Test.scala
@@ -0,0 +1,13 @@
+object Test {
+ def main(args: Array[String]): Unit = {
+ import scala.tools.reflect.ToolBox
+ val m = reflect.runtime.currentMirror
+ val u = m.universe
+ import u._
+ val tb = m.mkToolBox();
+ tb.compile(q"new p.Varargs(null, null)")
+ tb.compile(q"p.Varargs.staticMethod(null, null)")
+ tb.compile(q"(null: p.Varargs).instanceMethod(null, null)")
+ }
+}
+
diff --git a/test/files/run/toolbox-varargs/Varargs.java b/test/files/run/toolbox-varargs/Varargs.java
new file mode 100644
index 0000000000..da1dbbacc9
--- /dev/null
+++ b/test/files/run/toolbox-varargs/Varargs.java
@@ -0,0 +1,8 @@
+package p;
+
+public class Varargs {
+ public Varargs(String... args) {}
+ public static void staticMethod(String... args) {}
+
+ public void instanceMethod(String... args) {}
+}
diff --git a/test/junit/scala/collection/IteratorTest.scala b/test/junit/scala/collection/IteratorTest.scala
index d5389afd0c..1c1e50aed9 100644
--- a/test/junit/scala/collection/IteratorTest.scala
+++ b/test/junit/scala/collection/IteratorTest.scala
@@ -154,4 +154,14 @@ class IteratorTest {
results += (Stream from 1).toIterator.drop(10).toStream.drop(10).toIterator.next()
assertSameElements(List(1,1,21), results)
}
+ // SI-9332
+ @Test def spanExhaustsLeadingIterator(): Unit = {
+ def it = Iterator.iterate(0)(_ + 1).take(6)
+ val (x, y) = it.span(_ != 1)
+ val z = x.toList
+ assertEquals(1, z.size)
+ assertFalse(x.hasNext)
+ assertEquals(1, y.next)
+ assertFalse(x.hasNext) // was true, after advancing underlying iterator
+ }
}
diff --git a/test/junit/scala/collection/immutable/RangeConsistencyTest.scala b/test/junit/scala/collection/immutable/RangeConsistencyTest.scala
index 3980c31577..135796979d 100644
--- a/test/junit/scala/collection/immutable/RangeConsistencyTest.scala
+++ b/test/junit/scala/collection/immutable/RangeConsistencyTest.scala
@@ -137,4 +137,15 @@ class RangeConsistencyTest {
assert( (-3 to Int.MaxValue).dropWhile(_ <= 0).length == Int.MaxValue )
assert( (-3 to Int.MaxValue).span(_ <= 0) match { case (a,b) => a.length == 4 && b.length == Int.MaxValue } )
}
+
+ @Test
+ def testSI9348() {
+ // Test exclusive range with (end-start) != 0 (mod step)
+ assert( (0.0f until 0.4f by 0.25f) sameElements List(0.0f, 0.25f) )
+ assert( (1.0 until 2.2 by 0.5) sameElements List(1.0, 1.5, 2.0) )
+
+ def bd(d: Double) = BigDecimal(d)
+ val bdRange = bd(-10.0) until bd(0.0) by bd(4.5)
+ assert( bdRange sameElements List(bd(-10.0), bd(-5.5), bd(-1.0)) )
+ }
}
diff --git a/test/junit/scala/math/NumericTest.scala b/test/junit/scala/math/NumericTest.scala
index 9bf7d4f1e4..682dcbfd75 100644
--- a/test/junit/scala/math/NumericTest.scala
+++ b/test/junit/scala/math/NumericTest.scala
@@ -5,6 +5,9 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import scala.math.Numeric.FloatAsIfIntegral
+
+
@RunWith(classOf[JUnit4])
class NumericTest {
@@ -14,5 +17,28 @@ class NumericTest {
assertTrue(-0.0.abs equals 0.0)
assertTrue(-0.0f.abs equals 0.0f)
}
-}
+
+ /* Test for SI-9348 */
+ @Test
+ def testFloatAsIfIntegral {
+ val num = scala.math.Numeric.FloatAsIfIntegral
+ assertTrue(num.quot(1.0f, 0.5f) equals 2.0f)
+ assertTrue(num.quot(1.0f, 0.3f) equals 3.0f)
+ }
+
+ /* Test for SI-9348 */
+ @Test
+ def testDoubleAsIfIntegral {
+ val num = scala.math.Numeric.DoubleAsIfIntegral
+ assertTrue(num.quot(1.0, 0.25) equals 4.0)
+ assertTrue(num.quot(0.5, 0.15) equals 3.0)
+ }
+
+ /* Test for SI-9348 */
+ @Test
+ def testBigDecimalAsIfIntegral {
+ val num = scala.math.Numeric.BigDecimalAsIfIntegral
+ assertTrue(num.quot(BigDecimal(2.5), BigDecimal(0.5)) equals BigDecimal(5.0))
+ assertTrue(num.quot(BigDecimal(5.0), BigDecimal(2.0)) equals BigDecimal(2.0))
+ }}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
new file mode 100644
index 0000000000..3a85f03da2
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
@@ -0,0 +1,231 @@
+package scala.tools.nsc
+package backend.jvm
+package analysis
+
+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.asm.tree.{AbstractInsnNode, MethodNode}
+import scala.tools.nsc.backend.jvm.BTypes._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+import scala.tools.testing.ClearAfterClass
+import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
+import AsmUtils._
+
+import scala.collection.convert.decorateAsScala._
+
+object NullnessAnalyzerTest extends ClearAfterClass.Clearable {
+ var noOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+
+ def clear(): Unit = {
+ noOptCompiler = null
+ }
+}
+
+@RunWith(classOf[JUnit4])
+class NullnessAnalyzerTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = NullnessAnalyzerTest
+ val noOptCompiler = NullnessAnalyzerTest.noOptCompiler
+
+ def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C"): NullnessAnalyzer = {
+ val nullnessAnalyzer = new NullnessAnalyzer
+ nullnessAnalyzer.analyze(classInternalName, methodNode)
+ nullnessAnalyzer
+ }
+
+ /**
+ * Instructions that match `query` when textified.
+ * If `query` starts with a `+`, the next instruction is returned.
+ */
+ def findInstr(method: MethodNode, query: String): List[AbstractInsnNode] = {
+ val useNext = query(0) == '+'
+ val instrPart = if (useNext) query.drop(1) else query
+ val insns = method.instructions.iterator.asScala.find(i => textify(i) contains instrPart).toList
+ if (useNext) insns.map(_.getNext) else insns
+ }
+
+ def testNullness(analyzer: NullnessAnalyzer, method: MethodNode, query: String, index: Int, nullness: Nullness): Unit = {
+ for (i <- findInstr(method, query)) {
+ val r = analyzer.frameAt(i, method).getValue(index).nullness
+ assertTrue(s"Expected: $nullness, found: $r. At instr ${textify(i)}", nullness == r)
+ }
+ }
+
+ // debug / helper for writing tests
+ def showAllNullnessFrames(analyzer: NullnessAnalyzer, method: MethodNode): String = {
+ val instrLength = method.instructions.iterator.asScala.map(textify(_).length).max
+ val lines = for (i <- method.instructions.iterator.asScala) yield {
+ val f = analyzer.frameAt(i, method)
+ val frameString = {
+ if (f == null) "null"
+ else (0 until (f.getLocals + f.getStackSize)).iterator
+ .map(f.getValue(_).toString)
+ .map(s => "%8s".format(s))
+ .zipWithIndex.map({case (s, i) => s"$i: $s"})
+ .mkString(", ")
+ }
+ ("%"+ instrLength +"s: %s").format(textify(i), frameString)
+ }
+ lines.mkString("\n")
+ }
+
+ @Test
+ def showNullnessFramesTest(): Unit = {
+ val List(m) = compileMethods(noOptCompiler)("def f = this.toString")
+
+ // NOTE: the frame for an instruction represents the state *before* executing that instr.
+ // So in the frame for `ALOAD 0`, the stack is still empty.
+
+ val res =
+ """ L0: 0: NotNull
+ | LINENUMBER 1 L0: 0: NotNull
+ | ALOAD 0: 0: NotNull
+ |INVOKEVIRTUAL java/lang/Object.toString ()Ljava/lang/String;: 0: NotNull, 1: NotNull
+ | ARETURN: 0: NotNull, 1: Unknown1
+ | L0: null""".stripMargin
+ assertEquals(showAllNullnessFrames(newNullnessAnalyzer(m), m), res)
+ }
+
+ @Test
+ def thisNonNull(): Unit = {
+ val List(m) = compileMethods(noOptCompiler)("def f = this.toString")
+ val a = newNullnessAnalyzer(m)
+ testNullness(a, m, "ALOAD 0", 0, NotNull)
+ }
+
+ @Test
+ def instanceMethodCall(): Unit = {
+ val List(m) = compileMethods(noOptCompiler)("def f(a: String) = a.trim")
+ val a = newNullnessAnalyzer(m)
+ testNullness(a, m, "INVOKEVIRTUAL java/lang/String.trim", 1, Unknown)
+ testNullness(a, m, "ARETURN", 1, NotNull)
+ }
+
+ @Test
+ def constructorCall(): Unit = {
+ val List(m) = compileMethods(noOptCompiler)("def f = { val a = new Object; a.toString }")
+ val a = newNullnessAnalyzer(m)
+
+ // for reference, the output of showAllNullnessFrames(a, m) - note that the frame represents the state *before* executing the instr.
+ // NEW java/lang/Object: 0: NotNull, 1: Unknown
+ // DUP: 0: NotNull, 1: Unknown, 2: Unknown
+ // INVOKESPECIAL java/lang/Object.<init>: 0: NotNull, 1: Unknown, 2: Unknown, 3: Unknown
+ // ASTORE 1: 0: NotNull, 1: Unknown, 2: NotNull
+ // ALOAD 1: 0: NotNull, 1: NotNull
+ // INVOKEVIRTUAL java/lang/Object.toString: 0: NotNull, 1: NotNull, 2: NotNull
+ // ARETURN: 0: NotNull, 1: NotNull, 2: Unknown
+
+ for ((insn, index, nullness) <- List(
+ ("+NEW", 2, Unknown), // new value at slot 2 on the stack
+ ("+DUP", 3, Unknown),
+ ("+INVOKESPECIAL java/lang/Object", 2, NotNull), // after calling the initializer on 3, the value at 2 becomes NotNull
+ ("ASTORE 1", 1, Unknown), // before the ASTORE 1, nullness of the value in local 1 is Unknown
+ ("+ASTORE 1", 1, NotNull), // after storing the value at 2 in local 1, the local 1 is NotNull
+ ("+ALOAD 1", 2, NotNull), // loading the value 1 puts a NotNull value on the stack (at 2)
+ ("+INVOKEVIRTUAL java/lang/Object.toString", 2, Unknown) // nullness of value returned by `toString` is Unknown
+ )) testNullness(a, m, insn, index, nullness)
+ }
+
+ @Test
+ def explicitNull(): Unit = {
+ val List(m) = compileMethods(noOptCompiler)("def f = { var a: Object = null; a }")
+ val a = newNullnessAnalyzer(m)
+ for ((insn, index, nullness) <- List(
+ ("+ACONST_NULL", 2, Null),
+ ("+ASTORE 1", 1, Null),
+ ("+ALOAD 1", 2, Null)
+ )) testNullness(a, m, insn, index, nullness)
+ }
+
+ @Test
+ def stringLiteralsNotNull(): Unit = {
+ val List(m) = compileMethods(noOptCompiler)("""def f = { val a = "hi"; a.trim }""")
+ val a = newNullnessAnalyzer(m)
+ testNullness(a, m, "+ASTORE 1", 1, NotNull)
+ }
+
+ @Test
+ def newArraynotNull() {
+ val List(m) = compileMethods(noOptCompiler)("def f = { val a = new Array[Int](2); a(0) }")
+ val a = newNullnessAnalyzer(m)
+ testNullness(a, m, "+NEWARRAY T_INT", 2, NotNull) // new array on stack
+ testNullness(a, m, "+ASTORE 1", 1, NotNull) // local var (a)
+ }
+
+ @Test
+ def aliasBranching(): Unit = {
+ val code =
+ """def f(o: Object) = {
+ | var a: Object = o // a and o are aliases
+ | var b: Object = null
+ | var c: Object = null
+ | var d: Object = o
+ | if ("".trim == "") {
+ | b = o
+ | c = o // a, o, b, aliases
+ | d = null
+ | } else {
+ | b = a // a, o, b aliases
+ | d = null
+ | }
+ | b.toString // a, o, b aliases (so they become NotNull), but not c
+ | // d is null here, assinged in both branches.
+ |}
+ """.stripMargin
+ val List(m) = compileMethods(noOptCompiler)(code)
+ val a = newNullnessAnalyzer(m)
+
+ val trim = "INVOKEVIRTUAL java/lang/String.trim"
+ val toSt = "INVOKEVIRTUAL java/lang/Object.toString"
+ val end = s"+$toSt"
+ for ((insn, index, nullness) <- List(
+ (trim, 0, NotNull), // this
+ (trim, 1, Unknown), // parameter o
+ (trim, 2, Unknown), // a
+ (trim, 3, Null), // b
+ (trim, 4, Null), // c
+ (trim, 5, Unknown), // d
+
+ (toSt, 2, Unknown), // a, still the same
+ (toSt, 3, Unknown), // b, was re-assinged in both branches to Unknown
+ (toSt, 4, Unknown), // c, was re-assigned in one branch to Unknown
+ (toSt, 5, Null), // d, was assigned to null in both branches
+
+ (end, 2, NotNull), // a, NotNull (alias of b)
+ (end, 3, NotNull), // b, receiver of toString
+ (end, 4, Unknown), // c, no change (not an alias of b)
+ (end, 5, Null) // d, no change
+ )) testNullness(a, m, insn, index, nullness)
+ }
+
+ @Test
+ def testInstanceOf(): Unit = {
+ val code =
+ """def f(a: Object) = {
+ | val x = a
+ | x.isInstanceOf[Throwable] // x and a remain unknown - INSTANCEOF doesn't throw a NPE on null
+ | x.toString // x and a are not null
+ | a.asInstanceOf[String].trim // the stack value (LOAD of local a) is still not-null after the CHECKCAST
+ |}
+ """.stripMargin
+ val List(m) = compileMethods(noOptCompiler)(code)
+ val a = newNullnessAnalyzer(m)
+
+ val instof = "+INSTANCEOF"
+ val tost = "+INVOKEVIRTUAL java/lang/Object.toString"
+ val trim = "INVOKEVIRTUAL java/lang/String.trim"
+
+ for ((insn, index, nullness) <- List(
+ (instof, 1, Unknown), // a after INSTANCEOF
+ (instof, 2, Unknown), // x after INSTANCEOF
+ (tost, 1, NotNull),
+ (tost, 2, NotNull),
+ (trim, 3, NotNull) // receiver at `trim`
+ )) testNullness(a, m, insn, index, nullness)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
index 0fc3601603..b8c5f85c49 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -975,4 +975,20 @@ class InlinerTest extends ClearAfterClass {
val List(c) = compile(code)
assertInvoke(getSingleMethod(c, "t"), "java/lang/Error", "<init>")
}
+
+ @Test
+ def noRedunantNullChecks(): Unit = {
+ val code =
+ """class C {
+ | @inline final def f: String = "hai!"
+ | def t(c: C) = {c.f; c.f} // null check on the first, but not the second
+ |}
+ """.stripMargin
+
+ val List(c) = compile(code)
+ val t = getSingleMethod(c, "t").instructions
+ assertNoInvoke(t)
+ assert(2 == t.collect({case Ldc(_, "hai!") => }).size) // twice the body of f
+ assert(1 == t.collect({case Jump(IFNONNULL, _) => }).size) // one single null check
+ }
}
diff --git a/test/junit/scala/util/SortingTest.scala b/test/junit/scala/util/SortingTest.scala
new file mode 100644
index 0000000000..15a00c8903
--- /dev/null
+++ b/test/junit/scala/util/SortingTest.scala
@@ -0,0 +1,69 @@
+package scala.util
+
+import org.junit.Test
+import org.junit.Assert._
+import scala.math.{ Ordered, Ordering }
+import scala.reflect.ClassTag
+
+class SortingTest {
+ case class N(i: Int, j: Int) extends Ordered[N] { def compare(n: N) = if (i < n.i) -1 else if (i > n.i) 1 else 0 }
+
+ def mkA(n: Int, max: Int) = Array.tabulate(n)(i => N(util.Random.nextInt(max), i))
+
+ def isStable(a: Array[N]): Boolean = { var i = 1; while (i < a.length) { if (a(i).i < a(i-1).i || (a(i).i == a(i-1).i && a(i).j < a(i-1).j)) return false; i += 1 }; true }
+
+ def isAntistable(a: Array[N]): Boolean =
+ { var i = 1; while (i < a.length) { if (a(i).i > a(i-1).i || (a(i).i == a(i-1).i && a(i).j < a(i-1).j)) return false; i += 1 }; true }
+
+ def isSorted(a: Array[N]): Boolean = { var i = 1; while (i < a.length) { if (a(i).i < a(i-1).i) return false; i += 1 }; true }
+
+ def isAntisorted(a: Array[N]): Boolean = { var i = 1; while (i < a.length) { if (a(i).i > a(i-1).i) return false; i += 1 }; true }
+
+ val sizes = Seq.range(0, 65) ++ Seq(256, 1024, 9121, 65539)
+ val variety = Seq(1, 2, 10, 100, 1000, Int.MaxValue)
+ val workLimit = 1e6
+ val rng = new util.Random(198571)
+
+ val backwardsN = Ordering by ((n: N) => -n.i)
+
+ def runOneTest(size: Int, variety: Int): Unit = {
+ val xs = Array.tabulate(size)(i => N(rng.nextInt(variety), i))
+ val ys = Array.range(0, xs.length)
+ val zs = { val temp = xs.clone; java.util.Arrays.sort(temp, new java.util.Comparator[N] { def compare(a: N, b: N) = a.compare(b) }); temp }
+ val qxs = { val temp = xs.clone; Sorting.quickSort(temp); temp }
+ val pxs = { val temp = xs.clone; Sorting.quickSort(temp)(backwardsN); temp }
+ val sxs = { val temp = xs.clone; Sorting.stableSort(temp); temp }
+ val rxs = { val temp = xs.clone; Sorting.stableSort(temp)(implicitly[ClassTag[N]], backwardsN); temp }
+ val sys = Sorting.stableSort(ys.clone: Seq[Int], (i: Int) => xs(i))
+
+ assertTrue("Quicksort should be in order", isSorted(qxs))
+ assertTrue("Quicksort should be in reverse order", isAntisorted(pxs))
+ assertTrue("Stable sort should be sorted and stable", isStable(sxs))
+ assertTrue("Stable sort should be reverse sorted but stable", isAntistable(rxs))
+ assertTrue("Stable sorting by proxy should produce sorted stable list", isStable(sys.map(i => xs(i))))
+ assertTrue("Quicksort should produce canonical ordering", (qxs zip zs).forall{ case (a,b) => a.i == b.i })
+ assertTrue("Reverse quicksort should produce canonical ordering", (pxs.reverse zip zs).forall{ case (a,b) => a.i == b.i })
+ assertTrue("Stable sort should produce exact ordering", (sxs zip zs).forall{ case (a,b) => a == b })
+ assertTrue("Reverse stable sort should produce canonical ordering", (rxs.reverse zip zs).forall{ case (a,b) => a.i == b.i })
+ assertTrue("Proxy sort and direct sort should produce exactly the same thing", (sxs zip sys.map(i => xs(i))).forall{ case (a,b) => a == b })
+ }
+
+ @Test def testSortConsistency: Unit = {
+ for {
+ size <- sizes
+ v <- variety
+ i <- 0 until math.min(100, math.max(math.min(math.floor(math.pow(v, size)/2), math.ceil(workLimit / (math.log(math.max(2,size))/math.log(2) * size))), 1).toInt)
+ } runOneTest(size, v)
+
+ for (size <- sizes) {
+ val b = Array.fill(size)(rng.nextBoolean)
+ val bfwd = Sorting.stableSort(b.clone: Seq[Boolean])
+ val bbkw = Sorting.stableSort(b.clone: Seq[Boolean], (x: Boolean, y: Boolean) => x && !y)
+ assertTrue("All falses should be first", bfwd.dropWhile(_ == false).forall(_ == true))
+ assertTrue("All falses should be last when sorted backwards", bbkw.dropWhile(_ == true).forall(_ == false))
+ assertTrue("Sorting booleans should preserve the number of trues", b.count(_ == true) == bfwd.count(_ == true))
+ assertTrue("Backwards sorting booleans should preserve the number of trues", b.count(_ == true) == bbkw.count(_ == true))
+ assertTrue("Sorting should not change the sizes of arrays", b.length == bfwd.length && b.length == bbkw.length)
+ }
+ }
+}
diff --git a/test/scaladoc/run/SI-8210.check b/test/scaladoc/run/SI-8210.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-8210.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-8210.scala b/test/scaladoc/run/SI-8210.scala
new file mode 100644
index 0000000000..3bd818473f
--- /dev/null
+++ b/test/scaladoc/run/SI-8210.scala
@@ -0,0 +1,24 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+ override def code = """
+object Foo {
+ trait Config {
+ /** The bar obviously. */
+ def bar: Int
+ }
+ class ConfigBuilder extends Config {
+ /** @inheritdoc
+ *
+ * The default value is 1234.
+ */
+ var bar: Int = 1234
+ }
+}
+ """
+
+ def scaladocSettings = ""
+
+ def testModel(root: Package) = ()
+}