summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-03-20 20:15:45 -0700
committerPaul Phillips <paulp@improving.org>2012-03-20 20:15:45 -0700
commitfcc63e2b667bdbaddbaacc843de97f8db02f6426 (patch)
tree9ee7bd6e4cb9e487a0d5c5b9c812a68c4c0ed025
parent1c65152c7aaeb3aeaf8a5e39e6fd51e5b4b95836 (diff)
downloadscala-fcc63e2b667bdbaddbaacc843de97f8db02f6426.tar.gz
scala-fcc63e2b667bdbaddbaacc843de97f8db02f6426.tar.bz2
scala-fcc63e2b667bdbaddbaacc843de97f8db02f6426.zip
Restore irrefutability commits.
This reverts commit d8ba5d091e5641553b438ef9930a6023a2709dcd.
-rw-r--r--src/compiler/scala/reflect/internal/TreeInfo.scala20
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala34
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala2
-rw-r--r--test/files/neg/t5589neg.check37
-rw-r--r--test/files/neg/t5589neg.scala6
-rw-r--r--test/files/neg/t5589neg2.check9
-rw-r--r--test/files/neg/t5589neg2.scala13
-rw-r--r--test/files/pos/irrefutable.scala22
-rw-r--r--test/files/pos/t1336.scala10
-rw-r--r--test/files/pos/t5589.scala22
-rw-r--r--test/files/run/t4574.check2
-rw-r--r--test/files/run/t4574.scala13
12 files changed, 169 insertions, 21 deletions
diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala
index 769d7a9ed1..ce3de94335 100644
--- a/src/compiler/scala/reflect/internal/TreeInfo.scala
+++ b/src/compiler/scala/reflect/internal/TreeInfo.scala
@@ -17,7 +17,7 @@ abstract class TreeInfo {
val global: SymbolTable
import global._
- import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass }
+ import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass }
/* Does not seem to be used. Not sure what it does anyway.
def isOwnerDefinition(tree: Tree): Boolean = tree match {
@@ -312,6 +312,24 @@ abstract class TreeInfo {
case _ => false
}
+ /** Is this tree comprised of nothing but identifiers,
+ * but possibly in bindings or tuples? For instance
+ *
+ * foo @ (bar, (baz, quux))
+ *
+ * is a variable pattern; if the structure matches,
+ * then the remainder is inevitable.
+ */
+ def isVariablePattern(tree: Tree): Boolean = tree match {
+ case Bind(name, pat) => isVariablePattern(pat)
+ case Ident(name) => true
+ case Apply(sel, args) =>
+ ( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName)
+ && (args forall isVariablePattern)
+ )
+ case _ => false
+ }
+
/** Is this argument node of the form <expr> : _* ?
*/
def isWildcardStarArg(tree: Tree): Boolean = tree match {
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 0d2fbc5372..0bc88d1efd 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -262,29 +262,25 @@ abstract class TreeBuilder {
else if (stats.length == 1) stats.head
else Block(stats.init, stats.last)
+ def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = {
+ val cases = List(
+ CaseDef(condition, EmptyTree, Literal(Constant(true))),
+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
+ )
+ val matchTree = makeVisitor(cases, false, scrutineeName)
+
+ atPos(tree.pos)(Apply(Select(tree, nme.withFilter), matchTree :: Nil))
+ }
+
/** Create tree for for-comprehension generator <val pat0 <- rhs0> */
def makeGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = {
val pat1 = patvarTransformer.transform(pat)
val rhs1 =
- if (valeq) rhs
- else matchVarPattern(pat1) match {
- case Some(_) =>
- rhs
- case None =>
- atPos(rhs.pos) {
- Apply(
- Select(rhs, nme.filter),
- List(
- makeVisitor(
- List(
- CaseDef(pat1.duplicate, EmptyTree, Literal(Constant(true))),
- CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))),
- false,
- nme.CHECK_IF_REFUTABLE_STRING
- )))
- }
- }
- if (valeq) ValEq(pos, pat1, rhs1) else ValFrom(pos, pat1, rhs1)
+ if (valeq || treeInfo.isVariablePattern(pat)) rhs
+ else makeFilter(rhs, pat1.duplicate, nme.CHECK_IF_REFUTABLE_STRING)
+
+ if (valeq) ValEq(pos, pat1, rhs1)
+ else ValFrom(pos, pat1, rhs1)
}
def makeParam(pname: TermName, tpe: Tree) =
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index fa19f380fd..d98d248231 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1445,7 +1445,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
private def transformApply(tree: Apply): Tree = tree match {
case Apply(
- Select(qual, nme.filter),
+ Select(qual, nme.filter | nme.withFilter),
List(Function(
List(ValDef(_, pname, tpt, _)),
Match(_, CaseDef(pat1, _, _) :: _))))
diff --git a/test/files/neg/t5589neg.check b/test/files/neg/t5589neg.check
new file mode 100644
index 0000000000..b3ff16d7e4
--- /dev/null
+++ b/test/files/neg/t5589neg.check
@@ -0,0 +1,37 @@
+t5589neg.scala:2: warning: `withFilter' method does not yet exist on Either.RightProjection[Int,String], using `filter' method instead
+ def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:2: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2)
+ required: String
+ def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:3: warning: `withFilter' method does not yet exist on Either.RightProjection[Int,String], using `filter' method instead
+ def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:3: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2)
+ required: String
+ def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:4: error: constructor cannot be instantiated to expected type;
+ found : (T1,)
+ required: (String, Int)
+ def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:4: error: not found: value y2
+ def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:5: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2, T3)
+ required: (String, Int)
+ def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:5: error: not found: value y1
+ def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2))
+ ^
+t5589neg.scala:5: error: not found: value y2
+ def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2))
+ ^
+two warnings found
+7 errors found
diff --git a/test/files/neg/t5589neg.scala b/test/files/neg/t5589neg.scala
new file mode 100644
index 0000000000..31ff2c3693
--- /dev/null
+++ b/test/files/neg/t5589neg.scala
@@ -0,0 +1,6 @@
+class A {
+ def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2))
+ def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2))
+ def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2))
+ def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2))
+}
diff --git a/test/files/neg/t5589neg2.check b/test/files/neg/t5589neg2.check
new file mode 100644
index 0000000000..6af4955a83
--- /dev/null
+++ b/test/files/neg/t5589neg2.check
@@ -0,0 +1,9 @@
+t5589neg2.scala:7: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2)
+ required: String
+ for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok
+ ^
+t5589neg2.scala:7: error: not found: value d
+ for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok
+ ^
+two errors found
diff --git a/test/files/neg/t5589neg2.scala b/test/files/neg/t5589neg2.scala
new file mode 100644
index 0000000000..b7c7ab7218
--- /dev/null
+++ b/test/files/neg/t5589neg2.scala
@@ -0,0 +1,13 @@
+class A {
+ def f1(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = {
+ for (((((a, (b, (c, d))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // ok
+ }
+
+ def f2(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = {
+ for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok
+ }
+
+ def f3(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = {
+ for (((((a, (b, _)), es), fs), gs) <- x) yield (es ::: fs).mkString(", ") // ok
+ }
+} \ No newline at end of file
diff --git a/test/files/pos/irrefutable.scala b/test/files/pos/irrefutable.scala
new file mode 100644
index 0000000000..0a792b644a
--- /dev/null
+++ b/test/files/pos/irrefutable.scala
@@ -0,0 +1,22 @@
+// The test which this should perform but does not
+// is that f1 is recognized as irrefutable and f2 is not
+// This can be recognized via the generated classes:
+//
+// A$$anonfun$f1$1.class
+// A$$anonfun$f2$1.class
+// A$$anonfun$f2$2.class
+//
+// The extra one in $f2$ is the filter.
+//
+// !!! Marking with exclamation points so maybe someday
+// this test will be finished.
+class A {
+ case class Foo[T](x: T)
+
+ def f1(xs: List[Foo[Int]]) = {
+ for (Foo(x: Int) <- xs) yield x
+ }
+ def f2(xs: List[Foo[Any]]) = {
+ for (Foo(x: Int) <- xs) yield x
+ }
+}
diff --git a/test/files/pos/t1336.scala b/test/files/pos/t1336.scala
new file mode 100644
index 0000000000..63967985c7
--- /dev/null
+++ b/test/files/pos/t1336.scala
@@ -0,0 +1,10 @@
+object Foo {
+ def foreach( f : ((Int,Int)) => Unit ) {
+ println("foreach")
+ f(1,2)
+ }
+
+ for( (a,b) <- this ) {
+ println((a,b))
+ }
+}
diff --git a/test/files/pos/t5589.scala b/test/files/pos/t5589.scala
new file mode 100644
index 0000000000..69cbb20391
--- /dev/null
+++ b/test/files/pos/t5589.scala
@@ -0,0 +1,22 @@
+class A {
+ // First three compile.
+ def f1(x: Either[Int, String]) = x.right map (y => y)
+ def f2(x: Either[Int, String]) = for (y <- x.right) yield y
+ def f3(x: Either[Int, (String, Int)]) = x.right map { case (y1, y2) => (y1, y2) }
+ // Last one fails.
+ def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2))
+/**
+./a.scala:5: error: constructor cannot be instantiated to expected type;
+ found : (T1, T2)
+ required: Either[Nothing,(String, Int)]
+ def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2))
+ ^
+./a.scala:5: error: not found: value y1
+ def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2))
+ ^
+./a.scala:5: error: not found: value y2
+ def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2))
+ ^
+three errors found
+**/
+}
diff --git a/test/files/run/t4574.check b/test/files/run/t4574.check
new file mode 100644
index 0000000000..a4522fff24
--- /dev/null
+++ b/test/files/run/t4574.check
@@ -0,0 +1,2 @@
+I hereby refute null!
+I denounce null as unListLike!
diff --git a/test/files/run/t4574.scala b/test/files/run/t4574.scala
new file mode 100644
index 0000000000..1dde496aca
--- /dev/null
+++ b/test/files/run/t4574.scala
@@ -0,0 +1,13 @@
+object Test {
+ val xs: List[(Int, Int)] = List((2, 2), null)
+
+ def expectMatchError[T](msg: String)(body: => T) {
+ try { body ; assert(false, "Should not succeed.") }
+ catch { case _: MatchError => println(msg) }
+ }
+
+ def main(args: Array[String]): Unit = {
+ expectMatchError("I hereby refute null!")( for ((x, y) <- xs) yield x )
+ expectMatchError("I denounce null as unListLike!")( (null: Any) match { case List(_*) => true } )
+ }
+}