Skip to content

Commit 894c9fb

Browse files
committed
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.
1 parent a737b47 commit 894c9fb

File tree

3 files changed

+27
-13
lines changed

3 files changed

+27
-13
lines changed

src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -386,22 +386,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
386386
case tp2 @ TypeLambda(tparams2, body2) =>
387387
def compareHkLambda: Boolean = tp1.stripTypeVar match {
388388
case tp1 @ TypeLambda(tparams1, body1) =>
389-
// Don't compare bounds of lambdas, or t2994 will fail
390-
// The issue is that, logically, bounds should compare contravariantly,
391-
// so the bounds checking should look like this:
392-
//
393-
// tparams1.corresponds(tparams2)((tparam1, tparam2) =>
394-
// isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds))
395-
//
396-
// But that would invalidate a pattern such as
397-
// `[X0 <: Number] -> Number <:< [X0] -> Any`
398-
// This wpuld mean that there is no convenient means anymore to express a kind
399-
// as a supertype. The fix is to delay the checking of bounds so that only
400-
// bounds of * types are checked.
389+
/* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail
390+
* The issue is that, logically, bounds should compare contravariantly,
391+
* but that would invalidate a pattern exploited in t2994:
392+
*
393+
* [X0 <: Number] -> Number <:< [X0] -> Any
394+
*
395+
* Under the new scheme, `[X0] -> Any` is NOT a kind that subsumes
396+
* all other bounds. You'd have to write `[X0 >: Any <: Nothing] -> Any` instead.
397+
* This might look weird, but is the only logically correct way to do it.
398+
*
399+
* Note: it would be nice if this could trigger a migration warning, but I
400+
* am not sure how, since the code is buried so deep in subtyping logic.
401+
*/
402+
def boundsOK =
403+
ctx.scala2Mode ||
404+
tparams1.corresponds(tparams2)((tparam1, tparam2) =>
405+
isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds))
401406
val saved = comparingLambdas
402407
comparingLambdas = true
403408
try
404409
variancesConform(tparams1, tparams2) &&
410+
boundsOK &&
405411
isSubType(body1, body2.subst(tp2, tp1))
406412
finally comparingLambdas = saved
407413
case _ =>

tests/neg/hk-bounds.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,11 @@ object Test1 {
2121
baz[Foo] // error: Type argument [X0] -> Foo[X0] does not conform to lower bound [X0] -> Baz[X0]
2222

2323
}
24+
object Test2 {
25+
type Alias[F[X] <: Foo[X]] = F[Int]
26+
27+
def foo[M[_[_]], A[_]]: M[A] = null.asInstanceOf[M[A]]
28+
29+
val x = foo[Alias, Bar] // error: Type argument Test2.Alias does not conform to upper bound [X0 <: [X0] -> Any] -> Any
30+
31+
}

tests/neg/t2994.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object Naturals {
2121
// crashes scala-2.8.0 beta1
2222
trait MUL[n <: NAT, m <: NAT] extends NAT {
2323
trait curry[n[_[_], _], s[_]] { type f[z <: NAT] = n[s, z] }
24-
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
24+
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
2525
}
2626

2727
}

0 commit comments

Comments
 (0)