summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-05-03 23:41:10 -0700
committerPaul Phillips <paulp@improving.org>2012-05-04 01:22:23 -0700
commitaad6deae7204a7fc95b59ede61b188bb62f51188 (patch)
tree4c034353840a59ee001c8e330c23126a0c50fb13
parentf146d5826fc335ee1ca9c285d69086a7475cb71e (diff)
downloadscala-aad6deae7204a7fc95b59ede61b188bb62f51188.tar.gz
scala-aad6deae7204a7fc95b59ede61b188bb62f51188.tar.bz2
scala-aad6deae7204a7fc95b59ede61b188bb62f51188.zip
Fix for broken non-local returns.
Don't change the owner of a return if the new owner is nested inside the old owner. Closes SI-5612.
-rw-r--r--src/compiler/scala/reflect/internal/Trees.scala14
-rw-r--r--test/files/run/nonlocalreturn.check1
-rw-r--r--test/files/run/nonlocalreturn.scala15
-rw-r--r--test/files/run/t5612.check4
-rw-r--r--test/files/run/t5612.scala28
5 files changed, 59 insertions, 3 deletions
diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala
index 5f1a8f3fbe..c43528b8e9 100644
--- a/src/compiler/scala/reflect/internal/Trees.scala
+++ b/src/compiler/scala/reflect/internal/Trees.scala
@@ -315,11 +315,19 @@ trait Trees extends api.Trees { self: SymbolTable =>
class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser {
def changeOwner(tree: Tree) = tree match {
case Return(expr) =>
- if (tree.symbol == oldowner)
- tree.symbol = newowner
+ if (tree.symbol == oldowner) {
+ // SI-5612
+ if (newowner hasTransOwner oldowner)
+ log("NOT changing owner of %s because %s is nested in %s".format(tree, newowner, oldowner))
+ else {
+ log("changing owner of %s: %s => %s".format(tree, oldowner, newowner))
+ tree.symbol = newowner
+ }
+ }
case _: DefTree | _: Function =>
- if (tree.symbol != NoSymbol && tree.symbol.owner == oldowner)
+ if (tree.symbol != NoSymbol && tree.symbol.owner == oldowner) {
tree.symbol.owner = newowner
+ }
case _ =>
}
override def traverse(tree: Tree) {
diff --git a/test/files/run/nonlocalreturn.check b/test/files/run/nonlocalreturn.check
new file mode 100644
index 0000000000..aeb2d5e239
--- /dev/null
+++ b/test/files/run/nonlocalreturn.check
@@ -0,0 +1 @@
+Some(1)
diff --git a/test/files/run/nonlocalreturn.scala b/test/files/run/nonlocalreturn.scala
new file mode 100644
index 0000000000..3c1e7420ed
--- /dev/null
+++ b/test/files/run/nonlocalreturn.scala
@@ -0,0 +1,15 @@
+object Test {
+ def wrap[K](body: => K): K = body
+
+ def f(): Option[Int] = {
+ wrap({ return Some(1) ; None })
+ }
+
+ def main(args: Array[String]) {
+ println(f())
+ }
+}
+// java.lang.ClassCastException: scala.Some cannot be cast to scala.None$
+// at Test$$anonfun$f$1.apply(nonlocalreturn.scala:5)
+// at Test$$anonfun$f$1.apply(nonlocalreturn.scala:5)
+// at Test$.wrap(nonlocalreturn.scala:2)
diff --git a/test/files/run/t5612.check b/test/files/run/t5612.check
new file mode 100644
index 0000000000..9d19cca292
--- /dev/null
+++ b/test/files/run/t5612.check
@@ -0,0 +1,4 @@
+START for List(Two, Two, One, Three)
+TWO
+TWO
+ONE
diff --git a/test/files/run/t5612.scala b/test/files/run/t5612.scala
new file mode 100644
index 0000000000..48b3093548
--- /dev/null
+++ b/test/files/run/t5612.scala
@@ -0,0 +1,28 @@
+object L extends Enumeration {
+ val One, Two, Three = Value
+}
+
+class Foo {
+ def foo(xs: List[L.Value]) {
+ import scala.util.control.Breaks.{break, breakable}
+ println("START for " + xs)
+ breakable {
+ for (x <- xs) {
+ x match {
+ case L.One => println("ONE"); return
+ case L.Two => println("TWO")
+ case L.Three => println("THREE"); break
+ }
+ }
+ }
+ println("FINISH")
+ }
+}
+
+object Test {
+ def main(args: Array[String]) {
+ val f = new Foo()
+ val l = List(L.Two, L.Two, L.One, L.Three)
+ f.foo(l)
+ }
+}