summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBurak Emir <emir@epfl.ch>2007-02-05 17:17:17 +0000
committerBurak Emir <emir@epfl.ch>2007-02-05 17:17:17 +0000
commit2f4f3d3db7c2eb9ed535065ffed8098a99de0278 (patch)
treed3d8c20356bd8f76d1b4b7aa298507232ae028cb
parent912077c5f8d59b2585a3a5014c193a8399b672d1 (diff)
downloadscala-2f4f3d3db7c2eb9ed535065ffed8098a99de0278.tar.gz
scala-2f4f3d3db7c2eb9ed535065ffed8098a99de0278.tar.bz2
scala-2f4f3d3db7c2eb9ed535065ffed8098a99de0278.zip
exhaustivity reworked
-rw-r--r--src/compiler/scala/tools/nsc/matching/PatternMatchers.scala60
-rw-r--r--src/compiler/scala/tools/nsc/matching/PatternNodes.scala21
-rw-r--r--src/compiler/scala/tools/nsc/matching/TransMatcher.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala2
-rw-r--r--test/files/neg/patmatexhaust.check11
-rw-r--r--test/files/neg/patmatexhaust.scala10
-rw-r--r--test/files/run/patmatnew.scala37
-rw-r--r--test/files/run/regularpatmatnew.scala1
8 files changed, 116 insertions, 30 deletions
diff --git a/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala
index 42a3210ea8..be555e4cfd 100644
--- a/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala
+++ b/src/compiler/scala/tools/nsc/matching/PatternMatchers.scala
@@ -47,6 +47,9 @@ trait PatternMatchers requires (transform.ExplicitOuter with PatternNodes) {
protected var optimize = true
+ /** if this is false, will not check exhaustivity */
+ protected var doCheckExhaustive = true
+
/** the owner of the pattern matching expression
*/
var owner:Symbol = _
@@ -72,8 +75,9 @@ trait PatternMatchers requires (transform.ExplicitOuter with PatternNodes) {
var handleOuter: Tree=>Tree = _
/** init method, also needed in subclass AlgebraicMatcher
*/
- def initialize(selector: Tree, owner: Symbol, handleOuter:Tree=>Tree): Unit = {
+ def initialize(selector: Tree, doCheckExhaustive: Boolean, owner: Symbol, handleOuter:Tree=>Tree): Unit = {
this.owner = owner
+ this.doCheckExhaustive = doCheckExhaustive
this.selector = selector
this.handleOuter = handleOuter
this.root = pConstrPat(selector.pos, selector.tpe.widen);
@@ -430,11 +434,12 @@ trait PatternMatchers requires (transform.ExplicitOuter with PatternNodes) {
}
node
- case t @ UnApply(fn, args) => pUnapplyPat(tree.pos, fn)
+ case t @ UnApply(fn, args) =>
+ pUnapplyPat(tree.pos, fn)
case t @ Apply(fn, args) => // pattern with args
//Console.println("Apply node: "+t);
- //Console.println("isSeqApply "+isSeqApply(t));
+ //Console.println("isSeqApply "+isSeqApply(t)); // todo:do seq applies still exist?
if (isSeqApply(t)) {
args(0) match {
// case Sequence(ts)=>
@@ -621,6 +626,22 @@ print()
*/
protected def enter1(pat: Tree, index: Int, target: PatternNode,
casted: Symbol, env: CaseEnv): PatternNode = {
+
+ // special case List ... not yet
+/*
+ pat match {
+ case UnApply(fn, List(ArrayValue(zs, pats))) if isSameType(definitions.ListModule.tpe, fn.symbol.owner.tpe) =>
+ Console.println("special case List"+pats)
+ Console.println("special case zs = "+zs)
+ def makeConsPat(p:Tree) = {
+ val constpe = typeRef(definitions.ConsClass.tpe.prefix, definitions.ConsClass, List(zs.tpe))
+ val patNode = pConstrPat(pat.pos, )
+ patNode.and = newHeader(pat.pos, )
+ }
+ null
+ case _ =>
+ }
+*/
//Console.println("enter(" + pat + ", " + index + ", " + target + ", " + casted + ")");
var bodycond: PatternNode => Body = null // in case we run into a body (combination of typed pattern and constructor pattern, see bug#644)
val patArgs = patternArgs(pat); // get pattern arguments
@@ -1036,18 +1057,15 @@ print()
}
}
def andIsUnguardedBody(p1:PatternNode) = p1.and match {
- case p: Body => p.guard.length == 1 && p.guard(0) == EmptyTree
- case _ => false
- }
- def andIsGuarded(p1:PatternNode) = p1.and match {
- case p: Body => p.guard.length >= 1 && p.guard(0) == EmptyTree
+ case p: Body => p.hasUnguarded
case _ => false
}
- // returns true if this tree is optimizable
- // throws a warning if is not exhaustive
- def optimize1(selType:Type, alternatives1: PatternNode ): {Boolean, collection.immutable.Set[Symbol], collection.immutable.Set[Symbol]} = {
+ /** returns true if this tree is optimizable
+ * throws a warning if is not exhaustive
+ */
+ def optimize1(selType:Type, alternatives1: PatternNode): { Boolean, SymSet, SymSet } = {
if(alternatives1 eq null) return {false, emptySymbolSet, emptySymbolSet} // only case _
//Console.println("optimize1("+selType+","+alternatives1+")")
@@ -1097,7 +1115,6 @@ print()
coveredCases = emptySymbolSet
remainingCases = emptySymbolSet
}
-
case UnapplyPat(_,_) | SequencePat(_, _) | RightIgnoringSequencePat(_, _, _) =>
res = false
remainingCases = emptySymbolSet
@@ -1128,21 +1145,25 @@ print()
var res: Tree = Literal(Constant(false)); //.setInfo(defs.BooleanClass);
var lastSelector: Tree = null
var carryCovered: SymSet = emptySymbolSet
- var ignoreCovered = false // indicates whether we have given up, due to unapply
+ val oldDoCheckExhaustive = doCheckExhaustive
while (node ne null)
node match {
case _h:Header =>
+ //Console.println(_h.print("!!!! visit ", new StringBuilder()).toString())
+
val selector = _h.selector;
if(selector != lastSelector) {
carryCovered = emptySymbolSet;
- ignoreCovered = false
}
+ doCheckExhaustive = doCheckExhaustive && !_h.catchesAll // nice global variable here
+ //Console.print("doCheckExhaustive? "+doCheckExhaustive+ " ")
+ //Console.println(" catches all?"+_h.catchesAll)
//Console.println("sel:"+selector+" last"+lastSelector+" - "+(selector == lastSelector))
val next = _h.next;
//res = And(mkNegate(res), toTree(node.or, selector));
val {doOptimize, coveredCases, remainingCases} = optimize1(node.getTpe, node.or)
- if(!remainingCases.isEmpty && !ignoreCovered) {
+ if(!remainingCases.isEmpty && doCheckExhaustive) {
carryCovered = carryCovered ++ coveredCases // ??
if(next != null && andIsUnguardedBody(next.or)) {
// ignore, default case
@@ -1154,12 +1175,12 @@ print()
//Console.println("remain "+remainingCases+" carry covered "+ carryCovered + "real "+realRemainingCases)
if(!realRemainingCases.isEmpty) {
val word = if(realRemainingCases.size > 1) "cases " else "case "
+ //Console.println(_h.print("non-exhaustive ", new StringBuilder()).toString())
cunit.warning(node.pos, "does not cover "+word+realRemainingCases.elements.mkString("{",",","}"))
+ //Console.println("full"); print()
//Console.println(_h.print("", new StringBuilder()).toString())
}
}
- } else {
- ignoreCovered = true
}
if (doOptimize)
res = Or(res, toOptTree(node.or, selector));
@@ -1205,6 +1226,7 @@ print()
case _ =>
scala.Predef.error("error in toTree");
}
+ doCheckExhaustive = oldDoCheckExhaustive
return res
}
@@ -1364,7 +1386,7 @@ print()
}
}
Or(And(checkType,
- And(Apply(fn1,List(useSelector)),
+ And(Apply(handleOuter(fn1),List(useSelector)), // test
toTree(node.and))
),
toTree(node.or, selector.duplicate))
@@ -1387,7 +1409,7 @@ print()
Or(And(checkType,
squeezedBlock(
- List(ValDef(v,Apply(fn1,List(useSelector)))),
+ List(ValDef(v,Apply(handleOuter(fn1),List(useSelector)))),
And(__opt_nonemp__,
squeezedBlock(List(ValDef(casted, __opt_get__)),toTree(node.and))))
),
diff --git a/src/compiler/scala/tools/nsc/matching/PatternNodes.scala b/src/compiler/scala/tools/nsc/matching/PatternNodes.scala
index ffe011e458..2e8363f3ae 100644
--- a/src/compiler/scala/tools/nsc/matching/PatternNodes.scala
+++ b/src/compiler/scala/tools/nsc/matching/PatternNodes.scala
@@ -28,6 +28,12 @@ trait PatternNodes requires transform.ExplicitOuter {
z = z.or
}
}
+
+ def isUnguardedBody = this match {
+ case b:Body => b.hasUnguarded
+ case _ => false
+ }
+
def bodyToTree(): Tree = this match {
case _b:Body =>
_b.body(0)
@@ -211,7 +217,7 @@ trait PatternNodes requires transform.ExplicitOuter {
val next = _h.next
sb.append(indent + "HEADER(" + patNode.getTpe() +
", " + selector + ")").append('\n')
- patNode.or.print(indent + "|", sb)
+ if(patNode.or ne null) patNode.or.print(indent + "|", sb)
if (next ne null)
next.print(indent, sb)
else
@@ -293,12 +299,25 @@ trait PatternNodes requires transform.ExplicitOuter {
}
var isSubHeader = false;
+
+ // returns true if this header node has a catch all case
+
+ def catchesAll: Boolean = {
+ //Console.println(this.print(" catchesAll %%%%", new StringBuilder()).toString)
+ val p = findLast
+ (p.isDefaultPat && p.and.isUnguardedBody)
+ }
}
+ /** contains at least one body, so arrays are always nonempty
+ */
class Body(bound1: Array[Array[ValDef]], guard1:Array[Tree], body1:Array[Tree]) extends PatternNode {
var bound = bound1
var guard = guard1
var body = body1
+
+ def hasUnguarded = guard.exists { x => x == EmptyTree }
+
}
case class DefaultPat()extends PatternNode
diff --git a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala
index bf4ace47ea..433a33f3a7 100644
--- a/src/compiler/scala/tools/nsc/matching/TransMatcher.scala
+++ b/src/compiler/scala/tools/nsc/matching/TransMatcher.scala
@@ -278,7 +278,7 @@ with PatternMatchers */ {
/** handles all translation of pattern matching
*/
- def handlePattern(sel: Tree, ocases: List[CaseDef], owner:Symbol, handleOuter:Tree=>Tree): Tree = {
+ def handlePattern(sel: Tree, ocases: List[CaseDef], doCheckExhaustive: Boolean, owner:Symbol, handleOuter:Tree=>Tree): Tree = {
// TEMPORARY
//new NewMatcher().toIR(sel, ocases)
//
@@ -294,7 +294,7 @@ with PatternMatchers */ {
EmptyTree
} else {
val pm = new PatternMatcher()
- pm.initialize(sel, owner,handleOuter)
+ pm.initialize(sel, doCheckExhaustive, owner,handleOuter)
pm.construct(cases)
//if (global.log()) {
// global.log("internal pattern matching structure");
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index 9aa1d7ff23..25f4695af7 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -497,7 +497,7 @@ abstract class ExplicitOuter extends InfoTransform with TransMatcher with Patter
//Console.println("TransMatcher selector.tpe ="+selector.tpe+")")
//Console.println("TransMatcher resultType ="+resultType+")")
- val t_untyped = handlePattern(nselector, ncases, currentOwner, transform)
+ val t_untyped = handlePattern(nselector, ncases, checkExhaustive, currentOwner, transform)
try {
//Console.println("t_untyped "+t_untyped.toString())
val t = atPos(tree.pos) { localTyper.typed(t_untyped, resultType) }
diff --git a/test/files/neg/patmatexhaust.check b/test/files/neg/patmatexhaust.check
index 40054bad9f..6a0eda5861 100644
--- a/test/files/neg/patmatexhaust.check
+++ b/test/files/neg/patmatexhaust.check
@@ -10,11 +10,14 @@ patmatexhaust.scala:24: warning: does not cover case {class Kult}
patmatexhaust.scala:26: warning: does not cover case {class Qult}
case {Qult(), Kult(_)} => // Qult missing
^
-patmatexhaust.scala:45: warning: does not cover case {object Gu}
- def ma4(x:Deep) = x match { // missing cases: Gu
+patmatexhaust.scala:44: warning: does not cover cases {object Gu,class Gp}
+ def ma4(x:Deep) = x match { // missing cases: Gu, Gp
^
-patmatexhaust.scala:57: error: unreachable code
+patmatexhaust.scala:51: warning: does not cover case {class Gp}
+ case Ga =>
+ ^
+patmatexhaust.scala:65: error: unreachable code
case 1 =>
^
-5 warnings found
+6 warnings found
one error found
diff --git a/test/files/neg/patmatexhaust.scala b/test/files/neg/patmatexhaust.scala
index aaa32cda24..7370868363 100644
--- a/test/files/neg/patmatexhaust.scala
+++ b/test/files/neg/patmatexhaust.scala
@@ -27,7 +27,6 @@ class TestSealedExhaustive { // compile only
//case {Qult(), Qult()} =>
}
-
sealed class Deep
case object Ga extends Deep
@@ -42,11 +41,11 @@ class TestSealedExhaustive { // compile only
case _ =>
}
- def ma4(x:Deep) = x match { // missing cases: Gu
+ def ma4(x:Deep) = x match { // missing cases: Gu, Gp
case Ga =>
}
- def zma5(x:Deep) = x match { // exhaustive
+ def ma5(x:Deep) = x match { // Gp
case Gu =>
case _ if 1 == 0 =>
case Ga =>
@@ -56,6 +55,11 @@ class TestSealedExhaustive { // compile only
case List(1,2) =>
case x :: xs =>
}
+
+ def ma7 = List(1,2) match { //exhaustive
+ case 1::2::Nil =>
+ case _ =>
+ }
def redundant = 1 match { // include this otherwise script won't test this in files/neg
case 1 =>
case 1 =>
diff --git a/test/files/run/patmatnew.scala b/test/files/run/patmatnew.scala
index 10b0021f0f..749b0bc21d 100644
--- a/test/files/run/patmatnew.scala
+++ b/test/files/run/patmatnew.scala
@@ -14,6 +14,7 @@ trait Shmeez extends AnyRef with Treez {
}
object Test {
+/*
import scala.testing.SUnit._
def main(args:Array[String]): Unit = {
@@ -81,4 +82,40 @@ object Test {
}
}
}
+
+
+ // these are exhaustive matches
+ // should not generate any warnings
+ def f[A](z:{Option[A],Option[A]}) = z match {
+ case {None,Some(x)} => 1
+ case {Some(x),None } => 2
+ case {Some(x),Some(y)} => 3
+ case _ => 4
+ }
+
+ def g1[A](z:Option[List[A]]) = z match {
+ case Some(Nil) => true
+ case Some(x::Nil) => true
+ case _ => true
+ }
+
+ def g2[A](z:Option[List[A]]) = z match {
+ case Some(x::Nil) => true
+ case Some(_) => false
+ case _ => true
+ }
+
+ def h[A](x:{Option[A],Option[A]}) = x match {
+ case {None,_:Some[_]} => 1
+ case {_:Some[_],None } => 2
+ case {_:Some[_],_:Some[_]} => 3
+ case _ => 4
+ }
+*/
+ def i = List(1,2) match {
+ case List(1) =>
+ case List(1,2,xs @ _*) =>
+ case Nil =>
+ }
}
+
diff --git a/test/files/run/regularpatmatnew.scala b/test/files/run/regularpatmatnew.scala
index a12ed5dfc6..a3401b2b7e 100644
--- a/test/files/run/regularpatmatnew.scala
+++ b/test/files/run/regularpatmatnew.scala
@@ -107,6 +107,7 @@ object Test {
override def runTest() = {
val res = (Bar(Foo()):Con) match {
case Bar(xs@_*) => xs // this should be optimized away to a pattern Bar(xs)
+ case _ => Nil
}
assertEquals("res instance"+res.isInstanceOf[Seq[Con]]+" res(0)="+res(0), true, res.isInstanceOf[Seq[Foo]] && res(0) == Foo() )
}