aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala9
-rw-r--r--src/dotty/tools/dotc/transform/CapturedVars.scala32
-rw-r--r--tests/run/liftedTry.scala12
3 files changed, 43 insertions, 10 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 502b42987..fcd9ef224 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -592,13 +592,16 @@ class Definitions {
}
/** The classes for which a Ref type exists. */
- lazy val refClasses: collection.Set[Symbol] = ScalaNumericValueClasses + BooleanClass + ObjectClass
+ lazy val refClassKeys: collection.Set[Symbol] = ScalaNumericValueClasses + BooleanClass + ObjectClass
lazy val refClass: Map[Symbol, Symbol] =
- refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap
+ refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap
lazy val volatileRefClass: Map[Symbol, Symbol] =
- refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap
+ refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap
+
+ lazy val boxedRefClasses: collection.Set[Symbol] =
+ refClassKeys.flatMap(k => Set(refClass(k), volatileRefClass(k)))
def wrapArrayMethodName(elemtp: Type): TermName = {
val cls = elemtp.classSymbol
diff --git a/src/dotty/tools/dotc/transform/CapturedVars.scala b/src/dotty/tools/dotc/transform/CapturedVars.scala
index 86cf80073..0f1a60282 100644
--- a/src/dotty/tools/dotc/transform/CapturedVars.scala
+++ b/src/dotty/tools/dotc/transform/CapturedVars.scala
@@ -92,14 +92,34 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisTransfo
else id
}
+ /** If assignment is to a boxed ref type, e.g.
+ *
+ * intRef.elem = expr
+ *
+ * rewrite using a temporary var to
+ *
+ * val ev$n = expr
+ * intRef.elem = ev$n
+ *
+ * That way, we avoid the problem that `expr` might contain a `try` that would
+ * run on a non-empty stack (which is illegal under JVM rules). Note that LiftTry
+ * has already run before, so such `try`s would not be eliminated.
+ *
+ * Also: If the ref type lhs is followed by a cast (can be an artifact of nested translation),
+ * drop the cast.
+ */
override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = {
- val lhs1 = tree.lhs match {
- case TypeApply(Select(qual @ Select(qual2, nme.elem), nme.asInstanceOf_), _) =>
- assert(captured(qual2.symbol))
- qual
- case _ => tree.lhs
+ def recur(lhs: Tree): Tree = lhs match {
+ case TypeApply(Select(qual, nme.asInstanceOf_), _) =>
+ val Select(_, nme.elem) = qual
+ recur(qual)
+ case Select(_, nme.elem) if defn.boxedRefClasses.contains(lhs.symbol.maybeOwner) =>
+ val tempDef = transformFollowing(SyntheticValDef(ctx.freshName("ev$").toTermName, tree.rhs))
+ transformFollowing(Block(tempDef :: Nil, cpy.Assign(tree)(lhs, ref(tempDef.symbol))))
+ case _ =>
+ tree
}
- cpy.Assign(tree)(lhs1, tree.rhs)
+ recur(tree.lhs)
}
}
}
diff --git a/tests/run/liftedTry.scala b/tests/run/liftedTry.scala
index 2e4c25c2b..0d956fc1f 100644
--- a/tests/run/liftedTry.scala
+++ b/tests/run/liftedTry.scala
@@ -12,10 +12,20 @@ object Test {
foo(try 3 catch handle)
- def main(args: Array[String]) = {
+ def main(args: Array[String]): Unit = {
assert(x == 1)
assert(foo(2) == 2)
assert(foo(try raise(3) catch handle) == 3)
+ Tr.foo
}
}
+
+object Tr {
+ def fun(a: Int => Unit) = a(2)
+ def foo: Int = {
+ var s = 1
+ s = try {fun(s = _); 3} catch{ case ex: Throwable => s = 4; 5 }
+ s
+ }
+}