aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-07-15 10:30:30 +0200
committerMartin Odersky <odersky@gmail.com>2016-07-15 10:30:30 +0200
commit894c9fbf247765041fc32788c78b85f1b2b2a191 (patch)
treeaa918254e5b9869e10af3e967f980fd458e4c309
parenta737b47a92fe414a5e7f07bae171878c81bf9f45 (diff)
downloaddotty-894c9fbf247765041fc32788c78b85f1b2b2a191.tar.gz
dotty-894c9fbf247765041fc32788c78b85f1b2b2a191.tar.bz2
dotty-894c9fbf247765041fc32788c78b85f1b2b2a191.zip
Bounds of type lambdas compare contravariantly
Enable checking of bounds when comparing type lambdas. This invalidates a pattern used in t2994 and potentially other code, where a bound [X] -> Any is used as a template that is a legal supertype of all other bounds. The old behavior is still available under language:Scala2.
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala30
-rw-r--r--tests/neg/hk-bounds.scala8
-rw-r--r--tests/neg/t2994.scala2
3 files changed, 27 insertions, 13 deletions
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index 31cc87b3a..a895db178 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -386,22 +386,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case tp2 @ TypeLambda(tparams2, body2) =>
def compareHkLambda: Boolean = tp1.stripTypeVar match {
case tp1 @ TypeLambda(tparams1, body1) =>
- // Don't compare bounds of lambdas, or t2994 will fail
- // The issue is that, logically, bounds should compare contravariantly,
- // so the bounds checking should look like this:
- //
- // tparams1.corresponds(tparams2)((tparam1, tparam2) =>
- // isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds))
- //
- // But that would invalidate a pattern such as
- // `[X0 <: Number] -> Number <:< [X0] -> Any`
- // This wpuld mean that there is no convenient means anymore to express a kind
- // as a supertype. The fix is to delay the checking of bounds so that only
- // bounds of * types are checked.
+ /* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail
+ * The issue is that, logically, bounds should compare contravariantly,
+ * but that would invalidate a pattern exploited in t2994:
+ *
+ * [X0 <: Number] -> Number <:< [X0] -> Any
+ *
+ * Under the new scheme, `[X0] -> Any` is NOT a kind that subsumes
+ * all other bounds. You'd have to write `[X0 >: Any <: Nothing] -> Any` instead.
+ * This might look weird, but is the only logically correct way to do it.
+ *
+ * Note: it would be nice if this could trigger a migration warning, but I
+ * am not sure how, since the code is buried so deep in subtyping logic.
+ */
+ def boundsOK =
+ ctx.scala2Mode ||
+ tparams1.corresponds(tparams2)((tparam1, tparam2) =>
+ isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds))
val saved = comparingLambdas
comparingLambdas = true
try
variancesConform(tparams1, tparams2) &&
+ boundsOK &&
isSubType(body1, body2.subst(tp2, tp1))
finally comparingLambdas = saved
case _ =>
diff --git a/tests/neg/hk-bounds.scala b/tests/neg/hk-bounds.scala
index 80c8cfaa8..db6712d72 100644
--- a/tests/neg/hk-bounds.scala
+++ b/tests/neg/hk-bounds.scala
@@ -21,3 +21,11 @@ object Test1 {
baz[Foo] // error: Type argument [X0] -> Foo[X0] does not conform to lower bound [X0] -> Baz[X0]
}
+object Test2 {
+ type Alias[F[X] <: Foo[X]] = F[Int]
+
+ def foo[M[_[_]], A[_]]: M[A] = null.asInstanceOf[M[A]]
+
+ val x = foo[Alias, Bar] // error: Type argument Test2.Alias does not conform to upper bound [X0 <: [X0] -> Any] -> Any
+
+}
diff --git a/tests/neg/t2994.scala b/tests/neg/t2994.scala
index 6964a080e..e19397a3d 100644
--- a/tests/neg/t2994.scala
+++ b/tests/neg/t2994.scala
@@ -21,7 +21,7 @@ object Naturals {
// crashes scala-2.8.0 beta1
trait MUL[n <: NAT, m <: NAT] extends NAT {
trait curry[n[_[_], _], s[_]] { type f[z <: NAT] = n[s, z] }
- type a[s[_ <: NAT] <: NAT, z <: NAT] = n#a[curry[m#a, s]#f, z] // error: not a legal path // error: not a legal path
+ type a[s[_ <: NAT] <: NAT, z <: NAT] = n#a[curry[m#a, s]#f, z] // error: not a legal path // error: not a legal path // error: arg does not conform to bound // error: arg does not conform to bound
}
}