summaryrefslogtreecommitdiff
path: root/test/files/run/t10032.scala
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2016-11-08 11:58:01 +0100
committerLukas Rytz <lukas.rytz@gmail.com>2016-11-08 11:58:01 +0100
commitf297ca8d1f06086316ff3746250092e36ef9f74e (patch)
tree90755ce5098f1f6fee54d7611ae24311646a0822 /test/files/run/t10032.scala
parent10c609e750a7089055b126e6231e5ddb2f2e8623 (diff)
downloadscala-f297ca8d1f06086316ff3746250092e36ef9f74e.tar.gz
scala-f297ca8d1f06086316ff3746250092e36ef9f74e.tar.bz2
scala-f297ca8d1f06086316ff3746250092e36ef9f74e.zip
SI-10032 Fix code gen with returns in nested try-finally blocks
Return statements within `try` or `catch` blocks need special treatement if there's also a `finally` try { return 1 } finally { println() } For the return, the code generator emits a store to a local and a jump to a "cleanup" version of the finally block. There will be 3 versions of the finally block: - One reached through a handler, if the code in the try block throws; re-throws at the end - A "cleanup" version reached from returns within the try; reads the local and returns the value at the end - One reached for ordinary control flow, if there's no return and no exception within the try If there are multiple enclosing finally blocks, a "cleanup" version is emitted for each of them. The nested ones jump to the enclosing ones, the outermost one reads the local and returns. A global variable `shouldEmitCleanup` stores whether cleanup versions are required for the curren finally blocks. By mistake, this variable was not reset to `false` when emitting a `try-finally` nested within a `finally`: try { try { return 1 } finally { println() } // need cleanup version } finally { // need cleanup version try { println() } finally { println() } // no cleanup version needed! } In this commit we ensure that the variable is reset when emitting nested `try-finally` blocks.
Diffstat (limited to 'test/files/run/t10032.scala')
-rw-r--r--test/files/run/t10032.scala113
1 files changed, 113 insertions, 0 deletions
diff --git a/test/files/run/t10032.scala b/test/files/run/t10032.scala
new file mode 100644
index 0000000000..df6b114d05
--- /dev/null
+++ b/test/files/run/t10032.scala
@@ -0,0 +1,113 @@
+object Test extends App {
+ def a1(): Unit = println(" a1")
+ def a2(): Unit = println(" a2")
+ def a3(): Unit = println(" a3")
+
+ def i1: Int = { println(" i1"); 1 }
+ def i2: Int = { println(" i2"); 2 }
+ def i3: Int = { println(" i3"); 3 }
+
+ def e1: Int = { println(" e1"); throw new Exception() }
+
+ def t1: Int = {
+ println("t1")
+ try {
+ synchronized { return i1 }
+ } finally {
+ synchronized { a1() }
+ }
+ }
+
+ def t2: Int = {
+ println("t2")
+ try {
+ try { return i1 }
+ finally { a1() }
+ } finally {
+ try { a2() } finally { a3() }
+ }
+ }
+
+ def t3(i: => Int): Int = {
+ println("t3")
+ try {
+ try { return i }
+ finally { a1() }
+ } catch {
+ case _: Throwable =>
+ try { i2 }
+ finally { a2() } // no cleanup version
+ } finally {
+ a3()
+ }
+ }
+
+ def t4: Int = {
+ println("t4")
+ try {
+ return i1
+ } finally {
+ return i2
+ }
+ }
+
+ def t5(i: => Int): Int = {
+ println("t5")
+ try {
+ try {
+ try { return i }
+ finally { a1() }
+ } catch {
+ case _: Throwable => i2
+ }
+ } finally {
+ a3()
+ }
+ }
+
+ def t6: Int = {
+ println("t6")
+ try {
+ try { return i1 }
+ finally { return i2 }
+ } finally {
+ return i3
+ }
+ }
+
+ def t7(i: => Int): Int = {
+ println("t7")
+ try { i }
+ catch {
+ case _: Throwable =>
+ return i2
+ } finally {
+ a1() // cleanup required, early return in handler
+ }
+ }
+
+ def t8(): Int = {
+ println("t8")
+ try {
+ try { i1 }
+ finally { // no cleanup version
+ try { return i2 }
+ finally { a1() } // cleanup version required
+ }
+ } finally { // cleanup version required
+ a2()
+ }
+ }
+
+ assert(t1 == 1)
+ assert(t2 == 1)
+ assert(t3(i1) == 1)
+ assert(t3(e1) == 2)
+ assert(t4 == 2)
+ assert(t5(i1) == 1)
+ assert(t5(e1) == 2)
+ assert(t6 == 3)
+ assert(t7(i1) == 1)
+ assert(t7(e1) == 2)
+ assert(t8 == 2)
+}