Skip to content

Commit 3f46a75

Browse files
committed
Add LiftTry phase
Phase lifts tries that would be illegal because they execute on non-empty expression stacks.
1 parent 2a306dd commit 3f46a75

File tree

5 files changed

+96
-1
lines changed

5 files changed

+96
-1
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class Compiler {
4848
new ExtensionMethods,
4949
new ExpandSAMs,
5050
new TailRec,
51+
new LiftTry,
5152
new ClassOf),
5253
List(new PatternMatcher,
5354
new ExplicitOuter,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import TreeTransforms._
5+
import core.DenotTransformers._
6+
import core.Symbols._
7+
import core.Contexts._
8+
import core.Types._
9+
import core.Flags._
10+
import core.Decorators._
11+
import NonLocalReturns._
12+
13+
/** Lifts try's that might be executed on non-empty expression stacks
14+
* to their own methods. I.e.
15+
*
16+
* try body catch handler
17+
*
18+
* is lifted to
19+
*
20+
* { def liftedTree$n() = try body catch handler; liftedTree$n() }
21+
*/
22+
class LiftTry extends MiniPhase with IdentityDenotTransformer { thisTransform =>
23+
import ast.tpd._
24+
25+
/** the following two members override abstract members in Transform */
26+
val phaseName: String = "liftTry"
27+
28+
val treeTransform = new Transform(needLift = false)
29+
val liftingTransform = new Transform(needLift = true)
30+
31+
class Transform(needLift: Boolean) extends TreeTransform {
32+
def phase = thisTransform
33+
34+
override def prepareForApply(tree: Apply)(implicit ctx: Context) =
35+
if (tree.fun.symbol.is(Label)) this
36+
else liftingTransform
37+
38+
override def prepareForValDef(tree: ValDef)(implicit ctx: Context) =
39+
if (!tree.symbol.exists ||
40+
tree.symbol.isSelfSym ||
41+
tree.symbol.owner == ctx.owner.enclosingMethod) this
42+
else liftingTransform
43+
44+
override def prepareForAssign(tree: Assign)(implicit ctx: Context) =
45+
if (tree.lhs.symbol.maybeOwner == ctx.owner.enclosingMethod) this
46+
else liftingTransform
47+
48+
override def prepareForReturn(tree: Return)(implicit ctx: Context) =
49+
if (!isNonLocalReturn(tree)) this
50+
else liftingTransform
51+
52+
override def prepareForTemplate(tree: Template)(implicit ctx: Context) =
53+
treeTransform
54+
55+
override def transformTry(tree: Try)(implicit ctx: Context, info: TransformerInfo): Tree =
56+
if (needLift) {
57+
ctx.debuglog(i"lifting tree at ${tree.pos}, current owner = ${ctx.owner}")
58+
val fn = ctx.newSymbol(
59+
ctx.owner, ctx.freshName("liftedTree").toTermName, Synthetic | Method,
60+
MethodType(Nil, tree.tpe), coord = tree.pos)
61+
tree.changeOwnerAfter(ctx.owner, fn, thisTransform)
62+
Block(DefDef(fn, tree) :: Nil, ref(fn).appliedToNone)
63+
}
64+
else tree
65+
}
66+
}

test/dotc/tests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class tests extends CompilerTest {
115115
@Test def neg_zoo = compileFile(negDir, "zoo", xerrors = 12)
116116

117117
val negTailcallDir = negDir + "tailcall/"
118-
@Test def neg_tailcall_t1672b = compileFile(negTailcallDir, "t1672b", xerrors = 6)
118+
@Test def neg_tailcall_t1672b = compileFile(negTailcallDir, "t1672b", xerrors = 5)
119119
@Test def neg_tailcall_t3275 = compileFile(negTailcallDir, "t3275", xerrors = 1)
120120
@Test def neg_tailcall_t6574 = compileFile(negTailcallDir, "t6574", xerrors = 2)
121121
@Test def neg_tailcall = compileFile(negTailcallDir, "tailrec", xerrors = 7)

tests/run/StackMap.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test {
2+
var implicitsCache = null
3+
4+
def main(args: Array[String]): Unit = {
5+
implicitsCache = try{null} catch { case ex: Exception => null }
6+
}
7+
}

tests/run/liftedTry.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
object Test {
2+
3+
def raise(x: Int) = { throw new Exception(s"$x"); 0 }
4+
def handle: Throwable => Int = { case ex: Exception => ex.getMessage().toInt }
5+
6+
val x = try raise(1) catch handle
7+
8+
def foo(x: Int) = {
9+
val y = try raise(x) catch handle
10+
y
11+
}
12+
13+
foo(try 3 catch handle)
14+
15+
def main(args: Array[String]) = {
16+
assert(x == 1)
17+
assert(foo(2) == 2)
18+
assert(foo(try raise(3) catch handle) == 3)
19+
}
20+
}
21+

0 commit comments

Comments
 (0)