aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-05-06 18:58:56 +0200
committerMartin Odersky <odersky@gmail.com>2016-05-23 16:11:45 +0200
commit4baaa5ac03b165ef9b17922e85ef062460d002c9 (patch)
tree264ce0a0525fdeb17cb7ab18dd00d9a2d628c3d0 /src
parentcd8bf2d69cf1463fa16a69badb2a839f540bb2fa (diff)
downloaddotty-4baaa5ac03b165ef9b17922e85ef062460d002c9.tar.gz
dotty-4baaa5ac03b165ef9b17922e85ef062460d002c9.tar.bz2
dotty-4baaa5ac03b165ef9b17922e85ef062460d002c9.zip
Check equality that with == and != makes sense
This is done by checking each instance of an eqAny implicit so that it does not contain non-bottom instances of equality types as instances.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala54
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala3
3 files changed, 46 insertions, 13 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index b99d0716b..d9f3432bd 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -433,7 +433,7 @@ class Definitions {
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)
+ def Eq_eqAny(implicit ctx: Context) = EqModule.requiredMethod(nme.eqAny)
// Annotation base classes
lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation")
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index ff8e607ed..366044d72 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -193,7 +193,7 @@ object Implicits {
/** A successful search
* @param ref The implicit reference that succeeded
- * @param tree The typed tree that can needs to be inserted
+ * @param tree The typed tree that needs to be inserted
* @param ctx The context after the implicit search
*/
case class SearchSuccess(tree: tpd.Tree, ref: TermRef, tstate: TyperState) extends SearchResult {
@@ -472,12 +472,17 @@ trait Implicits { self: Typer =>
EmptyTree
}
- def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit =
- if (!ctx.isAfterTyper && !ltp.isError && !rtp.isError)
- inferImplicitArg(
+ /** Check that equality tests between types `ltp` and `rtp` make sense */
+ def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit = {
+ def assumedCanEqual = ltp.isError || rtp.isError || ltp <:< rtp || rtp <:< ltp
+ if (!ctx.isAfterTyper && !assumedCanEqual) {
+ val res = inferImplicitArg(
defn.EqType.appliedTo(ltp, rtp),
- _ => d"Values of types $ltp and $rtp cannot be compared with == or !=",
+ _ => ctx.error(d"Values of types $ltp and $rtp cannot be compared with == or !=", pos),
pos)
+ implicits.println(i"Eq witness found: $res: ${res.tpe}")
+ }
+ }
/** Find an implicit parameter or conversion.
* @param pt The expected type of the parameter or conversion.
@@ -493,14 +498,41 @@ trait Implicits { self: Typer =>
val prevConstr = ctx.typerState.constraint
ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) {
assert(!pt.isInstanceOf[ExprType])
- val isearch =
- if (ctx.settings.explaintypes.value) new ExplainedImplicitSearch(pt, argument, pos)
- else new ImplicitSearch(pt, argument, pos)
- val result = isearch.bestImplicit
+ // Search implicit argument with type `tp`
+ def searchImplicit(pt: Type) = {
+ val isearch =
+ if (ctx.settings.explaintypes.value) new ExplainedImplicitSearch(pt, argument, pos)
+ else new ImplicitSearch(pt, argument, pos)
+ isearch.bestImplicit
+ }
+ // Does there exist an implicit value of type `Eq[tp, tp]`?
+ def hasEq(tp: Type): Boolean = searchImplicit(defn.EqType.appliedTo(tp, tp)) match {
+ case result: SearchSuccess => result.ref.symbol != defn.Eq_eqAny
+ case result: AmbiguousImplicits => true
+ case _ => false
+ }
+ // A type `tp` is a valid `eqAny` argument if `tp` is a bottom type, or
+ // no implicit value of type `Eq[tp, tp]` exists.
+ def isValidEqAnyArg(tp: Type)(implicit ctx: Context) = {
+ fullyDefinedType(tp, "eqAny argument", pos)
+ defn.isBottomType(tp.dealias) || !hasEq(tp)
+ }
+ def areValidEqAnyArgs(tps: List[Type])(implicit ctx: Context) =
+ tps.forall(isValidEqAnyArg) || {
+ println(i"invalid eqAny[$tps%, %]"); false
+ }
+ val result = searchImplicit(pt)
result match {
case result: SearchSuccess =>
- result.tstate.commit()
- result
+ result.tree match {
+ case TypeApply(fn, targs)
+ if fn.symbol == defn.Eq_eqAny &&
+ !areValidEqAnyArgs(targs.tpes)(ctx.fresh.setTyperState(result.tstate)) =>
+ NoImplicitMatches
+ case _ =>
+ result.tstate.commit()
+ result
+ }
case result: AmbiguousImplicits =>
val deepPt = pt.deepenProto
if (deepPt ne pt) inferImplicit(deepPt, argument, pos)
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index b26ff4509..23e7cdb7b 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -1594,7 +1594,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case _ =>
if (ctx.mode is Mode.Pattern) {
tree match {
- case _: RefTree | _: Literal if !isVarPattern(tree) => checkCanEqual(pt, wtp, tree.pos)
+ case _: RefTree | _: Literal if !isVarPattern(tree) =>
+ checkCanEqual(pt, wtp, tree.pos)(ctx.retractMode(Mode.Pattern))
case _ =>
}
tree