@@ -77,11 +77,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
7777 def enumValueSymbols (using Context ): List [Symbol ] = { initSymbols; myEnumValueSymbols }
7878 def nonJavaEnumValueSymbols (using Context ): List [Symbol ] = { initSymbols; myNonJavaEnumValueSymbols }
7979
80- private def existingDef (sym : Symbol , clazz : ClassSymbol )(using Context ): Symbol = {
80+ private def existingDef (sym : Symbol , clazz : ClassSymbol )(using Context ): Symbol =
8181 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
8583
8684 private def synthesizeDef (sym : TermSymbol , rhsFn : List [List [Tree ]] => Context ?=> Tree )(using Context ): Tree =
8785 DefDef (sym, rhsFn(_)(using ctx.withOwner(sym))).withSpan(ctx.owner.span.focus)
@@ -236,12 +234,13 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
236234 * def equals(that: Any): Boolean =
237235 * (this eq that) || {
238236 * 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)
240238 * case _ => false
241239 * }
242240 * ```
243241 *
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.
245244 *
246245 * `@unchecked` is needed for parametric case classes.
247246 *
@@ -254,8 +253,14 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
254253 val sortedAccessors = accessors.sortBy(accessor => if (accessor.info.typeSymbol.isPrimitiveValueClass) 0 else 1 )
255254 val comparisons = sortedAccessors.map { accessor =>
256255 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)))
259264 val matchingCase = CaseDef (pattern, EmptyTree , rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
260265 val defaultCase = CaseDef (Underscore (defn.AnyType ), EmptyTree , Literal (Constant (false ))) // case _ => false
261266 val matchExpr = Match (that, List (matchingCase, defaultCase))
0 commit comments