Skip to content

Commit 836db8d

Browse files
committed
Throw a type error iwhen using hk-types in unions or intersections
Throw a type error instead of crashing when using higher-kinded types in unions or intersections. We check kindedness only in PostTyper (to avoid cycles), which means we might get into situations where we coming a higher-kinded type in a union or intersection, which is illegal. In this case we now diagnose the problem with a TypeError instead of failing an assert. However, after Typer we do fail since by then such situations should have been checked by then. Fixeds #16696
1 parent d99d9bf commit 836db8d

File tree

3 files changed

+46
-9
lines changed

3 files changed

+46
-9
lines changed

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ object Types {
10711071
* @param relaxedCheck if true type `Null` becomes a subtype of non-primitive value types in TypeComparer.
10721072
* @param matchLoosely if true the types `=> T` and `()T` are seen as overriding each other.
10731073
* @param checkClassInfo if true we check that ClassInfos are within bounds of abstract types
1074-
*
1074+
*
10751075
* @param isSubType a function used for checking subtype relationships.
10761076
*/
10771077
final def overrides(that: Type, relaxedCheck: Boolean, matchLoosely: => Boolean, checkClassInfo: Boolean = true,
@@ -3322,11 +3322,11 @@ object Types {
33223322
final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2)
33233323

33243324
object AndType {
3325-
def apply(tp1: Type, tp2: Type)(using Context): AndType = {
3326-
assert(tp1.isValueTypeOrWildcard &&
3327-
tp2.isValueTypeOrWildcard, i"$tp1 & $tp2 / " + s"$tp1 & $tp2")
3325+
def apply(tp1: Type, tp2: Type)(using Context): AndType =
3326+
def where = i"in intersection $tp1 & $tp2"
3327+
expectValueTypeOrWildcard(tp1, where)
3328+
expectValueTypeOrWildcard(tp2, where)
33283329
unchecked(tp1, tp2)
3329-
}
33303330

33313331
def balanced(tp1: Type, tp2: Type)(using Context): AndType =
33323332
tp1 match
@@ -3366,7 +3366,7 @@ object Types {
33663366
TypeComparer.liftIfHK(tp1, tp2, AndType.make(_, _, checkValid = false), makeHk, _ | _)
33673367
}
33683368

3369-
abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType {
3369+
abstract case class OrType protected(tp1: Type, tp2: Type) extends AndOrType {
33703370
def isAnd: Boolean = false
33713371
def isSoft: Boolean
33723372
private var myBaseClassesPeriod: Period = Nowhere
@@ -3399,9 +3399,6 @@ object Types {
33993399
myFactorCount
34003400
else 1
34013401

3402-
assert(tp1.isValueTypeOrWildcard &&
3403-
tp2.isValueTypeOrWildcard, s"$tp1 $tp2")
3404-
34053402
private var myJoin: Type = _
34063403
private var myJoinPeriod: Period = Nowhere
34073404

@@ -3476,6 +3473,9 @@ object Types {
34763473
object OrType {
34773474

34783475
def apply(tp1: Type, tp2: Type, soft: Boolean)(using Context): OrType = {
3476+
def where = i"in union $tp1 | $tp2"
3477+
expectValueTypeOrWildcard(tp1, where)
3478+
expectValueTypeOrWildcard(tp2, where)
34793479
assertUnerased()
34803480
unique(new CachedOrType(tp1, tp2, soft))
34813481
}
@@ -3506,6 +3506,11 @@ object Types {
35063506
TypeComparer.liftIfHK(tp1, tp2, OrType(_, _, soft = true), makeHk, _ & _)
35073507
}
35083508

3509+
def expectValueTypeOrWildcard(tp: Type, where: => String)(using Context): Unit =
3510+
if !tp.isValueTypeOrWildcard then
3511+
assert(!ctx.isAfterTyper, where) // we check correct kinds at PostTyper
3512+
throw TypeError(em"$tp is not a value type, cannot be used in $where")
3513+
35093514
/** An extractor object to pattern match against a nullable union.
35103515
* e.g.
35113516
*

tests/neg/i16696.check

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Error: tests/neg/i16696.scala:7:29 ----------------------------------------------------------------------------------
2+
7 | val boom1 = BoxMaker[Some].make1 // error
3+
| ^
4+
| Some is not a value type, cannot be used in in intersection Some & Int
5+
-- Error: tests/neg/i16696.scala:8:29 ----------------------------------------------------------------------------------
6+
8 | val boom2 = BoxMaker[Some].make2 // error
7+
| ^
8+
| Some is not a value type, cannot be used in in union Some | Int
9+
-- Error: tests/neg/i16696.scala:20:27 ---------------------------------------------------------------------------------
10+
20 | val boom = BoxMaker[Foo].make(_.foo) // error
11+
| ^
12+
| test2.Foo is not a value type, cannot be used in in intersection R & test2.Foo

tests/neg/i16696.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
object test1:
2+
class BoxMaker[T] {
3+
def make1: T & Int = ???
4+
def make2: T | Int = ???
5+
}
6+
7+
val boom1 = BoxMaker[Some].make1 // error
8+
val boom2 = BoxMaker[Some].make2 // error
9+
10+
object test2:
11+
class Box[R]
12+
13+
class BoxMaker[T] {
14+
def make[R <: T](f: T => Box[R]): Box[R & T] = ???
15+
}
16+
17+
trait Foo[A]{
18+
def foo: Box[Foo[Unit]]
19+
}
20+
val boom = BoxMaker[Foo].make(_.foo) // error

0 commit comments

Comments
 (0)