From f584d243487dcd1214291167707e2f53fef5ab5e Mon Sep 17 00:00:00 2001 From: Tiark Rompf Date: Thu, 11 Mar 2010 16:55:38 +0000 Subject: moved the continuations plugin into trunk. --- .../tools/selectivecps/CPSAnnotationChecker.scala | 448 +++++++++++++++++++++ .../plugin/scala/tools/selectivecps/CPSUtils.scala | 131 ++++++ .../tools/selectivecps/SelectiveANFTransform.scala | 389 ++++++++++++++++++ .../tools/selectivecps/SelectiveCPSPlugin.scala | 61 +++ .../tools/selectivecps/SelectiveCPSTransform.scala | 250 ++++++++++++ src/continuations/plugin/scalac-plugin.xml | 5 + 6 files changed, 1284 insertions(+) create mode 100644 src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala create mode 100644 src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala create mode 100644 src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala create mode 100644 src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala create mode 100644 src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala create mode 100644 src/continuations/plugin/scalac-plugin.xml (limited to 'src/continuations/plugin') diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala new file mode 100644 index 0000000000..dc7c297b92 --- /dev/null +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -0,0 +1,448 @@ +// $Id$ + +package scala.tools.selectivecps + +import scala.tools.nsc.Global + +import scala.collection.mutable.{Map, HashMap} + +import java.io.{StringWriter, PrintWriter} + +abstract class CPSAnnotationChecker extends CPSUtils { + val global: Global + import global._ + import definitions._ + + //override val verbose = true + + /** + * Checks whether @cps annotations conform + */ + object checker extends AnnotationChecker { + + /** 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 = filterAttribs(tpe1,MarkerCPSTypes) + val annots2 = filterAttribs(tpe2,MarkerCPSTypes) + + // @plus and @minus should only occur at the left, and never together + // TODO: insert check + val adaptPlusAnnots1 = filterAttribs(tpe1,MarkerCPSAdaptPlus) + val adaptMinusAnnots1 = filterAttribs(tpe1,MarkerCPSAdaptMinus) + + // @minus @cps is the same as no annotations + if (!adaptMinusAnnots1.isEmpty) + return annots2.isEmpty + + // to handle answer type modification, we must make @plus <:< @cps + if (!adaptPlusAnnots1.isEmpty && 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 + +/* + hack no longer needed since introduction of adaptBoundsToAnnotations! + + // special treatment of type parameter bounds + if ((tpe2.typeSymbol eq AnyClass)) { + // This is an ugly hack to allow instantiating Functions with an @cps + // return type. A better way would be to make sure everything goes through adapt, + // but that's a bit of work. Alternatively, an explicit hook could be added in + // Inferencer.checkBounds + val w = new StringWriter() + new Exception().printStackTrace(new PrintWriter(w, true)) + if (w.toString.contains("scala.tools.nsc.typechecker.Infer$Inferencer.checkBounds")) { + vprintln("Testing whether " + tpe1 + " <:< " + tpe2 + ". We're inside Inferencer.checkBounds, so we just return true.") + return true + } + } +*/ + 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 = filterAttribs(tpe, MarkerCPSTypes) + val annots2 = ts flatMap (filterAttribs(_, MarkerCPSTypes)) + + if (annots2.nonEmpty) { + val cpsLub = AnnotationInfo(global.lub(annots1:::annots2 map (_.atp)), Nil, Nil) + 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 = AnnotationInfo(appliedType(MarkerCPSTypes.tpe, List(NothingClass.tpe, AnyClass.tpe)), Nil, Nil) + if (isFunctionType(tparams.head.owner.tpe) || tparams.head.owner == PartialFunctionClass) { + vprintln("function bound: " + tparams.head.owner.tpe + "/"+bounds+"/"+targs) + if (targs.last.hasAnnotation(MarkerCPSTypes)) + bounds.reverse match { + case res::b if !res.hi.hasAnnotation(MarkerCPSTypes) => + (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) + if (targs.head.hasAnnotation(MarkerCPSTypes) && !bounds.head.hi.hasAnnotation(MarkerCPSTypes)) + TypeBounds(bounds.head.lo, bounds.head.hi.withAnnotation(anyAtCPS))::Nil + else bounds + } else + bounds + } + + + override def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = { + if (!cpsEnabled) return false + vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) + + val annots1 = filterAttribs(tree.tpe,MarkerCPSTypes) + val annots2 = filterAttribs(pt,MarkerCPSTypes) + + if ((mode & global.analyzer.PATTERNmode) != 0) { + //println("can adapt pattern annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) + if (!annots1.isEmpty) { + return true + } + } + +/* + //not precise enough + if ((mode & global.analyzer.TYPEmode) != 0 && (mode & global.analyzer.BYVALmode) != 0) { + //println("can adapt pattern annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) + if (!annots1.isEmpty) { + return true + } + } +*/ + + if ((mode & global.analyzer.EXPRmode) == 0) { + vprintln("only handling EXPRmode") + return false + } +/* + this interferes with overloading resolution + if ((mode & global.analyzer.BYVALmode) != 0 && tree.tpe <:< pt) { + vprintln("already compatible, can't adapt further") + return false + } +*/ + if ((mode & global.analyzer.EXPRmode) != 0) { + if ((annots1 corresponds annots2) { case (a1,a2) => a1.atp <:< a2.atp }) { + vprintln("already same, can't adapt further") + return false + } + + if (annots1.isEmpty && !annots2.isEmpty && ((mode & global.analyzer.BYVALmode) == 0)) { + //println("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) + val adapt = AnnotationInfo(MarkerCPSAdaptPlus.tpe, Nil, Nil) + if (!tree.tpe.annotations.contains(adapt)) { + // val base = tree.tpe <:< removeAllCPSAnnotations(pt) + // val known = global.analyzer.isFullyDefined(pt) + // println(same + "/" + base + "/" + known) + val same = true//annots2 forall { case AnnotationInfo(atp: TypeRef, _, _) => atp.typeArgs(0) =:= atp.typeArgs(1) } + // TBD: use same or not? + if (same) { + vprintln("yes we can!! (unit)") + return true + } + } + } else if (!annots1.isEmpty && ((mode & global.analyzer.BYVALmode) != 0)) { + val adapt = AnnotationInfo(MarkerCPSAdaptMinus.tpe, Nil, Nil) + if (!tree.tpe.annotations.contains(adapt)) { + vprintln("yes we can!! (byval)") + return true + } + } + } + false + } + + + override def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = { + if (!cpsEnabled) return tree + + // FIXME: there seems to be a problem when mode == 1 (expr, no poly) and + // there are wildcards inside an annotation (which we don't resolve yet) + // can we just instantiate things? <--- need to check this is still valid + + vprintln("adapt annotations " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) + + val annots1 = filterAttribs(tree.tpe,MarkerCPSTypes) + val annots2 = filterAttribs(pt,MarkerCPSTypes) + + if ((mode & global.analyzer.PATTERNmode) != 0) { + if (!annots1.isEmpty) { + return tree.setType(removeAllCPSAnnotations(tree.tpe)) + } + } + +/* + // doesn't work correctly -- still relying on addAnnotations to remove things from ValDef symbols + if ((mode & global.analyzer.TYPEmode) != 0 && (mode & global.analyzer.BYVALmode) != 0) { + if (!annots1.isEmpty) { + println("removing annotation from " + tree + "/" + tree.tpe) + val s = tree.setType(removeAllCPSAnnotations(tree.tpe)) + println(s) + s + } + } +*/ + + if ((mode & global.analyzer.EXPRmode) != 0) { + if (annots1.isEmpty && !annots2.isEmpty && ((mode & global.analyzer.BYVALmode) == 0)) { // 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) + + val adapt = AnnotationInfo(MarkerCPSAdaptPlus.tpe, Nil, Nil) + val same = true//annots2 forall { case AnnotationInfo(atp: TypeRef, _, _) => atp.typeArgs(0) =:= atp.typeArgs(1) } + // TBD: use same or not? see infer0.scala/infer1.scala + + // 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) + + // if (tree.tpe <:< removeAllCPSAnnotations(pt)) { + + //val known = global.analyzer.isFullyDefined(pt) + + if (same && !tree.tpe.annotations.contains(adapt)) { + if (true /*known*/) + return tree.setType(tree.tpe.withAnnotations(adapt::annots2)) // needed for #1807 + else + return tree.setType(tree.tpe.withAnnotations(adapt::Nil)) + } + tree + } else if (!annots1.isEmpty && ((mode & global.analyzer.BYVALmode) != 0)) { // dropping annotation + // add a marker annotation that will make tree.tpe behave as pt, subtyping wise + // tree will look like having no annotation + if (!tree.tpe.hasAnnotation(MarkerCPSAdaptMinus)) { + val adapt = AnnotationInfo(MarkerCPSAdaptMinus.tpe, Nil, Nil) + return tree.setType(tree.tpe.withAnnotations(adapt::Nil)) + } + } + } + tree + } + + + def updateAttributesFromChildren(tpe: Type, childAnnots: List[AnnotationInfo], byName: List[Tree]): Type = { + tpe match { + // 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 _ => + assert(childAnnots forall (_.atp.typeSymbol == MarkerCPSTypes), childAnnots) + /* + [] + [] = [] + plus + [] = plus + cps + [] = cps + plus cps + [] = plus cps + minus cps + [] = minus cp + 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 = tpe.hasAnnotation(MarkerCPSAdaptPlus) || (tpe.hasAnnotation(MarkerCPSTypes) && + byName.nonEmpty && byName.forall(_.tpe.hasAnnotation(MarkerCPSAdaptPlus))) + + // move @plus annotations outward from by-name children + if (childAnnots.isEmpty) { + if (plus) { // @plus or @plus @cps + for (t <- byName) { + //println("removeAnnotation " + t + " / " + t.tpe) + t.setType(removeAttribs(t.tpe, MarkerCPSAdaptPlus, MarkerCPSTypes)) + } + return tpe.withAnnotation(AnnotationInfo(MarkerCPSAdaptPlus.tpe, Nil, Nil)) + } else + return tpe + } + + val annots1 = filterAttribs(tpe, MarkerCPSTypes) + + if (annots1.isEmpty) { // nothing or @plus + val synth = MarkerCPSSynth.tpe + val annots2 = List(linearize(childAnnots)) + removeAttribs(tpe,MarkerCPSAdaptPlus).withAnnotations(AnnotationInfo(synth, Nil, Nil)::annots2) + } else { + val annot1 = single(annots1) + if (plus) { // @plus @cps + val synth = AnnotationInfo(MarkerCPSSynth.tpe, Nil, Nil) + val annot2 = linearize(childAnnots) + if (!(annot2.atp <:< annot1.atp)) + throw new TypeError(annot2 + " is not a subtype of " + annot1) + val res = removeAttribs(tpe, MarkerCPSAdaptPlus, MarkerCPSTypes).withAnnotations(List(synth, annot2)) + for (t <- byName) { + //println("removeAnnotation " + t + " / " + t.tpe) + t.setType(removeAttribs(t.tpe, MarkerCPSAdaptPlus, MarkerCPSTypes)) + } + res + } else if (tpe.hasAnnotation(MarkerCPSSynth)) { // @synth @cps + val annot2 = linearize(childAnnots) + if (!(annot2.atp <:< annot1.atp)) + throw new TypeError(annot2 + " is not a subtype of " + annot1) + removeAttribs(tpe, MarkerCPSTypes).withAnnotation(annot2) + } else { // @cps + removeAttribs(tpe, MarkerCPSTypes).withAnnotation(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(_, sym, List(elemtp)) if sym == ByNameParamClass => + 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.error("not a single cps annotation: " + xs)// FIXME: error message + xs(0) + } + + def transChildrenInOrder(tree: Tree, tpe: Type, childTrees: List[Tree], byName: List[Tree]) = { + val children = childTrees.flatMap { t => + if (t.tpe eq null) Nil else { + val types = filterAttribs(t.tpe, MarkerCPSTypes) + // TODO: check that it has been adapted and if so correctly + if (types.isEmpty) Nil else List(single(types)) + } + } + + 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 addAnnotations(tree: Tree, tpe: Type): Type = { + if (!cpsEnabled) return tpe + +// if (tree.tpe.hasAnnotation(MarkerCPSAdaptPlus)) +// println("addAnnotation " + tree + "/" + tpe) + + tree match { + + case Apply(fun @ Select(qual, name), args) if (fun.tpe ne null) && !fun.tpe.isErroneous => + + // 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(fun, args) if (fun.tpe ne null) && !fun.tpe.isErroneous => + + vprintln("[checker] checking unknown apply " + tree + "/" + tpe) + + transChildrenInOrder(tree, tpe, fun::(transArgList(fun, args).flatten), Nil) + + case TypeApply(fun, args) => + + vprintln("[checker] checking type apply " + tree + "/" + tpe) + + transChildrenInOrder(tree, tpe, List(fun), Nil) + + case Select(qual, name) => + + vprintln("[checker] checking select " + tree + "/" + tpe) + + // FIXME: put it back in?? (problem with test cases select.scala and Test2.scala) + // transChildrenInOrder(tree, tpe, List(qual)) + 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 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! + if (hasAnswerTypeAnn(tree.symbol.info)) { // is it okay to modify sym here? + vprintln("removing annotation from sym " + tree.symbol + "/" + tree.symbol.tpe + "/" + tpt) + tpt.setType(removeAllCPSAnnotations(tpt.tpe)) + tree.symbol.setInfo(removeAllCPSAnnotations(tree.symbol.info)) + } + tpe + + case _ => + tpe + } + + + } + } +} diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala new file mode 100644 index 0000000000..3e1f05e731 --- /dev/null +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -0,0 +1,131 @@ +// $Id$ + +package scala.tools.selectivecps + +import scala.tools.nsc.Global + +trait CPSUtils { + val global: Global + import global._ + import definitions._ + + var cpsEnabled = false + val verbose: Boolean = System.getProperty("cpsVerbose", "false") == "true" + def vprintln(x: =>Any): Unit = if (verbose) println(x) + + + lazy val MarkerCPSSym = definitions.getClass("scala.util.continuations.cpsSym") + lazy val MarkerCPSTypes = definitions.getClass("scala.util.continuations.cpsParam") + lazy val MarkerCPSSynth = definitions.getClass("scala.util.continuations.cpsSynth") + + lazy val MarkerCPSAdaptPlus = definitions.getClass("scala.util.continuations.cpsPlus") + lazy val MarkerCPSAdaptMinus = definitions.getClass("scala.util.continuations.cpsMinus") + + + lazy val Context = definitions.getClass("scala.util.continuations.ControlContext") + + lazy val ModCPS = definitions.getModule("scala.util.continuations") + lazy val MethShiftUnit = definitions.getMember(ModCPS, "shiftUnit") + lazy val MethShiftUnitR = definitions.getMember(ModCPS, "shiftUnitR") + lazy val MethShift = definitions.getMember(ModCPS, "shift") + lazy val MethShiftR = definitions.getMember(ModCPS, "shiftR") + lazy val MethReify = definitions.getMember(ModCPS, "reify") + lazy val MethReifyR = definitions.getMember(ModCPS, "reifyR") + + + lazy val allCPSAnnotations = List(MarkerCPSSym, MarkerCPSTypes, MarkerCPSSynth, + MarkerCPSAdaptPlus, MarkerCPSAdaptMinus) + + // annotation checker + + def filterAttribs(tpe:Type, cls:Symbol) = + tpe.annotations.filter(_.atp.typeSymbol == cls) + + def removeAttribs(tpe:Type, cls:Symbol*) = + tpe.withoutAnnotations.withAnnotations(tpe.annotations.filterNot(cls contains _.atp.typeSymbol)) + + def removeAllCPSAnnotations(tpe: Type) = removeAttribs(tpe, allCPSAnnotations:_*) + + def linearize(ann: List[AnnotationInfo]): AnnotationInfo = { + ann.reduceLeft { (a, b) => + val atp0::atp1::Nil = a.atp.normalize.typeArgs + val btp0::btp1::Nil = b.atp.normalize.typeArgs + val (u0,v0) = (atp0, atp1) + val (u1,v1) = (btp0, btp1) +/* + val (u0,v0) = (a.atp.typeArgs(0), a.atp.typeArgs(1)) + val (u1,v1) = (b.atp.typeArgs(0), b.atp.typeArgs(1)) + vprintln("check lin " + a + " andThen " + b) +*/ + vprintln("check lin " + a + " andThen " + b) + if (!(v1 <:< u0)) + throw new TypeError("illegal answer type modification: " + a + " andThen " + b) + // TODO: improve error message (but it is not very common) + AnnotationInfo(appliedType(MarkerCPSTypes.tpe, List(u1,v0)),Nil,Nil) + } + } + + // anf transform + + def getExternalAnswerTypeAnn(tp: Type) = { + tp.annotations.find(a => a.atp.typeSymbol == MarkerCPSTypes) match { + case Some(AnnotationInfo(atp, _, _)) => + val atp0::atp1::Nil = atp.normalize.typeArgs + Some((atp0, atp1)) + case None => + if (tp.hasAnnotation(MarkerCPSAdaptPlus)) + global.warning("trying to instantiate type " + tp + " to unknown cps type") + None + } + } + + def getAnswerTypeAnn(tp: Type) = { + tp.annotations.find(a => a.atp.typeSymbol == MarkerCPSTypes) match { + case Some(AnnotationInfo(atp, _, _)) => + if (!tp.hasAnnotation(MarkerCPSAdaptPlus)) {//&& !tp.hasAnnotation(MarkerCPSAdaptMinus)) + val atp0::atp1::Nil = atp.normalize.typeArgs + Some((atp0, atp1)) + } else + None + case None => None + } + } + + def hasAnswerTypeAnn(tp: Type) = { + tp.hasAnnotation(MarkerCPSTypes) && !tp.hasAnnotation(MarkerCPSAdaptPlus) /*&& + !tp.hasAnnotation(MarkerCPSAdaptMinus)*/ + } + + def hasSynthAnn(tp: Type) = { + tp.annotations.exists(a => a.atp.typeSymbol == MarkerCPSSynth) + } + + def updateSynthFlag(tree: Tree) = { // remove annotations if *we* added them (@synth present) + if (hasSynthAnn(tree.tpe)) { + log("removing annotation from " + tree) + tree.setType(removeAllCPSAnnotations(tree.tpe)) + } 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 + } + } + + // cps transform + +} \ No newline at end of file diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala new file mode 100644 index 0000000000..2b6ce67ef4 --- /dev/null +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -0,0 +1,389 @@ +// $Id$ + +package scala.tools.selectivecps + +import scala.tools.nsc._ +import scala.tools.nsc.transform._ +import scala.tools.nsc.symtab._ +import scala.tools.nsc.plugins._ + +import scala.tools.nsc.ast._ + +/** + * 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 + + /** 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) + + override def transform(tree: Tree): Tree = { + if (!cpsEnabled) return tree + + tree match { + + // TODO: 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, rhs) => + log("transforming " + dd.symbol) + + atOwner(dd.symbol) { + val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe)) + + log("result "+rhs1) + log("result is of type "+rhs1.tpe) + + treeCopy.DefDef(dd, mods, name, transformTypeDefs(tparams), transformValDefss(vparamss), + transform(tpt), rhs1) + } + + case ff @ Function(vparams, body) => + log("transforming anon function " + ff.symbol) + + atOwner(ff.symbol) { + val body1 = transExpr(body, None, getExternalAnswerTypeAnn(body.tpe)) + + log("result "+body1) + log("result is of type "+body1.tpe) + + treeCopy.Function(ff, transformValDefs(vparams), body1) + } + + case vd @ ValDef(mods, name, tpt, rhs) => // object-level valdefs + log("transforming valdef " + vd.symbol) + + atOwner(vd.symbol) { + + assert(getExternalAnswerTypeAnn(tpt.tpe) == None) + + val rhs1 = transExpr(rhs, None, None) + + treeCopy.ValDef(vd, mods, name, transform(tpt), rhs1) + } + + 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) + 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): Tree = { + transTailValue(tree, cpsA, cpsR) match { + case (Nil, b) => b + case (a, b) => + treeCopy.Block(tree, a,b) + } + } + + + def transArgList(fun: Tree, args: List[Tree], cpsA: CPSInfo): (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(_, sym, List(elemtp)) if sym == ByNameParamClass => + (Nil, transExpr(a, None, getAnswerTypeAnn(elemtp))) + case _ => + val (valStm, valExpr, valSpc) = transInlineValue(a, spc) + spc = valSpc + (valStm, valExpr) + } + }).unzip + + (stm,expr,spc) + } + + + def transValue(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo): (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) + + val tree1 = (treeCopy.Block(tree, a, b)) // no updateSynthFlag here!!! + + (Nil, tree1, cpsA) + + case If(cond, thenp, elsep) => + + val (condStats, condVal, spc) = transInlineValue(cond, cpsA) + + val (cpsA2, cpsR2) = (spc, linearize(spc, getAnswerTypeAnn(tree.tpe))) +// val (cpsA2, cpsR2) = (None, getAnswerTypeAnn(tree.tpe)) + val thenVal = transExpr(thenp, cpsA2, cpsR2) + val elseVal = transExpr(elsep, cpsA2, cpsR2) + + // 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) = (spc, linearize(spc, getAnswerTypeAnn(tree.tpe))) +// val (cpsA2, cpsR2) = (None, getAnswerTypeAnn(tree.tpe)) + + val caseVals = for { + cd @ CaseDef(pat, guard, body) <- cases + val bodyVal = transExpr(body, cpsA2, cpsR2) + } yield { + treeCopy.CaseDef(cd, transform(pat), transform(guard), bodyVal) + } + + (selStats, updateSynthFlag(treeCopy.Match(tree, selVal, caseVals)), spc) + + + case ldef @ LabelDef(name, params, rhs) => + if (hasAnswerTypeAnn(tree.tpe)) { + val sym = currentOwner.newMethod(tree.pos, name)//unit.fresh.newName(tree.pos, "myloopvar")) + .setInfo(ldef.symbol.info) + .setFlag(Flags.SYNTHETIC) + + val subst = new TreeSymSubstituter(List(ldef.symbol), List(sym)) + val rhsVal = transExpr(subst(rhs), None, getAnswerTypeAnn(tree.tpe)) + + val stm1 = localTyper.typed(DefDef(sym, rhsVal)) + val expr = localTyper.typed(Apply(Ident(sym), List())) + + (List(stm1), expr, cpsA) + } else { + val rhsVal = transExpr(rhs, None, None) + (Nil, updateSynthFlag(treeCopy.LabelDef(tree, name, params, rhsVal)), cpsA) + } + + + case Try(block, catches, finalizer) => + // no cps code allowed in try/catch/finally! + val blockVal = transExpr(block, None, None) + + val catchVals = for { + cd @ CaseDef(pat, guard, body) <- catches + val bodyVal = transExpr(body, None, None) + } yield { + treeCopy.CaseDef(cd, transform(pat), transform(guard), bodyVal) + } + + val finallyVal = transExpr(finalizer, None, None) + + (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) => + 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? + val (stms, expr, spc) = transInlineValue(expr0, cpsA) + val tpt1 = treeCopy.TypeTree(tpt).setType(removeAllCPSAnnotations(tpt.tpe)) + (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) + } + } + + def transTailValue(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo): (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! + log("cps type conversion (has: " + cpsA + "/" + spc + "/" + expr.tpe + ")") + log("cps type conversion (expected: " + cpsR.get + "): " + expr) + + if (!expr.tpe.hasAnnotation(MarkerCPSAdaptPlus)) + unit.warning(tree.pos, "expression " + tree + " is cps-transformed unexpectedly") + + try { + val Some((a, b)) = cpsR + + val res = localTyper.typed(atPos(tree.pos) { + Apply(TypeApply(gen.mkAttributedRef(MethShiftUnit), + List(TypeTree(plainTpe), TypeTree(a), TypeTree(b))), + List(expr)) + }) + return (stms, res) + + } catch { + case ex:TypeError => + unit.error(ex.pos, "cannot cps-transform expression " + tree + ": " + ex.msg) + } + } + + } else if (!cpsR.isDefined && bot.isDefined) { + // error! + log("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 (expr.tpe.hasAnnotation(MarkerCPSAdaptPlus)) { + unit.warning(tree.pos, "expression " + expr + "/" + expr.tpe + " should not have cps-plus annotation") + expr.setType(removeAllCPSAnnotations(expr.tpe)) + } + + // TODO: sanity check that types agree + } + + (stms, expr) + } + + def transInlineValue(tree: Tree, cpsA: CPSInfo): (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 = currentOwner.newValue(tree.pos, unit.fresh.newName(tree.pos, "tmp")) + .setInfo(valueTpe) + .setFlag(Flags.SYNTHETIC) + .setAnnotations(List(AnnotationInfo(MarkerCPSSym.tpe, Nil, Nil))) + + (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): (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) + if (spcVal.isDefined) { + 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)) + } + } + + def transBlock(stms: List[Tree], expr: Tree, cpsA: CPSInfo, cpsR: CPSInfo): (List[Tree], Tree) = { + stms match { + case Nil => + transTailValue(expr, cpsA, cpsR) + + case stm::rest => + var (rest2, expr2) = (rest, expr) + val (headStms, headSpc) = transInlineStm(stm, cpsA) + val (restStms, restExpr) = transBlock(rest2, expr2, headSpc, cpsR) + (headStms:::restStms, restExpr) + } + } + + + } +} diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala new file mode 100644 index 0000000000..7c56a78491 --- /dev/null +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala @@ -0,0 +1,61 @@ +// $Id$ + +package scala.tools.selectivecps + +import scala.tools.nsc +import scala.tools.nsc.typechecker._ +import nsc.Global +import nsc.Phase +import nsc.plugins.Plugin +import nsc.plugins.PluginComponent + +class SelectiveCPSPlugin(val global: Global) extends Plugin { + import global._ + + val name = "continuations" + val description = "applies selective cps conversion" + + val anfPhase = new SelectiveANFTransform() { + val global = SelectiveCPSPlugin.this.global + val runsAfter = List("pickler") + } + + val cpsPhase = new SelectiveCPSTransform() { + val global = SelectiveCPSPlugin.this.global + val runsAfter = List("selectiveanf") + } + + + val components = List[PluginComponent](anfPhase, cpsPhase) + + val checker = new CPSAnnotationChecker { + val global: SelectiveCPSPlugin.this.global.type = SelectiveCPSPlugin.this.global + } + global.addAnnotationChecker(checker.checker) + + + def setEnabled(flag: Boolean) = { + checker.cpsEnabled = flag + anfPhase.cpsEnabled = flag + cpsPhase.cpsEnabled = flag + } + + // TODO: require -enabled command-line flag + + override def processOptions(options: List[String], error: String => Unit) = { + var enabled = false + for (option <- options) { + if (option == "enable") { + enabled = true + } else { + error("Option not understood: "+option) + } + } + setEnabled(enabled) + } + + override val optionsHelp: Option[String] = + Some(" -P:continuations:enable Enable continuations") +// " -sourcepath Specify where to find input source files" + +} diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala new file mode 100644 index 0000000000..b906c12568 --- /dev/null +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -0,0 +1,250 @@ +// $Id$ + +package scala.tools.selectivecps + +import scala.collection._ + +import scala.tools.nsc._ +import scala.tools.nsc.transform._ +import scala.tools.nsc.plugins._ + +import scala.tools.nsc.ast.TreeBrowsers +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 { + // 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 + + /** the following two members override abstract members in Transform */ + val phaseName: String = "selectivecps" + + protected def newTransformer(unit: CompilationUnit): Transformer = + new CPSTransformer(unit) + + + /** - return symbol's transformed type, + */ + def transformInfo(sym: Symbol, tp: Type): Type = { + if (!cpsEnabled) return tp + + val newtp = transformCPSType(tp) + + if (newtp != tp) + log("transformInfo changed type for " + sym + " to " + newtp); + + if (sym == MethReifyR) + log("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 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.tpe, List(removeAllCPSAnnotations(tp), res, outer)) + case _ => + removeAllCPSAnnotations(tp) + } + } + } + + + class CPSTransformer(unit: CompilationUnit) extends TypingTransformer(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) => + log("found shift: " + tree) + atPos(tree.pos) { + val funR = gen.mkAttributedRef(MethShiftR) // TODO: correct? + log(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) => + log("found shiftUnit: " + tree) + atPos(tree.pos) { + val funR = gen.mkAttributedRef(MethShiftUnitR) // TODO: correct? + log(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.tpe, 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? + log(funR.tpe) + Apply( + TypeApply(funR, targs).setType(appliedType(funR.tpe, targs.map((t:Tree) => t.tpe))), + args.map(transform(_)) + ).setType(transformCPSType(tree.tpe)) + } + + 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)) => + + log("found marked ValDef "+name+" of type " + vd.symbol.tpe) + + val tpe = vd.symbol.tpe + val rhs1 = transform(rhs) + + log("valdef symbol " + vd.symbol + " has type " + tpe) + log("right hand side " + rhs1 + " has type " + rhs1.tpe) + + log("currentOwner: " + currentOwner) + log("currentMethod: " + currentMethod) + + + val (bodyStms, bodyExpr) = transBlock(rest, expr) + + 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) = { + + new TreeSymSubstituter(List(vd.symbol), List(ctxValSym)).traverse(body) + + val body2 = localTyper.typed(atPos(vd.symbol.pos) { body }) + + if ((body2.tpe == null) || !(body2.tpe.typeSymbol.tpe <:< Context.tpe)) { + 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(ctxR.pos, name).setInfo(tpe) + new TreeSymSubstituter(List(vd.symbol), List(arg)).traverse(body) + val fun = localTyper.typed(atPos(vd.symbol.pos) { Function(List(ValDef(arg)), body) }) // types body as well + arg.owner = fun.symbol + new ChangeOwnerTraverser(currentOwner, fun.symbol).traverse(body) + + log("fun.symbol: "+fun.symbol) + log("fun.symbol.owner: "+fun.symbol.owner) + log("arg.owner: "+arg.owner) + + log("fun.tpe:"+fun.tpe) + log("return type of fun:"+body.tpe) + + var methodName = "map" + + if (body.tpe != null) { + if (body.tpe.typeSymbol.tpe <:< Context.tpe) + methodName = "flatMap" + } + else + unit.error(rhs.pos, "cannot compute type for CPS-transformed function result") + + log("will use method:"+methodName) + + localTyper.typed(atPos(vd.symbol.pos) { + Apply(Select(ctxR, ctxR.tpe.member(methodName)), List(fun)) + }) + } + + try { + if (specialCaseTrivial) { + log("will optimize possible tail call: " + bodyExpr) + // val ctx = + // if (ctx.isTrivial) + // val = ctx.getTrivialValue; ... + // else + // ctx.flatMap { => ... } + val ctxSym = currentOwner.newValue(vd.symbol.name + "$shift").setInfo(rhs1.tpe) + val ctxDef = localTyper.typed(ValDef(ctxSym, rhs1)) + def ctxRef = localTyper.typed(Ident(ctxSym)) + val argSym = currentOwner.newValue(vd.symbol.name).setInfo(tpe) + val argDef = localTyper.typed(ValDef(argSym, Select(ctxRef, ctxRef.tpe.member("getTrivialValue")))) + val switchExpr = localTyper.typed(atPos(vd.symbol.pos) { + val body2 = duplicateTree(Block(bodyStms, bodyExpr)) // dup before typing! + If(Select(ctxRef, ctxSym.tpe.member("isTrivial")), + applyTrivial(argSym, Block(argDef::bodyStms, bodyExpr)), + applyCombinatorFun(ctxRef, body2)) + }) + (List(ctxDef), switchExpr) + } else { + // ctx.flatMap { => ... } + // or + // ctx.map { => ... } + (Nil, applyCombinatorFun(rhs1, Block(bodyStms, bodyExpr))) + } + } catch { + case ex:TypeError => + unit.error(ex.pos, ex.msg) + (bodyStms, bodyExpr) + } + + case _ => + val (a, b) = transBlock(rest, expr) + (transform(stm)::a, b) + } + } + } + + + } +} diff --git a/src/continuations/plugin/scalac-plugin.xml b/src/continuations/plugin/scalac-plugin.xml new file mode 100644 index 0000000000..04d42655c5 --- /dev/null +++ b/src/continuations/plugin/scalac-plugin.xml @@ -0,0 +1,5 @@ + + + continuations + scala.tools.selectivecps.SelectiveCPSPlugin + -- cgit v1.2.3