Skip to content

Commit 4814a8a

Browse files
committed
Update comparisons of higher-kinded types
Several fixes to make sure that Any is a supertype only of * types. Before there were some types that slipped through the net. Also, new tests.
1 parent f3873aa commit 4814a8a

File tree

9 files changed

+327
-25
lines changed

9 files changed

+327
-25
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ object SymDenotations {
667667

668668
/** Is this symbol a class references to which that are supertypes of null? */
669669
final def isNullableClass(implicit ctx: Context): Boolean =
670-
isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass
670+
isClass && !isValueClass && !is(ModuleClass) && symbol != defn.NothingClass
671671

672672
/** Is this definition accessible as a member of tree with type `pre`?
673673
* @param pre The type of the tree from which the selection is made

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

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -377,21 +377,22 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
377377
case _ =>
378378
val cls2 = tp2.symbol
379379
if (cls2.isClass) {
380-
if (cls2 eq AnyKindClass)
381-
return true
382-
else if (cls2.typeParams.nonEmpty && tp1.isHK)
383-
return recur(tp1, EtaExpansion(cls2.typeRef))
384-
else {
380+
if (cls2.typeParams.isEmpty) {
381+
if (cls2 eq AnyKindClass) return true
382+
if (tp1.isRef(defn.NothingClass)) return true
383+
if (tp1.isHK) return false
385384
val base = tp1.baseType(cls2)
386-
if (base.exists) {
387-
if (cls2.is(JavaDefined))
388-
// If `cls2` is parameterized, we are seeing a raw type, so we need to compare only the symbol
389-
return base.typeSymbol == cls2
390-
if (base ne tp1)
391-
return isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow)
392-
}
385+
if (base.exists && base.ne(tp1))
386+
return isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow)
393387
if (cls2 == defn.SingletonClass && tp1.isStable) return true
394388
}
389+
else if (cls2.is(JavaDefined)) {
390+
// If `cls2` is parameterized, we are seeing a raw type, so we need to compare only the symbol
391+
val base = tp1.baseType(cls2)
392+
if (base.typeSymbol == cls2) return true
393+
}
394+
else if (tp1.isHK && !tp1.isRef(defn.AnyKindClass))
395+
return recur(tp1, EtaExpansion(cls2.typeRef))
395396
}
396397
fourthTry
397398
}
@@ -487,13 +488,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
487488
isSubType(tp1.resType, tp2.resType.subst(tp2, tp1))
488489
finally comparedTypeLambdas = saved
489490
case _ =>
490-
if (tp1.isHK) {
491-
val tparams1 = tp1.typeParams
491+
val tparams1 = tp1.typeParams
492+
if (tparams1.nonEmpty)
492493
return recur(
493494
HKTypeLambda.fromParams(tparams1, tp1.appliedTo(tparams1.map(_.paramRef))),
494-
tp2
495-
)
496-
}
495+
tp2)
497496
else tp2 match {
498497
case EtaExpansion(tycon2) if tycon2.symbol.isClass =>
499498
return recur(tp1, tycon2)
@@ -552,7 +551,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
552551
def compareTypeBounds = tp1 match {
553552
case tp1 @ TypeBounds(lo1, hi1) =>
554553
((lo2 eq NothingType) || isSubType(lo2, lo1)) &&
555-
((hi2 eq AnyType) || (hi2 eq AnyKindType) || isSubType(hi1, hi2))
554+
((hi2 eq AnyType) && !hi1.isHK || (hi2 eq AnyKindType) || isSubType(hi1, hi2))
556555
case tp1: ClassInfo =>
557556
tp2 contains tp1
558557
case _ =>
@@ -1228,8 +1227,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
12281227
if (tp1 eq tp2) tp1
12291228
else if (!tp1.exists) tp2
12301229
else if (!tp2.exists) tp1
1231-
else if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp2
1232-
else if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp1
1230+
else if ((tp1 isRef AnyClass) && !tp2.isHK || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp2
1231+
else if ((tp2 isRef AnyClass) && !tp1.isHK || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp1
12331232
else tp2 match { // normalize to disjunctive normal form if possible.
12341233
case OrType(tp21, tp22) =>
12351234
tp1 & tp21 | tp1 & tp22

tests/neg/anykind.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ object AnyKinds {
2121
g[Int] // OK
2222
g[Nothing] // OK
2323

24+
trait X[F[_] <: AnyKind] { type L = F[Int]; def a: L = ??? } // error: cannot be used as a value type
2425

2526
}

tests/neg/anykind1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object Test {
2+
3+
// Checking edge cases that should not compile with kind-polymorphism
4+
trait X[F[_] <: AnyKind] { def a: Int }
5+
new X[Int] { } // error
6+
new X[Int] { }.a // error
7+
new X[Either] { } // error
8+
new X[Either] { }.a // error
9+
new X[({ type l[X, Y] = X })#l] { } // error
10+
11+
}

tests/neg/anykind2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ object AnyKinds {
1616
g[List] // error
1717
g[Nothing] // OK
1818

19+
1.asInstanceOf[AnyKind] // error
1920

2021
}

tests/neg/anykind3.scala

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
object Test {
3+
4+
def f[X] = ()
5+
f[List]
6+
7+
// Checking edge cases that should not compile with kind-polymorphism
8+
9+
trait X[A <: AnyKind, F[_ <: AnyKind]] { type B = F[A] }
10+
11+
val i0: X[Option, Double]#B = Some(5) // error
12+
13+
val i1: X[Option, List]#B = Some(5) // error
14+
15+
val i2: X[Option[Double], List]#B = Some(5) // error
16+
17+
val i3: X[Option[Double], ({ type l[X[_]] = X[Int] })#l]#B = Some(5) // error
18+
19+
val i4: X[Double, ({ type l[X[_]] = X[Int] })#l]#B = 5.0 // error
20+
21+
val i6: X[Either, ({ type l[X[_]] = X[Int] })#l]#B = Some(5) // error
22+
23+
val i7: X[Either, List]#B = Some(5) // error
24+
25+
trait Foo[A[_[_]]]
26+
val i8: X[Foo, ({ type l[X[_]] = X[Int] })#l]#B = Some(5) // error
27+
28+
trait X2[A <: AnyKind, B <: AnyKind] { def run[F[_ <: AnyKind]]: F[A] => F[B] }
29+
30+
val x21 = {
31+
new X2[Int, Int] { def run[F[_]]: F[Int] => F[Int] = identity[F[Int]] }
32+
.asInstanceOf[X2[List, Option[Double]]].run[({ type l[X[_]] = X[Int] })#l]
33+
}
34+
35+
val x22 = {
36+
new X2[Int, Int] { def run[F[_]]: F[Int] => F[Int] = identity[F[Int]] }
37+
.asInstanceOf[X2[List, Option[Double]]].run[List]
38+
}
39+
40+
trait X3[A <: AnyKind, B <: AnyKind, C <: AnyKind] { def run[F[_ <: AnyKind, _ <: AnyKind]]: F[A, C] => F[B, C] }
41+
42+
val x31 = {
43+
new X3[Int, Int, String] { def run[F[_, _]]: F[Int, String] => F[Int, String] = identity[F[Int, String]] }
44+
.asInstanceOf[X3[Option[Double], List, String]].run[Map]
45+
}
46+
val x32 = {
47+
new X3[Int, Int, String] { def run[F[_, _]]: F[Int, String] => F[Int, String] = identity[F[Int, String]] }
48+
.asInstanceOf[X3[List, Option[Double], String]].run[Map]
49+
}
50+
51+
trait X4[A <: AnyKind, B <: AnyKind, C] { def run[F[_ <: AnyKind, _]]: F[A, C] => F[B, C] }
52+
53+
trait Foo2[A]
54+
trait Bar[A]
55+
trait Bar2[A, B]
56+
57+
trait Toto[F[_], A]
58+
59+
val x41 = {
60+
new X3[Foo2, Foo2, Int] { def run[F[_[_], A]]: F[Foo2, Int] => F[Foo2, Int] = identity[F[Foo2, Int]] }
61+
.asInstanceOf[X3[Bar, Bar, Int]].run[Bar2]
62+
}
63+
}

tests/neg/anykind4.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
object Test {
3+
4+
trait Foo[T <: AnyKind]
5+
6+
def foo[T <: AnyKind](implicit f: Foo[T]): f.type = f
7+
8+
implicit def c1[T]: Foo[T] = ???
9+
implicit def c2[T[_]]: Foo[T] = ???
10+
11+
foo[List](c1) // error
12+
13+
foo[List](c2)
14+
}

tests/neg/i2492.scala

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)