From 7a259cadea8e42f19c9eb02cfc55800e23023540 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 23 Mar 2018 21:52:00 +0100 Subject: [PATCH 1/2] VCElideAllocations: Use convenience method tpd#equal --- .../src/dotty/tools/dotc/transform/VCElideAllocations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala b/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala index 91076cbc3cc3..8100bc0db427 100644 --- a/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala +++ b/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala @@ -29,7 +29,7 @@ class VCElideAllocations extends MiniPhase with IdentityDenotTransformer { case BinaryOp(NewWithArgs(tp1, List(u1)), op, NewWithArgs(tp2, List(u2))) if (tp1 eq tp2) && (op eq defn.Any_==) && isDerivedValueClass(tp1.typeSymbol) => // == is overloaded in primitive classes - applyOverloaded(u1, nme.EQ, List(u2), Nil, defn.BooleanType) + u1.equal(u2) // (new V(u)).underlying() => u case ValueClassUnbox(NewWithArgs(_, List(u))) => From 06541bc7a684f15bcb38830d0ec3e8bf30fe344d Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 23 Mar 2018 22:00:07 +0100 Subject: [PATCH 2/2] Fix #3140: Generate better `equals` for case/value classes - Do overloading resolution on `==` to avoid boxing primitives - Use `case _` instead of `case _: Any` for the default case, the latter produces suboptimal code. --- .../dotc/transform/SyntheticMethods.scala | 4 ++-- .../backend/jvm/DottyBytecodeTests.scala | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index a5cf2447418c..105763856049 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -164,11 +164,11 @@ class SyntheticMethods(thisPhase: DenotTransformer) { def wildcardAscription(tp: Type) = Typed(Underscore(tp), TypeTree(tp)) val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C) val comparisons = accessors map { accessor => - This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor)) } + This(clazz).select(accessor).equal(ref(thatAsClazz).select(accessor)) } val rhs = // this.x == this$0.x && this.y == x$0.y if (comparisons.isEmpty) Literal(Constant(true)) else comparisons.reduceLeft(_ and _) val matchingCase = CaseDef(pattern, EmptyTree, rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y - val defaultCase = CaseDef(wildcardAscription(defn.AnyType), EmptyTree, Literal(Constant(false))) // case _ => false + val defaultCase = CaseDef(Underscore(defn.AnyType), EmptyTree, Literal(Constant(false))) // case _ => false val matchExpr = Match(that, List(matchingCase, defaultCase)) if (isDerivedValueClass(clazz)) matchExpr else { diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index 9f90bedf2598..b6127681b131 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -241,4 +241,27 @@ class TestBCode extends DottyBytecodeTest { assert(!hasInstanceof, "Try case should not issue INSTANCEOF opcode\n") } } + + @Test def noBoxingInSyntheticEquals = { + val source = + """ + |case class Case(x: Long) + |class Value(val x: Long) extends AnyVal + """.stripMargin + + checkBCode(source) { dir => + for ((clsName, methodName) <- List(("Case", "equals"), ("Value$", "equals$extension"))) { + val moduleIn = dir.lookupName(s"$clsName.class", directory = false) + val moduleNode = loadClassNode(moduleIn.input) + val equalsMethod = getMethod(moduleNode, methodName) + + val callsEquals = instructionsFromMethod(equalsMethod).exists { + case i @ Invoke(_, _, "equals", _, _) => true + case i => false + } + + assert(!callsEquals, s"equals method should not be called in the definition of $clsName#$methodName\n") + } + } + } }