diff options
author | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-09-28 03:25:19 -0700 |
---|---|---|
committer | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-09-28 03:25:19 -0700 |
commit | 0614d2f512ad7b1b3885f81d9e6e779f447a6511 (patch) | |
tree | b515ad4420c96c709103e588e1e4c4c611a4297a /src/continuations | |
parent | 6b630ebb57306572e176a646e5bec7f3132ff18e (diff) | |
parent | cd14c3087ceca8145c3013c2751648b936f1256d (diff) | |
download | scala-0614d2f512ad7b1b3885f81d9e6e779f447a6511.tar.gz scala-0614d2f512ad7b1b3885f81d9e6e779f447a6511.tar.bz2 scala-0614d2f512ad7b1b3885f81d9e6e779f447a6511.zip |
Merge pull request #994 from phaller/issue/5314
SI-5314 - CPS transform of return statement fails (resubmission of #987)
Diffstat (limited to 'src/continuations')
-rw-r--r-- | src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala | 61 | ||||
-rw-r--r-- | src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala | 91 |
2 files changed, 124 insertions, 28 deletions
diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index a20ff1667b..b373b3d0de 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -150,10 +150,8 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { if ((mode & global.analyzer.EXPRmode) != 0) { if ((annots1 corresponds annots2)(_.atp <:< _.atp)) { vprintln("already same, can't adapt further") - return false - } - - if (annots1.isEmpty && !annots2.isEmpty && ((mode & global.analyzer.BYVALmode) == 0)) { + false + } else if (annots1.isEmpty && !annots2.isEmpty && ((mode & global.analyzer.BYVALmode) == 0)) { //println("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) if (!hasPlusMarker(tree.tpe)) { // val base = tree.tpe <:< removeAllCPSAnnotations(pt) @@ -163,17 +161,26 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { // TBD: use same or not? //if (same) { vprintln("yes we can!! (unit)") - return true + true //} - } - } else if (!annots1.isEmpty && ((mode & global.analyzer.BYVALmode) != 0)) { - if (!hasMinusMarker(tree.tpe)) { + } else false + } else if (!hasPlusMarker(tree.tpe) && annots1.isEmpty && !annots2.isEmpty && ((mode & global.analyzer.RETmode) != 0)) { + vprintln("checking enclosing method's result type without annotations") + tree.tpe <:< pt.withoutAnnotations + } else if (!hasMinusMarker(tree.tpe) && !annots1.isEmpty && ((mode & global.analyzer.BYVALmode) != 0)) { + val optCpsTypes: Option[(Type, Type)] = cpsParamTypes(tree.tpe) + val optExpectedCpsTypes: Option[(Type, Type)] = cpsParamTypes(pt) + if (optCpsTypes.isEmpty || optExpectedCpsTypes.isEmpty) { vprintln("yes we can!! (byval)") - return true + true + } else { // check cps param types + val cpsTpes = optCpsTypes.get + val cpsPts = optExpectedCpsTypes.get + // class cpsParam[-B,+C], therefore: + cpsPts._1 <:< cpsTpes._1 && cpsTpes._2 <:< cpsPts._2 } - } - } - false + } else false + } else false } override def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = { @@ -184,6 +191,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { val patMode = (mode & global.analyzer.PATTERNmode) != 0 val exprMode = (mode & global.analyzer.EXPRmode) != 0 val byValMode = (mode & global.analyzer.BYVALmode) != 0 + val retMode = (mode & global.analyzer.RETmode) != 0 val annotsTree = cpsParamAnnotation(tree.tpe) val annotsExpected = cpsParamAnnotation(pt) @@ -210,9 +218,38 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { val res = tree modifyType addMinusMarker vprintln("adapted annotations (by val) of " + tree + " to " + res.tpe) res + } else if (retMode && !hasPlusMarker(tree.tpe) && annotsTree.isEmpty && annotsExpected.nonEmpty) { + // add a marker annotation that will make tree.tpe behave as pt, subtyping wise + // tree will look like having any possible annotation + + // note 1: we are only adding a plus marker if the method's result type is a cps type + // (annotsExpected.nonEmpty == cpsParamAnnotation(pt).nonEmpty) + // note 2: we are not adding the expected cps annotations, since they will be added + // by adaptTypeOfReturn (see below). + val res = tree modifyType (_ withAnnotations List(newPlusMarker())) + vprintln("adapted annotations (return) of " + tree + " to " + res.tpe) + res } else tree } + /** Returns an adapted type for a return expression if the method's result type (pt) is a CPS type. + * Otherwise, it returns the `default` type (`typedReturn` passes `NothingClass.tpe`). + * + * A return expression in a method that has a CPS result type is an error unless the return + * is in tail position. Therefore, we are making sure that only the types of return expressions + * are adapted which will either be removed, or lead to an error. + */ + override def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = { + // only adapt if method's result type (pt) is cps type + val annots = cpsParamAnnotation(pt) + if (annots.nonEmpty) { + // return type of `tree` without plus marker, but only if it doesn't have other cps annots + if (hasPlusMarker(tree.tpe) && !hasCpsParamTypes(tree.tpe)) + tree.setType(removeAttribs(tree.tpe, MarkerCPSAdaptPlus)) + tree.tpe + } else default + } + def updateAttributesFromChildren(tpe: Type, childAnnots: List[AnnotationInfo], byName: List[Tree]): Type = { tpe match { // Would need to push annots into each alternative of overloaded type diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 51760d2807..ba87cadfeb 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -32,6 +32,55 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with implicit val _unit = unit // allow code in CPSUtils.scala to report errors var cpsAllowed: Boolean = false // detect cps code in places we do not handle (yet) + object RemoveTailReturnsTransformer extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Block(stms, r @ Return(expr)) => + treeCopy.Block(tree, stms, expr) + + case Block(stms, expr) => + treeCopy.Block(tree, stms, transform(expr)) + + case If(cond, r1 @ Return(thenExpr), r2 @ Return(elseExpr)) => + treeCopy.If(tree, cond, transform(thenExpr), transform(elseExpr)) + + case If(cond, r1 @ Return(thenExpr), elseExpr) => + treeCopy.If(tree, cond, transform(thenExpr), transform(elseExpr)) + + case If(cond, thenExpr, r2 @ Return(elseExpr)) => + treeCopy.If(tree, cond, transform(thenExpr), transform(elseExpr)) + + case If(cond, thenExpr, elseExpr) => + treeCopy.If(tree, cond, transform(thenExpr), transform(elseExpr)) + + case Try(block, catches, finalizer) => + treeCopy.Try(tree, + transform(block), + (catches map (t => transform(t))).asInstanceOf[List[CaseDef]], + transform(finalizer)) + + case CaseDef(pat, guard, r @ Return(expr)) => + treeCopy.CaseDef(tree, pat, guard, expr) + + case CaseDef(pat, guard, body) => + treeCopy.CaseDef(tree, pat, guard, transform(body)) + + case Return(_) => + unit.error(tree.pos, "return expressions in CPS code must be in tail position") + tree + + case _ => + super.transform(tree) + } + } + + def removeTailReturns(body: Tree): Tree = { + // support body with single return expression + body match { + case Return(expr) => expr + case _ => RemoveTailReturnsTransformer.transform(body) + } + } + override def transform(tree: Tree): Tree = { if (!cpsEnabled) return tree @@ -46,11 +95,14 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // this would cause infinite recursion. But we could remove the // ValDef case here. - case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs0) => debuglog("transforming " + dd.symbol) atOwner(dd.symbol) { - val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe)) + val rhs = + if (cpsParamTypes(tpt.tpe).nonEmpty) removeTailReturns(rhs0) + else rhs0 + val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe))(getExternalAnswerTypeAnn(tpt.tpe).isDefined) debuglog("result "+rhs1) debuglog("result is of type "+rhs1.tpe) @@ -75,6 +127,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with val ext = getExternalAnswerTypeAnn(body.tpe) val pureBody = getAnswerTypeAnn(body.tpe).isEmpty + implicit val isParentImpure = ext.isDefined def transformPureMatch(tree: Tree, selector: Tree, cases: List[CaseDef]) = { val caseVals = cases map { case cd @ CaseDef(pat, guard, body) => @@ -154,8 +207,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } - def transExpr(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo): Tree = { - transTailValue(tree, cpsA, cpsR) match { + def transExpr(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo)(implicit isAnyParentImpure: Boolean = false): Tree = { + transTailValue(tree, cpsA, cpsR)(cpsR.isDefined || isAnyParentImpure) match { case (Nil, b) => b case (a, b) => treeCopy.Block(tree, a,b) @@ -163,7 +216,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } - def transArgList(fun: Tree, args: List[Tree], cpsA: CPSInfo): (List[List[Tree]], List[Tree], CPSInfo) = { + def transArgList(fun: Tree, args: List[Tree], cpsA: CPSInfo)(implicit isAnyParentImpure: Boolean): (List[List[Tree]], List[Tree], CPSInfo) = { val formals = fun.tpe.paramTypes val overshoot = args.length - formals.length @@ -172,7 +225,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with val (stm,expr) = (for ((a,tp) <- args.zip(formals ::: List.fill(overshoot)(NoType))) yield { tp match { case TypeRef(_, ByNameParamClass, List(elemtp)) => - (Nil, transExpr(a, None, getAnswerTypeAnn(elemtp))) + // note that we're not passing just isAnyParentImpure + (Nil, transExpr(a, None, getAnswerTypeAnn(elemtp))(getAnswerTypeAnn(elemtp).isDefined || isAnyParentImpure)) case _ => val (valStm, valExpr, valSpc) = transInlineValue(a, spc) spc = valSpc @@ -184,7 +238,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } - def transValue(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo): (List[Tree], Tree, CPSInfo) = { + // precondition: cpsR.isDefined "implies" isAnyParentImpure + def transValue(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo)(implicit isAnyParentImpure: Boolean): (List[Tree], Tree, CPSInfo) = { // return value: (stms, expr, spc), where spc is CPSInfo after stms but *before* expr implicit val pos = tree.pos tree match { @@ -192,7 +247,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with val (cpsA2, cpsR2) = (cpsA, linearize(cpsA, getAnswerTypeAnn(tree.tpe))) // tbd // val (cpsA2, cpsR2) = (None, getAnswerTypeAnn(tree.tpe)) - val (a, b) = transBlock(stms, expr, cpsA2, cpsR2) + val (a, b) = transBlock(stms, expr, cpsA2, cpsR2)(cpsR2.isDefined || isAnyParentImpure) val tree1 = (treeCopy.Block(tree, a, b)) // no updateSynthFlag here!!! (Nil, tree1, cpsA) @@ -206,8 +261,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with val (cpsA2, cpsR2) = if (hasSynthMarker(tree.tpe)) (spc, linearize(spc, getAnswerTypeAnn(tree.tpe))) else (None, getAnswerTypeAnn(tree.tpe)) // if no cps in condition, branches must conform to tree.tpe directly - val thenVal = transExpr(thenp, cpsA2, cpsR2) - val elseVal = transExpr(elsep, cpsA2, cpsR2) + val thenVal = transExpr(thenp, cpsA2, cpsR2)(cpsR2.isDefined || isAnyParentImpure) + val elseVal = transExpr(elsep, cpsA2, cpsR2)(cpsR2.isDefined || isAnyParentImpure) // check that then and else parts agree (not necessary any more, but left as sanity check) if (cpsR.isDefined) { @@ -227,7 +282,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with else (None, getAnswerTypeAnn(tree.tpe)) val caseVals = cases map { case cd @ CaseDef(pat, guard, body) => - val bodyVal = transExpr(body, cpsA2, cpsR2) + val bodyVal = transExpr(body, cpsA2, cpsR2)(cpsR2.isDefined || isAnyParentImpure) treeCopy.CaseDef(cd, transform(pat), transform(guard), bodyVal) } @@ -245,7 +300,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // currentOwner.newMethod(name, tree.pos, Flags.SYNTHETIC) setInfo ldef.symbol.info val sym = ldef.symbol resetFlag Flags.LABEL val rhs1 = rhs //new TreeSymSubstituter(List(ldef.symbol), List(sym)).transform(rhs) - val rhsVal = transExpr(rhs1, None, getAnswerTypeAnn(tree.tpe)) changeOwner (currentOwner -> sym) + val rhsVal = transExpr(rhs1, None, getAnswerTypeAnn(tree.tpe))(getAnswerTypeAnn(tree.tpe).isDefined || isAnyParentImpure) changeOwner (currentOwner -> sym) val stm1 = localTyper.typed(DefDef(sym, rhsVal)) // since virtpatmat does not rely on fall-through, don't call the labels it emits @@ -284,6 +339,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with (stms, updateSynthFlag(treeCopy.Assign(tree, transform(lhs), expr)), spc) case Return(expr0) => + if (isAnyParentImpure) + unit.error(tree.pos, "return expression not allowed, since method calls CPS method") val (stms, expr, spc) = transInlineValue(expr0, cpsA) (stms, updateSynthFlag(treeCopy.Return(tree, expr)), spc) @@ -321,7 +378,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } } - def transTailValue(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo): (List[Tree], Tree) = { + // precondition: cpsR.isDefined "implies" isAnyParentImpure + def transTailValue(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo)(implicit isAnyParentImpure: Boolean): (List[Tree], Tree) = { val (stms, expr, spc) = transValue(tree, cpsA, cpsR) @@ -398,7 +456,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with (stms, expr) } - def transInlineValue(tree: Tree, cpsA: CPSInfo): (List[Tree], Tree, CPSInfo) = { + def transInlineValue(tree: Tree, cpsA: CPSInfo)(implicit isAnyParentImpure: Boolean): (List[Tree], Tree, CPSInfo) = { val (stms, expr, spc) = transValue(tree, cpsA, None) // never required to be cps @@ -425,7 +483,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with - def transInlineStm(stm: Tree, cpsA: CPSInfo): (List[Tree], CPSInfo) = { + def transInlineStm(stm: Tree, cpsA: CPSInfo)(implicit isAnyParentImpure: Boolean): (List[Tree], CPSInfo) = { stm match { // TODO: what about DefDefs? @@ -455,7 +513,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } } - def transBlock(stms: List[Tree], expr: Tree, cpsA: CPSInfo, cpsR: CPSInfo): (List[Tree], Tree) = { + // precondition: cpsR.isDefined "implies" isAnyParentImpure + def transBlock(stms: List[Tree], expr: Tree, cpsA: CPSInfo, cpsR: CPSInfo)(implicit isAnyParentImpure: Boolean): (List[Tree], Tree) = { def rec(currStats: List[Tree], currAns: CPSInfo, accum: List[Tree]): (List[Tree], Tree) = currStats match { case Nil => |