Skip to content

Throw a type error when using hk-types in unions or intersections #16712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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.
*
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/repl/TabcompleteTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
}
}
12 changes: 12 additions & 0 deletions tests/neg/i16696.check
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions tests/neg/i16696.scala
Original file line number Diff line number Diff line change
@@ -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