summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala19
-rw-r--r--test/files/run/t7852.check0
-rw-r--r--test/files/run/t7852.flags1
-rw-r--r--test/files/run/t7852.scala39
4 files changed, 59 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 843299398b..a80fee876e 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -1319,6 +1319,8 @@ abstract class GenICode extends SubComponent {
/** Some useful equality helpers.
*/
def isNull(t: Tree) = cond(t) { case Literal(Constant(null)) => true }
+ def isLiteral(t: Tree) = cond(t) { case Literal(_) => true }
+ def isNonNullExpr(t: Tree) = isLiteral(t) || ((t.symbol ne null) && t.symbol.isModule)
/* If l or r is constant null, returns the other ; otherwise null */
def ifOneIsNull(l: Tree, r: Tree) = if (isNull(l)) r else if (isNull(r)) l else null
@@ -1514,6 +1516,23 @@ abstract class GenICode extends SubComponent {
val branchesReachable = !ctx1.bb.ignore
ctx1.bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ObjectReference)
branchesReachable
+ } else if (isNonNullExpr(l)) {
+ // Avoid null check if L is statically non-null.
+ //
+ // "" == expr -> "".equals(expr)
+ // Nil == expr -> Nil.equals(expr)
+ //
+ // Common enough (through pattern matching) to treat this specially here rather than
+ // hoping that -Yconst-opt is enabled. The impossible branches for null checks lead
+ // to spurious "branch not covered" warnings in Jacoco code coverage.
+ var ctx1 = genLoad(l, ctx, ObjectReference)
+ val branchesReachable = !ctx1.bb.ignore
+ ctx1 = genLoad(r, ctx1, ObjectReference)
+ ctx1.bb emitOnly(
+ CALL_METHOD(Object_equals, Dynamic),
+ CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)
+ )
+ branchesReachable
} else {
val eqEqTempLocal = getTempLocal
var ctx1 = genLoad(l, ctx, ObjectReference)
diff --git a/test/files/run/t7852.check b/test/files/run/t7852.check
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/files/run/t7852.check
diff --git a/test/files/run/t7852.flags b/test/files/run/t7852.flags
new file mode 100644
index 0000000000..f6262fd3e0
--- /dev/null
+++ b/test/files/run/t7852.flags
@@ -0,0 +1 @@
+-Ynooptimise
diff --git a/test/files/run/t7852.scala b/test/files/run/t7852.scala
new file mode 100644
index 0000000000..c93db718fd
--- /dev/null
+++ b/test/files/run/t7852.scala
@@ -0,0 +1,39 @@
+import scala.tools.partest.BytecodeTest
+import scala.tools.asm
+import scala.tools.asm.util._
+import scala.tools.nsc.util.stringFromWriter
+import scala.collection.JavaConverters._
+
+object Test extends BytecodeTest {
+ val nullChecks = Set(asm.Opcodes.IFNONNULL, asm.Opcodes.IFNULL)
+
+ def show: Unit = {
+ def test(methodName: String, expected: Int) {
+ val classNode = loadClassNode("Lean")
+ val methodNode = getMethod(classNode, methodName)
+ val got = countNullChecks(methodNode.instructions)
+ assert(got == expected, s"expected $expected but got $got comparisons")
+ }
+ test("string", expected = 0)
+ test("module", expected = 0)
+ test("moduleIndirect", expected = 2)
+ }
+
+ def countNullChecks(insnList: asm.tree.InsnList): Int =
+ insnList.iterator.asScala.map(_.getOpcode).count(nullChecks)
+}
+
+class Lean {
+ def string {
+ "" == toString
+ }
+
+ def module {
+ Nil == (toString: Any)
+ }
+
+ def moduleIndirect {
+ val n: Nil.type = null
+ n == (toString: Any) // still need null checks here.
+ }
+}