diff options
author | shack <shack@epfl.ch> | 2008-01-22 11:26:46 +0000 |
---|---|---|
committer | shack <shack@epfl.ch> | 2008-01-22 11:26:46 +0000 |
commit | 3e1f51aad2c854eae6fd4b6aa60d0382a20d3d20 (patch) | |
tree | a541ab986869c5e3d56b132a7f7359fa11b89a7f /src | |
parent | e8558ed48a00979a261e0fec21731e555e835f43 (diff) | |
download | scala-3e1f51aad2c854eae6fd4b6aa60d0382a20d3d20.tar.gz scala-3e1f51aad2c854eae6fd4b6aa60d0382a20d3d20.tar.bz2 scala-3e1f51aad2c854eae6fd4b6aa60d0382a20d3d20.zip |
Fixed #311 and #312
Disable tail call recursion for certain cases when compiling for MSIL to
adhere to CIL conventions
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/CleanUp.scala | 32 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/TailCalls.scala | 11 |
3 files changed, 43 insertions, 10 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index 362a4f0347..7fed4c77c6 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -910,7 +910,13 @@ abstract class GenMSIL extends SubComponent { **/ def adaptBlocks(blocks: List[BasicBlock], exh: ExceptionHandler): (List[BasicBlock], Option[BasicBlock]) = { def outsideTargets(block: BasicBlock, blocks: List[BasicBlock]) = { - block.successors.filter(scc => !blocks.contains(scc)) + /* The catch block of the ExceptionHandler is always a successor of any block inside the try + * (see successors method in BasicBlocks.scala) + * Thus, this successor does not correspond to a jump outside the exception handler + * and has to be ignored when computing the list of blocks leaving the exception handler. */ + val res = block.successors.filter(scc => !blocks.contains(scc) && scc != exh.startBlock) + if (settings.debug.value) log("outside of " + block + " = " + res + " succ " + block.successors) + res } // get leaving blocks and their outside targets def leavingBlocks(blocks: List[BasicBlock]): List[(BasicBlock, List[BasicBlock])] = { @@ -952,11 +958,13 @@ abstract class GenMSIL extends SubComponent { val lBlock = p._1 val target = p._2(0) // the elemets of p._2 are all the same, checked before replaceJump(lBlock, target, jumpOutBlock) + if (settings.debug.value) log("replacing " + lBlock + " target " + target + " jump out " + jumpOutBlock) }) (blocks ::: List(jumpOutBlock), Some(jumpOutBlock)) } val leaving = leavingBlocks(blocks) + if (settings.debug.value) log("leaving " + leaving) if (leaving.length == 0) (blocks, None) else if (leaving.length == 1) { diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index b0140dae73..ea6e66344b 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -504,12 +504,15 @@ abstract class CleanUp extends Transform { /* end of dynamic call transformer. */ - case Template(parents, self, body) if !forMSIL => + case Template(parents, self, body) => localTyper = typer.atOwner(tree, currentOwner) - classConstantMeth.clear - newDefs.clear - val body1 = transformTrees(body) - copy.Template(tree, parents, self, newDefs.toList ::: body1) + if (!forMSIL) { + classConstantMeth.clear + newDefs.clear + val body1 = transformTrees(body) + copy.Template(tree, parents, self, newDefs.toList ::: body1) + } + else super.transform(tree) case Literal(c) if (c.tag == ClassTag) && !forMSIL=> val tpe = c.typeValue @@ -524,6 +527,25 @@ abstract class CleanUp extends Transform { else tree } } + + /* MSIL requires that the stack is empty at the end of a try-block. + * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they + * store their result in a local variable. The catch blocks are adjusted as well. + * The try tree is subsituted by a block whose result expression is read of that variable. */ + case theTry @ Try(block, catches, finalizer) + if theTry.tpe.typeSymbol != definitions.UnitClass && theTry.tpe.typeSymbol != definitions.AllClass => + val tpe = theTry.tpe.widen + val tempVar = currentOwner.newValue(theTry.pos, unit.fresh.newName("exceptionResult")) + .setInfo(tpe).setFlag(Flags.MUTABLE) + + val newBlock = Block(Nil, Assign(Ident(tempVar), transform(block))) + val newCatches = for (CaseDef(pattern, guard, body) <- catches) yield { + CaseDef(pattern, transform(guard), Block(Nil, Assign(Ident(tempVar), transform(body)))) + } + val newTry = Try(newBlock, newCatches, finalizer) + val res = Block(List(ValDef(tempVar, EmptyTree), newTry), Ident(tempVar)) + localTyper.typed(res) + case _ => super.transform(tree) } diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index b2e7fb079e..20d7b97320 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -248,16 +248,17 @@ abstract class TailCalls extends Transform case Typed(expr, tpt) => super.transform(tree) case Apply(tapply @ TypeApply(fun, targs), vargs) => + lazy val defaultTree = copy.Apply(tree, tapply, transformTrees(vargs, mkContext(ctx, false))) if ( ctx.currentMethod.isFinal && ctx.tailPos && isSameTypes(ctx.tparams, targs map (_.tpe.typeSymbol)) && isRecursiveCall(fun)) { fun match { - case Select(receiver, _) => rewriteTailCall(fun, receiver :: transformTrees(vargs, mkContext(ctx, false))) + case Select(receiver, _) => if (!forMSIL) rewriteTailCall(fun, receiver :: transformTrees(vargs, mkContext(ctx, false))) else defaultTree case _ => rewriteTailCall(fun, This(currentClass) :: transformTrees(vargs, mkContext(ctx, false))) } } else - copy.Apply(tree, tapply, transformTrees(vargs, mkContext(ctx, false))) + defaultTree case TypeApply(fun, args) => super.transform(tree) @@ -267,15 +268,17 @@ abstract class TailCalls extends Transform copy.Apply(tree, fun, transformTrees(args)) case Apply(fun, args) => + lazy val defaultTree = copy.Apply(tree, fun, transformTrees(args, mkContext(ctx, false))) if (ctx.currentMethod.isFinal && ctx.tailPos && isRecursiveCall(fun)) { fun match { - case Select(receiver, _) => rewriteTailCall(fun, receiver :: transformTrees(args, mkContext(ctx, false))) + case Select(receiver, _) => if (!forMSIL) rewriteTailCall(fun, receiver :: transformTrees(args, mkContext(ctx, false))) else defaultTree case _ => rewriteTailCall(fun, This(currentClass) :: transformTrees(args, mkContext(ctx, false))) } } else - copy.Apply(tree, fun, transformTrees(args, mkContext(ctx, false))) + defaultTree + case Super(qual, mix) => tree |