From 7553e6901d52ace00bfcb670336c480766c8301c Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 5 Oct 2010 15:34:37 +0000 Subject: Improves exhaustiveness analysis to not warn ab... Improves exhaustiveness analysis to not warn about types which cannot match due to nonconformant type parameters. Also, look at the different warnings emitted in the test case based on the presence of a constraint. Nifty! Closes #3683, no review. --- .../scala/tools/nsc/matching/MatrixAdditions.scala | 25 +++++++++++++--------- test/files/neg/bug3683a.check | 6 ++++++ test/files/neg/bug3683a.flags | 1 + test/files/neg/bug3683a.scala | 20 +++++++++++++++++ test/files/neg/bug3683b.check | 6 ++++++ test/files/neg/bug3683b.scala | 21 ++++++++++++++++++ test/files/neg/exhausting.check | 9 ++++++-- test/files/neg/exhausting.scala | 7 +++++- 8 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 test/files/neg/bug3683a.check create mode 100644 test/files/neg/bug3683a.flags create mode 100644 test/files/neg/bug3683a.scala create mode 100644 test/files/neg/bug3683b.check create mode 100644 test/files/neg/bug3683b.scala diff --git a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala index 6141b2333c..b880dea858 100644 --- a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala +++ b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala @@ -173,19 +173,24 @@ trait MatrixAdditions extends ast.TreeDSL private def rowCoversCombo(row: Row, combos: List[Combo]) = row.guard.isEmpty && (combos forall (c => c isCovered row.pats(c.index))) - private def requiresExhaustive(s: Symbol) = { - (s hasFlag MUTABLE) && // indicates that have not yet checked exhaustivity - !(s hasFlag TRANS_FLAG) && // indicates @unchecked - (s.tpe.typeSymbol.isSealed) && { - s resetFlag MUTABLE // side effects MUTABLE flag - !isValueClass(s.tpe.typeSymbol) // but make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte - } + private def requiresExhaustive(sym: Symbol) = { + (sym.isMutable) && // indicates that have not yet checked exhaustivity + !(sym hasFlag TRANS_FLAG) && // indicates @unchecked + (sym.tpe.typeSymbol.isSealed) && + !isValueClass(sym.tpe.typeSymbol) // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte } private lazy val inexhaustives: List[List[Combo]] = { - val collected = - for ((pv, i) <- tvars.zipWithIndex ; val sym = pv.lhs ; if requiresExhaustive(sym)) yield - i -> sym.tpe.typeSymbol.sealedDescendants + // let's please not get too clever side-effecting the mutable flag. + val toCollect = tvars.zipWithIndex filter { case (pv, i) => requiresExhaustive(pv.sym) } + val collected = toCollect map { case (pv, i) => + // okay, now reset the flag + pv.sym resetFlag MUTABLE + // have to filter out children which cannot match: see ticket #3683 for an example + val kids = pv.tpe.typeSymbol.sealedDescendants filter (_.tpe matchesPattern pv.tpe) + + i -> kids + } val folded = collected.foldRight(List[List[Combo]]())((c, xs) => { diff --git a/test/files/neg/bug3683a.check b/test/files/neg/bug3683a.check new file mode 100644 index 0000000000..a1c5b9c56d --- /dev/null +++ b/test/files/neg/bug3683a.check @@ -0,0 +1,6 @@ +bug3683a.scala:14: error: match is not exhaustive! +missing combination XX + + w match { + ^ +one error found diff --git a/test/files/neg/bug3683a.flags b/test/files/neg/bug3683a.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/bug3683a.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/bug3683a.scala b/test/files/neg/bug3683a.scala new file mode 100644 index 0000000000..6d1915213a --- /dev/null +++ b/test/files/neg/bug3683a.scala @@ -0,0 +1,20 @@ +sealed trait Foo +sealed trait Bar extends Foo +sealed trait W[T >: Bar <: Foo] +case class X() extends W[Foo] +case class XX() extends W[Bar] +case class Y() extends W[Bar] +case class Z[T >: Bar <: Foo]( + z1: W[T] +) extends W[T] + +object Main { + // should warn for not including XX() + def f1(w: W[Bar]): Int = { + w match { + // case XX() => 2 + case Y() => 1 + case Z(z) => f1(z) + } + } +} \ No newline at end of file diff --git a/test/files/neg/bug3683b.check b/test/files/neg/bug3683b.check new file mode 100644 index 0000000000..3952633d1d --- /dev/null +++ b/test/files/neg/bug3683b.check @@ -0,0 +1,6 @@ +bug3683b.scala:15: error: constructor cannot be instantiated to expected type; + found : X + required: W[Bar] + case X() => 1 + ^ +one error found diff --git a/test/files/neg/bug3683b.scala b/test/files/neg/bug3683b.scala new file mode 100644 index 0000000000..646e418121 --- /dev/null +++ b/test/files/neg/bug3683b.scala @@ -0,0 +1,21 @@ +sealed trait Foo +sealed trait Bar extends Foo +sealed trait W[T >: Bar <: Foo] +case class X() extends W[Foo] +case class XX() extends W[Bar] +case class Y() extends W[Bar] +case class Z[T >: Bar <: Foo]( + z1: W[T] +) extends W[T] + +object Main { + // should fail for including X() + def f1(w: W[Bar]): Int = { + w match { + case X() => 1 + case XX() => 2 + case Y() => 1 + case Z(z) => f1(z) + } + } +} \ No newline at end of file diff --git a/test/files/neg/exhausting.check b/test/files/neg/exhausting.check index 6383a6eaca..d3f2251f79 100644 --- a/test/files/neg/exhausting.check +++ b/test/files/neg/exhausting.check @@ -14,11 +14,16 @@ missing combination Bar3 def fail3[T](x: Foo[T]) = x match { ^ exhausting.scala:31: error: match is not exhaustive! +missing combination Bar2 Bar2 + + def fail4[T <: AnyRef](xx: (Foo[T], Foo[T])) = xx match { + ^ +exhausting.scala:36: error: match is not exhaustive! missing combination Bar1 Bar2 missing combination Bar1 Bar3 missing combination Bar2 Bar1 missing combination Bar2 Bar2 - def fail4[T](xx: (Foo[T], Foo[T])) = xx match { + def fail5[T](xx: (Foo[T], Foo[T])) = xx match { ^ -four errors found +5 errors found diff --git a/test/files/neg/exhausting.scala b/test/files/neg/exhausting.scala index 8b1ea817e4..0741d7467b 100644 --- a/test/files/neg/exhausting.scala +++ b/test/files/neg/exhausting.scala @@ -28,7 +28,12 @@ object Test { case Bar1 => "ok" case Bar2 => "ok" } - def fail4[T](xx: (Foo[T], Foo[T])) = xx match { + def fail4[T <: AnyRef](xx: (Foo[T], Foo[T])) = xx match { + case (Bar1, Bar1) => () + case (Bar2, Bar3) => () + case (Bar3, _) => () + } + def fail5[T](xx: (Foo[T], Foo[T])) = xx match { case (Bar1, Bar1) => () case (Bar2, Bar3) => () case (Bar3, _) => () -- cgit v1.2.3