1

Can't figure out why Scala 2.13 is unhappy about pattern matching here

  trait A {
    sealed trait T
    case class TImpl() extends T
  }

  trait Fail[B <: A] {
    val a: B // no error if `a: A`
    def foo(t : a.T): Unit = t match {
      case _: a.TImpl => // "match may not be exhaustive. It would fail on the following input: TImpl()"
    }
  }

Any reasonable workaround? Seems to be fine with Dotty

eprst
  • 733
  • 6
  • 14

1 Answers1

2

If you want to see why in principle the pattern matching can be not exhaustive, for instance see the following example

trait A {
  sealed trait T
  case class TImpl() extends T
}

trait Fail[B <: A] {
  val a: B
  def foo(t : a.T): Unit = t match {
    case _: a.TImpl => 
  }
}

class B extends A
val b = new B
class FailImpl extends Fail[b.type] {
  override val a: b.type = b
}
val fail: Fail[b.type] = new FailImpl

class C
case class CImpl() extends C with b.T
val x = CImpl()
fail.foo(x) // MatchError

You could say that actually we didn't have C. Well, compiler should be smart enough to figure out that.

If you want to switch the warning off you can write @unchecked

def foo(t : a.T): Unit = (t: @unchecked) match {
  case _: a.TImpl => 
}

According to specification,

If the selector of a pattern match is an instance of a sealed class, the compilation of pattern matching can emit warnings which diagnose that a given set of patterns is not exhaustive, i.e. that there is a possibility of a MatchError being raised at run-time.

https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#pattern-matching-expressions

Compiler can emit a warning but not must. So absence of warning doesn't guarantee that a pattern matching is exhaustive.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • OK, my confusion was based on the wrong assumption that `sealed` class can only be extended in the same compilation unit, i.e. `A` in this case. Your example shows to exploit the fact that it is not true. Do you know how Dotty manages to do a better job here? There's no warning in my example, but it does warn if I add another case class without corresponding matching branch. – eprst Sep 21 '20 at 22:58
  • 1
    @eprst The compilation unit is the entire file, not `A`. – Alexey Romanov Sep 22 '20 at 07:54
  • Fair enough. And to answer my question above, it's the same in Dotty. There's no warning, but `MatchError` still happens. I'd limit `sealed` to the tighter scope. – eprst Sep 22 '20 at 15:38