Skip to content

Commit 7f2c4f6

Browse files
authored
Merge pull request #10449 from dotty-staging/fix-#10415
Fix #10415: Use `CanEqual` instead of `Eql` for multiversal equality
2 parents e13089f + b5fb11b commit 7f2c4f6

40 files changed

+234
-193
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -842,8 +842,10 @@ class Definitions {
842842

843843
@tu lazy val TastyReflectionClass: ClassSymbol = requiredClass("scala.tasty.Reflection")
844844

845-
@tu lazy val EqlClass: ClassSymbol = requiredClass("scala.Eql")
846-
def Eql_eqlAny(using Context): TermSymbol = EqlClass.companionModule.requiredMethod(nme.eqlAny)
845+
@tu lazy val CanEqualClass: ClassSymbol = getClassIfDefined("scala.Eql").orElse(requiredClass("scala.CanEqual")).asClass
846+
def CanEqual_canEqualAny(using Context): TermSymbol =
847+
val methodName = if CanEqualClass.name == tpnme.Eql then nme.eqlAny else nme.canEqualAny
848+
CanEqualClass.companionModule.requiredMethod(methodName)
847849

848850
@tu lazy val TypeBoxClass: ClassSymbol = requiredClass("scala.runtime.TypeBox")
849851
@tu lazy val TypeBox_CAP: TypeSymbol = TypeBoxClass.requiredType(tpnme.CAP)

compiler/src/dotty/tools/dotc/core/Denotations.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ import collection.mutable.ListBuffer
7171
*/
7272
object Denotations {
7373

74-
implicit def eqDenotation: Eql[Denotation, Denotation] = Eql.derived
74+
implicit def eqDenotation: CanEqual[Denotation, Denotation] = CanEqual.derived
7575

7676
/** A PreDenotation represents a group of single denotations or a single multi-denotation
7777
* It is used as an optimization to avoid forming MultiDenotations too eagerly.

compiler/src/dotty/tools/dotc/core/Names.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ object Names {
3232
* in a name table. A derived term name adds a tag, and possibly a number
3333
* or a further simple name to some other name.
3434
*/
35-
abstract class Name extends Designator, Showable derives Eql {
35+
abstract class Name extends Designator, Showable derives CanEqual {
3636

3737
/** A type for names of the same kind as this name */
3838
type ThisName <: Name

compiler/src/dotty/tools/dotc/core/StdNames.scala

+2
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ object StdNames {
362362
val CAP: N = "CAP"
363363
val Constant: N = "Constant"
364364
val ConstantType: N = "ConstantType"
365+
val Eql: N = "Eql"
365366
val EnumValue: N = "EnumValue"
366367
val ExistentialTypeTree: N = "ExistentialTypeTree"
367368
val Flag : N = "Flag"
@@ -432,6 +433,7 @@ object StdNames {
432433
val bundle: N = "bundle"
433434
val bytes: N = "bytes"
434435
val canEqual_ : N = "canEqual"
436+
val canEqualAny : N = "canEqualAny"
435437
val cbnArg: N = "<cbn-arg>"
436438
val checkInitialized: N = "checkInitialized"
437439
val ClassManifestFactory: N = "ClassManifestFactory"

compiler/src/dotty/tools/dotc/core/Symbols.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import config.Printers.typr
3737

3838
object Symbols {
3939

40-
implicit def eqSymbol: Eql[Symbol, Symbol] = Eql.derived
40+
implicit def eqSymbol: CanEqual[Symbol, Symbol] = CanEqual.derived
4141

4242
/** Tree attachment containing the identifiers in a tree as a sorted array */
4343
val Ids: Property.Key[Array[String]] = new Property.Key

compiler/src/dotty/tools/dotc/core/Types.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ object Types {
4646

4747
@sharable private var nextId = 0
4848

49-
implicit def eqType: Eql[Type, Type] = Eql.derived
49+
implicit def eqType: CanEqual[Type, Type] = CanEqual.derived
5050

5151
/** Main class representing types.
5252
*

compiler/src/dotty/tools/dotc/semanticdb/Language.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dotty.tools.dotc.semanticdb
22

33
import dotty.tools.dotc.semanticdb.internal._
44

5-
sealed trait Language(val value: Int) extends SemanticdbEnum derives Eql
5+
sealed trait Language(val value: Int) extends SemanticdbEnum derives CanEqual
66

77
object Language {
88

compiler/src/dotty/tools/dotc/semanticdb/Range.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final case class Range(
1212
startCharacter: Int,
1313
endLine: Int,
1414
endCharacter: Int
15-
) extends SemanticdbMessage[Range] derives Eql {
15+
) extends SemanticdbMessage[Range] derives CanEqual {
1616
@sharable
1717
private var __serializedSizeCachedValue: Int = 0
1818
private def __computeSerializedValue(): Int = {

compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ object Scala3:
2525

2626
private val WILDCARDTypeName = nme.WILDCARD.toTypeName
2727

28-
enum SymbolKind derives Eql:
28+
enum SymbolKind derives CanEqual:
2929
kind =>
3030

3131
case Val, Var, Setter, Abstract

compiler/src/dotty/tools/dotc/semanticdb/Schema.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dotty.tools.dotc.semanticdb
22

33
import dotty.tools.dotc.semanticdb.internal._
44

5-
sealed trait Schema(val value: Int) extends SemanticdbEnum derives Eql
5+
sealed trait Schema(val value: Int) extends SemanticdbEnum derives CanEqual
66

77
object Schema {
88

compiler/src/dotty/tools/dotc/semanticdb/SymbolInformation.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ object SymbolInformation {
77

88
val defaultInstance = SymbolInformation("", Language.UNKNOWN_LANGUAGE, SymbolInformation.Kind.UNKNOWN_KIND, 0, "")
99

10-
sealed trait Kind(val value: Int) extends SemanticdbEnum derives Eql {
10+
sealed trait Kind(val value: Int) extends SemanticdbEnum derives CanEqual {
1111
def isUnknownKind: Boolean = this == Kind.UNKNOWN_KIND
1212
def isLocal: Boolean = this == Kind.LOCAL
1313
def isField: Boolean = this == Kind.FIELD
@@ -67,7 +67,7 @@ object SymbolInformation {
6767
}
6868
}
6969

70-
sealed trait Property(val value: Int) extends SemanticdbEnum derives Eql {
70+
sealed trait Property(val value: Int) extends SemanticdbEnum derives CanEqual {
7171
def isUnknownProperty: Boolean = this == Property.UNKNOWN_PROPERTY
7272
def isAbstract: Boolean = this == Property.ABSTRACT
7373
def isFinal: Boolean = this == Property.FINAL
@@ -131,7 +131,7 @@ final case class SymbolInformation(
131131
kind: SymbolInformation.Kind,
132132
properties: Int,
133133
displayName: String
134-
) extends SemanticdbMessage[SymbolInformation] derives Eql {
134+
) extends SemanticdbMessage[SymbolInformation] derives CanEqual {
135135
@sharable
136136
private var __serializedSizeCachedValue: Int = 0
137137
private def __computeSerializedValue(): Int = {

compiler/src/dotty/tools/dotc/semanticdb/SymbolOccurrence.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import scala.annotation.internal.sharable
55

66
object SymbolOccurrence {
77

8-
sealed trait Role(val value: Int) extends SemanticdbEnum derives Eql {
8+
sealed trait Role(val value: Int) extends SemanticdbEnum derives CanEqual {
99
def isDefinition: Boolean = this == Role.DEFINITION
1010
def isReference: Boolean = this == Role.REFERENCE
1111
}
@@ -33,7 +33,7 @@ final case class SymbolOccurrence(
3333
symbol: String,
3434
range: Option[Range],
3535
role: SymbolOccurrence.Role
36-
) extends SemanticdbMessage[SymbolOccurrence] derives Eql {
36+
) extends SemanticdbMessage[SymbolOccurrence] derives CanEqual {
3737
@sharable
3838
private var __serializedSizeCachedValue: Int = 0
3939
private def __computeSerializedValue(): Int = {

compiler/src/dotty/tools/dotc/semanticdb/TextDocument.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ final case class TextDocument(
1515
md5: String,
1616
symbols: Seq[SymbolInformation],
1717
occurrences: Seq[SymbolOccurrence]
18-
) extends SemanticdbMessage[TextDocument] derives Eql {
18+
) extends SemanticdbMessage[TextDocument] derives CanEqual {
1919
@sharable
2020
private var __serializedSizeCachedValue: Int = 0
2121
private def __computeSerializedValue(): Int = {

compiler/src/dotty/tools/dotc/semanticdb/TextDocuments.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object TextDocuments {
1212
}
1313
val defaultInstance: TextDocuments = TextDocuments(Nil)
1414
}
15-
final case class TextDocuments(documents: Seq[TextDocument]) extends SemanticdbMessage[TextDocuments] derives Eql {
15+
final case class TextDocuments(documents: Seq[TextDocument]) extends SemanticdbMessage[TextDocuments] derives CanEqual {
1616
@sharable
1717
private var __serializedSizeCachedValue: Int = 0
1818
private def __computeSerializedValue(): Int = {

compiler/src/dotty/tools/dotc/typer/Deriving.scala

+14-14
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ trait Deriving {
6767
* the deriving ADT
6868
* (b) a single parameter type class with a parameter of kind * and an ADT with
6969
* one or more type parameter of kind *
70-
* (c) the Eql type class
70+
* (c) the CanEqual type class
7171
*
72-
* See detailed descriptions in deriveSingleParameter and deriveEql below.
72+
* See detailed descriptions in deriveSingleParameter and deriveCanEqual below.
7373
*
7474
* If it passes the checks, enter a type class instance for it in the current scope.
7575
*
@@ -145,7 +145,7 @@ trait Deriving {
145145
//
146146
// given derived$TC[a, b, c] given TC[a], TC[b], TC[c]: TC[a, b, c]
147147
//
148-
// This, like the derivation for Eql, is a special case of the
148+
// This, like the derivation for CanEqual, is a special case of the
149149
// earlier more general multi-parameter type class model for which
150150
// the heuristic is typically a good one.
151151

@@ -185,8 +185,8 @@ trait Deriving {
185185
cannotBeUnified
186186
}
187187

188-
def deriveEql: Unit = {
189-
// Specific derives rules for the Eql type class ... (c) above
188+
def deriveCanEqual: Unit = {
189+
// Specific derives rules for the CanEqual type class ... (c) above
190190
//
191191
// This has been extracted from the earlier more general multi-parameter
192192
// type class model. Modulo the assumptions below, the implied semantics
@@ -196,26 +196,26 @@ trait Deriving {
196196
// 1. Type params of the deriving class correspond to all and only
197197
// elements of the deriving class which are relevant to equality (but:
198198
// type params could be phantom, or the deriving class might have an
199-
// element of a non-Eql type non-parametrically).
199+
// element of a non-CanEqual type non-parametrically).
200200
//
201201
// 2. Type params of kinds other than * can be assumed to be irrelevant to
202202
// the derivation (but: eg. Foo[F[_]](fi: F[Int])).
203203
//
204204
// Are they reasonable? They cover some important cases (eg. Tuples of all
205-
// arities). derives Eql is opt-in, so if the semantics don't match those
205+
// arities). derives CanEqual is opt-in, so if the semantics don't match those
206206
// appropriate for the deriving class the author of that class can provide
207207
// their own instance in the normal way. That being so, the question turns
208208
// on whether there are enough types which fit these semantics for the
209209
// feature to pay its way.
210210

211211
// Procedure:
212212
// We construct a two column matrix of the deriving class type parameters
213-
// and the Eql type class parameters.
213+
// and the CanEqual type class parameters.
214214
//
215215
// Rows: parameters of the deriving class
216-
// Columns: parameters of the Eql type class (L/R)
216+
// Columns: parameters of the CanEqual type class (L/R)
217217
//
218-
// Running example: type class: class Eql[L, R], deriving class: class A[T, U, V]
218+
// Running example: type class: class CanEqual[L, R], deriving class: class A[T, U, V]
219219
// clsParamss =
220220
// T_L T_R
221221
// U_L U_R
@@ -225,15 +225,15 @@ trait Deriving {
225225
tparam.copy(name = s"${tparam.name}_$$_${tcparam.name}".toTypeName)
226226
.asInstanceOf[TypeSymbol])
227227
}
228-
// Retain only rows with L/R params of kind * which Eql can be applied to.
228+
// Retain only rows with L/R params of kind * which CanEqual can be applied to.
229229
// No pairwise evidence will be required for params of other kinds.
230230
val firstKindedParamss = clsParamss.filter {
231231
case param :: _ => !param.info.isLambdaSub
232232
case _ => false
233233
}
234234

235235
// The types of the required evidence parameters. In the running example:
236-
// Eql[T_L, T_R], Eql[U_L, U_R], Eql[V_L, V_R]
236+
// CanEqual[T_L, T_R], CanEqual[U_L, U_R], CanEqual[V_L, V_R]
237237
val evidenceParamInfos =
238238
for (row <- firstKindedParamss)
239239
yield row.map(_.typeRef)
@@ -244,12 +244,12 @@ trait Deriving {
244244
for (n <- List.range(0, typeClassArity))
245245
yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef))
246246

247-
// Eql[A[T_L, U_L, V_L], A[T_R, U_R, V_R]]
247+
// CanEqual[A[T_L, U_L, V_L], A[T_R, U_R, V_R]]
248248
addInstance(clsParamss.flatten, evidenceParamInfos, instanceTypes)
249249
}
250250

251251
if (typeClassArity == 1) deriveSingleParameter
252-
else if (typeClass == defn.EqlClass) deriveEql
252+
else if (typeClass == defn.CanEqualClass) deriveCanEqual
253253
else if (typeClassArity == 0)
254254
report.error(i"type ${typeClass.name} in derives clause of ${cls.name} has no type parameters", derived.srcPos)
255255
else

compiler/src/dotty/tools/dotc/typer/Implicits.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ trait Implicits:
883883
else s"parameter $paramName" } of $methodStr"
884884
}
885885

886-
/** An Eql[T, U] instance is assumed
886+
/** A CanEqual[T, U] instance is assumed
887887
* - if one of T, U is an error type, or
888888
* - if one of T, U is a subtype of the lifted version of the other,
889889
* unless strict equality is set.
@@ -913,8 +913,8 @@ trait Implicits:
913913
/** Check that equality tests between types `ltp` and `rtp` make sense */
914914
def checkCanEqual(ltp: Type, rtp: Type, span: Span)(using Context): Unit =
915915
if (!ctx.isAfterTyper && !assumedCanEqual(ltp, rtp)) {
916-
val res = implicitArgTree(defn.EqlClass.typeRef.appliedTo(ltp, rtp), span)
917-
implicits.println(i"Eql witness found for $ltp / $rtp: $res: ${res.tpe}")
916+
val res = implicitArgTree(defn.CanEqualClass.typeRef.appliedTo(ltp, rtp), span)
917+
implicits.println(i"CanEqual witness found for $ltp / $rtp: $res: ${res.tpe}")
918918
}
919919

920920
/** Find an implicit parameter or conversion.
@@ -1047,7 +1047,7 @@ trait Implicits:
10471047
private def nestedContext() =
10481048
ctx.fresh.setMode(ctx.mode &~ Mode.ImplicitsEnabled)
10491049

1050-
private def isCoherent = pt.isRef(defn.EqlClass)
1050+
private def isCoherent = pt.isRef(defn.CanEqualClass)
10511051

10521052
val wideProto = pt.widenExpr
10531053

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

+13-13
Original file line numberDiff line numberDiff line change
@@ -77,27 +77,27 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
7777
}
7878
end synthesizedTypeTest
7979

80-
/** If `formal` is of the form Eql[T, U], try to synthesize an
81-
* `Eql.eqlAny[T, U]` as solution.
80+
/** If `formal` is of the form CanEqual[T, U], try to synthesize an
81+
* `CanEqual.canEqualAny[T, U]` as solution.
8282
*/
83-
val synthesizedEql: SpecialHandler = (formal, span) =>
83+
val synthesizedCanEqual: SpecialHandler = (formal, span) =>
8484

85-
/** Is there an `Eql[T, T]` instance, assuming -strictEquality? */
85+
/** Is there an `CanEqual[T, T]` instance, assuming -strictEquality? */
8686
def hasEq(tp: Type)(using Context): Boolean =
87-
val inst = typer.inferImplicitArg(defn.EqlClass.typeRef.appliedTo(tp, tp), span)
87+
val inst = typer.inferImplicitArg(defn.CanEqualClass.typeRef.appliedTo(tp, tp), span)
8888
!inst.isEmpty && !inst.tpe.isError
8989

90-
/** Can we assume the eqlAny instance for `tp1`, `tp2`?
90+
/** Can we assume the canEqualAny instance for `tp1`, `tp2`?
9191
* This is the case if assumedCanEqual(tp1, tp2), or
92-
* one of `tp1`, `tp2` has a reflexive `Eql` instance.
92+
* one of `tp1`, `tp2` has a reflexive `CanEqual` instance.
9393
*/
9494
def validEqAnyArgs(tp1: Type, tp2: Type)(using Context) =
9595
typer.assumedCanEqual(tp1, tp2)
9696
|| withMode(Mode.StrictEquality) {
9797
!hasEq(tp1) && !hasEq(tp2)
9898
}
9999

100-
/** Is an `Eql[cls1, cls2]` instance assumed for predefined classes `cls1`, cls2`? */
100+
/** Is an `CanEqual[cls1, cls2]` instance assumed for predefined classes `cls1`, cls2`? */
101101
def canComparePredefinedClasses(cls1: ClassSymbol, cls2: ClassSymbol): Boolean =
102102

103103
def cmpWithBoxed(cls1: ClassSymbol, cls2: ClassSymbol) =
@@ -129,8 +129,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
129129
false
130130
end canComparePredefinedClasses
131131

132-
/** Some simulated `Eql` instances for predefined types. It's more efficient
133-
* to do this directly instead of setting up a lot of `Eql` instances to
132+
/** Some simulated `CanEqual` instances for predefined types. It's more efficient
133+
* to do this directly instead of setting up a lot of `CanEqual` instances to
134134
* interpret.
135135
*/
136136
def canComparePredefined(tp1: Type, tp2: Type) =
@@ -143,10 +143,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
143143
List(arg1, arg2).foreach(fullyDefinedType(_, "eq argument", span))
144144
if canComparePredefined(arg1, arg2)
145145
|| !Implicits.strictEquality && explore(validEqAnyArgs(arg1, arg2))
146-
then ref(defn.Eql_eqlAny).appliedToTypes(args).withSpan(span)
146+
then ref(defn.CanEqual_canEqualAny).appliedToTypes(args).withSpan(span)
147147
else EmptyTree
148148
case _ => EmptyTree
149-
end synthesizedEql
149+
end synthesizedCanEqual
150150

151151
/** Creates a tree that will produce a ValueOf instance for the requested type.
152152
* An EmptyTree is returned if materialization fails.
@@ -363,7 +363,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
363363
val specialHandlers = List(
364364
defn.ClassTagClass -> synthesizedClassTag,
365365
defn.TypeTestClass -> synthesizedTypeTest,
366-
defn.EqlClass -> synthesizedEql,
366+
defn.CanEqualClass -> synthesizedCanEqual,
367367
defn.ValueOfClass -> synthesizedValueOf,
368368
defn.Mirror_ProductClass -> synthesizedProductMirror,
369369
defn.Mirror_SumClass -> synthesizedSumMirror,

compiler/src/dotty/tools/dotc/util/SourceFile.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
183183
override def toString: String = file.toString
184184
}
185185
object SourceFile {
186-
implicit def eqSource: Eql[SourceFile, SourceFile] = Eql.derived
186+
implicit def eqSource: CanEqual[SourceFile, SourceFile] = CanEqual.derived
187187

188188
implicit def fromContext(using Context): SourceFile = ctx.source
189189

compiler/test-resources/repl/i4184

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ scala> object foo { class Foo }
22
// defined object foo
33
scala> object bar { class Foo }
44
// defined object bar
5-
scala> implicit def eqFoo: Eql[foo.Foo, foo.Foo] = Eql.derived
6-
def eqFoo: Eql[foo.Foo, foo.Foo]
5+
scala> implicit def eqFoo: CanEqual[foo.Foo, foo.Foo] = CanEqual.derived
6+
def eqFoo: CanEqual[foo.Foo, foo.Foo]
77
scala> object Bar { new foo.Foo == new bar.Foo }
88
1 | object Bar { new foo.Foo == new bar.Foo }
99
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)