diff options
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/CapturedVars.scala | 32 | ||||
-rw-r--r-- | tests/run/liftedTry.scala | 12 |
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 + } +} |