diff options
author | James Iry <jamesiry@gmail.com> | 2013-01-18 16:24:41 -0800 |
---|---|---|
committer | James Iry <jamesiry@gmail.com> | 2013-01-25 12:53:39 -0800 |
commit | 0b52a5199ba8b949bd80de1eaf589420ef91d6f6 (patch) | |
tree | 1b73dec3b1804df969f19a63ff8af744135995a6 /src | |
parent | 2fa859e1b3eb2ac57058feaba87d96adfbac9209 (diff) | |
download | scala-0b52a5199ba8b949bd80de1eaf589420ef91d6f6.tar.gz scala-0b52a5199ba8b949bd80de1eaf589420ef91d6f6.tar.bz2 scala-0b52a5199ba8b949bd80de1eaf589420ef91d6f6.zip |
SI-6863 Fix verify error in captured var inited from expr with try/catch
If a captured var was inited from a try/catch we did something
reasonable. But if the var was inited from a more complicated expression
(if/else, a block, match/case, etc) that ended with
a try/catch then we didn't and we were generating faulty byte code.
This fix patches LambdaLift to add the missing cases.
For known simple expressions, the translation is just new *Ref(expr).
For try/catch, if/else, match/case, and blocks this recursively
walks down the internal result expressions to translate them. E.g.
if(cond) trueExpr else falseExpr becomes if(cone) translate(trueExpr)
else translate(falseExpr)
For unknown expression types, the translation is {val temp = expr; new
*Ref(expr) }
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/LambdaLift.scala | 41 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 2 |
2 files changed, 33 insertions, 10 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 448079abed..845843e9d6 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -451,20 +451,45 @@ abstract class LambdaLift extends InfoTransform { } case arg => arg } - /** Wrap expr argument in new *Ref(..) constructor, but make - * sure that Try expressions stay at toplevel. + + /** Wrap expr argument in new *Ref(..) constructor. But try/catch + * is a problem because a throw will clear the stack and post catch + * we would expect the partially-constructed object to be on the stack + * for the call to init. So we recursively + * search for "leaf" result expressions where we know its safe + * to put the new *Ref(..) constructor or, if all else fails, transform + * an expr to { val temp=expr; new *Ref(temp) }. + * The reason we narrowly look for try/catch in captured var definitions + * is because other try/catch expression have already been lifted + * see SI-6863 */ - def refConstr(expr: Tree): Tree = expr match { + def refConstr(expr: Tree): Tree = typer.typedPos(expr.pos) {expr match { + // very simple expressions can be wrapped in a new *Ref(expr) because they can't have + // a try/catch in final expression position. + case Ident(_) | Apply(_, _) | Literal(_) | New(_) | Select(_, _) | Throw(_) | Assign(_, _) | ValDef(_, _, _, _) | Return(_) | EmptyTree => + New(sym.tpe, expr) case Try(block, catches, finalizer) => Try(refConstr(block), catches map refConstrCase, finalizer) + case Block(stats, expr) => + Block(stats, refConstr(expr)) + case If(cond, trueBranch, falseBranch) => + If(cond, refConstr(trueBranch), refConstr(falseBranch)) + case Match(selector, cases) => + Match(selector, cases map refConstrCase) + // if we can't figure out what else to do, turn expr into {val temp1 = expr; new *Ref(temp1)} to avoid + // any possibility of try/catch in the *Ref constructor. This should be a safe tranformation as a default + // though it potentially wastes a variable slot. In particular this case handles LabelDefs. case _ => - New(sym.tpe, expr) - } + debuglog("assigning expr to temp: " + (expr.pos)) + val tempSym = currentOwner.newValue(unit.freshTermName("temp"), expr.pos) setInfo expr.tpe + val tempDef = ValDef(tempSym, expr) setPos expr.pos + val tempRef = Ident(tempSym) setPos expr.pos + Block(tempDef, New(sym.tpe, tempRef)) + }} def refConstrCase(cdef: CaseDef): CaseDef = CaseDef(cdef.pat, cdef.guard, refConstr(cdef.body)) - treeCopy.ValDef(tree, mods, name, tpt1, typer.typedPos(rhs.pos) { - refConstr(constructorArg) - }) + + treeCopy.ValDef(tree, mods, name, tpt1, refConstr(constructorArg)) } else tree case Return(Block(stats, value)) => Block(stats, treeCopy.Return(tree, value)) setType tree.tpe setPos tree.pos diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 9908bd689e..96cb40b1b5 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -603,8 +603,6 @@ abstract class UnCurry extends InfoTransform } case ValDef(_, _, _, rhs) => if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit) - // a local variable that is mutable and free somewhere later should be lifted - // as lambda lifting (coming later) will wrap 'rhs' in an Ref object. if (!sym.owner.isSourceMethod) withNeedLift(true) { super.transform(tree) } else |