aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer/Implicits.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-11-01 16:08:46 +0100
committerMartin Odersky <odersky@gmail.com>2015-11-01 16:08:46 +0100
commit5ed617b9ac205b831ec69b782472b1afc5752378 (patch)
treeeab7d1dd8608a633222ef53b4005364eca3b2226 /src/dotty/tools/dotc/typer/Implicits.scala
parent08e880231ff5facd55a80bed0391b22fe85a9f44 (diff)
downloaddotty-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.scala30
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