奇怪的“匹配可能不是详尽无遗”的警告

时间:2013-08-11 10:25:27

标签: scala pattern-matching inner-classes

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"
  }

}

编译说

[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{

Main#bar成功并返回"c"

我做错了吗?或者这是scalac bug?

https://github.com/scalaz/scalaz/issues/468

1 个答案:

答案 0 :(得分:4)

不同Foo的路径依赖性Cs是不同的。

这可能是它抱怨的原因。 (警告中存在已知的错误。)(例如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"
  }
}

这更好地代表了你能做的事情,并使警告沉默:

  def bar(a: A) = a match {
    case _: Foo#C => "c"     // instanceof Foo$C etc
    case _: Foo#D => "d"
    case _: Foo#E => "e"
  }

更新:在查看不相关的stackoverflow时,还有更多内容,即an open issue I happened to notice。 (实际的,而不是Q& A网站。)

简而言之,它尝试将嵌套类中的“外部”指针优化到它们的封闭实例,如果发生这种情况,则不能再在匹配中包含外部实例。通常它会测试instanceof和它的外部是正确的。

bar移入特征并删除final会禁用优化并修复匹配。

  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

如果内部类是最终的,scalac至少会抱怨:

badseal.scala:17: warning: The outer reference in this type test cannot be checked at run time.
    case _: C => "c" 
          ^

但是如果匹配在对象中,则此消息的启发式似乎会中断,因此您不再看到它。

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

然后

  val f1 = new Foo { }
  Console println X.bar(new f1.C)

正确警告并正确抛出。

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)