Notice that
case 1. summon[CanEqual[Int, String]] doesn't compile even without importing scala.language.strictEquality
case 2. summon[CanEqual[Cat, Dog]]
- compiles without importing
scala.language.strictEquality but
- doesn't compile with such importing.
a) Instances of type class CanEqual are generated by the compiler (as well as scala.reflect.ClassTag, scala.reflect.TypeTest, scala.ValueOf, scala.deriving.Mirror.Product, scala.deriving.Mirror.Sum, scala.deriving.Mirror)
val specialHandlers = List(
defn.ClassTagClass -> synthesizedClassTag,
defn.TypeTestClass -> synthesizedTypeTest,
defn.CanEqualClass -> synthesizedCanEqual,
defn.ValueOfClass -> synthesizedValueOf,
defn.Mirror_ProductClass -> synthesizedProductMirror,
defn.Mirror_SumClass -> synthesizedSumMirror,
defn.MirrorClass -> synthesizedMirror)
https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L489-L499
b) The thing is that the case when one of type parameters L, R of CanEqual[-L, -R] is a numeric value class (Byte, Short, Char, Int, Long, Float, Double) is handled differently:
val synthesizedCanEqual: SpecialHandler = (formal, span) =>
...
if canComparePredefined(arg1, arg2)
|| !Implicits.strictEquality && explore(validEqAnyArgs(arg1, arg2))
...
https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L147-L148
Notice that here if the answer is given by canComparePredefined then it doesn't matter whether strictEquality is switched on.
c) canComparePredefined calls
def canComparePredefinedClasses(cls1: ClassSymbol, cls2: ClassSymbol): Boolean =
...
if cls1.isPrimitiveValueClass then
if cls2.isPrimitiveValueClass then
cls1 == cls2 || cls1.isNumericValueClass && cls2.isNumericValueClass
else
cmpWithBoxed(cls1, cls2)
else if cls2.isPrimitiveValueClass then
cmpWithBoxed(cls2, cls1)
...
else
false
https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L108-L114
Notice that here if one of L, R is a numeric value class then the other must be too (taking into account boxing) so that summon[CanEqual[Int, Int]], summon[CanEqual[Double, Double]], summon[CanEqual[Int, Double]] compile but summon[CanEqual[Int, String]] doesn't compile.