Skip to content

Commit 06541bc

Browse files
committed
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.
1 parent 7a259ca commit 06541bc

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,11 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
164164
def wildcardAscription(tp: Type) = Typed(Underscore(tp), TypeTree(tp))
165165
val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C)
166166
val comparisons = accessors map { accessor =>
167-
This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor)) }
167+
This(clazz).select(accessor).equal(ref(thatAsClazz).select(accessor)) }
168168
val rhs = // this.x == this$0.x && this.y == x$0.y
169169
if (comparisons.isEmpty) Literal(Constant(true)) else comparisons.reduceLeft(_ and _)
170170
val matchingCase = CaseDef(pattern, EmptyTree, rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
171-
val defaultCase = CaseDef(wildcardAscription(defn.AnyType), EmptyTree, Literal(Constant(false))) // case _ => false
171+
val defaultCase = CaseDef(Underscore(defn.AnyType), EmptyTree, Literal(Constant(false))) // case _ => false
172172
val matchExpr = Match(that, List(matchingCase, defaultCase))
173173
if (isDerivedValueClass(clazz)) matchExpr
174174
else {

compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,27 @@ class TestBCode extends DottyBytecodeTest {
241241
assert(!hasInstanceof, "Try case should not issue INSTANCEOF opcode\n")
242242
}
243243
}
244+
245+
@Test def noBoxingInSyntheticEquals = {
246+
val source =
247+
"""
248+
|case class Case(x: Long)
249+
|class Value(val x: Long) extends AnyVal
250+
""".stripMargin
251+
252+
checkBCode(source) { dir =>
253+
for ((clsName, methodName) <- List(("Case", "equals"), ("Value$", "equals$extension"))) {
254+
val moduleIn = dir.lookupName(s"$clsName.class", directory = false)
255+
val moduleNode = loadClassNode(moduleIn.input)
256+
val equalsMethod = getMethod(moduleNode, methodName)
257+
258+
val callsEquals = instructionsFromMethod(equalsMethod).exists {
259+
case i @ Invoke(_, _, "equals", _, _) => true
260+
case i => false
261+
}
262+
263+
assert(!callsEquals, s"equals method should not be called in the definition of $clsName#$methodName\n")
264+
}
265+
}
266+
}
244267
}

0 commit comments

Comments
 (0)