Skip to content

Commit edaa193

Browse files
committed
Fix CaputuredVars/LiftTry interaction.
CapturedVars introduced an assignment that could cause a try to be executed with a non-empty stack, even after LiftTry had already run. We now avoid this by introducing a temporary variable.
1 parent b30843c commit edaa193

File tree

3 files changed

+43
-10
lines changed

3 files changed

+43
-10
lines changed

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,13 +592,16 @@ class Definitions {
592592
}
593593

594594
/** The classes for which a Ref type exists. */
595-
lazy val refClasses: collection.Set[Symbol] = ScalaNumericValueClasses + BooleanClass + ObjectClass
595+
lazy val refClassKeys: collection.Set[Symbol] = ScalaNumericValueClasses + BooleanClass + ObjectClass
596596

597597
lazy val refClass: Map[Symbol, Symbol] =
598-
refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap
598+
refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap
599599

600600
lazy val volatileRefClass: Map[Symbol, Symbol] =
601-
refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap
601+
refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap
602+
603+
lazy val boxedRefClasses: collection.Set[Symbol] =
604+
refClassKeys.flatMap(k => Set(refClass(k), volatileRefClass(k)))
602605

603606
def wrapArrayMethodName(elemtp: Type): TermName = {
604607
val cls = elemtp.classSymbol

src/dotty/tools/dotc/transform/CapturedVars.scala

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,34 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisTransfo
9292
else id
9393
}
9494

95+
/** If assignment is to a boxed ref type, e.g.
96+
*
97+
* intRef.elem = expr
98+
*
99+
* rewrite using a temporary var to
100+
*
101+
* val ev$n = expr
102+
* intRef.elem = ev$n
103+
*
104+
* That way, we avoid the problem that `expr` might contain a `try` that would
105+
* run on a non-empty stack (which is illegal under JVM rules). Note that LiftTry
106+
* has already run before, so such `try`s would not be eliminated.
107+
*
108+
* Also: If the ref type lhs is followed by a cast (can be an artifact of nested translation),
109+
* drop the cast.
110+
*/
95111
override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = {
96-
val lhs1 = tree.lhs match {
97-
case TypeApply(Select(qual @ Select(qual2, nme.elem), nme.asInstanceOf_), _) =>
98-
assert(captured(qual2.symbol))
99-
qual
100-
case _ => tree.lhs
112+
def recur(lhs: Tree): Tree = lhs match {
113+
case TypeApply(Select(qual, nme.asInstanceOf_), _) =>
114+
val Select(_, nme.elem) = qual
115+
recur(qual)
116+
case Select(_, nme.elem) if defn.boxedRefClasses.contains(lhs.symbol.maybeOwner) =>
117+
val tempDef = transformFollowing(SyntheticValDef(ctx.freshName("ev$").toTermName, tree.rhs))
118+
transformFollowing(Block(tempDef :: Nil, cpy.Assign(tree)(lhs, ref(tempDef.symbol))))
119+
case _ =>
120+
tree
101121
}
102-
cpy.Assign(tree)(lhs1, tree.rhs)
122+
recur(tree.lhs)
103123
}
104124
}
105125
}

tests/run/liftedTry.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,20 @@ object Test {
1212

1313
foo(try 3 catch handle)
1414

15-
def main(args: Array[String]) = {
15+
def main(args: Array[String]): Unit = {
1616
assert(x == 1)
1717
assert(foo(2) == 2)
1818
assert(foo(try raise(3) catch handle) == 3)
19+
Tr.foo
1920
}
2021
}
2122

23+
24+
object Tr {
25+
def fun(a: Int => Unit) = a(2)
26+
def foo: Int = {
27+
var s = 1
28+
s = try {fun(s = _); 3} catch{ case ex: Throwable => s = 4; 5 }
29+
s
30+
}
31+
}

0 commit comments

Comments
 (0)