aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala7
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala12
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala64
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala56
-rw-r--r--tests/neg/EqualityStrawman2.scala96
-rw-r--r--tests/pending/neg/EqualityStrawman2.scala126
7 files changed, 220 insertions, 142 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 7f59cbed0..b99d0716b 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -424,10 +424,17 @@ class Definitions {
lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
+
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
def ClassTagClass(implicit ctx: Context) = ClassTagType.symbol.asClass
def ClassTagModule(implicit ctx: Context) = ClassTagClass.companionModule
+ lazy val EqType = ctx.requiredClassRef("scala.Eq")
+ def EqClass(implicit ctx: Context) = EqType.symbol.asClass
+ def EqModule(implicit ctx: Context) = EqClass.companionModule
+
+ def Eq_eqAny(implicit ctx: Context) = EqModule.requiredMethodRef(nme.eqAny)
+
// Annotation base classes
lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation")
def AnnotationClass(implicit ctx: Context) = AnnotationType.symbol.asClass
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index e2add1a52..81f6da0e2 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -394,6 +394,7 @@ object StdNames {
val equals_ : N = "equals"
val error: N = "error"
val eval: N = "eval"
+ val eqAny: N = "eqAny"
val ex: N = "ex"
val experimental: N = "experimental"
val f: N = "f"
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 6ce98606b..2b0cc4033 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -603,7 +603,17 @@ trait Applications extends Compatibility { self: Typer =>
failedVal
}
}
- else realApply
+ else {
+ val app = realApply
+ app match {
+ case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType =>
+ val op = fn.symbol
+ if (op == defn.Any_== || op == defn.Any_!=)
+ checkCanEqual(left.tpe.widen, right.tpe.widen, app.pos)
+ case _ =>
+ }
+ app
+ }
}
/** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index 940170ceb..ff8e607ed 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -23,6 +23,8 @@ import Constants._
import Applications._
import ProtoTypes._
import ErrorReporting._
+import Inferencing.fullyDefinedType
+import Trees._
import Hashable._
import config.Config
import config.Printers._
@@ -415,6 +417,68 @@ trait Implicits { self: Typer =>
}
}
+ /** Find an implicit argument for parameter `formal`.
+ * @param error An error handler that gets an error message parameter
+ * which is itself parameterized by another string,
+ * indicating where the implicit parameter is needed
+ */
+ def inferImplicitArg(formal: Type, error: (String => String) => Unit, pos: Position)(implicit ctx: Context): Tree =
+ inferImplicit(formal, EmptyTree, pos) match {
+ case SearchSuccess(arg, _, _) =>
+ arg
+ case ambi: AmbiguousImplicits =>
+ error(where => s"ambiguous implicits: ${ambi.explanation} of $where")
+ EmptyTree
+ case failure: SearchFailure =>
+ val arg = synthesizedClassTag(formal, pos)
+ if (!arg.isEmpty) arg
+ else {
+ var msgFn = (where: String) =>
+ d"no implicit argument of type $formal found for $where" + failure.postscript
+ for {
+ notFound <- formal.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot)
+ Trees.Literal(Constant(raw: String)) <- notFound.argument(0)
+ } {
+ msgFn = where =>
+ err.implicitNotFoundString(
+ raw,
+ formal.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
+ formal.argInfos)
+ }
+ error(msgFn)
+ EmptyTree
+ }
+ }
+
+ /** If `formal` is of the form ClassTag[T], where `T` is a class type,
+ * synthesize a class tag for `T`.
+ */
+ def synthesizedClassTag(formal: Type, pos: Position)(implicit ctx: Context): Tree = {
+ if (formal.isRef(defn.ClassTagClass))
+ formal.argTypes match {
+ case arg :: Nil =>
+ val tp = fullyDefinedType(arg, "ClassTag argument", pos)
+ tp.underlyingClassRef(refinementOK = false) match {
+ case tref: TypeRef =>
+ return ref(defn.ClassTagModule)
+ .select(nme.apply)
+ .appliedToType(tp)
+ .appliedTo(clsOf(tref))
+ .withPos(pos)
+ case _ =>
+ }
+ case _ =>
+ }
+ EmptyTree
+ }
+
+ def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit =
+ if (!ctx.isAfterTyper && !ltp.isError && !rtp.isError)
+ inferImplicitArg(
+ defn.EqType.appliedTo(ltp, rtp),
+ _ => d"Values of types $ltp and $rtp cannot be compared with == or !=",
+ pos)
+
/** Find an implicit parameter or conversion.
* @param pt The expected type of the parameter or conversion.
* @param argument If an implicit conversion is searched, the argument to which
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index f7fa318cd..b26ff4509 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -1540,26 +1540,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree.withType(wtp.resultType)
}
val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) =>
- def where = d"parameter $pname of $methodStr"
- inferImplicit(formal, EmptyTree, tree.pos.endPos) match {
- case SearchSuccess(arg, _, _) =>
- arg
- case ambi: AmbiguousImplicits =>
- implicitArgError(s"ambiguous implicits: ${ambi.explanation} of $where")
- case failure: SearchFailure =>
- val arg = synthesizedClassTag(formal)
- if (!arg.isEmpty) arg
- else {
- var msg = d"no implicit argument of type $formal found for $where" + failure.postscript
- for (notFound <- formal.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot);
- Literal(Constant(raw: String)) <- notFound.argument(0))
- msg = err.implicitNotFoundString(
- raw,
- formal.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
- formal.argInfos)
- implicitArgError(msg)
- }
- }
+ def implicitArgError(msg: String => String) =
+ errors += (() => msg(d"parameter $pname of $methodStr"))
+ inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
}
if (errors.nonEmpty) {
// If there are several arguments, some arguments might already
@@ -1609,8 +1592,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
else
missingArgs
case _ =>
- if (tree.tpe <:< pt) tree
- else if (ctx.mode is Mode.Pattern) tree // no subtype check for pattern
+ if (ctx.mode is Mode.Pattern) {
+ tree match {
+ case _: RefTree | _: Literal if !isVarPattern(tree) => checkCanEqual(pt, wtp, tree.pos)
+ case _ =>
+ }
+ tree
+ }
+ else if (tree.tpe <:< pt) tree
else if (wtp.isInstanceOf[MethodType]) missingArgs
else {
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
@@ -1618,29 +1607,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
adaptToSubType(wtp)
}
}
-
- /** If `formal` is of the form ClassTag[T], where `T` is a class type,
- * synthesize a class tag for `T`.
- */
- def synthesizedClassTag(formal: Type): Tree = {
- if (formal.isRef(defn.ClassTagClass))
- formal.argTypes match {
- case arg :: Nil =>
- val tp = fullyDefinedType(arg, "ClassTag argument", tree.pos)
- tp.underlyingClassRef(refinementOK = false) match {
- case tref: TypeRef =>
- return ref(defn.ClassTagModule)
- .select(nme.apply)
- .appliedToType(tp)
- .appliedTo(clsOf(tref))
- .withPos(tree.pos.endPos)
- case _ =>
- }
- case _ =>
- }
- EmptyTree
- }
-
/** Adapt an expression of constant type to a different constant type `tpe`. */
def adaptConstant(tree: Tree, tpe: ConstantType): Tree = {
def lit = Literal(tpe.value).withPos(tree.pos)
diff --git a/tests/neg/EqualityStrawman2.scala b/tests/neg/EqualityStrawman2.scala
deleted file mode 100644
index f71c7d3d7..000000000
--- a/tests/neg/EqualityStrawman2.scala
+++ /dev/null
@@ -1,96 +0,0 @@
-object equality {
-
- trait Eq[-T, -U]
- object Eq extends Eq[Any, Any]
-
- implicit class EqualsDeco[T](val x: T) extends AnyVal {
- def ===[U] (y: U)(implicit ce: Eq[T, U]) = x.equals(y)
- }
-
- type EqEq[T] = Eq[T, T]
-
- trait EqClass
-
- implicit def eqAny: Eq[Any, Any] = Eq
-
- implicit def mixedEq1 : Eq[Any, EqClass] = Eq
- implicit def mixedEq1alt: Eq[Any, EqClass] = Eq
- implicit def mixedEq2 : Eq[EqClass, Any] = Eq
- implicit def mixedEq2alt: Eq[EqClass, Any] = Eq
-
-// implicit def eqEq[U, T <: U with EqClass[U]]: Eq[T, T] = Eq
-
- case class Str(str: String) extends EqClass
- case class Num(x: Int) extends EqClass
-
- case class Other(x: Int)
-
- trait Option[+T] extends EqClass
- case class Some[+T](x: T) extends Option[T]
- case object None extends Option[Nothing]
-
- implicit def eqStr: Eq[Str, Str] = Eq
- implicit def eqNum: Eq[Num, Num] = Eq
- implicit def eqOption[T, U](implicit ce: Eq[T, U]): Eq[Option[T], Option[U]] = Eq
-
- def main(args: Array[String]): Unit = {
- val x = Str("abc")
- (Some(x): Option[Str]) === (None: Option[Str])
-
- x === x
-
- val n = Num(2)
- val m = Num(3)
- n === m
-
- Other(1) === Other(2)
-
- (Some(x) === None)//(eqOption)
- (Some(x) === Some(Str("")))(eqOption)
- val z: Option[Str] = Some(Str("abc"))
- z === Some(x)
- z === None
- Some(x) === z
- None === z
-/*
- Other(3) === null
- Str("x") === null
- null === Other(3)
- null === Str("x")
- null === null*/
-
- class Fruit extends EqClass
- class Apple extends Fruit
- class Pear extends Fruit
- implicit def eqFruit: Eq[Fruit, Fruit] = Eq
-
- Some(new Apple) === Some(new Pear)
-
-
- def ddistinct[T: EqEq](xs: List[T]): List[T] = xs match {
- case Nil => Nil
- case x :: xs => x :: xs.filterNot(x === _)
- }
-
- ddistinct(List(z, z, z))
-
- n match {
- case None =>
- }
-
- Some(new Apple) === Some(Str("xx")) // error
- x === n // error
- n === x // error
- x === Other(1) // error
- Other(2) === x // error
- z === Some(n) // error
- z === n // error
- Some(n) === z // error
- n === z // error
- Other(1) === z // error
- z === Other(1) // error
- ddistinct(List(z, n)) // error
-
-
- }
-}
diff --git a/tests/pending/neg/EqualityStrawman2.scala b/tests/pending/neg/EqualityStrawman2.scala
new file mode 100644
index 000000000..e72e675c2
--- /dev/null
+++ b/tests/pending/neg/EqualityStrawman2.scala
@@ -0,0 +1,126 @@
+object equality {
+
+ trait Eq[T, U]
+ def Eq[T, U]: Eq[T, U] = new Eq[T, U]{}
+
+ implicit class EqualsDeco[T](val x: T) extends AnyVal {
+ def ===[U] (y: U)(implicit ce: Eq[T, U]) = x.equals(y)
+ }
+
+ type EqEq[T] = Eq[T, T]
+
+ trait EqClass[T]
+
+ implicit def eqAny[T, U]: Eq[T, U] = Eq
+/*
+ implicit def mixedEq1[T, U](implicit ce: Eq[T, U]): Eq[T, Any] = Eq
+ implicit def mixedEq2[T, U](implicit ce: Eq[T, U]): Eq[Any, T] = Eq
+ implicit def mixedEq1alt : Eq[Any, EqClass] = Eq
+ implicit def mixedEq2 : Eq[EqClass, Any] = Eq
+ implicit def mixedEq2alt : Eq[EqClass, Any] = Eq
+ implicit def mixedNull1[T]: Eq[T, Null] = Eq
+ implicit def mixedNull2[T]: Eq[Null, T] = Eq
+*/
+ implicit def eqString: Eq[String, String] = Eq
+ implicit def eqInt: Eq[Int, Int] = Eq
+ implicit def eqOption[T, U](implicit ce: Eq[T, U]): Eq[Option[T], Option[U]] = Eq
+
+/*
+ implicit def eqEq[UE, TE <: UE with EqClass[UE]]: Eq[TE, TE] = Eq
+
+ case class Str(str: String) extends EqClass[Str]
+ case class Num(x: Int) extends EqClass[Num]
+
+ case class Other(x: Int)
+
+ trait Option[+T] extends EqClass[_]
+ case class Some[+T](x: T) extends Option[T]
+ case object None extends Option[Nothing]
+
+ //implicit def eqStr: Eq[Str, Str] = Eq
+ //implicit def eqNum: Eq[Num, Num] = Eq
+
+ implicit def eqOption[T, U](implicit ce: Eq[T, U]): Eq[Option[T], Option[U]] = Eq
+*/
+ def some[T](x: T): Option[T] = Some(x)
+
+ def main(args: Array[String]): Unit = {
+ val x = "abc"
+ x === x
+
+ val n = 2
+ n === n
+ x === 1 // error
+ n === x // error
+
+ 1.0 === 1.0
+ 1.0 === new Object
+ 1.0 === n // error
+
+ some(n) === some(n)
+ some(n) === 1 // error
+ some(n) === some(1) // error
+ 1 === some(n) // error
+ some(1) === some(n)
+ some(1.0) === some(new Object)
+ some(1) === some(x) // error
+ some(x) === some(1) // error
+ some(x) === some(1.0)
+
+/*
+ val n = Num(2)
+ val m = Num(3)
+ n === m
+
+ Other(1) === Other(2)
+
+ Some(x) === Some(Str(""))
+ val z: Option[Str] = Some(Str("abc"))
+ z === Some(x)
+ z === None
+ Some(x) === z
+ None === z
+ Some(Other(1)) === None
+
+ Other(3) === null
+ Str("x") === null
+// null === Other(3)
+// null === Str("x")
+// null === null
+
+ class Fruit extends EqClass
+ class Apple extends Fruit
+ class Pear extends Fruit
+ implicit def eqFruit: Eq[Fruit, Fruit] = Eq
+
+ Some(new Apple) === Some(new Pear)
+
+
+ def ddistinct[T: EqEq](xs: List[T]): List[T] = xs match {
+ case Nil => Nil
+ case x :: xs => x :: xs.filterNot(x === _)
+ }
+
+ ddistinct(List(z, z, z))
+
+ n match {
+ case None =>
+ }
+
+ Some(new Apple) === Some(Str("xx")) // error
+ Some(new Apple) === Some(Other(1)) // error
+ Some(Other(1)) === Some(new Apple) // error
+ x === n // error
+ n === x // error
+ x === Other(1) // error
+ Other(2) === x // error
+ z === Some(n) // error
+ z === n // error
+ Some(n) === z // error
+ n === z // error
+ Other(1) === z // error
+ z === Other(1) // error
+ ddistinct(List(z, n)) // error
+*/
+ }
+}