Reputation: 3118
in Scala 2.10.2
sealed abstract class A
sealed trait Foo{
sealed abstract class B extends A
final class C extends B
final class D extends B
final class E extends A
}
object Main extends Foo{
def bar = (new C(): A) match{
case _: C => "c"
case _: D => "d"
case _: E => "e"
}
}
compiler says
[warn] A.scala:12: match may not be exhaustive. [warn] It would fail on the following inputs: C(), D(), E() [warn] def bar = (new C(): A) match{
but Main#bar
success and return "c"
.
Am I doing something wrong? or this is scalac bug?
https://github.com/scalaz/scalaz/issues/468
Upvotes: 2
Views: 1176
Reputation: 39577
The path-dependent Cs of different Foos are different.
That may be why it complains. (There are known bugs in the warnings.) (Such as this one.)
final class C extends B {
def f(c: C) = "ok" // adding this to C
}
object Test extends App {
val f = new Foo { }
Console println (X bar new f.C())
val c = new f.C
c.f(X.c) // doesn't compile
}
object X extends Foo{
val c = new C
def bar(a: A) = a match {
case _: C => "c"
case _: D => "d"
case _: E => "e"
}
}
This better represents what you're able to do, and silences the warnings:
def bar(a: A) = a match {
case _: Foo#C => "c" // instanceof Foo$C etc
case _: Foo#D => "d"
case _: Foo#E => "e"
}
Update: There is more to be said, namely, an open issue I happened to notice while looking at an unrelated stackoverflow. (An actual one, not the Q&A site.)
In short, it tries to optimize away the "outer" pointer from nested classes to their enclosing instances, and if that happens, you can no longer include the outer instance in the match. Normally it will test both instanceof and that its outer is the right one.
Moving bar
into the trait and removing final
disables the optimization and fixes the match.
public static java.lang.String bar(badseal.Foo, badseal.A);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=2
0: aload_1
1: astore_2
2: aload_2
3: instanceof #9 // class badseal/Foo$D
6: ifeq 26
9: aload_2
10: checkcast #9 // class badseal/Foo$D
13: invokevirtual #13 // Method badseal/Foo$D.badseal$Foo$D$$$outer:()Lbadseal/Foo;
16: aload_0
17: if_acmpne 26
20: ldc #15 // String d
If the inner classes are final, scalac will at least complain:
badseal.scala:17: warning: The outer reference in this type test cannot be checked at run time.
case _: C => "c"
^
But if the match is in the object, the heuristic for this message seems to break so you don't see it anymore.
sealed abstract class A
trait Foo {
sealed abstract class B extends A
class C extends B
class D extends B
class E extends A
def bar(a: A) = a match {
case _: C => "c"
case _: D => "d"
case _: E => "e"
}
}
object X extends Foo
Then
val f1 = new Foo { }
Console println X.bar(new f1.C)
Correctly warns and correctly throws.
apm@mara:~/tmp$ skalac -unchecked badseal.scala ; skala badseal.Test
badseal.scala:11: warning: match may not be exhaustive.
It would fail on the following inputs: C(), D(), E()
def bar(a: A) = a match {
^
one warning found
scala.MatchError: badseal.Foo$C@756bc09d (of class badseal.Foo$C)
Upvotes: 4