summaryrefslogtreecommitdiff
path: root/src/continuations/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/continuations/plugin')
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala520
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala137
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala545
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala56
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala383
-rw-r--r--src/continuations/plugin/scalac-plugin.xml5
6 files changed, 0 insertions, 1646 deletions
diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
deleted file mode 100644
index 3963447de3..0000000000
--- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
+++ /dev/null
@@ -1,520 +0,0 @@
-// $Id$
-
-package scala.tools.selectivecps
-
-import scala.tools.nsc.{ Global, Mode }
-import scala.tools.nsc.MissingRequirementError
-
-abstract class CPSAnnotationChecker extends CPSUtils {
- val global: Global
- import global._
- import analyzer.{AnalyzerPlugin, Typer}
- import definitions._
-
- //override val verbose = true
- @inline override final def vprintln(x: =>Any): Unit = if (verbose) println(x)
-
- /**
- * Checks whether @cps annotations conform
- */
- object checker extends AnnotationChecker {
- private[CPSAnnotationChecker] def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker()
- private[CPSAnnotationChecker] def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker()
-
- private[CPSAnnotationChecker] def cleanPlus(tp: Type) =
- removeAttribs(tp, MarkerCPSAdaptPlus, MarkerCPSTypes)
- private[CPSAnnotationChecker] def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) =
- cleanPlus(tp) withAnnotations newAnnots.toList
-
- /** Check annotations to decide whether tpe1 <:< tpe2 */
- def annotationsConform(tpe1: Type, tpe2: Type): Boolean = {
- if (!cpsEnabled) return true
-
- vprintln("check annotations: " + tpe1 + " <:< " + tpe2)
-
- // Nothing is least element, but Any is not the greatest
- if (tpe1.typeSymbol eq NothingClass)
- return true
-
- val annots1 = cpsParamAnnotation(tpe1)
- val annots2 = cpsParamAnnotation(tpe2)
-
- // @plus and @minus should only occur at the left, and never together
- // TODO: insert check
-
- // @minus @cps is the same as no annotations
- if (hasMinusMarker(tpe1))
- return annots2.isEmpty
-
- // to handle answer type modification, we must make @plus <:< @cps
- if (hasPlusMarker(tpe1) && annots1.isEmpty)
- return true
-
- // @plus @cps will fall through and compare the @cps type args
- // @cps parameters must match exactly
- if ((annots1 corresponds annots2)(_.atp <:< _.atp))
- return true
-
- // Need to handle uninstantiated type vars specially:
-
- // g map (x => x) with expected type List[Int] @cps
- // results in comparison ?That <:< List[Int] @cps
-
- // Instantiating ?That to an annotated type would fail during
- // transformation.
-
- // Instead we force-compare tpe1 <:< tpe2.withoutAnnotations
- // to trigger instantiation of the TypeVar to the base type
-
- // This is a bit unorthodox (we're only supposed to look at
- // annotations here) but seems to work.
-
- if (!annots2.isEmpty && !tpe1.isGround)
- return tpe1 <:< tpe2.withoutAnnotations
-
- false
- }
-
- /** Refine the computed least upper bound of a list of types.
- * All this should do is add annotations. */
- override def annotationsLub(tpe: Type, ts: List[Type]): Type = {
- if (!cpsEnabled) return tpe
-
- val annots1 = cpsParamAnnotation(tpe)
- val annots2 = ts flatMap cpsParamAnnotation
-
- if (annots2.nonEmpty) {
- val cpsLub = newMarker(global.lub(annots1:::annots2 map (_.atp)))
- val tpe1 = if (annots1.nonEmpty) removeAttribs(tpe, MarkerCPSTypes) else tpe
- tpe1.withAnnotation(cpsLub)
- }
- else tpe
- }
-
- /** Refine the bounds on type parameters to the given type arguments. */
- override def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = {
- if (!cpsEnabled) return bounds
-
- val anyAtCPS = newCpsParamsMarker(NothingTpe, AnyTpe)
- if (isFunctionType(tparams.head.owner.tpe_*) || isPartialFunctionType(tparams.head.owner.tpe_*)) {
- vprintln("function bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs)
- if (hasCpsParamTypes(targs.last))
- bounds.reverse match {
- case res::b if !hasCpsParamTypes(res.hi) =>
- (TypeBounds(res.lo, res.hi.withAnnotation(anyAtCPS))::b).reverse
- case _ => bounds
- }
- else
- bounds
- }
- else if (tparams.head.owner == ByNameParamClass) {
- vprintln("byname bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs)
- val TypeBounds(lo, hi) = bounds.head
- if (hasCpsParamTypes(targs.head) && !hasCpsParamTypes(hi))
- TypeBounds(lo, hi withAnnotation anyAtCPS) :: Nil
- else bounds
- } else
- bounds
- }
- }
-
- object plugin extends AnalyzerPlugin {
-
- import checker._
-
- override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = {
- if (!cpsEnabled) return false
- vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + mode + " / " + pt)
-
- val annots1 = cpsParamAnnotation(tree.tpe)
- val annots2 = cpsParamAnnotation(pt)
-
- if (mode.inPatternMode) {
- //println("can adapt pattern annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt)
- if (!annots1.isEmpty) {
- return true
- }
- }
-
-/*
- // not precise enough -- still relying on addAnnotations to remove things from ValDef symbols
- if (mode.inAllModes(TYPEmode | BYVALmode)) {
- if (!annots1.isEmpty) {
- return true
- }
- }
-*/
-
-/*
- this interferes with overloading resolution
- if (mode.inByValMode && tree.tpe <:< pt) {
- vprintln("already compatible, can't adapt further")
- return false
- }
-*/
- if (mode.inExprMode) {
- if ((annots1 corresponds annots2)(_.atp <:< _.atp)) {
- vprintln("already same, can't adapt further")
- false
- } else if (annots1.isEmpty && !annots2.isEmpty && !mode.inByValMode) {
- //println("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt)
- if (!hasPlusMarker(tree.tpe)) {
- // val base = tree.tpe <:< removeAllCPSAnnotations(pt)
- // val known = global.analyzer.isFullyDefined(pt)
- // println(same + "/" + base + "/" + known)
- //val same = annots2 forall { case AnnotationInfo(atp: TypeRef, _, _) => atp.typeArgs(0) =:= atp.typeArgs(1) }
- // TBD: use same or not?
- //if (same) {
- vprintln("yes we can!! (unit)")
- true
- //}
- } else false
- } else if (!hasPlusMarker(tree.tpe) && annots1.isEmpty && !annots2.isEmpty && typer.context.inReturnExpr) {
- vprintln("checking enclosing method's result type without annotations")
- tree.tpe <:< pt.withoutAnnotations
- } else if (!hasMinusMarker(tree.tpe) && !annots1.isEmpty && mode.inByValMode) {
- 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)")
- 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
- }
- } else false
- } else false
- }
-
- override def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = {
- if (!cpsEnabled) return tree
-
- vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + mode + " / " + pt)
-
- val annotsTree = cpsParamAnnotation(tree.tpe)
- val annotsExpected = cpsParamAnnotation(pt)
- def isMissingExpectedAnnots = annotsTree.isEmpty && annotsExpected.nonEmpty
-
- // not sure I rephrased this comment correctly:
- // replacing `mode.inPatternMode` in the condition below by `mode.inPatternMode || mode.inAllModes(TYPEmode | BYVALmode)`
- // doesn't work correctly -- still relying on addAnnotations to remove things from ValDef symbols
- if (mode.inPatternMode && annotsTree.nonEmpty) tree modifyType removeAllCPSAnnotations
- else if (mode.typingExprNotValue && !hasPlusMarker(tree.tpe) && isMissingExpectedAnnots) { // shiftUnit
- // add a marker annotation that will make tree.tpe behave as pt, subtyping wise
- // tree will look like having any possible annotation
- //println("adapt annotations " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt)
-
- // CAVEAT:
- // for monomorphic answer types we want to have @plus @cps (for better checking)
- // for answer type modification we want to have only @plus (because actual answer type may differ from pt)
-
- val res = tree modifyType (_ withAnnotations newPlusMarker() :: annotsExpected) // needed for #1807
- vprintln("adapted annotations (not by val) of " + tree + " to " + res.tpe)
- res
- } else if (mode.typingExprByValue && !hasMinusMarker(tree.tpe) && annotsTree.nonEmpty) { // dropping annotation
- // 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 addMinusMarker
- vprintln("adapted annotations (by val) of " + tree + " to " + res.tpe)
- res
- } else if (typer.context.inReturnExpr && !hasPlusMarker(tree.tpe) && isMissingExpectedAnnots) {
- // 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 (_ withAnnotation 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 `NothingTpe`).
- *
- * 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 pluginsTypedReturn(default: Type, typer: Typer, tree: Return, pt: Type): Type = {
- val expr = tree.expr
- // only adapt if method's result type (pt) is cps type
- val annots = cpsParamAnnotation(pt)
- if (annots.nonEmpty) {
- // return type of `expr` without plus marker, but only if it doesn't have other cps annots
- if (hasPlusMarker(expr.tpe) && !hasCpsParamTypes(expr.tpe))
- expr.setType(removeAttribs(expr.tpe, MarkerCPSAdaptPlus))
- expr.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
- // But we can't, since alternatives aren't types but symbols, which we
- // can't change (we'd be affecting symbols globally)
- /*
- case OverloadedType(pre, alts) =>
- OverloadedType(pre, alts.map((sym: Symbol) => updateAttributes(pre.memberType(sym), annots)))
- */
- case OverloadedType(pre, alts) => tpe //reconstruct correct annotations later
- case MethodType(params, restpe) => tpe
- case PolyType(params, restpe) => tpe
- case _ =>
- assert(childAnnots forall (_ matches MarkerCPSTypes), childAnnots)
- /*
- [] + [] = []
- plus + [] = plus
- cps + [] = cps
- plus cps + [] = plus cps
- minus cps + [] = minus cps
- synth cps + [] = synth cps // <- synth on left - does it happen?
-
- [] + cps = cps
- plus + cps = synth cps
- cps + cps = cps! <- lin
- plus cps + cps = synth cps! <- unify
- minus cps + cps = minus cps! <- lin
- synth cps + cps = synth cps! <- unify
- */
-
- val plus = hasPlusMarker(tpe) || (
- hasCpsParamTypes(tpe)
- && byName.nonEmpty
- && (byName forall (t => hasPlusMarker(t.tpe)))
- )
-
- // move @plus annotations outward from by-name children
- if (childAnnots.isEmpty) return {
- if (plus) { // @plus or @plus @cps
- byName foreach (_ modifyType cleanPlus)
- addPlusMarker(tpe)
- }
- else tpe
- }
-
- val annots1 = cpsParamAnnotation(tpe)
-
- if (annots1.isEmpty) { // nothing or @plus
- cleanPlusWith(tpe)(newSynthMarker(), linearize(childAnnots))
- }
- else {
- val annot1 = single(annots1)
- if (plus) { // @plus @cps
- val annot2 = linearize(childAnnots)
-
- if (annot2.atp <:< annot1.atp) {
- try cleanPlusWith(tpe)(newSynthMarker(), annot2)
- finally byName foreach (_ modifyType cleanPlus)
- }
- else throw new TypeError(annot2 + " is not a subtype of " + annot1)
- }
- else if (hasSynthMarker(tpe)) { // @synth @cps
- val annot2 = linearize(childAnnots)
- if (annot2.atp <:< annot1.atp)
- cleanPlusWith(tpe)(annot2)
- else
- throw new TypeError(annot2 + " is not a subtype of " + annot1)
- }
- else // @cps
- cleanPlusWith(tpe)(linearize(childAnnots:::annots1))
- }
- }
- }
-
- def transArgList(fun: Tree, args: List[Tree]): List[List[Tree]] = {
- val formals = fun.tpe.paramTypes
- val overshoot = args.length - formals.length
-
- for ((a,tp) <- args.zip(formals ::: List.fill(overshoot)(NoType))) yield {
- tp match {
- case TypeRef(_, ByNameParamClass, List(elemtp)) =>
- Nil // TODO: check conformance??
- case _ =>
- List(a)
- }
- }
- }
-
-
- def transStms(stms: List[Tree]): List[Tree] = stms match {
- case ValDef(mods, name, tpt, rhs)::xs =>
- rhs::transStms(xs)
- case Assign(lhs, rhs)::xs =>
- rhs::transStms(xs)
- case x::xs =>
- x::transStms(xs)
- case Nil =>
- Nil
- }
-
- def single(xs: List[AnnotationInfo]) = xs match {
- case List(x) => x
- case _ =>
- global.globalError("not a single cps annotation: " + xs)
- xs(0)
- }
-
- def emptyOrSingleList(xs: List[AnnotationInfo]) = if (xs.isEmpty) Nil else List(single(xs))
-
- def transChildrenInOrder(tree: Tree, tpe: Type, childTrees: List[Tree], byName: List[Tree]) = {
- def inspect(t: Tree): List[AnnotationInfo] = {
- if (t.tpe eq null) Nil else {
- val extra: List[AnnotationInfo] = t.tpe match {
- case _: MethodType | _: PolyType | _: OverloadedType =>
- // method types, poly types and overloaded types do not obtain cps annotions by propagation
- // need to reconstruct transitively from their children.
- t match {
- case Select(qual, name) => inspect(qual)
- case Apply(fun, args) => (fun::(transArgList(fun,args).flatten)) flatMap inspect
- case TypeApply(fun, args) => (fun::(transArgList(fun,args).flatten)) flatMap inspect
- case _ => Nil
- }
- case _ => Nil
- }
-
- val types = cpsParamAnnotation(t.tpe)
- // TODO: check that it has been adapted and if so correctly
- extra ++ emptyOrSingleList(types)
- }
- }
- val children = childTrees flatMap inspect
-
- val newtpe = updateAttributesFromChildren(tpe, children, byName)
-
- if (!newtpe.annotations.isEmpty)
- vprintln("[checker] inferred " + tree + " / " + tpe + " ===> "+ newtpe)
-
- newtpe
- }
-
- /** Modify the type that has thus far been inferred
- * for a tree. All this should do is add annotations. */
-
- override def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = {
- if (!cpsEnabled) {
- val report = try hasCpsParamTypes(tpe) catch { case _: MissingRequirementError => false }
- if (report)
- global.reporter.error(tree.pos, "this code must be compiled with the Scala continuations plugin enabled")
-
- return tpe
- }
-
-// if (tree.tpe.hasAnnotation(MarkerCPSAdaptPlus))
-// println("addAnnotation " + tree + "/" + tpe)
-
- tree match {
-
- case Apply(fun @ Select(qual, name), args) if fun.isTyped =>
-
- // HACK: With overloaded methods, fun will never get annotated. This is because
- // the 'overloaded' type gets annotated, but not the alternatives (among which
- // fun's type is chosen)
-
- vprintln("[checker] checking select apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, qual::(transArgList(fun, args).flatten), Nil)
-
- case Apply(TypeApply(fun @ Select(qual, name), targs), args) if fun.isTyped => // not trigge
-
- vprintln("[checker] checking select apply type-apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, qual::(transArgList(fun, args).flatten), Nil)
-
- case TypeApply(fun @ Select(qual, name), args) if fun.isTyped =>
- def stripNullaryMethodType(tp: Type) = tp match { case NullaryMethodType(restpe) => restpe case tp => tp }
- vprintln("[checker] checking select type-apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, stripNullaryMethodType(tpe), List(qual, fun), Nil)
-
- case Apply(fun, args) if fun.isTyped =>
-
- vprintln("[checker] checking unknown apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, fun::(transArgList(fun, args).flatten), Nil)
-
- case TypeApply(fun, args) =>
-
- vprintln("[checker] checking unknown type apply " + tree + "/" + tpe)
-
- transChildrenInOrder(tree, tpe, List(fun), Nil)
-
- case Select(qual, name) if qual.isTyped =>
-
- vprintln("[checker] checking select " + tree + "/" + tpe)
-
- // straightforward way is problematic (see select.scala and Test2.scala)
- // transChildrenInOrder(tree, tpe, List(qual), Nil)
-
- // the problem is that qual may be of type OverloadedType (or MethodType) and
- // we cannot safely annotate these. so we just ignore these cases and
- // clean up later in the Apply/TypeApply trees.
-
- if (hasCpsParamTypes(qual.tpe)) {
- // however there is one special case:
- // if it's a method without parameters, just apply it. normally done in adapt, but
- // we have to do it here so we don't lose the cps information (wouldn't trigger our
- // adapt and there is no Apply/TypeApply created)
- tpe match {
- case NullaryMethodType(restpe) =>
- //println("yep: " + restpe + "," + restpe.getClass)
- transChildrenInOrder(tree, restpe, List(qual), Nil)
- case _ : PolyType => tpe
- case _ : MethodType => tpe
- case _ : OverloadedType => tpe
- case _ =>
- transChildrenInOrder(tree, tpe, List(qual), Nil)
- }
- } else
- tpe
-
- case If(cond, thenp, elsep) =>
- transChildrenInOrder(tree, tpe, List(cond), List(thenp, elsep))
-
- case Match(select, cases) =>
- transChildrenInOrder(tree, tpe, List(select), cases:::(cases map { case CaseDef(_, _, body) => body }))
-
- case Try(block, catches, finalizer) =>
- val tpe1 = transChildrenInOrder(tree, tpe, Nil, block::catches:::(catches map { case CaseDef(_, _, body) => body }))
-
- val annots = cpsParamAnnotation(tpe1)
- if (annots.nonEmpty) {
- val ann = single(annots)
- val (atp0, atp1) = annTypes(ann)
- if (!(atp0 =:= atp1))
- throw new TypeError("only simple cps types allowed in try/catch blocks (found: " + tpe1 + ")")
- if (!finalizer.isEmpty) // no finalizers allowed. see explanation in SelectiveCPSTransform
- reporter.error(tree.pos, "try/catch blocks that use continuations cannot have finalizers")
- }
- tpe1
-
- case Block(stms, expr) =>
- // if any stm has annotation, so does block
- transChildrenInOrder(tree, tpe, transStms(stms), List(expr))
-
- case ValDef(mods, name, tpt, rhs) =>
- vprintln("[checker] checking valdef " + name + "/"+tpe+"/"+tpt+"/"+tree.symbol.tpe)
- // ValDef symbols must *not* have annotations!
- // lazy vals are currently not supported
- // but if we erase here all annotations, compiler will complain only
- // when generating bytecode.
- // This way lazy vals will be reported as unsupported feature later rather than weird type error.
- if (hasAnswerTypeAnn(tree.symbol.info) && !mods.isLazy) { // is it okay to modify sym here?
- vprintln("removing annotation from sym " + tree.symbol + "/" + tree.symbol.tpe + "/" + tpt)
- tpt modifyType removeAllCPSAnnotations
- tree.symbol modifyInfo removeAllCPSAnnotations
- }
- tpe
-
- case _ =>
- tpe
- }
-
-
- }
- }
-}
diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala
deleted file mode 100644
index 98d0695865..0000000000
--- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala
+++ /dev/null
@@ -1,137 +0,0 @@
-// $Id$
-
-package scala.tools.selectivecps
-
-import scala.tools.nsc.Global
-
-trait CPSUtils {
- val global: Global
- import global._
-
- val cpsEnabled: Boolean
- val verbose: Boolean = System.getProperty("cpsVerbose", "false") == "true"
- def vprintln(x: =>Any): Unit = if (verbose) println(x)
-
- object cpsNames {
- val catches = newTermName("$catches")
- val ex = newTermName("$ex")
- val flatMapCatch = newTermName("flatMapCatch")
- val getTrivialValue = newTermName("getTrivialValue")
- val isTrivial = newTermName("isTrivial")
- val reify = newTermName("reify")
- val reifyR = newTermName("reifyR")
- val shift = newTermName("shift")
- val shiftR = newTermName("shiftR")
- val shiftSuffix = newTermName("$shift")
- val shiftUnit0 = newTermName("shiftUnit0")
- val shiftUnit = newTermName("shiftUnit")
- val shiftUnitR = newTermName("shiftUnitR")
- }
-
- lazy val MarkerCPSSym = rootMirror.getRequiredClass("scala.util.continuations.cpsSym")
- lazy val MarkerCPSTypes = rootMirror.getRequiredClass("scala.util.continuations.cpsParam")
- lazy val MarkerCPSSynth = rootMirror.getRequiredClass("scala.util.continuations.cpsSynth")
- lazy val MarkerCPSAdaptPlus = rootMirror.getRequiredClass("scala.util.continuations.cpsPlus")
- lazy val MarkerCPSAdaptMinus = rootMirror.getRequiredClass("scala.util.continuations.cpsMinus")
-
- lazy val Context = rootMirror.getRequiredClass("scala.util.continuations.ControlContext")
- lazy val ModCPS = rootMirror.getPackage("scala.util.continuations")
-
- lazy val MethShiftUnit = definitions.getMember(ModCPS, cpsNames.shiftUnit)
- lazy val MethShiftUnit0 = definitions.getMember(ModCPS, cpsNames.shiftUnit0)
- lazy val MethShiftUnitR = definitions.getMember(ModCPS, cpsNames.shiftUnitR)
- lazy val MethShift = definitions.getMember(ModCPS, cpsNames.shift)
- 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 allCPSAnnotations = List(MarkerCPSSym, MarkerCPSTypes, MarkerCPSSynth,
- MarkerCPSAdaptPlus, MarkerCPSAdaptMinus)
-
- // TODO - needed? Can these all use the same annotation info?
- protected def newSynthMarker() = newMarker(MarkerCPSSynth)
- protected def newPlusMarker() = newMarker(MarkerCPSAdaptPlus)
- protected def newMinusMarker() = newMarker(MarkerCPSAdaptMinus)
- protected def newMarker(tpe: Type): AnnotationInfo = AnnotationInfo marker tpe
- protected def newMarker(sym: Symbol): AnnotationInfo = AnnotationInfo marker sym.tpe
-
- protected def newCpsParamsMarker(tp1: Type, tp2: Type) =
- newMarker(appliedType(MarkerCPSTypes, tp1, tp2))
-
- // annotation checker
-
- protected def annTypes(ann: AnnotationInfo): (Type, Type) = {
- val tp0 :: tp1 :: Nil = ann.atp.dealiasWiden.typeArgs
- ((tp0, tp1))
- }
- protected def hasMinusMarker(tpe: Type) = tpe hasAnnotation MarkerCPSAdaptMinus
- protected def hasPlusMarker(tpe: Type) = tpe hasAnnotation MarkerCPSAdaptPlus
- protected def hasSynthMarker(tpe: Type) = tpe hasAnnotation MarkerCPSSynth
- protected def hasCpsParamTypes(tpe: Type) = tpe hasAnnotation MarkerCPSTypes
- protected def cpsParamTypes(tpe: Type) = tpe getAnnotation MarkerCPSTypes map annTypes
-
- def filterAttribs(tpe:Type, cls:Symbol) =
- tpe.annotations filter (_ matches cls)
-
- def removeAttribs(tpe: Type, classes: Symbol*) =
- tpe filterAnnotations (ann => !(classes exists (ann matches _)))
-
- def removeAllCPSAnnotations(tpe: Type) = removeAttribs(tpe, allCPSAnnotations:_*)
-
- def cpsParamAnnotation(tpe: Type) = filterAttribs(tpe, MarkerCPSTypes)
-
- def linearize(ann: List[AnnotationInfo]): AnnotationInfo = {
- ann reduceLeft { (a, b) =>
- val (u0,v0) = annTypes(a)
- val (u1,v1) = annTypes(b)
- // vprintln("check lin " + a + " andThen " + b)
-
- if (v1 <:< u0)
- newCpsParamsMarker(u1, v0)
- else
- throw new TypeError("illegal answer type modification: " + a + " andThen " + b)
- }
- }
-
- // 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 getAnswerTypeAnn(tp: Type): Option[(Type, Type)] =
- cpsParamTypes(tp) filterNot (_ => hasPlusMarker(tp))
-
- def hasAnswerTypeAnn(tp: Type) =
- hasCpsParamTypes(tp) && !hasPlusMarker(tp)
-
- def updateSynthFlag(tree: Tree) = { // remove annotations if *we* added them (@synth present)
- if (hasSynthMarker(tree.tpe)) {
- log("removing annotation from " + tree)
- tree modifyType removeAllCPSAnnotations
- } else
- tree
- }
-
- type CPSInfo = Option[(Type,Type)]
-
- def linearize(a: CPSInfo, b: CPSInfo)(implicit unit: CompilationUnit, pos: Position): CPSInfo = {
- (a,b) match {
- case (Some((u0,v0)), Some((u1,v1))) =>
- vprintln("check lin " + a + " andThen " + b)
- if (!(v1 <:< u0)) {
- unit.error(pos,"cannot change answer type in composition of cps expressions " +
- "from " + u1 + " to " + v0 + " because " + v1 + " is not a subtype of " + u0 + ".")
- throw new Exception("check lin " + a + " andThen " + b)
- }
- Some((u1,v0))
- case (Some(_), _) => a
- case (_, Some(_)) => b
- case _ => None
- }
- }
-}
diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala
deleted file mode 100644
index ae95a1bdac..0000000000
--- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala
+++ /dev/null
@@ -1,545 +0,0 @@
-// $Id$
-
-package scala.tools.selectivecps
-
-import scala.tools.nsc.transform._
-import scala.tools.nsc.symtab._
-import scala.tools.nsc.plugins._
-
-/**
- * In methods marked @cps, explicitly name results of calls to other @cps methods
- */
-abstract class SelectiveANFTransform extends PluginComponent with Transform with
- TypingTransformers with CPSUtils {
- // inherits abstract value `global` and class `Phase` from Transform
-
- import global._ // the global environment
- import definitions._ // standard classes and methods
- import typer.atOwner // methods to type trees
-
- override def description = "ANF pre-transform for @cps"
-
- /** the following two members override abstract members in Transform */
- val phaseName: String = "selectiveanf"
-
- protected def newTransformer(unit: CompilationUnit): Transformer =
- new ANFTransformer(unit)
-
- class ANFTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
-
- 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
-
- tree match {
-
- // Maybe we should further generalize the transform and move it over
- // to the regular Transformer facility. But then, actual and required cps
- // state would need more complicated (stateful!) tracking.
-
- // Making the default case use transExpr(tree, None, None) instead of
- // calling super.transform() would be a start, but at the moment,
- // this would cause infinite recursion. But we could remove the
- // ValDef case here.
-
- 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 rhs0
- val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe))(getExternalAnswerTypeAnn(tpt.tpe).isDefined)
-
- debuglog("result "+rhs1)
- debuglog("result is of type "+rhs1.tpe)
-
- treeCopy.DefDef(dd, mods, name, transformTypeDefs(tparams), transformValDefss(vparamss),
- transform(tpt), rhs1)
- }
-
- case ff @ Function(vparams, body) =>
- debuglog("transforming anon function " + ff.symbol)
-
- atOwner(ff.symbol) {
-
- //val body1 = transExpr(body, None, getExternalAnswerTypeAnn(body.tpe))
-
- // need to special case partial functions: if expected type is @cps
- // but all cases are pure, then we would transform
- // { x => x match { case A => ... }} to
- // { x => shiftUnit(x match { case A => ... })}
- // which Uncurry cannot handle (see function6.scala)
- // thus, we push down the shiftUnit to each of the case bodies
-
- 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) =>
- // if (!hasPlusMarker(body.tpe)) body modifyType (_ withAnnotation newPlusMarker()) // TODO: to avoid warning
- val bodyVal = transExpr(body, None, ext) // ??? triggers "cps-transformed unexpectedly" warning in transTailValue
- treeCopy.CaseDef(cd, transform(pat), transform(guard), bodyVal)
- }
- treeCopy.Match(tree, transform(selector), caseVals)
- }
-
- def transformPureVirtMatch(body: Block, selDef: ValDef, cases: List[Tree], matchEnd: Tree) = {
- val stats = transform(selDef) :: (cases map (transExpr(_, None, ext)))
- treeCopy.Block(body, stats, transExpr(matchEnd, None, ext))
- }
-
- val body1 = body match {
- case Match(selector, cases) if ext.isDefined && pureBody =>
- transformPureMatch(body, selector, cases)
-
- // virtpatmat switch
- case Block(List(selDef: ValDef), mat@Match(selector, cases)) if ext.isDefined && pureBody =>
- treeCopy.Block(body, List(transform(selDef)), transformPureMatch(mat, selector, cases))
-
- // virtpatmat
- case b@Block(matchStats@((selDef: ValDef) :: cases), matchEnd) if ext.isDefined && pureBody && (matchStats forall treeInfo.hasSynthCaseSymbol) =>
- transformPureVirtMatch(b, selDef, cases, matchEnd)
-
- // virtpatmat that stores the scrut separately -- TODO: can we eliminate this case??
- case Block(List(selDef0: ValDef), mat@Block(matchStats@((selDef: ValDef) :: cases), matchEnd)) if ext.isDefined && pureBody && (matchStats forall treeInfo.hasSynthCaseSymbol)=>
- treeCopy.Block(body, List(transform(selDef0)), transformPureVirtMatch(mat, selDef, cases, matchEnd))
-
- case _ =>
- transExpr(body, None, ext)
- }
-
- debuglog("anf result "+body1+"\nresult is of type "+body1.tpe)
-
- treeCopy.Function(ff, transformValDefs(vparams), body1)
- }
-
- case vd @ ValDef(mods, name, tpt, rhs) => // object-level valdefs
- debuglog("transforming valdef " + vd.symbol)
-
- if (getExternalAnswerTypeAnn(tpt.tpe).isEmpty) {
-
- atOwner(vd.symbol) {
-
- val rhs1 = transExpr(rhs, None, None)
-
- treeCopy.ValDef(vd, mods, name, transform(tpt), rhs1)
- }
- } else {
- unit.error(tree.pos, "cps annotations not allowed on by-value parameters or value definitions")
- super.transform(tree)
- }
-
- case TypeTree() =>
- // circumvent cpsAllowed here
- super.transform(tree)
-
- case Apply(_,_) =>
- // this allows reset { ... } in object constructors
- // it's kind of a hack to put it here (see note above)
- transExpr(tree, None, None)
-
- case _ =>
- if (hasAnswerTypeAnn(tree.tpe)) {
- if (!cpsAllowed) {
- if (tree.symbol.isLazy)
- unit.error(tree.pos, "implementation restriction: cps annotations not allowed on lazy value definitions")
- else
- unit.error(tree.pos, "cps code not allowed here / " + tree.getClass + " / " + tree)
- }
- log(tree)
- }
-
- cpsAllowed = false
- super.transform(tree)
- }
- }
-
-
- 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)
- }
- }
-
-
- 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
-
- var spc: CPSInfo = cpsA
-
- val (stm,expr) = (for ((a,tp) <- args.zip(formals ::: List.fill(overshoot)(NoType))) yield {
- tp match {
- case TypeRef(_, ByNameParamClass, List(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
- (valStm, valExpr)
- }
- }).unzip
-
- (stm,expr,spc)
- }
-
-
- // 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 {
- case Block(stms, expr) =>
- 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)(cpsR2.isDefined || isAnyParentImpure)
- val tree1 = (treeCopy.Block(tree, a, b)) // no updateSynthFlag here!!!
-
- (Nil, tree1, cpsA)
-
- case If(cond, thenp, elsep) =>
- /* possible situations:
- cps before (cpsA)
- cps in condition (spc) <-- synth flag set if *only* here!
- cps in (one or both) branches */
- val (condStats, condVal, spc) = transInlineValue(cond, cpsA)
- 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)(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) {
- if (elsep == EmptyTree)
- unit.error(tree.pos, "always need else part in cps code")
- }
- if (hasAnswerTypeAnn(thenVal.tpe) != hasAnswerTypeAnn(elseVal.tpe)) {
- unit.error(tree.pos, "then and else parts must both be cps code or neither of them")
- }
-
- (condStats, updateSynthFlag(treeCopy.If(tree, condVal, thenVal, elseVal)), spc)
-
- case Match(selector, cases) =>
- val (selStats, selVal, spc) = transInlineValue(selector, cpsA)
- val (cpsA2, cpsR2) =
- if (hasSynthMarker(tree.tpe)) (spc, linearize(spc, getAnswerTypeAnn(tree.tpe)))
- else (None, getAnswerTypeAnn(tree.tpe))
-
- val caseVals = cases map { case cd @ CaseDef(pat, guard, body) =>
- val bodyVal = transExpr(body, cpsA2, cpsR2)(cpsR2.isDefined || isAnyParentImpure)
- treeCopy.CaseDef(cd, transform(pat), transform(guard), bodyVal)
- }
-
- (selStats, updateSynthFlag(treeCopy.Match(tree, selVal, caseVals)), spc)
-
- // this is utterly broken: LabelDefs need to be considered together when transforming them to DefDefs:
- // suppose a Block {L1; ... ; LN}
- // this should become {D1def ; ... ; DNdef ; D1()}
- // where D$idef = def L$i(..) = {L$i.body; L${i+1}(..)}
-
- case ldef @ LabelDef(name, params, rhs) =>
- // println("trans LABELDEF "+(name, params, tree.tpe, hasAnswerTypeAnn(tree.tpe)))
- // TODO why does the labeldef's type have a cpsMinus annotation, whereas the rhs does not? (BYVALmode missing/too much somewhere?)
- if (hasAnswerTypeAnn(tree.tpe)) {
- // 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))(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
- // transBlock will take care of calling the first label
- // calling each labeldef is wrong, since some labels may be jumped over
- // we can get away with this for now since the only other labels we emit are for tailcalls/while loops,
- // which do not have consecutive labeldefs (and thus fall-through is irrelevant)
- if (treeInfo.hasSynthCaseSymbol(ldef)) (List(stm1), localTyper.typed{Literal(Constant(()))}, cpsA)
- else {
- assert(params.isEmpty, "problem in ANF transforming label with non-empty params "+ ldef)
- (List(stm1), localTyper.typed{Apply(Ident(sym), List())}, cpsA)
- }
- } else {
- val rhsVal = transExpr(rhs, None, None)
- (Nil, updateSynthFlag(treeCopy.LabelDef(tree, name, params, rhsVal)), cpsA)
- }
-
-
- case Try(block, catches, finalizer) =>
- val blockVal = transExpr(block, cpsA, cpsR)
-
- val catchVals = for {
- cd @ CaseDef(pat, guard, body) <- catches
- bodyVal = transExpr(body, cpsA, cpsR)
- } yield {
- treeCopy.CaseDef(cd, transform(pat), transform(guard), bodyVal)
- }
-
- val finallyVal = transExpr(finalizer, None, None) // for now, no cps in finally
-
- (Nil, updateSynthFlag(treeCopy.Try(tree, blockVal, catchVals, finallyVal)), cpsA)
-
- case Assign(lhs, rhs) =>
- // allow cps code in rhs only
- val (stms, expr, spc) = transInlineValue(rhs, cpsA)
- (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)
-
- case Throw(expr0) =>
- val (stms, expr, spc) = transInlineValue(expr0, cpsA)
- (stms, updateSynthFlag(treeCopy.Throw(tree, expr)), spc)
-
- case Typed(expr0, tpt) =>
- // TODO: should x: A @cps[B,C] have a special meaning?
- // type casts used in different ways (see match2.scala, #3199)
- val (stms, expr, spc) = transInlineValue(expr0, cpsA)
- val tpt1 = if (treeInfo.isWildcardStarArg(tree)) tpt else
- treeCopy.TypeTree(tpt).setType(removeAllCPSAnnotations(tpt.tpe))
-// (stms, updateSynthFlag(treeCopy.Typed(tree, expr, tpt1)), spc)
- (stms, treeCopy.Typed(tree, expr, tpt1).setType(removeAllCPSAnnotations(tree.tpe)), spc)
-
- case TypeApply(fun, args) =>
- val (stms, expr, spc) = transInlineValue(fun, cpsA)
- (stms, updateSynthFlag(treeCopy.TypeApply(tree, expr, args)), spc)
-
- case Select(qual, name) =>
- val (stms, expr, spc) = transInlineValue(qual, cpsA)
- (stms, updateSynthFlag(treeCopy.Select(tree, expr, name)), spc)
-
- case Apply(fun, args) =>
- val (funStm, funExpr, funSpc) = transInlineValue(fun, cpsA)
- val (argStm, argExpr, argSpc) = transArgList(fun, args, funSpc)
-
- (funStm ::: (argStm.flatten), updateSynthFlag(treeCopy.Apply(tree, funExpr, argExpr)),
- argSpc)
-
- case _ =>
- cpsAllowed = true
- (Nil, transform(tree), cpsA)
- }
- }
-
- // 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)
-
- val bot = linearize(spc, getAnswerTypeAnn(expr.tpe))(unit, tree.pos)
-
- val plainTpe = removeAllCPSAnnotations(expr.tpe)
-
- if (cpsR.isDefined && !bot.isDefined) {
-
- if (!expr.isEmpty && (expr.tpe.typeSymbol ne NothingClass)) {
- // must convert!
- debuglog("cps type conversion (has: " + cpsA + "/" + spc + "/" + expr.tpe + ")")
- debuglog("cps type conversion (expected: " + cpsR.get + "): " + expr)
-
- if (!hasPlusMarker(expr.tpe))
- unit.warning(tree.pos, "expression " + tree + " is cps-transformed unexpectedly")
-
- try {
- val Some((a, b)) = cpsR
- /* Since shiftUnit is bounded [A,B,C>:B] this may not typecheck
- * if C is overly specific. So if !(B <:< C), call shiftUnit0
- * instead, which takes only two type arguments.
- */
- val conforms = a <:< b
- val call = localTyper.typedPos(tree.pos)(
- Apply(
- TypeApply(
- gen.mkAttributedRef( if (conforms) MethShiftUnit else MethShiftUnit0 ),
- List(TypeTree(plainTpe), TypeTree(a)) ++ ( if (conforms) List(TypeTree(b)) else Nil )
- ),
- List(expr)
- )
- )
- // This is today's sick/meaningless heuristic for spotting breakdown so
- // we don't proceed until stack traces start draping themselves over everything.
- // If there are wildcard types in the tree and B == Nothing, something went wrong.
- // (I thought WildcardTypes would be enough, but nope. 'reset0 { 0 }' has them.)
- //
- // Code as simple as reset((_: String).length)
- // will crash meaninglessly without this check. See SI-3718.
- //
- // TODO - obviously this should be done earlier, differently, or with
- // a more skilled hand. Most likely, all three.
- if ((b.typeSymbol eq NothingClass) && call.tpe.exists(_ eq WildcardType))
- unit.error(tree.pos, "cannot cps-transform malformed (possibly in shift/reset placement) expression")
- else
- return ((stms, call))
- }
- catch {
- case ex:TypeError =>
- unit.error(ex.pos, "cannot cps-transform expression " + tree + ": " + ex.msg)
- }
- }
-
- } else if (!cpsR.isDefined && bot.isDefined) {
- // error!
- debuglog("cps type error: " + expr)
- //println("cps type error: " + expr + "/" + expr.tpe + "/" + getAnswerTypeAnn(expr.tpe))
-
- //println(cpsR + "/" + spc + "/" + bot)
-
- unit.error(tree.pos, "found cps expression in non-cps position")
- } else {
- // 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")
- expr modifyType removeAllCPSAnnotations
- }
-
- // TODO: sanity check that types agree
- }
-
- (stms, expr)
- }
-
- 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
-
- getAnswerTypeAnn(expr.tpe) match {
- case spcVal @ Some(_) =>
-
- val valueTpe = removeAllCPSAnnotations(expr.tpe)
-
- val sym: Symbol = (
- currentOwner.newValue(newTermName(unit.fresh.newName("tmp")), tree.pos, Flags.SYNTHETIC)
- setInfo valueTpe
- setAnnotations List(AnnotationInfo(MarkerCPSSym.tpe_*, Nil, Nil))
- )
- expr.changeOwner(currentOwner -> sym)
-
- (stms ::: List(ValDef(sym, expr) setType(NoType)),
- Ident(sym) setType(valueTpe) setPos(tree.pos), linearize(spc, spcVal)(unit, tree.pos))
-
- case _ =>
- (stms, expr, spc)
- }
-
- }
-
-
-
- def transInlineStm(stm: Tree, cpsA: CPSInfo)(implicit isAnyParentImpure: Boolean): (List[Tree], CPSInfo) = {
- stm match {
-
- // TODO: what about DefDefs?
- // TODO: relation to top-level val def?
- // TODO: what about lazy vals?
-
- case tree @ ValDef(mods, name, tpt, rhs) =>
- val (stms, anfRhs, spc) = atOwner(tree.symbol) { transValue(rhs, cpsA, None) }
-
- val tv = new ChangeOwnerTraverser(tree.symbol, currentOwner)
- stms.foreach(tv.traverse(_))
-
- // TODO: symbol might already have annotation. Should check conformance
- // TODO: better yet: do without annotations on symbols
-
- val spcVal = getAnswerTypeAnn(anfRhs.tpe)
- spcVal foreach (_ => tree.symbol setAnnotations List(AnnotationInfo(MarkerCPSSym.tpe_*, Nil, Nil)))
-
- (stms:::List(treeCopy.ValDef(tree, mods, name, tpt, anfRhs)), linearize(spc, spcVal)(unit, tree.pos))
-
- case _ =>
- val (headStms, headExpr, headSpc) = transInlineValue(stm, cpsA)
- val valSpc = getAnswerTypeAnn(headExpr.tpe)
- (headStms:::List(headExpr), linearize(headSpc, valSpc)(unit, stm.pos))
- }
- }
-
- // 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 =>
- val (anfStats, anfExpr) = transTailValue(expr, currAns, cpsR)
- (accum ++ anfStats, anfExpr)
-
- case stat :: rest =>
- val (stats, nextAns) = transInlineStm(stat, currAns)
- rec(rest, nextAns, accum ++ stats)
- }
-
- val (anfStats, anfExpr) = rec(stms, cpsA, List())
- // println("\nanf-block:\n"+ ((stms :+ expr) mkString ("{", "\n", "}")) +"\nBECAME\n"+ ((anfStats :+ anfExpr) mkString ("{", "\n", "}")))
- // println("synth case? "+ (anfStats map (t => (t, t.isDef, treeInfo.hasSynthCaseSymbol(t)))))
- // SUPER UGLY HACK: handle virtpatmat-style matches, whose labels have already been turned into DefDefs
- if (anfStats.nonEmpty && (anfStats forall (t => !t.isDef || treeInfo.hasSynthCaseSymbol(t)))) {
- val (prologue, rest) = (anfStats :+ anfExpr) span (s => !s.isInstanceOf[DefDef]) // find first case
- // println("rest: "+ rest)
- // val (defs, calls) = rest partition (_.isInstanceOf[DefDef])
- if (rest.nonEmpty) {
- // the filter drops the ()'s emitted when transValue encountered a LabelDef
- val stats = prologue ++ (rest filter (_.isInstanceOf[DefDef])).reverse // ++ calls
- // println("REVERSED "+ (stats mkString ("{", "\n", "}")))
- (stats, localTyper.typed{Apply(Ident(rest.head.symbol), List())}) // call first label to kick-start the match
- } else (anfStats, anfExpr)
- } else (anfStats, anfExpr)
- }
- }
-}
diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala
deleted file mode 100644
index a7e82e949b..0000000000
--- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala
+++ /dev/null
@@ -1,56 +0,0 @@
-// $Id$
-
-package scala.tools.selectivecps
-
-import scala.tools.nsc
-import nsc.Global
-import nsc.plugins.Plugin
-import nsc.plugins.PluginComponent
-
-class SelectiveCPSPlugin(val global: Global) extends Plugin {
- val name = "continuations"
- val description = "applies selective cps conversion"
-
- val pluginEnabled = options contains "enable"
-
- val anfPhase = new {
- val global = SelectiveCPSPlugin.this.global
- val cpsEnabled = pluginEnabled
- override val enabled = cpsEnabled
- } with SelectiveANFTransform {
- val runsAfter = List("pickler")
- }
-
- val cpsPhase = new {
- val global = SelectiveCPSPlugin.this.global
- val cpsEnabled = pluginEnabled
- override val enabled = cpsEnabled
- } with SelectiveCPSTransform {
- val runsAfter = List("selectiveanf")
- override val runsBefore = List("uncurry")
- }
-
- val components = List[PluginComponent](anfPhase, cpsPhase)
-
- val checker = new {
- val global: SelectiveCPSPlugin.this.global.type = SelectiveCPSPlugin.this.global
- val cpsEnabled = pluginEnabled
- } with CPSAnnotationChecker
-
- // TODO don't muck up global with unused checkers
- global.addAnnotationChecker(checker.checker)
- global.analyzer.addAnalyzerPlugin(checker.plugin)
-
- global.log("instantiated cps plugin: " + this)
-
- override def init(options: List[String], error: String => Unit) = {
- options foreach {
- case "enable" => // in initializer
- case arg => error(s"Bad argument: $arg")
- }
- pluginEnabled
- }
-
- override val optionsHelp: Option[String] =
- Some(" -P:continuations:enable Enable continuations")
-}
diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala
deleted file mode 100644
index 0210ad3459..0000000000
--- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala
+++ /dev/null
@@ -1,383 +0,0 @@
-// $Id$
-
-package scala.tools.selectivecps
-
-import scala.tools.nsc.transform._
-import scala.tools.nsc.plugins._
-import scala.tools.nsc.ast._
-
-/**
- * In methods marked @cps, CPS-transform assignments introduced by ANF-transform phase.
- */
-abstract class SelectiveCPSTransform extends PluginComponent with
- InfoTransform with TypingTransformers with CPSUtils with TreeDSL {
- // inherits abstract value `global` and class `Phase` from Transform
-
- import global._ // the global environment
- import definitions._ // standard classes and methods
- import typer.atOwner // methods to type trees
-
- override def description = "@cps-driven transform of selectiveanf assignments"
-
- /** the following two members override abstract members in Transform */
- val phaseName: String = "selectivecps"
-
- protected def newTransformer(unit: CompilationUnit): Transformer =
- new CPSTransformer(unit)
-
- /** This class does not change linearization */
- override def changesBaseClasses = false
-
- /** - return symbol's transformed type,
- */
- def transformInfo(sym: Symbol, tp: Type): Type = {
- if (!cpsEnabled) return tp
-
- val newtp = transformCPSType(tp)
-
- if (newtp != tp)
- debuglog("transformInfo changed type for " + sym + " to " + newtp);
-
- if (sym == MethReifyR)
- debuglog("transformInfo (not)changed type for " + sym + " to " + newtp);
-
- newtp
- }
-
- def transformCPSType(tp: Type): Type = { // TODO: use a TypeMap? need to handle more cases?
- tp match {
- case PolyType(params,res) => PolyType(params, transformCPSType(res))
- case NullaryMethodType(res) => NullaryMethodType(transformCPSType(res))
- case MethodType(params,res) => MethodType(params, transformCPSType(res))
- case TypeRef(pre, sym, args) => TypeRef(pre, sym, args.map(transformCPSType(_)))
- case _ =>
- getExternalAnswerTypeAnn(tp) match {
- case Some((res, outer)) =>
- appliedType(Context.tpeHK, List(removeAllCPSAnnotations(tp), res, outer))
- case _ =>
- removeAllCPSAnnotations(tp)
- }
- }
- }
-
-
- class CPSTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
- private val patmatTransformer = patmat.newTransformer(unit)
-
- override def transform(tree: Tree): Tree = {
- if (!cpsEnabled) return tree
- postTransform(mainTransform(tree))
- }
-
- def postTransform(tree: Tree): Tree = {
- tree.setType(transformCPSType(tree.tpe))
- }
-
-
- def mainTransform(tree: Tree): Tree = {
- tree match {
-
- // TODO: can we generalize this?
-
- case Apply(TypeApply(fun, targs), args)
- if (fun.symbol == MethShift) =>
- debuglog("found shift: " + tree)
- atPos(tree.pos) {
- val funR = gen.mkAttributedRef(MethShiftR) // TODO: correct?
- //gen.mkAttributedSelect(gen.mkAttributedSelect(gen.mkAttributedSelect(gen.mkAttributedIdent(ScalaPackage),
- //ScalaPackage.tpe.member("util")), ScalaPackage.tpe.member("util").tpe.member("continuations")), MethShiftR)
- //gen.mkAttributedRef(ModCPS.tpe, MethShiftR) // TODO: correct?
- debuglog("funR.tpe: " + funR.tpe)
- Apply(
- TypeApply(funR, targs).setType(appliedType(funR.tpe, targs.map((t:Tree) => t.tpe))),
- args.map(transform(_))
- ).setType(transformCPSType(tree.tpe))
- }
-
- case Apply(TypeApply(fun, targs), args)
- if (fun.symbol == MethShiftUnit) =>
- debuglog("found shiftUnit: " + tree)
- atPos(tree.pos) {
- val funR = gen.mkAttributedRef(MethShiftUnitR) // TODO: correct?
- debuglog("funR.tpe: " + funR.tpe)
- Apply(
- TypeApply(funR, List(targs(0), targs(1))).setType(appliedType(funR.tpe,
- List(targs(0).tpe, targs(1).tpe))),
- args.map(transform(_))
- ).setType(appliedType(Context.tpeHK, List(targs(0).tpe,targs(1).tpe,targs(1).tpe)))
- }
-
- case Apply(TypeApply(fun, targs), args)
- if (fun.symbol == MethReify) =>
- log("found reify: " + tree)
- atPos(tree.pos) {
- val funR = gen.mkAttributedRef(MethReifyR) // TODO: correct?
- debuglog("funR.tpe: " + funR.tpe)
- Apply(
- TypeApply(funR, targs).setType(appliedType(funR.tpe, targs.map((t:Tree) => t.tpe))),
- args.map(transform(_))
- ).setType(transformCPSType(tree.tpe))
- }
-
- 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 unlikely).
-
- // CAVEAT: finalizers are surprisingly tricky!
- // the problem is that they cannot easily be removed
- // from the regular control path and hence will
- // also be invoked after creating the Context object.
-
- /*
- object Test {
- def foo1 = {
- throw new Exception("in sub")
- shift((k:Int=>Int) => k(1))
- 10
- }
- def foo2 = {
- shift((k:Int=>Int) => k(2))
- 20
- }
- def foo3 = {
- shift((k:Int=>Int) => k(3))
- throw new Exception("in sub")
- 30
- }
- def foo4 = {
- shift((k:Int=>Int) => 4)
- throw new Exception("in sub")
- 40
- }
- def bar(x: Int) = try {
- if (x == 1)
- foo1
- else if (x == 2)
- foo2
- else if (x == 3)
- foo3
- else //if (x == 4)
- foo4
- } catch {
- case _ =>
- println("exception")
- 0
- } finally {
- println("done")
- }
- }
-
- reset(Test.bar(1)) // should print: exception,done,0
- reset(Test.bar(2)) // should print: done,20 <-- but prints: done,done,20
- reset(Test.bar(3)) // should print: exception,done,0 <-- but prints: done,exception,done,0
- reset(Test.bar(4)) // should print: 4 <-- but prints: done,4
- */
-
- val block1 = transform(block)
- val catches1 = transformCaseDefs(catches)
- val finalizer1 = transform(finalizer)
-
- if (hasAnswerTypeAnn(tree.tpe)) {
- //vprintln("CPS Transform: " + tree + "/" + tree.tpe + "/" + block1.tpe)
-
- val (stms, expr1) = block1 match {
- case Block(stms, expr) => (stms, expr)
- case expr => (Nil, expr)
- }
-
- val targettp = transformCPSType(tree.tpe)
-
- val pos = catches.head.pos
- val funSym = currentOwner.newValueParameter(cpsNames.catches, pos).setInfo(appliedType(PartialFunctionClass, ThrowableTpe, targettp))
- val funDef = localTyper.typedPos(pos) {
- ValDef(funSym, Match(EmptyTree, catches1))
- }
- val expr2 = localTyper.typedPos(pos) {
- Apply(Select(expr1, expr1.tpe.member(cpsNames.flatMapCatch)), List(Ident(funSym)))
- }
-
- val exSym = currentOwner.newValueParameter(cpsNames.ex, pos).setInfo(ThrowableTpe)
-
- import CODE._
- // generate a case that is supported directly by the back-end
- val catchIfDefined = CaseDef(
- Bind(exSym, Ident(nme.WILDCARD)),
- EmptyTree,
- IF ((REF(funSym) DOT nme.isDefinedAt)(REF(exSym))) THEN (REF(funSym) APPLY (REF(exSym))) ELSE Throw(REF(exSym))
- )
-
- val catch2 = localTyper.typedCases(List(catchIfDefined), ThrowableTpe, targettp)
- //typedCases(tree, catches, ThrowableTpe, pt)
-
- patmatTransformer.transform(localTyper.typed(Block(List(funDef), treeCopy.Try(tree, treeCopy.Block(block1, stms, expr2), catch2, finalizer1))))
-
-
-/*
- disabled for now - see notes above
-
- val expr3 = if (!finalizer.isEmpty) {
- val pos = finalizer.pos
- val finalizer2 = duplicateTree(finalizer1)
- val fun = Function(List(), finalizer2)
- val expr3 = localTyper.typedPos(pos) { Apply(Select(expr2, expr2.tpe.member("mapFinally")), List(fun)) }
-
- val chown = new ChangeOwnerTraverser(currentOwner, fun.symbol)
- chown.traverse(finalizer2)
-
- expr3
- } else
- expr2
-*/
- } else {
- treeCopy.Try(tree, block1, catches1, finalizer1)
- }
-
- case Block(stms, expr) =>
-
- val (stms1, expr1) = transBlock(stms, expr)
- treeCopy.Block(tree, stms1, expr1)
-
- case _ =>
- super.transform(tree)
- }
- }
-
-
-
- def transBlock(stms: List[Tree], expr: Tree): (List[Tree], Tree) = {
-
- stms match {
- case Nil =>
- (Nil, transform(expr))
-
- case stm::rest =>
-
- stm match {
- case vd @ ValDef(mods, name, tpt, rhs)
- if (vd.symbol.hasAnnotation(MarkerCPSSym)) =>
-
- debuglog("found marked ValDef "+name+" of type " + vd.symbol.tpe)
-
- val tpe = vd.symbol.tpe
- val rhs1 = atOwner(vd.symbol) { transform(rhs) }
- rhs1.changeOwner(vd.symbol -> currentOwner) // TODO: don't traverse twice
-
- debuglog("valdef symbol " + vd.symbol + " has type " + tpe)
- debuglog("right hand side " + rhs1 + " has type " + rhs1.tpe)
-
- debuglog("currentOwner: " + currentOwner)
- debuglog("currentMethod: " + currentMethod)
-
- val (bodyStms, bodyExpr) = transBlock(rest, expr)
- // FIXME: result will later be traversed again by TreeSymSubstituter and
- // ChangeOwnerTraverser => exp. running time.
- // Should be changed to fuse traversals into one.
-
- val specialCaseTrivial = bodyExpr match {
- case Apply(fun, args) =>
- // for now, look for explicit tail calls only.
- // are there other cases that could profit from specializing on
- // trivial contexts as well?
- (bodyExpr.tpe.typeSymbol == Context) && (currentMethod == fun.symbol)
- case _ => false
- }
-
- def applyTrivial(ctxValSym: Symbol, body: Tree) = {
-
- val body1 = (new TreeSymSubstituter(List(vd.symbol), List(ctxValSym)))(body)
-
- val body2 = localTyper.typedPos(vd.symbol.pos) { body1 }
-
- // in theory it would be nicer to look for an @cps annotation instead
- // of testing for Context
- if ((body2.tpe == null) || !(body2.tpe.typeSymbol == Context)) {
- //println(body2 + "/" + body2.tpe)
- unit.error(rhs.pos, "cannot compute type for CPS-transformed function result")
- }
- body2
- }
-
- def applyCombinatorFun(ctxR: Tree, body: Tree) = {
- val arg = currentOwner.newValueParameter(name, ctxR.pos).setInfo(tpe)
- val body1 = (new TreeSymSubstituter(List(vd.symbol), List(arg)))(body)
- val fun = localTyper.typedPos(vd.symbol.pos) { Function(List(ValDef(arg)), body1) } // types body as well
- arg.owner = fun.symbol
- body1.changeOwner(currentOwner -> fun.symbol)
-
- // see note about multiple traversals above
-
- debuglog("fun.symbol: "+fun.symbol)
- debuglog("fun.symbol.owner: "+fun.symbol.owner)
- debuglog("arg.owner: "+arg.owner)
-
- debuglog("fun.tpe:"+fun.tpe)
- debuglog("return type of fun:"+body1.tpe)
-
- var methodName = nme.map
-
- if (body1.tpe != null) {
- if (body1.tpe.typeSymbol == Context)
- methodName = nme.flatMap
- }
- else
- unit.error(rhs.pos, "cannot compute type for CPS-transformed function result")
-
- debuglog("will use method:"+methodName)
-
- localTyper.typedPos(vd.symbol.pos) {
- Apply(Select(ctxR, ctxR.tpe.member(methodName)), List(fun))
- }
- }
-
- // TODO use gen.mkBlock after 2.11.0-M6. Why wait? It allows us to still build in development
- // mode with `ant -DskipLocker=1`
- def mkBlock(stms: List[Tree], expr: Tree) = if (stms.nonEmpty) Block(stms, expr) else expr
-
- try {
- if (specialCaseTrivial) {
- debuglog("will optimize possible tail call: " + bodyExpr)
-
- // FIXME: flatMap impl has become more complicated due to
- // exceptions. do we need to put a try/catch in the then part??
-
- // val ctx = <rhs>
- // if (ctx.isTrivial)
- // val <lhs> = ctx.getTrivialValue; ... <--- TODO: try/catch ??? don't bother for the moment...
- // else
- // ctx.flatMap { <lhs> => ... }
- val ctxSym = currentOwner.newValue(newTermName("" + vd.symbol.name + cpsNames.shiftSuffix)).setInfo(rhs1.tpe)
- val ctxDef = localTyper.typed(ValDef(ctxSym, rhs1))
- def ctxRef = localTyper.typed(Ident(ctxSym))
- val argSym = currentOwner.newValue(vd.symbol.name.toTermName).setInfo(tpe)
- val argDef = localTyper.typed(ValDef(argSym, Select(ctxRef, ctxRef.tpe.member(cpsNames.getTrivialValue))))
- val switchExpr = localTyper.typedPos(vd.symbol.pos) {
- val body2 = mkBlock(bodyStms, bodyExpr).duplicate // dup before typing!
- If(Select(ctxRef, ctxSym.tpe.member(cpsNames.isTrivial)),
- applyTrivial(argSym, mkBlock(argDef::bodyStms, bodyExpr)),
- applyCombinatorFun(ctxRef, body2))
- }
- (List(ctxDef), switchExpr)
- } else {
- // ctx.flatMap { <lhs> => ... }
- // or
- // ctx.map { <lhs> => ... }
- (Nil, applyCombinatorFun(rhs1, mkBlock(bodyStms, bodyExpr)))
- }
- } catch {
- case ex:TypeError =>
- unit.error(ex.pos, ex.msg)
- (bodyStms, bodyExpr)
- }
-
- case _ =>
- val stm1 = transform(stm)
- val (a, b) = transBlock(rest, expr)
- (stm1::a, b)
- }
- }
- }
-
-
- }
-}
diff --git a/src/continuations/plugin/scalac-plugin.xml b/src/continuations/plugin/scalac-plugin.xml
deleted file mode 100644
index 04d42655c5..0000000000
--- a/src/continuations/plugin/scalac-plugin.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- $Id$ -->
-<plugin>
- <name>continuations</name>
- <classname>scala.tools.selectivecps.SelectiveCPSPlugin</classname>
-</plugin>