aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-03-08 22:06:22 +0100
committerMartin Odersky <odersky@gmail.com>2017-03-08 22:06:35 +0100
commitaa2f9078d76a21d828a06b8e324d31a502ee505c (patch)
treebf657e58c1a78357349def2309dd41c0cecdd9a1
parent180dfdc7e81d632e599fb0a545025720e8000573 (diff)
downloaddotty-aa2f9078d76a21d828a06b8e324d31a502ee505c.tar.gz
dotty-aa2f9078d76a21d828a06b8e324d31a502ee505c.tar.bz2
dotty-aa2f9078d76a21d828a06b8e324d31a502ee505c.zip
Drop special case around Function1
Now only Scala2 mode treats Function1's as implicit conversions. Instead we introduce a new subclass ImplicitConverter of Function1, instances of which are turned into implicit conversions.
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Implicits.scala30
-rw-r--r--library/src/dotty/DottyPredef.scala20
-rw-r--r--tests/pos-scala2/typerep-stephane.scala (renamed from tests/pos/typerep-stephane.scala)0
-rw-r--r--tests/pos-scala2/viewtest1.scala (renamed from tests/pos/viewtest1.scala)0
-rw-r--r--tests/pos/t0786.scala2
-rw-r--r--tests/run/iterator-from.scala6
-rw-r--r--tests/run/t8280.scala6
8 files changed, 40 insertions, 26 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index fcbb2f974..1be47c1da 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -338,6 +338,8 @@ class Definitions {
def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol
def Predef_eqAny(implicit ctx: Context) = DottyPredefModule.requiredMethod(nme.eqAny)
+ lazy val Predef_ImplicitConverterR = DottyPredefModule.requiredClass("ImplicitConverter").typeRef
+ def Predef_ImplicitConverter(implicit ctx: Context) = Predef_ImplicitConverterR.symbol
lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays")
def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol
diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
index 6dbb2216c..ebbcbcc95 100644
--- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
@@ -86,35 +86,25 @@ object Implicits {
// However, Predef.$conforms is not eligible, because it is a no-op.
//
// In principle, it would be cleanest if only implicit methods qualified
- // as implicit conversions. The reasons for deviating are as follows:
- // Keeping Function1: It's still used quite often (for instance, view
- // bounds map to implicits of function types) and there is no feasible workaround.
- // One tempting workaround would be to add a global def
- //
- // implicit def convertIfFuntion1[A, B](x: A)(implicit ev: A => B): B = ev(a)
- //
- // But that would throw out the baby with the bathwater. Now, every subtype of
- // function gives again rise to an implicit conversion. So it's better to just accept
- // function types in their dual roles.
- //
- // The reason for the treatment of <:< and conforms is similar. We could
- // avoid the clause by having a standard conversion like this in Predef:
+ // as implicit conversions. We could achieve that by having standard conversions like
+ // this in Predef:
//
// implicit def convertIfConforms[A, B](x: A)(implicit ev: A <:< B): B = ev(a)
+ // implicit def convertIfConverter[A, B](x: A)(implicit ev: ImplicitConverter[A, B]): B = ev(a)
//
- // But that would slow down implicit search a lot, because this conversion is
- // eligible for all pairs of types, and therefore is tried a lot. So we
- // emulate the existence of a such a conversion directly in the search.
+ // (Once `<:<` inherits from `ImplicitConverter` we only need the 2nd one.)
+ // But clauses like this currently slow down implicit search a lot, because
+ // they are eligible for all pairs of types, and therefore are tried too often.
+ // We emulate instead these conversions directly in the search.
// The reason for leaving out `Predef_conforms` is that we know it adds
// nothing since it only relates subtype with supertype.
//
// We keep the old behavior under -language:Scala2.
- val isFunction =
- if (ctx.scala2Mode) tpw.derivesFrom(defn.FunctionClass(1))
- else tpw.isRef(defn.FunctionClass(1))
+ val isFunctionInS2 = ctx.scala2Mode && tpw.derivesFrom(defn.FunctionClass(1))
+ val isImplicitConverter = tpw.derivesFrom(defn.Predef_ImplicitConverter)
val isConforms =
tpw.derivesFrom(defn.Predef_Conforms) && ref.symbol != defn.Predef_conforms
- !(isFunction || isConforms)
+ !(isFunctionInS2 || isImplicitConverter || isConforms)
}
def discardForValueType(tpw: Type): Boolean = tpw match {
diff --git a/library/src/dotty/DottyPredef.scala b/library/src/dotty/DottyPredef.scala
index 12040e0f3..e78fa9239 100644
--- a/library/src/dotty/DottyPredef.scala
+++ b/library/src/dotty/DottyPredef.scala
@@ -36,4 +36,24 @@ object DottyPredef {
implicit def eqNumFloat : Eq[Number, Float] = Eq
implicit def eqDoubleNum: Eq[Double, Number] = Eq
implicit def eqNumDouble: Eq[Number, Double] = Eq
+
+ /** A class for implicit values that can serve as implicit conversions
+ * The implicit resolution algorithm will act as if there existed
+ * the additional implicit definition:
+ *
+ * def $implicitConversion[T, U](x: T)(c: ImplicitConverter[T, U]): U = c(x)
+ *
+ * However, the presence of this definition would slow down implicit search since
+ * its outermost type matches any pair of types. Therefore, implicit search
+ * contains a special case in `Implicits#discardForView` which emulates the
+ * conversion in a more efficient way.
+ *
+ * Note that this is a SAM class - function literals are automatically converted
+ * to `ImplicitConverter` values.
+ *
+ * Also note that in bootstrapped dotty, `Predef.<:<` should inherit from
+ * `ImplicitConverter`. This would cut the number of special cases in
+ * `discardForView` from two to one.
+ */
+ abstract class ImplicitConverter[-T, +U] extends Function1[T, U]
}
diff --git a/tests/pos/typerep-stephane.scala b/tests/pos-scala2/typerep-stephane.scala
index 2cb899591..2cb899591 100644
--- a/tests/pos/typerep-stephane.scala
+++ b/tests/pos-scala2/typerep-stephane.scala
diff --git a/tests/pos/viewtest1.scala b/tests/pos-scala2/viewtest1.scala
index 38945ad2f..38945ad2f 100644
--- a/tests/pos/viewtest1.scala
+++ b/tests/pos-scala2/viewtest1.scala
diff --git a/tests/pos/t0786.scala b/tests/pos/t0786.scala
index b320de0ed..9346afdff 100644
--- a/tests/pos/t0786.scala
+++ b/tests/pos/t0786.scala
@@ -15,7 +15,7 @@ object ImplicitProblem {
def eval = f(nullval[T]).eval + 1
}
- def depth[T](n: T)(implicit ev: T => Rep[T]) = n.eval
+ def depth[T](n: T)(implicit ev: T => Rep[T]) = ev(n).eval
def main(args: Array[String]): Unit = {
println(depth(nullval[M[Int]])) // (1) this works
diff --git a/tests/run/iterator-from.scala b/tests/run/iterator-from.scala
index 4f403680c..c7c0f9809 100644
--- a/tests/run/iterator-from.scala
+++ b/tests/run/iterator-from.scala
@@ -11,7 +11,9 @@ object Test extends dotty.runtime.LegacyApp {
val maxKey = 50
val maxValue = 50
- def testSet[A <% Ordered[A]](set: SortedSet[A], list: List[A]): Unit = {
+ implicit def convertIfView[A](x: A)(implicit view: A => Ordered[A]): Ordered[A] = view(x)
+
+ def testSet[A: Ordering](set: SortedSet[A], list: List[A]): Unit = {
val distinctSorted = list.distinct.sorted
assertEquals("Set size wasn't the same as list sze", set.size, distinctSorted.size)
@@ -24,7 +26,7 @@ object Test extends dotty.runtime.LegacyApp {
}
}
- def testMap[A <% Ordered[A], B](map: SortedMap[A, B], list: List[(A, B)]): Unit = {
+ def testMap[A: Ordering, B](map: SortedMap[A, B], list: List[(A, B)]): Unit = {
val distinctSorted = distinctByKey(list).sortBy(_._1)
assertEquals("Map size wasn't the same as list sze", map.size, distinctSorted.size)
diff --git a/tests/run/t8280.scala b/tests/run/t8280.scala
index 1d2c56b85..5fcbad0a3 100644
--- a/tests/run/t8280.scala
+++ b/tests/run/t8280.scala
@@ -74,14 +74,14 @@ object Moop3 {
// Dotty deviation. This fails for Dotty with ambiguity error for similar reasons as ob1.
}
object ob2 {
- implicit val f1: Int => String = _ => "Int"
+ implicit val f1: ImplicitConverter[Int, String] = _ => "Int"
implicit def f2(x: Long): String = "Long"
println(5: String)
}
object ob3 {
- implicit val f1: Int => String = _ => "Int"
- implicit val f2: Long => String = _ => "Long"
+ implicit val f1: ImplicitConverter[Int, String] = _ => "Int"
+ implicit val f2: ImplicitConverter[Long, String] = _ => "Long"
println(5: String)
}