Skip to content

Commit ae5dd91

Browse files
committed
Definitions enabling multiversal equals
These follow he strawman design. One addition `Null' also needs overloaded variants to work correctly in all comparisons.
1 parent 0bd8d71 commit ae5dd91

File tree

3 files changed

+84
-6
lines changed

3 files changed

+84
-6
lines changed

src/dotty/Eq.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dotty
2+
import scala.annotation.implicitNotFound
3+
4+
/** A marker class indicating that values of kind `T` can be compared. */
5+
class Eq[-T]
6+
7+
/** Besides being a companion object, this object
8+
* can also be used as a value that's compatible with
9+
* any instance of `Eq`.
10+
*/
11+
object Eq extends Eq[Any] {
12+
13+
/** A type that always causes an "implicit not found".
14+
* Used to mark some overloaded variants of `==` as errors.
15+
*/
16+
@implicitNotFound("cannot compare value of type ${T} with a value outside its equality class")
17+
trait Impossible[T]
18+
19+
/** An implicit that provides an `Eq` instance for all types `T`
20+
* such that `T <: EqClass[T]`.
21+
*/
22+
implicit def eqEq[T <: EqClass[T]]: Eq[T] = Eq
23+
24+
}
25+

src/dotty/EqClass.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package dotty
2+
import scala.annotation.implicitNotFound
3+
4+
/** A class providing a specialized notion of equality, allowing
5+
* only values in the same equality class to be compared.
6+
*/
7+
trait EqClass[-T] {
8+
9+
/** Comparison operation between values in the same equality class */
10+
final def == [T >: this.type <: EqClass[_]](other: T)(implicit ce: Eq[T]): Boolean = this.equals(other)
11+
12+
/** A "dead-end" overload which will lead to an implicit not found error
13+
* when comparing a value in an equality class with a value tha's outside
14+
* all equality classes.
15+
*/
16+
final def == [T](other: T)(implicit ce: Eq.Impossible[T]): Boolean = ???
17+
18+
/** A special overload for comparisons with `null` */
19+
final def == (other: Null): Boolean = this.equals(other)
20+
}
21+

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

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,12 @@ class Definitions {
8686
}
8787

8888
private def newPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int,
89-
resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = {
89+
resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags,
90+
typeBounds: List[TypeBounds] = Nil) = {
9091
val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount)
91-
val tparamBounds = tparamNames map (_ => TypeBounds.empty)
92+
val tparamBounds =
93+
if (typeBounds.nonEmpty) typeBounds
94+
else tparamNames map (_ => TypeBounds.empty)
9295
val ptype = PolyType(tparamNames)(_ => tparamBounds, resultTypeFn)
9396
newMethod(cls, name, ptype, flags)
9497
}
@@ -171,8 +174,27 @@ class Definitions {
171174
lazy val Any_isInstanceOf = newT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final)
172175
lazy val Any_asInstanceOf = newT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final)
173176

177+
/** A method with signature
178+
*
179+
* def == [T <: EqClass[_]]($x0: T)(implicit $x1: Eq.Impossible[T]): Boolean = ???
180+
*/
181+
lazy val Any_forbidden_== = newMethod(AnyClass, nme.EQ, new ForbiddenEqCompleter, Final)
182+
183+
class ForbiddenEqCompleter extends LazyType {
184+
override def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
185+
denot.info =
186+
PolyType(tpnme.syntheticTypeParamNames(1))(
187+
pt => List(TypeBounds.upper(EqClassType.appliedTo(TypeBounds.empty))),
188+
pt => MethodType(List(PolyParam(pt, 0)),
189+
ImplicitMethodType(List(EqImpossibleType.appliedTo(PolyParam(pt, 0))),
190+
BooleanType)))
191+
}
192+
}
193+
194+
lazy val Any_null_== = newMethod(AnyClass, nme.EQ, MethodType(List(NullType), BooleanType), Final)
195+
174196
def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode,
175-
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf)
197+
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf, Any_forbidden_==, Any_null_==)
176198

177199
lazy val ObjectClass: ClassSymbol = {
178200
val cls = ctx.requiredClass("java.lang.Object")
@@ -212,8 +234,13 @@ class Definitions {
212234
lazy val NothingClass: ClassSymbol = newCompleteClassSymbol(
213235
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef))
214236
def NothingType = NothingClass.typeRef
215-
lazy val NullClass: ClassSymbol = newCompleteClassSymbol(
216-
ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef))
237+
lazy val NullClass: ClassSymbol = {
238+
val nc = newCompleteClassSymbol(
239+
ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef))
240+
val Null_Equals_== = newMethod(nc, nme.EQ, MethodType(List(EqClassType), BooleanType)).entered
241+
val Null_Null_== = newMethod(nc, nme.EQ, MethodType(List(nc.typeRef), BooleanType)).entered
242+
nc
243+
}
217244
def NullType = NullClass.typeRef
218245

219246
lazy val ScalaPredefModuleRef = ctx.requiredModuleRef("scala.Predef")
@@ -282,7 +309,6 @@ class Definitions {
282309
lazy val ArrayModuleType = ctx.requiredModuleRef("scala.Array")
283310
def ArrayModule(implicit ctx: Context) = ArrayModuleType.symbol.moduleClass.asClass
284311

285-
286312
lazy val UnitType: TypeRef = valueTypeRef("scala.Unit", BoxedUnitType, java.lang.Void.TYPE, UnitEnc)
287313
def UnitClass(implicit ctx: Context) = UnitType.symbol.asClass
288314
lazy val BooleanType = valueTypeRef("scala.Boolean", BoxedBooleanType, java.lang.Boolean.TYPE, BooleanEnc)
@@ -401,6 +427,12 @@ class Definitions {
401427
lazy val StringAdd_plusR = StringAddClass.requiredMethodRef(nme.raw.PLUS)
402428
def StringAdd_+(implicit ctx: Context) = StringAdd_plusR.symbol
403429

430+
lazy val EqType: TypeRef = ctx.requiredClassRef("dotty.Eq")
431+
def EqClass(implicit ctx: Context) = EqType.symbol.asClass
432+
lazy val EqClassType: TypeRef = ctx.requiredClassRef("dotty.EqClass")
433+
def EqClassClass(implicit ctx: Context) = EqClassType.symbol.asClass
434+
lazy val EqImpossibleType: TypeRef = ctx.requiredClassRef("dotty.Eq.Impossible")
435+
def EqImpossibleClass(implicit ctx: Context) = EqImpossibleType.symbol.asClass
404436
lazy val PairType: TypeRef = ctx.requiredClassRef("dotty.Pair")
405437
def PairClass(implicit ctx: Context) = PairType.symbol.asClass
406438
lazy val PartialFunctionType: TypeRef = ctx.requiredClassRef("scala.PartialFunction")

0 commit comments

Comments
 (0)