@@ -77,11 +77,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
77
77
def enumValueSymbols (using Context ): List [Symbol ] = { initSymbols; myEnumValueSymbols }
78
78
def nonJavaEnumValueSymbols (using Context ): List [Symbol ] = { initSymbols; myNonJavaEnumValueSymbols }
79
79
80
- private def existingDef (sym : Symbol , clazz : ClassSymbol )(using Context ): Symbol = {
80
+ private def existingDef (sym : Symbol , clazz : ClassSymbol )(using Context ): Symbol =
81
81
val existing = sym.matchingMember(clazz.thisType)
82
- if (existing != sym && ! existing.is(Deferred )) existing
83
- else NoSymbol
84
- }
82
+ if existing != sym && ! existing.is(Deferred ) then existing else NoSymbol
85
83
86
84
private def synthesizeDef (sym : TermSymbol , rhsFn : List [List [Tree ]] => Context ?=> Tree )(using Context ): Tree =
87
85
DefDef (sym, rhsFn(_)(using ctx.withOwner(sym))).withSpan(ctx.owner.span.focus)
@@ -236,12 +234,13 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
236
234
* def equals(that: Any): Boolean =
237
235
* (this eq that) || {
238
236
* that match {
239
- * case x$0 @ (_: C @unchecked) => this.x == this$0.x && this.y == x$0.y
237
+ * case x$0 @ (_: C @unchecked) => this.x == this$0.x && this.y == x$0.y && that.canEqual(this)
240
238
* case _ => false
241
239
* }
242
240
* ```
243
241
*
244
- * If `C` is a value class the initial `eq` test is omitted.
242
+ * If `C` is a value class, the initial `eq` test is omitted.
243
+ * The `canEqual` test can be omitted if it is known that `canEqual` return always true.
245
244
*
246
245
* `@unchecked` is needed for parametric case classes.
247
246
*
@@ -254,8 +253,14 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
254
253
val sortedAccessors = accessors.sortBy(accessor => if (accessor.info.typeSymbol.isPrimitiveValueClass) 0 else 1 )
255
254
val comparisons = sortedAccessors.map { accessor =>
256
255
This (clazz).select(accessor).equal(ref(thatAsClazz).select(accessor)) }
257
- val rhs = // this.x == this$0.x && this.y == x$0.y
258
- if (comparisons.isEmpty) Literal (Constant (true )) else comparisons.reduceLeft(_ and _)
256
+ var rhs = // this.x == this$0.x && this.y == x$0.y && that.canEqual(this)
257
+ if comparisons.isEmpty then Literal (Constant (true )) else comparisons.reduceLeft(_ and _)
258
+ val canEqualMeth = existingDef(defn.Product_canEqual , clazz)
259
+ if ! clazz.is(Final ) || canEqualMeth.exists && ! canEqualMeth.is(Synthetic ) then
260
+ rhs = rhs.and(
261
+ ref(thatAsClazz)
262
+ .select(canEqualMeth.orElse(defn.Product_canEqual ))
263
+ .appliedTo(This (clazz)))
259
264
val matchingCase = CaseDef (pattern, EmptyTree , rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
260
265
val defaultCase = CaseDef (Underscore (defn.AnyType ), EmptyTree , Literal (Constant (false ))) // case _ => false
261
266
val matchExpr = Match (that, List (matchingCase, defaultCase))
0 commit comments