diff options
author | Paul Phillips <paulp@improving.org> | 2012-03-20 20:15:45 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-03-20 20:15:45 -0700 |
commit | fcc63e2b667bdbaddbaacc843de97f8db02f6426 (patch) | |
tree | 9ee7bd6e4cb9e487a0d5c5b9c812a68c4c0ed025 | |
parent | 1c65152c7aaeb3aeaf8a5e39e6fd51e5b4b95836 (diff) | |
download | scala-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.scala | 20 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala | 34 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 2 | ||||
-rw-r--r-- | test/files/neg/t5589neg.check | 37 | ||||
-rw-r--r-- | test/files/neg/t5589neg.scala | 6 | ||||
-rw-r--r-- | test/files/neg/t5589neg2.check | 9 | ||||
-rw-r--r-- | test/files/neg/t5589neg2.scala | 13 | ||||
-rw-r--r-- | test/files/pos/irrefutable.scala | 22 | ||||
-rw-r--r-- | test/files/pos/t1336.scala | 10 | ||||
-rw-r--r-- | test/files/pos/t5589.scala | 22 | ||||
-rw-r--r-- | test/files/run/t4574.check | 2 | ||||
-rw-r--r-- | test/files/run/t4574.scala | 13 |
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 } ) + } +} |