diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2016-11-09 14:10:59 +0100 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2016-11-09 14:42:01 +0100 |
commit | c1e9b0a951ee5298244c6456af3641ee966e101b (patch) | |
tree | f090170211e20b101d7477e9b269a7c0d10d945a /src | |
parent | f297ca8d1f06086316ff3746250092e36ef9f74e (diff) | |
download | scala-c1e9b0a951ee5298244c6456af3641ee966e101b.tar.gz scala-c1e9b0a951ee5298244c6456af3641ee966e101b.tar.bz2 scala-c1e9b0a951ee5298244c6456af3641ee966e101b.zip |
Fix returns from within finalizers
When a return in a finalizer was reached through a return within the try
block, the backend ignored the return in the finalizer:
try {
try { return 1 }
finally { return 2 }
} finally { println() }
This expression should evaluate to 2 (it does in 2.11.8), but in 2.12.0
it the result is 1.
The Scala spec is currently incomplete, it does not say that a finalizer
should be exectuted if a return occurs within a try block, and it does
not specify what happens if also the finally block has a return.
So we follow the Java spec, which basically says: if the finally blocks
completes abruptly for reason S, then the entire try statement completes
abruptly with reason S. An abrupt termination of the try block for a
different reason R is discarded.
Abrupt completion is basically returning or throwing.
Diffstat (limited to 'src')
3 files changed, 4 insertions, 13 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 0b07e12917..b0815b0008 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -488,16 +488,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { bc emitRETURN returnType case nextCleanup :: rest => if (saveReturnValue) { - if (insideCleanupBlock) { - reporter.warning(r.pos, "Return statement found in finally-clause, discarding its return-value in favor of that of a more deeply nested return.") - bc drop returnType - } else { - // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. - if (earlyReturnVar == null) { - earlyReturnVar = locals.makeLocal(returnType, "earlyReturnVar") - } - locals.store(earlyReturnVar) + // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. + if (earlyReturnVar == null) { + earlyReturnVar = locals.makeLocal(returnType, "earlyReturnVar") } + locals.store(earlyReturnVar) } bc goTo nextCleanup shouldEmitCleanup = true diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index dbad37cd5b..fdb5687311 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -255,7 +255,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { // used by genLoadTry() and genSynchronized() var earlyReturnVar: Symbol = null var shouldEmitCleanup = false - var insideCleanupBlock = false // line numbers var lastEmittedLineNr = -1 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala index 9d4ef44546..add2c5ffe6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala @@ -348,13 +348,10 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { // `shouldEmitCleanup` can be set, and at the same time this try expression may lack a finally-clause. // In other words, all combinations of (hasFinally, shouldEmitCleanup) are valid. if (hasFinally && currentFinallyBlockNeedsCleanup) { - val savedInsideCleanup = insideCleanupBlock - insideCleanupBlock = true markProgramPoint(finCleanup) // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. emitFinalizer(finalizer, null, isDuplicate = true) pendingCleanups() - insideCleanupBlock = savedInsideCleanup } /* ------ (4) finally-clause-for-normal-nonEarlyReturn-exit |