diff options
author | Martin Odersky <odersky@gmail.com> | 2015-11-01 16:08:46 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-11-01 16:08:46 +0100 |
commit | 5ed617b9ac205b831ec69b782472b1afc5752378 (patch) | |
tree | eab7d1dd8608a633222ef53b4005364eca3b2226 /src/dotty/tools/dotc/typer/Implicits.scala | |
parent | 08e880231ff5facd55a80bed0391b22fe85a9f44 (diff) | |
download | dotty-5ed617b9ac205b831ec69b782472b1afc5752378.tar.gz dotty-5ed617b9ac205b831ec69b782472b1afc5752378.tar.bz2 dotty-5ed617b9ac205b831ec69b782472b1afc5752378.zip |
Better handling of implicits over numeric types.
Compiling scala.math.BigDecimal and scala.math.BigInteger shows a problem.
The conversion `int2bigInt` is not applicable to a Byte because `Byte -> Int`
requires another implicit conversion.
We fix that by using a new method relaxed_<:< for implicit compatibility checks,
which always admits numeric widenings.
This leads to another problem. Now the conversions
implicit def byteToInt(x: Byte): Int
implicit def byteToShort(x: Byte): Short
are ambiguous when we try to convert from Byte to Int.
We fix that by adding a "tie-break" to implicit search
where if several methods match a numeric value result type
and all have numeric value types as result types, we
pick the numerically largest type that matches.
Diffstat (limited to 'src/dotty/tools/dotc/typer/Implicits.scala')
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 30 |
1 files changed, 26 insertions, 4 deletions
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index e3626fe20..7dbf6d17c 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -44,19 +44,19 @@ object Implicits { /** Return those references in `refs` that are compatible with type `pt`. */ protected def filterMatching(pt: Type)(implicit ctx: Context): List[TermRef] = track("filterMatching") { - def refMatches(ref: TermRef)(implicit ctx: Context) = { + def refMatches(ref: TermRef)(implicit ctx: Context) = /*ctx.traceIndented(i"refMatches $ref $pt")*/ { def discardForView(tpw: Type, argType: Type): Boolean = tpw match { case mt: MethodType => mt.isImplicit || mt.paramTypes.length != 1 || - !(argType <:< mt.paramTypes.head)(ctx.fresh.setExploreTyperState) + !(argType relaxed_<:< mt.paramTypes.head)(ctx.fresh.setExploreTyperState) case poly: PolyType => poly.resultType match { case mt: MethodType => mt.isImplicit || mt.paramTypes.length != 1 || - !(argType <:< wildApprox(mt.paramTypes.head)(ctx.fresh.setExploreTyperState)) + !(argType relaxed_<:< wildApprox(mt.paramTypes.head)(ctx.fresh.setExploreTyperState)) case rtp => discardForView(wildApprox(rtp), argType) } @@ -530,6 +530,25 @@ trait Implicits { self: Typer => case nil => acc } + /** If the (result types of) the expected type, and both alternatives + * are all numeric value types, return the alternative which is + * the smaller numeric subtype, if it exists. (This alternative is then + * discarded). + */ + def tieBreak(alt1: SearchSuccess, alt2: SearchSuccess): SearchResult = { + def isNumeric(tp: Type) = tp.typeSymbol.isNumericValueClass + def isProperSubType(tp1: Type, tp2: Type) = + tp1.isValueSubType(tp2) && !tp2.isValueSubType(tp1) + val rpt = pt.resultType + val rt1 = alt1.ref.widen.resultType + val rt2 = alt2.ref.widen.resultType + if (isNumeric(rpt) && isNumeric(rt1) && isNumeric(rt2)) + if (isProperSubType(rt1, rt2)) alt1 + else if (isProperSubType(rt2, rt1)) alt2 + else NoImplicitMatches + else NoImplicitMatches + } + /** Convert a (possibly empty) list of search successes into a single search result */ def condense(hits: List[SearchSuccess]): SearchResult = hits match { case best :: alts => @@ -539,7 +558,10 @@ trait Implicits { self: Typer => println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}") isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState) */ - new AmbiguousImplicits(best.ref, alt.ref, pt, argument) + tieBreak(best, alt) match { + case eliminated: SearchSuccess => condense(hits.filter(_ ne eliminated)) + case _ => new AmbiguousImplicits(best.ref, alt.ref, pt, argument) + } case None => ctx.runInfo.useCount(best.ref) += 1 best |