Using Scala 3.4.2:
Vector(1, 2, 3) match
case Vector() => println("empty")
case _ :+ last => println(s"last: $last")
gives me an (in my opinion incorrect) nonexhaustiveness warning
[warn] -- [E029] Pattern Match Exhaustivity Warning: ...
[warn] 5 | Vector(1, 2, 3) match
[warn] | ^^^^^^^^^^^^^^^
[warn] |match may not be exhaustive.
[warn] |
[warn] |It would fail on pattern case: Vector(_, _*), Vector(_, _*), Vector(_, _*), Vector(_, _*), Vector(_, _*), Vector(_, _*)
If I use Seq
instead of Vector
, the code compiles fine.
It looks like this is the same thing for Scala 3 that has been discussed for Scala 2 in the bug https://github.com/scala/bug/issues/12240.
Also, it might relate to https://github.com/scala/bug/issues/12252 which is marked "fixed in Scala 3".
My questions are:
Since there is no Scala 3 specification released yet (AFAIK it is still in development), I'll use Scala 2.13 spec (2.13 spec + list of changes in Reference is the closest approximation of a Scala 3 specification that we currently have):
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.
There is only a mention about sealed
when mentioning exhaustive checks. For anything else, the behavior is not defined by the spec, so at best it's implementation-specific.
Scala 2.13.4 release notes mentions -Xlint:strict-unsealed-patmat
flag which may add more exhaustive checks.
-Xlint
with all options, or if you have something like sbt-tpolecat, which enables a lot of flagsVector
is a sealed trait
(on 2.13, and so on Scala 3 since it uses 2.13's standard library)
bugs you mentioned are about exhaustive checks of unapplySeq
, so basically things like:
Vector(1, 2, 3) match
case Vector() => println("empty")
case Vector(multiple*) => println(s"last: ${multiple.last}")
while +:
was mentioned there as something that should be anylyzed by the compiler, it was more of a wish - it is NOT implemented and I expected it will never be because it's just an arbitrary
object :+ {
def unapply[A, CC[_] <: Seq[_], C <: SeqOps[A, CC, C]](t: C with SeqOps[A, CC, C]): Option[(A, C)] = ...
}
There is no way for the compiler to guess what this does internally and how it relates to other matches. Someone would have to adds a special case in the compiler... which could not be added for any other user-provided unapply
so I don't expect it to happen.
Putting it all together:
Seq
compiler does not try to check for exhaustiveness by default (with some -X flag it might)Vector
it's a sealed
so it will try to prove an exhaustive checkunapplySeq
(case Vector(...) =>
)+:
nor :+
are Vector.unapplySeq
so the improvements above does not concern them, and they are not exhausting any possibilities as far as the compiler is concerned.