diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0c5614e29a60..b8b6b7248ee2 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1071,7 +1071,7 @@ object Types { * @param relaxedCheck if true type `Null` becomes a subtype of non-primitive value types in TypeComparer. * @param matchLoosely if true the types `=> T` and `()T` are seen as overriding each other. * @param checkClassInfo if true we check that ClassInfos are within bounds of abstract types - * + * * @param isSubType a function used for checking subtype relationships. */ final def overrides(that: Type, relaxedCheck: Boolean, matchLoosely: => Boolean, checkClassInfo: Boolean = true, @@ -3322,11 +3322,11 @@ object Types { final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2) object AndType { - def apply(tp1: Type, tp2: Type)(using Context): AndType = { - assert(tp1.isValueTypeOrWildcard && - tp2.isValueTypeOrWildcard, i"$tp1 & $tp2 / " + s"$tp1 & $tp2") + def apply(tp1: Type, tp2: Type)(using Context): AndType = + def where = i"in intersection $tp1 & $tp2" + expectValueTypeOrWildcard(tp1, where) + expectValueTypeOrWildcard(tp2, where) unchecked(tp1, tp2) - } def balanced(tp1: Type, tp2: Type)(using Context): AndType = tp1 match @@ -3366,7 +3366,7 @@ object Types { TypeComparer.liftIfHK(tp1, tp2, AndType.make(_, _, checkValid = false), makeHk, _ | _) } - abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType { + abstract case class OrType protected(tp1: Type, tp2: Type) extends AndOrType { def isAnd: Boolean = false def isSoft: Boolean private var myBaseClassesPeriod: Period = Nowhere @@ -3399,9 +3399,6 @@ object Types { myFactorCount else 1 - assert(tp1.isValueTypeOrWildcard && - tp2.isValueTypeOrWildcard, s"$tp1 $tp2") - private var myJoin: Type = _ private var myJoinPeriod: Period = Nowhere @@ -3476,6 +3473,9 @@ object Types { object OrType { def apply(tp1: Type, tp2: Type, soft: Boolean)(using Context): OrType = { + def where = i"in union $tp1 | $tp2" + expectValueTypeOrWildcard(tp1, where) + expectValueTypeOrWildcard(tp2, where) assertUnerased() unique(new CachedOrType(tp1, tp2, soft)) } @@ -3506,6 +3506,11 @@ object Types { TypeComparer.liftIfHK(tp1, tp2, OrType(_, _, soft = true), makeHk, _ & _) } + def expectValueTypeOrWildcard(tp: Type, where: => String)(using Context): Unit = + if !tp.isValueTypeOrWildcard then + assert(!ctx.isAfterTyper, where) // we check correct kinds at PostTyper + throw TypeError(em"$tp is not a value type, cannot be used $where") + /** An extractor object to pattern match against a nullable union. * e.g. * diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index ab735a07b092..910584a9b5e7 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -235,6 +235,6 @@ class TabcompleteTests extends ReplTest { } @Test def i9334 = initially { - assertEquals(Nil, tabComplete("class Foo[T]; classOf[Foo].")) + assert(tabComplete("class Foo[T]; classOf[Foo].").contains("getName")) } } diff --git a/tests/neg/i16696.check b/tests/neg/i16696.check new file mode 100644 index 000000000000..2cac6a9c595a --- /dev/null +++ b/tests/neg/i16696.check @@ -0,0 +1,12 @@ +-- Error: tests/neg/i16696.scala:7:29 ---------------------------------------------------------------------------------- +7 | val boom1 = BoxMaker[Some].make1 // error + | ^ + | Some is not a value type, cannot be used in intersection Some & Int +-- Error: tests/neg/i16696.scala:8:29 ---------------------------------------------------------------------------------- +8 | val boom2 = BoxMaker[Some].make2 // error + | ^ + | Some is not a value type, cannot be used in union Some | Int +-- Error: tests/neg/i16696.scala:20:27 --------------------------------------------------------------------------------- +20 | val boom = BoxMaker[Foo].make(_.foo) // error + | ^ + | test2.Foo is not a value type, cannot be used in intersection R & test2.Foo diff --git a/tests/neg/i16696.scala b/tests/neg/i16696.scala new file mode 100644 index 000000000000..f54b884960fa --- /dev/null +++ b/tests/neg/i16696.scala @@ -0,0 +1,20 @@ +object test1: + class BoxMaker[T] { + def make1: T & Int = ??? + def make2: T | Int = ??? + } + + val boom1 = BoxMaker[Some].make1 // error + val boom2 = BoxMaker[Some].make2 // error + +object test2: + class Box[R] + + class BoxMaker[T] { + def make[R <: T](f: T => Box[R]): Box[R & T] = ??? + } + + trait Foo[A]{ + def foo: Box[Foo[Unit]] + } + val boom = BoxMaker[Foo].make(_.foo) // error