Skip to content

Commit

Permalink
Show GADT bounds in type mismatch message
Browse files Browse the repository at this point in the history
The inferred GADT bounds can be non-trivial so it makes sense to display
them.

This commit also showcases the now-improved GADT support in Dotty (both
tests/pos/gadt-eval.scala and tests/neg/gadt-eval.scala used to compile,
and both compile in Scala 2).
  • Loading branch information
smarter committed Feb 13, 2018
1 parent 726dcc0 commit 0060f66
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 2 deletions.
12 changes: 10 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/Formatting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,12 @@ object Formatting {
case param: TypeParamRef =>
s"is a type variable${addendum("constraint", ctx.typeComparer.bounds(param))}"
case sym: Symbol =>
s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", sym.info)}"
val info =
if (ctx.gadt.bounds.contains(sym))
sym.info & ctx.gadt.bounds(sym)
else
sym.info
s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", info)}"
case tp: SkolemType =>
s"is an unknown value of type ${tp.widen.show}"
}
Expand All @@ -183,7 +188,10 @@ object Formatting {
def needsExplanation(entry: Recorded) = entry match {
case param: TypeParamRef => ctx.typerState.constraint.contains(param)
case skolem: SkolemType => true
case _ => false
case sym: Symbol =>
ctx.gadt.bounds.contains(sym) && ctx.gadt.bounds(sym) != TypeBounds.empty
case _ =>
assert(false, "unreachable")
}

val toExplain: List[(String, Recorded)] = seen.toList.flatMap {
Expand Down
33 changes: 33 additions & 0 deletions tests/neg/gadt-eval.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
sealed trait Exp[T]
case class Lit(value: Int) extends Exp[Int]
case class Pair[A, B](fst: Exp[A], snd: Exp[B]) extends Exp[(A, B)]

object Test {
def eval[T](e: Exp[T]): T = e match {
case Lit(x) =>
x
case Pair(a, b) =>
(eval(a), eval(a)) // error:
// -- [E007] Type Mismatch Error: tests/neg/gadt-eval.scala:10:6 ------------------
// 10 | (eval(a), eval(a))
// | ^^^^^^^^^^^^^^^^^^
// | found: (_$1, _$1)
// | required: T
// |
// | where: T is a type in method eval which is an alias of (_$1, _$2)
}

def eval2[T](e: Exp[T]): T = e match {
case e: Lit =>
e.value
case e: Pair[t1, t2] =>
(eval(e.fst), eval(e.fst)) // error:
//-- [E007] Type Mismatch Error: tests/neg/gadt-eval.scala:24:6 ------------------
//24 | (eval(e.fst), eval(e.fst))
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^
// | found: (t1, t1)
// | required: T
// |
// | where: T is a type in method eval2 which is an alias of (t1, t2)
}
}
19 changes: 19 additions & 0 deletions tests/pos/gadt-eval.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
sealed trait Exp[T]
case class Lit(value: Int) extends Exp[Int]
case class Pair[A, B](fst: Exp[A], snd: Exp[B]) extends Exp[(A, B)]

object Test {
def eval[T](e: Exp[T]): T = e match {
case Lit(x) =>
x
case Pair(a, b) =>
(eval(a), eval(b))
}

def eval2[T](e: Exp[T]): T = e match {
case e: Lit =>
e.value
case e: Pair[t1, t2] =>
(eval(e.fst), eval(e.snd))
}
}

0 comments on commit 0060f66

Please sign in to comment.