diff options
author | Martin Odersky <odersky@gmail.com> | 2016-05-06 18:58:56 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-05-23 16:11:45 +0200 |
commit | 4baaa5ac03b165ef9b17922e85ef062460d002c9 (patch) | |
tree | 264ce0a0525fdeb17cb7ab18dd00d9a2d628c3d0 | |
parent | cd8bf2d69cf1463fa16a69badb2a839f540bb2fa (diff) | |
download | dotty-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.
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 54 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 3 |
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 |