aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-03-08 11:35:05 +0100
committerMartin Odersky <odersky@gmail.com>2017-03-08 17:50:20 +0100
commitf07697b25294eaafb1c86698c44a699ec1c0d1ba (patch)
tree5a08ac595fc61bfa467ce987728bee6d51839306 /compiler/src/dotty
parentc84480960cf618c29705dbaab9332d304a081524 (diff)
downloaddotty-f07697b25294eaafb1c86698c44a699ec1c0d1ba.tar.gz
dotty-f07697b25294eaafb1c86698c44a699ec1c0d1ba.tar.bz2
dotty-f07697b25294eaafb1c86698c44a699ec1c0d1ba.zip
Disallow subtypes of Function1 acting as implicit conversions
The new test `falseView.scala` shows the problem. We might create an implicit value of some type that happens to be a subtype of Function1. We might now expect that this gives us an implicit conversion, this is most often unintended and surprising. See the comment in Implicits#discardForView for a discussion why we picked the particular scheme implemented here.
Diffstat (limited to 'compiler/src/dotty')
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Implicits.scala32
2 files changed, 29 insertions, 5 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index 3cab75f93..fcbb2f974 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -299,6 +299,8 @@ class Definitions {
lazy val ScalaPredefModuleRef = ctx.requiredModuleRef("scala.Predef")
def ScalaPredefModule(implicit ctx: Context) = ScalaPredefModuleRef.symbol
+ lazy val Predef_ConformsR = ScalaPredefModule.requiredClass("$less$colon$less").typeRef
+ def Predef_Conforms(implicit ctx: Context) = Predef_ConformsR.symbol
lazy val Predef_conformsR = ScalaPredefModule.requiredMethodRef("$conforms")
def Predef_conforms(implicit ctx: Context) = Predef_conformsR.symbol
lazy val Predef_classOfR = ScalaPredefModule.requiredMethodRef("classOf")
diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
index 759cc62e9..933e26564 100644
--- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
@@ -82,11 +82,33 @@ object Implicits {
case tpw: TermRef =>
false // can't discard overloaded refs
case tpw =>
- //if (ctx.typer.isApplicable(tp, argType :: Nil, resultType))
- // println(i"??? $tp is applicable to $this / typeSymbol = ${tpw.typeSymbol}")
- !tpw.derivesFrom(defn.FunctionClass(1)) ||
- ref.symbol == defn.Predef_conforms //
- // as an implicit conversion, Predef.$conforms is a no-op, so exclude it
+ // Only direct instances of Function1 and direct or indirect instances of <:< are eligible as views.
+ // 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:
+ //
+ // implicit def convertIfConforms[A, B](x: A)(implicit ev: 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.
+ // The reason for leaving out `Predef_conforms` is that we know it adds
+ // nothing since it only relates subtype with supertype.
+ !tpw.isRef(defn.FunctionClass(1)) &&
+ (!tpw.derivesFrom(defn.Predef_Conforms) || ref.symbol == defn.Predef_conforms)
}
def discardForValueType(tpw: Type): Boolean = tpw match {