diff options
author | Tiark Rompf <tiark.rompf@epfl.ch> | 2010-03-16 08:19:59 +0000 |
---|---|---|
committer | Tiark Rompf <tiark.rompf@epfl.ch> | 2010-03-16 08:19:59 +0000 |
commit | 166c496d5784d0be3611b270d9eba39f1273e048 (patch) | |
tree | bb52feed6cd9221f98d36edf9da303336098ad89 /src/continuations | |
parent | 324eeff963585af9248885473beeabfe8770ed68 (diff) | |
download | scala-166c496d5784d0be3611b270d9eba39f1273e048.tar.gz scala-166c496d5784d0be3611b270d9eba39f1273e048.tar.bz2 scala-166c496d5784d0be3611b270d9eba39f1273e048.zip |
added support for continuations in try/catch bl...
added support for continuations in try/catch blocks. review by
community.
Diffstat (limited to 'src/continuations')
5 files changed, 126 insertions, 24 deletions
diff --git a/src/continuations/library/scala/util/continuations/ControlContext.scala b/src/continuations/library/scala/util/continuations/ControlContext.scala index 709d2d2b02..9ed83a7e71 100644 --- a/src/continuations/library/scala/util/continuations/ControlContext.scala +++ b/src/continuations/library/scala/util/continuations/ControlContext.scala @@ -14,21 +14,39 @@ private class cpsMinus extends Annotation // implementation detail -@serializable final class ControlContext[+A,-B,+C](val fun: (A => B) => C, val x: A) { +@serializable final class ControlContext[+A,-B,+C](val fun: (A => B, Throwable => B) => C, val x: A) { - final def map[A1](f: (A => A1)): ControlContext[A1,B,C] = { - if (fun eq null) - new ControlContext(null, f(x)) - else + /* + final def map[A1](f: A => A1): ControlContext[A1,B,C] = { new ControlContext((k:(A1 => B)) => fun((x:A) => k(f(x))), null.asInstanceOf[A1]) - } + } - /* final def flatMap[A1,B1<:B](f: (A => ControlContext[A1,B1,B])): ControlContext[A1,B1,C] = { new ControlContext((k:(A1 => B1)) => fun((x:A) => f(x).fun(k))) } */ + + final def map[A1](f: A => A1): ControlContext[A1,B,C] = { + if (fun eq null) + new ControlContext(null, f(x)) + else + new ControlContext({ (k: A1 => B, thr: Throwable => B) => + fun( { (x:A) => + var done = false + try { + val res = f(x) + done = true + k(res) + } catch { + case ex if !done => + thr(ex) + } + }, thr) + }, null.asInstanceOf[A1]) + } + + // it would be nice if @inline would turn the trivial path into a tail call. // unfortunately it doesn't, so we do it ourselves in SelectiveCPSTransform @@ -36,25 +54,62 @@ private class cpsMinus extends Annotation // implementation detail if (fun eq null) f(x).asInstanceOf[ControlContext[A1,B1,C]] else - new ControlContext({ k:(A1 => B1) => - fun { (x:A) => - val ctxR = f(x) - val res: C1 = ctxR.foreach(k) - res - } + new ControlContext({ (k: A1 => B1, thr: Throwable => B1) => + fun( { (x:A) => + var done = false + try { + val ctxR = f(x) + done = true + val res: C1 = ctxR.foreachFull(k, thr) // => B1 + res + } catch { + case ex if !done => + thr(ex).asInstanceOf[B] // => B NOTE: in general this is unsafe! + } // However, the plugin will not generate offending code + }, thr.asInstanceOf[Throwable=>B]) // => B }, null.asInstanceOf[A1]) } - final def foreach(f: (A => B)) = { + final def foreach(f: A => B) = foreachFull(f, throw _) + + def foreachFull(f: A => B, g: Throwable => B): C = { if (fun eq null) f(x).asInstanceOf[C] else - fun(f) + fun(f, g) } + final def isTrivial = fun eq null final def getTrivialValue = x.asInstanceOf[A] // need filter or other functions? + final def flatCat[A1>:A,B1<:B,C1>:C<:B1](pf: PartialFunction[Throwable, ControlContext[A1,B1,C1]]): ControlContext[A1,B1,C1] = { + if (fun eq null) + this + else { + val fun1 = (ret1: A1 => B1, thr1: Throwable => B1) => { + val thr: Throwable => B1 = { t: Throwable => + var captureExceptions = true + try { + if (pf.isDefinedAt(t)) { + val cc1 = pf(t) + captureExceptions = false + cc1.foreachFull(ret1, thr1) // Throw => B + } else { + captureExceptions = false + thr1(t) // Throw => B1 + } + } catch { + case t1 if captureExceptions => thr1(t1) // => E2 + } + } + foreachFull(ret1, thr)// fun(ret1, thr) // => B + } + new ControlContext(fun1, null.asInstanceOf[A1]) + } + } + + // TODO: finally } diff --git a/src/continuations/library/scala/util/continuations/package.scala b/src/continuations/library/scala/util/continuations/package.scala index 0d924565c1..1fa89da2b4 100644 --- a/src/continuations/library/scala/util/continuations/package.scala +++ b/src/continuations/library/scala/util/continuations/package.scala @@ -55,7 +55,7 @@ package object continuations { } def shiftR[A,B,C](fun: (A => B) => C): ControlContext[A,B,C] = { - new ControlContext(fun, null.asInstanceOf[A]) + new ControlContext((f:A=>B,g:Throwable=>B) => fun(f), null.asInstanceOf[A]) } def reifyR[A,B,C](ctx: => ControlContext[A,B,C]): ControlContext[A,B,C] = { diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index dc7c297b92..14135a24ae 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -422,7 +422,20 @@ abstract class CPSAnnotationChecker extends CPSUtils { case Match(select, cases) => - transChildrenInOrder(tree, tpe, List(select), cases:::cases map { case CaseDef(_, _, body) => body }) + // TODO: can there be cases that are not CaseDefs?? check partialMap vs map! + transChildrenInOrder(tree, tpe, List(select), cases:::(cases partialMap { case CaseDef(_, _, body) => body })) + + case Try(block, catches, finalizer) => + val tpe1 = transChildrenInOrder(tree, tpe, Nil, block::catches:::(catches partialMap { case CaseDef(_, _, body) => body })) + + val annots = filterAttribs(tpe1, MarkerCPSTypes) + if (annots.nonEmpty) { + val ann = single(annots) + val atp0::atp1::Nil = ann.atp.normalize.typeArgs + if (!(atp0 =:= atp1)) + throw new TypeError("only simple cps types allowed in try/catch blocks (found: " + tpe1 + ")") + } + tpe1 case Block(stms, expr) => // if any stm has annotation, so does block diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 2b6ce67ef4..a09772d236 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -206,17 +206,16 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with case Try(block, catches, finalizer) => - // no cps code allowed in try/catch/finally! - val blockVal = transExpr(block, None, None) + val blockVal = transExpr(block, cpsA, cpsR) val catchVals = for { cd @ CaseDef(pat, guard, body) <- catches - val bodyVal = transExpr(body, None, None) + val bodyVal = transExpr(body, cpsA, cpsR) } yield { treeCopy.CaseDef(cd, transform(pat), transform(guard), bodyVal) } - val finallyVal = transExpr(finalizer, None, None) + val finallyVal = transExpr(finalizer, None, None) // for now, no cps in finally (Nil, updateSynthFlag(treeCopy.Try(tree, blockVal, catchVals, finallyVal)), cpsA) @@ -297,7 +296,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } else if (!cpsR.isDefined && bot.isDefined) { // error! log("cps type error: " + expr) - println("cps type error: " + expr + "/" + expr.tpe + "/" + getAnswerTypeAnn(expr.tpe)) + //println("cps type error: " + expr + "/" + expr.tpe + "/" + getAnswerTypeAnn(expr.tpe)) println(cpsR + "/" + spc + "/" + bot) @@ -306,7 +305,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // all is well if (expr.tpe.hasAnnotation(MarkerCPSAdaptPlus)) { - unit.warning(tree.pos, "expression " + expr + "/" + expr.tpe + " should not have cps-plus annotation") + unit.warning(tree.pos, "expression " + expr + " of type " + expr.tpe + " is not expected to have a cps type") expr.setType(removeAllCPSAnnotations(expr.tpe)) } diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index b906c12568..7f1610e8cc 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -116,7 +116,42 @@ abstract class SelectiveCPSTransform extends PluginComponent with ).setType(transformCPSType(tree.tpe)) } - case Block(stms, expr) => + case Try(block, catches, finalizer) => + // currently duplicates the catch block into a partial function + // this is kinda risky, but we don't expect there will be lots + // of try/catches inside catch blocks (exp. blowup) + + val block1 = transform(block) + val catches1 = transformCaseDefs(catches) + val finalizer1 = transform(finalizer) + + if (block1.tpe.typeSymbol.tpe <:< Context.tpe) { + //println("CPS Transform: " + tree) + + val pos = catches.head.pos + + val (stms, expr) = block1 match { + case Block(stms, expr) => (stms, expr) + case expr => (Nil, expr) + } + + val arg = currentOwner.newValueParameter(pos, "$ex").setInfo(ThrowableClass.tpe) + val catches2 = catches1 map (duplicateTree(_).asInstanceOf[CaseDef]) + val rhs = Match(Ident(arg), catches2) + val fun = Function(List(ValDef(arg)), rhs) + val expr2 = localTyper.typed(atPos(pos) { Apply(Select(expr, expr.tpe.member("flatCat")), List(fun)) }) + val block2 = treeCopy.Block(block1, stms, expr2) + + arg.owner = fun.symbol + val chown = new ChangeOwnerTraverser(currentOwner, fun.symbol) + chown.traverse(rhs) + + treeCopy.Try(tree, block2, catches1, finalizer1) + } else { + treeCopy.Try(tree, block1, catches1, finalizer1) + } + + case Block(stms, expr) => val (stms1, expr1) = transBlock(stms, expr) treeCopy.Block(tree, stms1, expr1) |