scala/scala — pull request #1892 by retronym (2013-01-13 14:08:47+00:00)
Title: SI-6479 Don't lift try exprs in label arguments.

The new pattern matcher uses label jumps to GOTO
the next case. Uncurry treated these like regular
method arguments, and performed the liftedTree()
transformation, which ensures that try expressions
are only used in a statement position. Even try
in statement position of a block used as such an argument
are subject to the same transform.

This transform stems from the JVM limitation,
that try/catch does not leave a value on the stack.
See b194446.

This commit changes Uncurry to avoid this transform
for arguments to label jumps. This avoids needlessly
indirect code, and enables tail call elimination in
more cases.

As an example, Scala 2.10.0 transforms the last
method of the enclosed test case to:

try {
case val x1: Int = 1;
case5(){
if (2.==(x1))
{
val x2: Int = x1;
matchEnd4({
{
def liftedTree2(): Unit = try {
throw new scala.runtime.NonLocalReturnControl[Unit](nonLocalReturnKey1, ())
} catch {
case (e @ (_: ClassNotFoundException)) => ()
};
liftedTree2()
};
TailrecAfterTryCatch.this.bad()
})
}
else
case6()
};
case6(){
matchEnd4(throw new MatchError(x1))
};
matchEnd4(x: Unit){
x
}
} catch {
case (ex @ (_: scala.runtime.NonLocalReturnControl[Unit @unchecked])) => if (ex.key().eq(nonLocalReturnKey1))
ex.value()
else
throw ex
}

After this patch:

@scala.annotation.tailrec final def bad(): Unit = {
case val x1: Int = 1;
case5(){
if (2.==(x1))
{
val x2: Int = x1;
matchEnd4({
try {
return ()
} catch {
case (e @ (_: ClassNotFoundException)) => ()
};
TailrecAfterTryCatch.this.bad()
})
}
else
case6()
};
case6(){
matchEnd4(throw new MatchError(x1))
};
matchEnd4(x: Unit){
x
}
}

Review by @dragos