diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 19 | ||||
-rw-r--r-- | test/files/run/t7852.check | 0 | ||||
-rw-r--r-- | test/files/run/t7852.flags | 1 | ||||
-rw-r--r-- | test/files/run/t7852.scala | 39 |
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. + } +} |