From 75e36233a0ea290cee98a35bc295feed4b18237e Mon Sep 17 00:00:00 2001 From: phaller Date: Mon, 23 Jul 2012 14:03:22 +0200 Subject: SI-5314 - CPS transform of return statement fails Enable return expressions in CPS code if they are in tail position. Note that tail returns are only removed in methods that do not call `shift` or `reset` (otherwise, an error is reported). Addresses the issues pointed out in a previous pull request: https://github.com/scala/scala/pull/720 - Addresses all issues mentioned here: https://github.com/scala/scala/pull/720#issuecomment-6429705 - Move transformation methods to SelectiveANFTransform.scala: https://github.com/scala/scala/pull/720#commitcomment-1477497 - Do not keep a list of tail returns. Tests: - continuations-neg/t5314-missing-result-type.scala - continuations-neg/t5314-type-error.scala - continuations-neg/t5314-npe.scala - continuations-neg/t5314-return-reset.scala - continuations-run/t5314.scala - continuations-run/t5314-2.scala - continuations-run/t5314-3.scala --- .../scala/tools/nsc/typechecker/Modes.scala | 6 +- .../scala/tools/nsc/typechecker/Typers.scala | 3 +- .../tools/selectivecps/CPSAnnotationChecker.scala | 45 ++++++++--- .../plugin/scala/tools/selectivecps/CPSUtils.scala | 7 ++ .../tools/selectivecps/SelectiveANFTransform.scala | 89 +++++++++++++++++++++- 5 files changed, 135 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Modes.scala b/src/compiler/scala/tools/nsc/typechecker/Modes.scala index e9ea99faab..d942d080cb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Modes.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Modes.scala @@ -86,6 +86,10 @@ trait Modes { */ final val TYPEPATmode = 0x10000 + /** RETmode is set when we are typing a return expression. + */ + final val RETmode = 0x20000 + final private val StickyModes = EXPRmode | PATTERNmode | TYPEmode | ALTmode final def onlyStickyModes(mode: Int) = @@ -133,4 +137,4 @@ trait Modes { def modeString(mode: Int): String = if (mode == 0) "NOmode" else (modeNameMap filterKeys (bit => inAllModes(mode, bit))).values mkString " " -} \ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a6d7424837..21bc890a20 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4087,7 +4087,8 @@ trait Typers extends Modes with Adaptations with Tags { ReturnWithoutTypeError(tree, enclMethod.owner) } else { context.enclMethod.returnsSeen = true - val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe) + val expr1: Tree = typed(expr, EXPRmode | BYVALmode | RETmode, restpt.tpe) + // Warn about returning a value if no value can be returned. if (restpt.tpe.typeSymbol == UnitClass) { // The typing in expr1 says expr is Unit (it has already been coerced if diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index a20ff1667b..dbbc428428 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,6 +218,12 @@ 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 no annotation + val res = tree modifyType (_ withAnnotations List(newPlusMarker())) + vprintln("adapted annotations (return) of " + tree + " to " + res.tpe) + res } else tree } @@ -466,6 +480,13 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } tpe + case ret @ Return(expr) => + // only change type if this return will (a) be removed (in tail position) or (b) cause + // an error (not in tail position) + if (expr.tpe != null && hasPlusMarker(expr.tpe)) + ret setType expr.tpe + ret.tpe + case _ => tpe } diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 46c644bcd6..db7ef4b540 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -27,6 +27,8 @@ trait CPSUtils { val shiftUnit0 = newTermName("shiftUnit0") val shiftUnit = newTermName("shiftUnit") val shiftUnitR = newTermName("shiftUnitR") + val reset = newTermName("reset") + val reset0 = newTermName("reset0") } lazy val MarkerCPSSym = rootMirror.getRequiredClass("scala.util.continuations.cpsSym") @@ -45,10 +47,15 @@ trait CPSUtils { lazy val MethShiftR = definitions.getMember(ModCPS, cpsNames.shiftR) lazy val MethReify = definitions.getMember(ModCPS, cpsNames.reify) lazy val MethReifyR = definitions.getMember(ModCPS, cpsNames.reifyR) + lazy val MethReset = definitions.getMember(ModCPS, cpsNames.reset) + lazy val MethReset0 = definitions.getMember(ModCPS, cpsNames.reset0) lazy val allCPSAnnotations = List(MarkerCPSSym, MarkerCPSTypes, MarkerCPSSynth, MarkerCPSAdaptPlus, MarkerCPSAdaptMinus) + lazy val allCPSMethods = List(MethShiftUnit, MethShiftUnit0, MethShiftUnitR, MethShift, MethShiftR, + MethReify, MethReifyR, MethReset, MethReset0) + // TODO - needed? Can these all use the same annotation info? protected def newSynthMarker() = newMarker(MarkerCPSSynth) protected def newPlusMarker() = newMarker(MarkerCPSAdaptPlus) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 51760d2807..ed313342ec 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -32,6 +32,84 @@ 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) + /* Does not attempt to remove tail returns. + * Only checks whether the method contains returns as well as calls to CPS methods. + */ + class CheckCPSMethodsTraverser extends Traverser { + var cpsMethodsSeen = false + var returnsSeen: Option[Tree] = None + override def traverse(tree: Tree): Unit = tree match { + case Ident(_) | Select(_, _) => + if (tree.hasSymbol && (allCPSMethods contains tree.symbol)) + cpsMethodsSeen = true + super.traverse(tree) + case Return(_) => + returnsSeen = Some(tree) + case _ => + super.traverse(tree) + } + } + + /* Also checks whether the method calls a CPS method in which case an error is produced + */ + class RemoveTailReturnsTransformer extends Transformer { + var cpsMethodsSeen = false + var returnsSeen: Option[Tree] = None + override def transform(tree: Tree): Tree = tree match { + case Block(stms, r @ Return(expr)) => + returnsSeen = Some(r) + treeCopy.Block(tree, stms, expr) + + case Block(stms, expr) => + treeCopy.Block(tree, stms, transform(expr)) + + case If(cond, r1 @ Return(thenExpr), r2 @ Return(elseExpr)) => + returnsSeen = Some(r1) + 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)) => + returnsSeen = Some(r) + 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 Ident(_) | Select(_, _) => + if (tree.hasSymbol && (allCPSMethods contains tree.symbol)) + cpsMethodsSeen = true + super.transform(tree) + + case _ => + super.transform(tree) + } + } + + def removeTailReturns(body: Tree): Tree = { + // support body with single return expression + body match { + case Return(expr) => expr + case _ => + val tr = new RemoveTailReturnsTransformer + val res = tr.transform(body) + if (tr.returnsSeen.nonEmpty && tr.cpsMethodsSeen) + unit.error(tr.returnsSeen.get.pos, "return expressions not allowed, since method calls CPS method") + res + } + } + override def transform(tree: Tree): Tree = { if (!cpsEnabled) return tree @@ -46,10 +124,19 @@ 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 rhs = + if (cpsParamTypes(tpt.tpe).nonEmpty) removeTailReturns(rhs0) + else { + val checker = new CheckCPSMethodsTraverser + checker.traverse(rhs0) + if (checker.returnsSeen.nonEmpty && checker.cpsMethodsSeen) + unit.error(checker.returnsSeen.get.pos, "return expressions not allowed, since method calls CPS method") + rhs0 + } val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe)) debuglog("result "+rhs1) -- cgit v1.2.3 From b9c3a3b083833d2166b3d2ca2d1ccbca36b83c71 Mon Sep 17 00:00:00 2001 From: phaller Date: Wed, 8 Aug 2012 16:20:07 +0200 Subject: Replace CheckCPSMethodTraverser with additional parameter on transformer methods Other fixes: - remove CPSUtils.allCPSMethods - add clarifying comment about adding a plus marker to a return expression --- .../scala/tools/nsc/typechecker/Typers.scala | 1 - .../tools/selectivecps/CPSAnnotationChecker.scala | 3 + .../plugin/scala/tools/selectivecps/CPSUtils.scala | 7 -- .../tools/selectivecps/SelectiveANFTransform.scala | 84 +++++++--------------- .../continuations-neg/t5314-return-reset.check | 2 +- 5 files changed, 29 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 21bc890a20..79c1e660f0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4088,7 +4088,6 @@ trait Typers extends Modes with Adaptations with Tags { } else { context.enclMethod.returnsSeen = true val expr1: Tree = typed(expr, EXPRmode | BYVALmode | RETmode, restpt.tpe) - // Warn about returning a value if no value can be returned. if (restpt.tpe.typeSymbol == UnitClass) { // The typing in expr1 says expr is Unit (it has already been coerced if diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index dbbc428428..122fe32ed3 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -221,6 +221,9 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } 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 no annotation + + // note that we are only adding a plus marker if the method's result type is a CPS type + // (annotsExpected.nonEmpty == cpsParamAnnotation(pt).nonEmpty) val res = tree modifyType (_ withAnnotations List(newPlusMarker())) vprintln("adapted annotations (return) of " + tree + " to " + res.tpe) res diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index db7ef4b540..46c644bcd6 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -27,8 +27,6 @@ trait CPSUtils { val shiftUnit0 = newTermName("shiftUnit0") val shiftUnit = newTermName("shiftUnit") val shiftUnitR = newTermName("shiftUnitR") - val reset = newTermName("reset") - val reset0 = newTermName("reset0") } lazy val MarkerCPSSym = rootMirror.getRequiredClass("scala.util.continuations.cpsSym") @@ -47,15 +45,10 @@ trait CPSUtils { lazy val MethShiftR = definitions.getMember(ModCPS, cpsNames.shiftR) lazy val MethReify = definitions.getMember(ModCPS, cpsNames.reify) lazy val MethReifyR = definitions.getMember(ModCPS, cpsNames.reifyR) - lazy val MethReset = definitions.getMember(ModCPS, cpsNames.reset) - lazy val MethReset0 = definitions.getMember(ModCPS, cpsNames.reset0) lazy val allCPSAnnotations = List(MarkerCPSSym, MarkerCPSTypes, MarkerCPSSynth, MarkerCPSAdaptPlus, MarkerCPSAdaptMinus) - lazy val allCPSMethods = List(MethShiftUnit, MethShiftUnit0, MethShiftUnitR, MethShift, MethShiftR, - MethReify, MethReifyR, MethReset, MethReset0) - // TODO - needed? Can these all use the same annotation info? protected def newSynthMarker() = newMarker(MarkerCPSSynth) protected def newPlusMarker() = newMarker(MarkerCPSAdaptPlus) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index ed313342ec..1783264e5c 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -32,39 +32,15 @@ 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) - /* Does not attempt to remove tail returns. - * Only checks whether the method contains returns as well as calls to CPS methods. - */ - class CheckCPSMethodsTraverser extends Traverser { - var cpsMethodsSeen = false - var returnsSeen: Option[Tree] = None - override def traverse(tree: Tree): Unit = tree match { - case Ident(_) | Select(_, _) => - if (tree.hasSymbol && (allCPSMethods contains tree.symbol)) - cpsMethodsSeen = true - super.traverse(tree) - case Return(_) => - returnsSeen = Some(tree) - case _ => - super.traverse(tree) - } - } - - /* Also checks whether the method calls a CPS method in which case an error is produced - */ - class RemoveTailReturnsTransformer extends Transformer { - var cpsMethodsSeen = false - var returnsSeen: Option[Tree] = None + object RemoveTailReturnsTransformer extends Transformer { override def transform(tree: Tree): Tree = tree match { case Block(stms, r @ Return(expr)) => - returnsSeen = Some(r) treeCopy.Block(tree, stms, expr) case Block(stms, expr) => treeCopy.Block(tree, stms, transform(expr)) case If(cond, r1 @ Return(thenExpr), r2 @ Return(elseExpr)) => - returnsSeen = Some(r1) treeCopy.If(tree, cond, transform(thenExpr), transform(elseExpr)) case If(cond, thenExpr, elseExpr) => @@ -77,7 +53,6 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with transform(finalizer)) case CaseDef(pat, guard, r @ Return(expr)) => - returnsSeen = Some(r) treeCopy.CaseDef(tree, pat, guard, expr) case CaseDef(pat, guard, body) => @@ -87,11 +62,6 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with unit.error(tree.pos, "return expressions in CPS code must be in tail position") tree - case Ident(_) | Select(_, _) => - if (tree.hasSymbol && (allCPSMethods contains tree.symbol)) - cpsMethodsSeen = true - super.transform(tree) - case _ => super.transform(tree) } @@ -101,12 +71,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // support body with single return expression body match { case Return(expr) => expr - case _ => - val tr = new RemoveTailReturnsTransformer - val res = tr.transform(body) - if (tr.returnsSeen.nonEmpty && tr.cpsMethodsSeen) - unit.error(tr.returnsSeen.get.pos, "return expressions not allowed, since method calls CPS method") - res + case _ => RemoveTailReturnsTransformer.transform(body) } } @@ -130,14 +95,8 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with atOwner(dd.symbol) { val rhs = if (cpsParamTypes(tpt.tpe).nonEmpty) removeTailReturns(rhs0) - else { - val checker = new CheckCPSMethodsTraverser - checker.traverse(rhs0) - if (checker.returnsSeen.nonEmpty && checker.cpsMethodsSeen) - unit.error(checker.returnsSeen.get.pos, "return expressions not allowed, since method calls CPS method") - rhs0 - } - val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe)) + else rhs0 + val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe))(getExternalAnswerTypeAnn(tpt.tpe).isDefined) debuglog("result "+rhs1) debuglog("result is of type "+rhs1.tpe) @@ -162,6 +121,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) => @@ -241,8 +201,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) @@ -250,7 +210,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 @@ -259,7 +219,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 @@ -271,7 +232,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 { @@ -279,7 +241,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) @@ -293,8 +255,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) { @@ -314,7 +276,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) } @@ -332,7 +294,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 @@ -371,6 +333,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) @@ -408,7 +372,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) @@ -485,7 +450,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 @@ -512,7 +477,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? @@ -542,7 +507,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 => diff --git a/test/files/continuations-neg/t5314-return-reset.check b/test/files/continuations-neg/t5314-return-reset.check index e288556546..4c817ae14d 100644 --- a/test/files/continuations-neg/t5314-return-reset.check +++ b/test/files/continuations-neg/t5314-return-reset.check @@ -1,4 +1,4 @@ -t5314-return-reset.scala:14: error: return expressions not allowed, since method calls CPS method +t5314-return-reset.scala:14: error: return expression not allowed, since method calls CPS method if (rnd.nextInt(100) > 50) return 5 // not allowed, since method is calling `reset` ^ one error found -- cgit v1.2.3 From 8d020fab9758ced93eb18fa51c906b95ec104aed Mon Sep 17 00:00:00 2001 From: phaller Date: Thu, 9 Aug 2012 14:50:30 +0200 Subject: Add missing cases in tail return transform Disabled warnings that no longer apply because of tail returns. Add several test cases. --- .../plugin/scala/tools/selectivecps/CPSUtils.scala | 9 ++------- .../scala/tools/selectivecps/SelectiveANFTransform.scala | 10 +++++++++- test/files/continuations-run/t5314.check | 4 ++++ test/files/continuations-run/t5314.scala | 11 +++++++++++ 4 files changed, 26 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 46c644bcd6..90c961f195 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -96,13 +96,8 @@ trait CPSUtils { // anf transform - def getExternalAnswerTypeAnn(tp: Type) = { - cpsParamTypes(tp) orElse { - if (hasPlusMarker(tp)) - global.warning("trying to instantiate type " + tp + " to unknown cps type") - None - } - } + def getExternalAnswerTypeAnn(tp: Type) = + cpsParamTypes(tp) orElse None def getAnswerTypeAnn(tp: Type): Option[(Type, Type)] = cpsParamTypes(tp) filterNot (_ => hasPlusMarker(tp)) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 1783264e5c..99838dfc25 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -43,6 +43,12 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with 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)) @@ -440,7 +446,9 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // all is well if (hasPlusMarker(expr.tpe)) { - unit.warning(tree.pos, "expression " + expr + " of type " + expr.tpe + " is not expected to have a cps type") + // the following warning no longer applies, since expr may have originated from a tail return expr + // note that it would be illegal to remove the plus marker (thus disabling required transformations) + //unit.warning(tree.pos, "expression " + expr + " of type " + expr.tpe + " is not expected to have a cps type") expr modifyType removeAllCPSAnnotations } diff --git a/test/files/continuations-run/t5314.check b/test/files/continuations-run/t5314.check index 4951e7caae..4b35d8e6d0 100644 --- a/test/files/continuations-run/t5314.check +++ b/test/files/continuations-run/t5314.check @@ -1,3 +1,7 @@ +7 +7 +7 +8 8 hi 8 diff --git a/test/files/continuations-run/t5314.scala b/test/files/continuations-run/t5314.scala index 0bdea19824..d611016ce4 100644 --- a/test/files/continuations-run/t5314.scala +++ b/test/files/continuations-run/t5314.scala @@ -29,6 +29,17 @@ object Test extends App { def nocps(x: Int): Int = { return x; x } + def foo2(x:Int): Int @cps[Int] = 7 + def bar2(x:Int): Int @cps[Int] = { foo2(x); return 7 } + def bar3(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else return foo2(x) } + def bar4(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else foo2(x) } + def bar5(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else 8 } + println(reset { bar2(10) }) + println(reset { bar3(10) }) + println(reset { bar4(10) }) + println(reset { bar5(10) }) + + /* original test case */ val repro = new ReturnRepro repro.caller repro.caller2 -- cgit v1.2.3 From 47519b4eb6b4aec78e3c13e08811c7346c61acc4 Mon Sep 17 00:00:00 2001 From: phaller Date: Thu, 9 Aug 2012 17:23:47 +0200 Subject: Revert "Add missing cases in tail return transform" This reverts commit 8d020fab9758ced93eb18fa51c906b95ec104aed. --- .../plugin/scala/tools/selectivecps/CPSUtils.scala | 9 +++++++-- .../scala/tools/selectivecps/SelectiveANFTransform.scala | 10 +--------- test/files/continuations-run/t5314.check | 4 ---- test/files/continuations-run/t5314.scala | 11 ----------- 4 files changed, 8 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 90c961f195..46c644bcd6 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -96,8 +96,13 @@ trait CPSUtils { // anf transform - def getExternalAnswerTypeAnn(tp: Type) = - cpsParamTypes(tp) orElse None + def getExternalAnswerTypeAnn(tp: Type) = { + cpsParamTypes(tp) orElse { + if (hasPlusMarker(tp)) + global.warning("trying to instantiate type " + tp + " to unknown cps type") + None + } + } def getAnswerTypeAnn(tp: Type): Option[(Type, Type)] = cpsParamTypes(tp) filterNot (_ => hasPlusMarker(tp)) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 99838dfc25..1783264e5c 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -43,12 +43,6 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with 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)) @@ -446,9 +440,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // all is well if (hasPlusMarker(expr.tpe)) { - // the following warning no longer applies, since expr may have originated from a tail return expr - // note that it would be illegal to remove the plus marker (thus disabling required transformations) - //unit.warning(tree.pos, "expression " + expr + " of type " + expr.tpe + " is not expected to have a cps type") + unit.warning(tree.pos, "expression " + expr + " of type " + expr.tpe + " is not expected to have a cps type") expr modifyType removeAllCPSAnnotations } diff --git a/test/files/continuations-run/t5314.check b/test/files/continuations-run/t5314.check index 4b35d8e6d0..4951e7caae 100644 --- a/test/files/continuations-run/t5314.check +++ b/test/files/continuations-run/t5314.check @@ -1,7 +1,3 @@ -7 -7 -7 -8 8 hi 8 diff --git a/test/files/continuations-run/t5314.scala b/test/files/continuations-run/t5314.scala index d611016ce4..0bdea19824 100644 --- a/test/files/continuations-run/t5314.scala +++ b/test/files/continuations-run/t5314.scala @@ -29,17 +29,6 @@ object Test extends App { def nocps(x: Int): Int = { return x; x } - def foo2(x:Int): Int @cps[Int] = 7 - def bar2(x:Int): Int @cps[Int] = { foo2(x); return 7 } - def bar3(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else return foo2(x) } - def bar4(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else foo2(x) } - def bar5(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else 8 } - println(reset { bar2(10) }) - println(reset { bar3(10) }) - println(reset { bar4(10) }) - println(reset { bar5(10) }) - - /* original test case */ val repro = new ReturnRepro repro.caller repro.caller2 -- cgit v1.2.3 From 834a8faa123f168e8baea772b06ebce2874ff431 Mon Sep 17 00:00:00 2001 From: phaller Date: Sun, 12 Aug 2012 17:17:54 +0200 Subject: Simplify the adaptation of types of return expressions Add `adaptTypeOfReturn` hook to `AnnotationCheckers`. Move adaptation of types of return expressions from `addAnnotations` to `typedReturn` via `adaptTypeOfReturn` hook. This resolves an inconsistency where previously types could have a plus marker without additional CPS annotations. This also adds additional test cases. --- .../scala/tools/nsc/typechecker/Typers.scala | 3 ++- .../tools/selectivecps/CPSAnnotationChecker.scala | 29 ++++++++++++++-------- .../tools/selectivecps/SelectiveANFTransform.scala | 6 +++++ .../reflect/internal/AnnotationCheckers.scala | 26 +++++++++++++++++++ test/files/continuations-run/t5314-with-if.check | 1 + test/files/continuations-run/t5314-with-if.scala | 17 +++++++++++++ test/files/continuations-run/t5314.check | 4 +++ test/files/continuations-run/t5314.scala | 11 ++++++++ 8 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 test/files/continuations-run/t5314-with-if.check create mode 100644 test/files/continuations-run/t5314-with-if.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 79c1e660f0..8ba2d9e0fd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4096,7 +4096,8 @@ trait Typers extends Modes with Adaptations with Tags { if (typed(expr).tpe.typeSymbol != UnitClass) unit.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded") } - treeCopy.Return(tree, checkDead(expr1)) setSymbol enclMethod.owner setType NothingClass.tpe + treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner) + .setType(adaptTypeOfReturn(expr1, restpt.tpe, NothingClass.tpe)) } } } diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 122fe32ed3..eb96f87e0e 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -220,16 +220,32 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { 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 no annotation + // tree will look like having any possible annotation - // note that we are only adding a plus marker if the method's result type is a CPS type - // (annotsExpected.nonEmpty == cpsParamAnnotation(pt).nonEmpty) + // 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 } + // only adapt type if this return will + // (a) be removed (in tail position and method's result type (pt) is cps type), or + // (b) cause 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 @@ -483,13 +499,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } tpe - case ret @ Return(expr) => - // only change type if this return will (a) be removed (in tail position) or (b) cause - // an error (not in tail position) - if (expr.tpe != null && hasPlusMarker(expr.tpe)) - ret setType expr.tpe - ret.tpe - case _ => tpe } diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 1783264e5c..ba87cadfeb 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -43,6 +43,12 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with 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)) diff --git a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala index 9f102c5712..05f80c8a0c 100644 --- a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala +++ b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala @@ -47,6 +47,13 @@ trait AnnotationCheckers { * before. If the implementing class cannot do the adaptiong, it * should return the tree unchanged.*/ def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree + + /** Adapt the type of a return expression. The decision of an annotation checker + * whether the type should be adapted is based on the type of the expression + * which is returned, as well as the result type of the method (pt). + * By default, this method simply returns the passed `default` type. + */ + def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = default } // Syncnote: Annotation checkers inaccessible to reflection, so no sync in var necessary. @@ -118,4 +125,23 @@ trait AnnotationCheckers { annotationCheckers.foldLeft(tree)((tree, checker) => checker.adaptAnnotations(tree, mode, pt)) } + + /** Let a registered annotation checker adapt the type of a return expression. + * Annotation checkers that cannot do the adaptation should simply return + * the `default` argument. + * + * Note that the result is undefined if more than one annotation checker + * returns an adapted type which is not a subtype of `default`. + */ + def adaptTypeOfReturn(tree: Tree, pt: Type, default: => Type): Type = { + val adaptedTypes = annotationCheckers flatMap { checker => + val adapted = checker.adaptTypeOfReturn(tree, pt, default) + if (!(adapted <:< default)) List(adapted) + else List() + } + adaptedTypes match { + case fst :: _ => fst + case List() => default + } + } } diff --git a/test/files/continuations-run/t5314-with-if.check b/test/files/continuations-run/t5314-with-if.check new file mode 100644 index 0000000000..7f8f011eb7 --- /dev/null +++ b/test/files/continuations-run/t5314-with-if.check @@ -0,0 +1 @@ +7 diff --git a/test/files/continuations-run/t5314-with-if.scala b/test/files/continuations-run/t5314-with-if.scala new file mode 100644 index 0000000000..5840199a3c --- /dev/null +++ b/test/files/continuations-run/t5314-with-if.scala @@ -0,0 +1,17 @@ +import scala.util.continuations._ + +object Test extends App { + + def foo(x:Int): Int @cps[Int] = 7 + + def bar(x:Int): Int @cps[Int] = { + val v = foo(x) + if (v > 0) + return v + else + return 10 + } + + println(reset { bar(10) }) + +} diff --git a/test/files/continuations-run/t5314.check b/test/files/continuations-run/t5314.check index 4951e7caae..4b35d8e6d0 100644 --- a/test/files/continuations-run/t5314.check +++ b/test/files/continuations-run/t5314.check @@ -1,3 +1,7 @@ +7 +7 +7 +8 8 hi 8 diff --git a/test/files/continuations-run/t5314.scala b/test/files/continuations-run/t5314.scala index 0bdea19824..d611016ce4 100644 --- a/test/files/continuations-run/t5314.scala +++ b/test/files/continuations-run/t5314.scala @@ -29,6 +29,17 @@ object Test extends App { def nocps(x: Int): Int = { return x; x } + def foo2(x:Int): Int @cps[Int] = 7 + def bar2(x:Int): Int @cps[Int] = { foo2(x); return 7 } + def bar3(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else return foo2(x) } + def bar4(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else foo2(x) } + def bar5(x:Int): Int @cps[Int] = { foo2(x); if (x == 7) return 7 else 8 } + println(reset { bar2(10) }) + println(reset { bar3(10) }) + println(reset { bar4(10) }) + println(reset { bar5(10) }) + + /* original test case */ val repro = new ReturnRepro repro.caller repro.caller2 -- cgit v1.2.3 From cd14c3087ceca8145c3013c2751648b936f1256d Mon Sep 17 00:00:00 2001 From: phaller Date: Mon, 20 Aug 2012 14:35:03 +0200 Subject: Improve doc comment on adaptTypeOfReturn in CPSAnnotationChecker --- .../plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index eb96f87e0e..b373b3d0de 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -232,9 +232,13 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } else tree } - // only adapt type if this return will - // (a) be removed (in tail position and method's result type (pt) is cps type), or - // (b) cause an error + /** 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) -- cgit v1.2.3