summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.xml157
-rw-r--r--src/continuations/library/scala/util/continuations/package.scala65
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala448
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala131
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala389
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala61
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala250
-rw-r--r--src/continuations/plugin/scalac-plugin.xml5
-rw-r--r--test/files/continuations-neg/function0.check6
-rw-r--r--test/files/continuations-neg/function0.scala16
-rw-r--r--test/files/continuations-neg/function2.check6
-rw-r--r--test/files/continuations-neg/function2.scala16
-rw-r--r--test/files/continuations-neg/function3.check6
-rw-r--r--test/files/continuations-neg/function3.scala15
-rw-r--r--test/files/continuations-neg/infer0.check4
-rw-r--r--test/files/continuations-neg/infer0.scala14
-rw-r--r--test/files/continuations-neg/infer2.check4
-rw-r--r--test/files/continuations-neg/infer2.scala19
-rw-r--r--test/files/continuations-neg/t1929.check6
-rw-r--r--test/files/continuations-neg/t1929.scala17
-rw-r--r--test/files/continuations-neg/t2285.check6
-rw-r--r--test/files/continuations-neg/t2285.scala11
-rw-r--r--test/files/continuations-neg/t2949.check6
-rw-r--r--test/files/continuations-neg/t2949.scala15
-rwxr-xr-xtest/files/continuations-run/basics.check2
-rwxr-xr-xtest/files/continuations-run/basics.scala23
-rw-r--r--test/files/continuations-run/function1.check1
-rw-r--r--test/files/continuations-run/function1.scala16
-rw-r--r--test/files/continuations-run/function4.check1
-rw-r--r--test/files/continuations-run/function4.scala15
-rw-r--r--test/files/continuations-run/function5.check1
-rw-r--r--test/files/continuations-run/function5.scala15
-rw-r--r--test/files/continuations-run/ifelse0.check2
-rw-r--r--test/files/continuations-run/ifelse0.scala18
-rw-r--r--test/files/continuations-run/ifelse1.check4
-rw-r--r--test/files/continuations-run/ifelse1.scala25
-rw-r--r--test/files/continuations-run/ifelse2.check4
-rw-r--r--test/files/continuations-run/ifelse2.scala16
-rw-r--r--test/files/continuations-run/ifelse3.check2
-rw-r--r--test/files/continuations-run/ifelse3.scala21
-rw-r--r--test/files/continuations-run/infer1.scala33
-rw-r--r--test/files/continuations-run/match0.check2
-rw-r--r--test/files/continuations-run/match0.scala18
-rw-r--r--test/files/continuations-run/match1.check2
-rw-r--r--test/files/continuations-run/match1.scala18
-rw-r--r--test/files/continuations-run/match2.check2
-rw-r--r--test/files/continuations-run/match2.scala26
-rw-r--r--test/files/continuations-run/t1807.check1
-rw-r--r--test/files/continuations-run/t1807.scala14
-rw-r--r--test/files/continuations-run/t1808.scala10
-rw-r--r--test/files/continuations-run/t1820.scala14
-rw-r--r--test/files/continuations-run/t1821.check4
-rw-r--r--test/files/continuations-run/t1821.scala20
-rw-r--r--test/files/continuations-run/while0.check1
-rw-r--r--test/files/continuations-run/while0.scala22
-rw-r--r--test/files/continuations-run/while1.check11
-rw-r--r--test/files/continuations-run/while1.scala22
-rw-r--r--test/files/continuations-run/while2.check19
-rw-r--r--test/files/continuations-run/while2.scala23
-rw-r--r--test/pending/continuations-run/example0.scala9
-rw-r--r--test/pending/continuations-run/example1.scala9
-rw-r--r--test/pending/continuations-run/example16.scala9
-rw-r--r--test/pending/continuations-run/example2.scala9
-rw-r--r--test/pending/continuations-run/example3.scala9
-rw-r--r--test/pending/continuations-run/example4.scala9
-rw-r--r--test/pending/continuations-run/example5.scala9
-rw-r--r--test/pending/continuations-run/example6.scala9
-rw-r--r--test/pending/continuations-run/example7.scala9
-rw-r--r--test/pending/continuations-run/example8.scala9
-rw-r--r--test/pending/continuations-run/example9.scala9
-rw-r--r--test/pending/continuations-run/foreach.check4
-rw-r--r--test/pending/continuations-run/foreach.scala33
-rw-r--r--test/pending/continuations-run/function6.scala15
-rw-r--r--test/pending/continuations-run/select-run.log20
-rw-r--r--test/pending/continuations-run/select.check2
-rw-r--r--test/pending/continuations-run/select.scala21
-rw-r--r--test/pending/continuations-run/t2864.scala18
-rw-r--r--test/pending/continuations-run/t2934.scala14
78 files changed, 2322 insertions, 5 deletions
diff --git a/build.xml b/build.xml
index 3e65916912..7c1a7ffa87 100644
--- a/build.xml
+++ b/build.xml
@@ -370,7 +370,7 @@ LOCAL REFERENCE BUILD (LOCKER)
<touch file="${build-locker.dir}/compiler.complete" verbose="no"/>
<stopwatch name="locker.comp.timer" action="total"/>
</target>
-
+
<target name="locker.done" depends="locker.comp">
<touch file="${build-locker.dir}/all.complete" verbose="no"/>
<path id="locker.classpath">
@@ -447,6 +447,7 @@ QUICK BUILD (QUICK)
<include name="library/**"/>
<include name="dbc/**"/>
<include name="actors/**"/>
+ <include name="continuations/**"/>
<include name="swing/**"/>
</srcfiles>
</uptodate>
@@ -598,7 +599,62 @@ QUICK BUILD (QUICK)
<stopwatch name="quick.comp.timer" action="total"/>
</target>
- <target name="quick.pre-scalap" depends="quick.comp">
+
+ <target name="quick.pre-plugins" depends="quick.comp" unless="quick.available">
+ <condition property="quick.plugins.needed">
+ <not><available file="${build-quick.dir}/plugins.complete"/></not>
+ </condition>
+ </target>
+
+ <target name="quick.plugins" depends="quick.pre-plugins" if="quick.plugins.needed">
+ <stopwatch name="quick.plugins.timer"/>
+ <mkdir dir="${build-quick.dir}/classes/continuations-plugin"/>
+ <scalacfork
+ destdir="${build-quick.dir}/classes/continuations-plugin"
+ compilerpathref="locker.classpath"
+ params="${scalac.args.quick}"
+ srcdir="${src.dir}/continuations/plugin"
+ jvmargs="${scalacfork.jvmargs}">
+ <include name="**/*.scala"/>
+ <compilationpath>
+ <pathelement location="${build-quick.dir}/classes/library"/>
+ <pathelement location="${build-quick.dir}/classes/compiler"/>
+ </compilationpath>
+ </scalacfork>
+ <copy todir="${build-quick.dir}/classes/continuations-plugin">
+ <fileset dir="${src.dir}/continuations/plugin">
+ <include name="**/*.tmpl"/>
+ <include name="**/*.xml"/>
+ <include name="**/*.js"/>
+ <include name="**/*.css"/>
+ <include name="**/*.properties"/>
+ <include name="**/*.swf"/>
+ <include name="**/*.png"/>
+ </fileset>
+ </copy>
+ <!-- not very nice to create jar here but needed to load plugin -->
+ <mkdir dir="${build-quick.dir}/plugins"/>
+ <jar destfile="${build-quick.dir}/plugins/continuations.jar">
+ <fileset dir="${build-quick.dir}/classes/continuations-plugin"/>
+ </jar>
+ <!-- might split off library part into its own ant target -->
+ <scalacfork
+ destdir="${build-quick.dir}/classes/library"
+ compilerpathref="locker.classpath"
+ params="${scalac.args.quick} -Xplugin:${build-quick.dir}/plugins/continuations.jar -Xplugin-require:continuations -P:continuations:enable"
+ srcdir="${src.dir}/continuations/library"
+ jvmargs="${scalacfork.jvmargs}">
+ <include name="**/*.scala"/>
+ <compilationpath>
+ <pathelement location="${build-quick.dir}/classes/library"/>
+ <pathelement location="${lib.dir}/forkjoin.jar"/>
+ </compilationpath>
+ </scalacfork>
+ <touch file="${build-quick.dir}/plugins.complete" verbose="no"/>
+ <stopwatch name="quick.plugins.timer" action="total"/>
+ </target>
+
+ <target name="quick.pre-scalap" depends="quick.plugins">
<uptodate property="quick.scalap.available" targetfile="${build-quick.dir}/scalap.complete">
<srcfiles dir="${src.dir}/scalap"/>
</uptodate>
@@ -802,7 +858,21 @@ PACKED QUICK BUILD (PACK)
<copy file="${jline.jar}" toDir="${build-pack.dir}/lib"/>
</target>
- <target name="pack.pre-partest" depends="pack.comp">
+ <target name="pack.pre-plugins" depends="pack.comp">
+ <uptodate
+ property="pack.plugins.available"
+ targetfile="${build-pack.dir}/plugins/continuations.jar"
+ srcfile="${build-quick.dir}/plugins.complete"/>
+ </target>
+
+ <target name="pack.plugins" depends="pack.pre-plugins" unless="pack.plugins.available">
+ <mkdir dir="${build-pack.dir}/plugins"/>
+ <jar destfile="${build-pack.dir}/plugins/continuations.jar">
+ <fileset dir="${build-quick.dir}/classes/continuations-plugin"/>
+ </jar>
+ </target>
+
+ <target name="pack.pre-partest" depends="pack.plugins">
<uptodate
property="pack.partest.available"
targetfile="${build-pack.dir}/lib/scala-partest.jar"
@@ -1041,7 +1111,61 @@ BOOTSTRAPPING BUILD (STRAP)
<stopwatch name="strap.comp.timer" action="total"/>
</target>
- <target name="strap.pre-scalap" depends="strap.comp">
+ <target name="strap.pre-plugins" depends="strap.comp" unless="strap.available">
+ <condition property="strap.plugins.needed">
+ <not><available file="${build-strap.dir}/plugins.complete"/></not>
+ </condition>
+ </target>
+
+ <target name="strap.plugins" depends="strap.pre-plugins" if="strap.plugins.needed">
+ <stopwatch name="strap.plugins.timer"/>
+ <mkdir dir="${build-strap.dir}/classes/continuations-plugin"/>
+ <scalacfork
+ destdir="${build-strap.dir}/classes/continuations-plugin"
+ compilerpathref="pack.classpath"
+ params="${scalac.args.quick}"
+ srcdir="${src.dir}/continuations/plugin"
+ jvmargs="${scalacfork.jvmargs}">
+ <include name="**/*.scala"/>
+ <compilationpath>
+ <pathelement location="${build-strap.dir}/classes/library"/>
+ <pathelement location="${build-strap.dir}/classes/compiler"/>
+ </compilationpath>
+ </scalacfork>
+ <copy todir="${build-strap.dir}/classes/continuations-plugin">
+ <fileset dir="${src.dir}/continuations/plugin">
+ <include name="**/*.tmpl"/>
+ <include name="**/*.xml"/>
+ <include name="**/*.js"/>
+ <include name="**/*.css"/>
+ <include name="**/*.properties"/>
+ <include name="**/*.swf"/>
+ <include name="**/*.png"/>
+ </fileset>
+ </copy>
+ <!-- not very nice to create jar here but needed to load plugin -->
+ <mkdir dir="${build-strap.dir}/plugins"/>
+ <jar destfile="${build-strap.dir}/plugins/continuations.jar">
+ <fileset dir="${build-strap.dir}/classes/continuations-plugin"/>
+ </jar>
+ <!-- might split off library part into its own ant target -->
+ <scalacfork
+ destdir="${build-strap.dir}/classes/library"
+ compilerpathref="pack.classpath"
+ params="${scalac.args.quick} -Xplugin:${build-strap.dir}/plugins/continuations.jar -Xplugin-require:continuations -P:continuations:enable"
+ srcdir="${src.dir}/continuations/library"
+ jvmargs="${scalacfork.jvmargs}">
+ <include name="**/*.scala"/>
+ <compilationpath>
+ <pathelement location="${build-strap.dir}/classes/library"/>
+ <pathelement location="${lib.dir}/forkjoin.jar"/>
+ </compilationpath>
+ </scalacfork>
+ <touch file="${build-strap.dir}/plugins.complete" verbose="no"/>
+ <stopwatch name="strap.plugins.timer" action="total"/>
+ </target>
+
+ <target name="strap.pre-scalap" depends="strap.plugins">
<uptodate property="strap.scalap.available" targetfile="${build-strap.dir}/scalap.complete">
<srcfiles dir="${src.dir}/scalap"/>
</uptodate>
@@ -1283,6 +1407,7 @@ DOCUMENTATION
classpathref="pack.classpath">
<src>
<files includes="${src.dir}/actors"/>
+ <files includes="${src.dir}/actors"/> <!-- why twice ?? -->
<files includes="${src.dir}/library/scala"/>
<files includes="${src.dir}/swing"/>
</src>
@@ -1390,6 +1515,7 @@ BOOTRAPING TEST AND TEST SUITE
<exclude name="**/*.properties"/>
<exclude name="bin/**"/>
<exclude name="*.complete"/>
+ <exclude name="plugins/*.jar"/>
</same>
</target>
@@ -1439,7 +1565,24 @@ BOOTRAPING TEST AND TEST SUITE
</partest>
</target>
- <target name="test.done" depends="test.suite, test.stability"/>
+ <target name="test.continuations.suite" depends="pack.done">
+ <property name="partest.srcdir" value="files" />
+ <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java"
+ timeout="2400000" javaccmd="${javac.cmd}"
+ srcdir="${partest.srcdir}"
+ scalacopts="${scalac.args.optimise} -Xplugin:${build-pack.dir}/plugins/continuations.jar -Xplugin-require:continuations -P:continuations:enable">
+ <compilationpath>
+ <path refid="pack.classpath"/>
+ <fileset dir="${partest.dir}/files/lib" includes="*.jar" />
+ </compilationpath>
+ <negtests dir="${partest.dir}/${partest.srcdir}/continuations-neg" includes="*.scala"/>
+ <runtests dir="${partest.dir}/${partest.srcdir}">
+ <include name="continuations-run/**/*.scala"/>
+ </runtests>
+ </partest>
+ </target>
+
+ <target name="test.done" depends="test.suite, test.continuations.suite, test.stability"/>
<!-- ===========================================================================
DISTRIBUTION
@@ -1467,6 +1610,10 @@ DISTRIBUTION
<copy toDir="${dist.dir}/etc">
<fileset dir="${build-pack.dir}/etc"/>
</copy>
+ <mkdir dir="${dist.dir}/plugins"/>
+ <copy toDir="${dist.dir}/plugins">
+ <fileset dir="${build-pack.dir}/plugins"/>
+ </copy>
</target>
<target name="dist.doc" depends="dist.base">
diff --git a/src/continuations/library/scala/util/continuations/package.scala b/src/continuations/library/scala/util/continuations/package.scala
new file mode 100644
index 0000000000..0d924565c1
--- /dev/null
+++ b/src/continuations/library/scala/util/continuations/package.scala
@@ -0,0 +1,65 @@
+// $Id$
+
+
+// TODO: scaladoc
+
+package scala.util
+
+package object continuations {
+
+ type cps[A] = cpsParam[A,A]
+
+ type suspendable = cps[Unit]
+
+
+ def shift[A,B,C](fun: (A => B) => C): A @cpsParam[B,C] = {
+ throw new NoSuchMethodException("this code has to be compiled with the Scala continuations plugin enabled")
+ }
+
+ def reset[A,C](ctx: =>(A @cpsParam[A,C])): C = {
+ val ctxR = reify[A,A,C](ctx)
+ if (ctxR.isTrivial)
+ ctxR.getTrivialValue.asInstanceOf[C]
+ else
+ ctxR.foreach((x:A) => x)
+ }
+
+ def reset0[A](ctx: =>(A @cpsParam[A,A])): A = reset(ctx)
+
+ def run[A](ctx: =>(Any @cpsParam[Unit,A])): A = {
+ val ctxR = reify[Any,Unit,A](ctx)
+ if (ctxR.isTrivial)
+ ctxR.getTrivialValue.asInstanceOf[A]
+ else
+ ctxR.foreach((x:Any) => ())
+ }
+
+
+ // methods below are primarily implementation details and are not
+ // needed frequently in client code
+
+ def shiftUnit0[A,B](x: A): A @cpsParam[B,B] = {
+ shiftUnit[A,B,B](x)
+ }
+
+ def shiftUnit[A,B,C>:B](x: A): A @cpsParam[B,C] = {
+ throw new NoSuchMethodException("this code has to be compiled with the Scala continuations plugin enabled")
+ }
+
+ def reify[A,B,C](ctx: =>(A @cpsParam[B,C])): ControlContext[A,B,C] = {
+ throw new NoSuchMethodException("this code has to be compiled with the Scala continuations plugin enabled")
+ }
+
+ def shiftUnitR[A,B](x: A): ControlContext[A,B,B] = {
+ new ControlContext(null, x)
+ }
+
+ def shiftR[A,B,C](fun: (A => B) => C): ControlContext[A,B,C] = {
+ new ControlContext(fun, null.asInstanceOf[A])
+ }
+
+ def reifyR[A,B,C](ctx: => ControlContext[A,B,C]): ControlContext[A,B,C] = {
+ ctx
+ }
+
+}
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 <path> 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 = <rhs>
+ // if (ctx.isTrivial)
+ // val <lhs> = ctx.getTrivialValue; ...
+ // else
+ // ctx.flatMap { <lhs> => ... }
+ 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 { <lhs> => ... }
+ // or
+ // ctx.map { <lhs> => ... }
+ (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 @@
+<!-- $Id$ -->
+<plugin>
+ <name>continuations</name>
+ <classname>scala.tools.selectivecps.SelectiveCPSPlugin</classname>
+</plugin>
diff --git a/test/files/continuations-neg/function0.check b/test/files/continuations-neg/function0.check
new file mode 100644
index 0000000000..0a66763a0f
--- /dev/null
+++ b/test/files/continuations-neg/function0.check
@@ -0,0 +1,6 @@
+function0.scala:11: error: type mismatch;
+ found : () => Int @scala.util.continuations.cpsParam[Int,Int]
+ required: () => Int
+ val g: () => Int = f
+ ^
+one error found
diff --git a/test/files/continuations-neg/function0.scala b/test/files/continuations-neg/function0.scala
new file mode 100644
index 0000000000..4112ee3835
--- /dev/null
+++ b/test/files/continuations-neg/function0.scala
@@ -0,0 +1,16 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+ val f = () => shift { k: (Int=>Int) => k(7) }
+ val g: () => Int = f
+
+ println(reset(g()))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-neg/function2.check b/test/files/continuations-neg/function2.check
new file mode 100644
index 0000000000..4833057652
--- /dev/null
+++ b/test/files/continuations-neg/function2.check
@@ -0,0 +1,6 @@
+function2.scala:11: error: type mismatch;
+ found : () => Int
+ required: () => Int @util.continuations.package.cps[Int]
+ val g: () => Int @cps[Int] = f
+ ^
+one error found
diff --git a/test/files/continuations-neg/function2.scala b/test/files/continuations-neg/function2.scala
new file mode 100644
index 0000000000..ae0fda509d
--- /dev/null
+++ b/test/files/continuations-neg/function2.scala
@@ -0,0 +1,16 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+ val f = () => 7
+ val g: () => Int @cps[Int] = f
+
+ println(reset(g()))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-neg/function3.check b/test/files/continuations-neg/function3.check
new file mode 100644
index 0000000000..4705ad9ed9
--- /dev/null
+++ b/test/files/continuations-neg/function3.check
@@ -0,0 +1,6 @@
+function3.scala:10: error: type mismatch;
+ found : Int @scala.util.continuations.cpsParam[Int,Int]
+ required: Int
+ val g: () => Int = () => shift { k: (Int=>Int) => k(7) }
+ ^
+one error found
diff --git a/test/files/continuations-neg/function3.scala b/test/files/continuations-neg/function3.scala
new file mode 100644
index 0000000000..0c3f1667e5
--- /dev/null
+++ b/test/files/continuations-neg/function3.scala
@@ -0,0 +1,15 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+ val g: () => Int = () => shift { k: (Int=>Int) => k(7) }
+
+ println(reset(g()))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-neg/infer0.check b/test/files/continuations-neg/infer0.check
new file mode 100644
index 0000000000..1dd072ef09
--- /dev/null
+++ b/test/files/continuations-neg/infer0.check
@@ -0,0 +1,4 @@
+infer0.scala:11: error: cannot cps-transform expression 8: type arguments [Int(8),String,Int] do not conform to method shiftUnit's type parameter bounds [A,B,C >: B]
+ test(8)
+ ^
+one error found
diff --git a/test/files/continuations-neg/infer0.scala b/test/files/continuations-neg/infer0.scala
new file mode 100644
index 0000000000..894d5228b1
--- /dev/null
+++ b/test/files/continuations-neg/infer0.scala
@@ -0,0 +1,14 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test(x: => Int @cpsParam[String,Int]) = 7
+
+ def main(args: Array[String]): Any = {
+ test(8)
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-neg/infer2.check b/test/files/continuations-neg/infer2.check
new file mode 100644
index 0000000000..59eb670bc3
--- /dev/null
+++ b/test/files/continuations-neg/infer2.check
@@ -0,0 +1,4 @@
+infer2.scala:14: error: illegal answer type modification: scala.util.continuations.cpsParam[String,Int] andThen scala.util.continuations.cpsParam[String,Int]
+ test { sym(); sym() }
+ ^
+one error found
diff --git a/test/files/continuations-neg/infer2.scala b/test/files/continuations-neg/infer2.scala
new file mode 100644
index 0000000000..a890ac1fc4
--- /dev/null
+++ b/test/files/continuations-neg/infer2.scala
@@ -0,0 +1,19 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test(x: => Int @cpsParam[String,Int]) = 7
+
+ def sym() = shift { k: (Int => String) => 9 }
+
+
+ def main(args: Array[String]): Any = {
+ test { sym(); sym() }
+ }
+
+}
+
+
diff --git a/test/files/continuations-neg/t1929.check b/test/files/continuations-neg/t1929.check
new file mode 100644
index 0000000000..f42c3a1e15
--- /dev/null
+++ b/test/files/continuations-neg/t1929.check
@@ -0,0 +1,6 @@
+t1929.scala:8: error: type mismatch;
+ found : Int @scala.util.continuations.cpsParam[String,java.lang.String] @scala.util.continuations.cpsSynth
+ required: Int @scala.util.continuations.cpsParam[Int,java.lang.String]
+ reset {
+ ^
+one error found
diff --git a/test/files/continuations-neg/t1929.scala b/test/files/continuations-neg/t1929.scala
new file mode 100644
index 0000000000..02eda9170d
--- /dev/null
+++ b/test/files/continuations-neg/t1929.scala
@@ -0,0 +1,17 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+ def main(args : Array[String]) {
+ reset {
+ println("up")
+ val x = shift((k:Int=>String) => k(8) + k(2))
+ println("down " + x)
+ val y = shift((k:Int=>String) => k(3))
+ println("down2 " + y)
+ y + x
+ }
+ }
+} \ No newline at end of file
diff --git a/test/files/continuations-neg/t2285.check b/test/files/continuations-neg/t2285.check
new file mode 100644
index 0000000000..d5dff6a4f2
--- /dev/null
+++ b/test/files/continuations-neg/t2285.check
@@ -0,0 +1,6 @@
+t2285.scala:9: error: type mismatch;
+ found : Int @scala.util.continuations.cpsParam[String,String] @scala.util.continuations.cpsSynth
+ required: Int @scala.util.continuations.cpsParam[Int,String]
+ def foo() = reset { bar(); 7 }
+ ^
+one error found
diff --git a/test/files/continuations-neg/t2285.scala b/test/files/continuations-neg/t2285.scala
new file mode 100644
index 0000000000..f3c7f4c89c
--- /dev/null
+++ b/test/files/continuations-neg/t2285.scala
@@ -0,0 +1,11 @@
+// $Id$
+
+import scala.util.continuations._
+
+object Test {
+
+ def bar() = shift { k: (String => String) => k("1") }
+
+ def foo() = reset { bar(); 7 }
+
+}
diff --git a/test/files/continuations-neg/t2949.check b/test/files/continuations-neg/t2949.check
new file mode 100644
index 0000000000..dd9768807c
--- /dev/null
+++ b/test/files/continuations-neg/t2949.check
@@ -0,0 +1,6 @@
+t2949.scala:13: error: type mismatch;
+ found : Int
+ required: ? @scala.util.continuations.cpsParam[List[?],Any]
+ x * y
+ ^
+one error found
diff --git a/test/files/continuations-neg/t2949.scala b/test/files/continuations-neg/t2949.scala
new file mode 100644
index 0000000000..ce27c7c0e8
--- /dev/null
+++ b/test/files/continuations-neg/t2949.scala
@@ -0,0 +1,15 @@
+// $Id$
+
+import scala.util.continuations._
+
+object Test {
+
+ def reflect[A,B](xs : List[A]) = shift{ xs.flatMap[B, List[B]] }
+ def reify[A, B](x : A @cpsParam[List[A], B]) = reset{ List(x) }
+
+ def main(args: Array[String]): Unit = println(reify {
+ val x = reflect[Int, Int](List(1,2,3))
+ val y = reflect[Int, Int](List(2,4,8))
+ x * y
+ })
+}
diff --git a/test/files/continuations-run/basics.check b/test/files/continuations-run/basics.check
new file mode 100755
index 0000000000..54c059fdcb
--- /dev/null
+++ b/test/files/continuations-run/basics.check
@@ -0,0 +1,2 @@
+28
+28 \ No newline at end of file
diff --git a/test/files/continuations-run/basics.scala b/test/files/continuations-run/basics.scala
new file mode 100755
index 0000000000..9df209b11c
--- /dev/null
+++ b/test/files/continuations-run/basics.scala
@@ -0,0 +1,23 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def m0() = {
+ shift((k:Int => Int) => k(k(7))) * 2
+ }
+
+ def m1() = {
+ 2 * shift((k:Int => Int) => k(k(7)))
+ }
+
+ def main(args: Array[String]) = {
+
+ println(reset(m0()))
+ println(reset(m1()))
+
+ }
+
+}
diff --git a/test/files/continuations-run/function1.check b/test/files/continuations-run/function1.check
new file mode 100644
index 0000000000..7f8f011eb7
--- /dev/null
+++ b/test/files/continuations-run/function1.check
@@ -0,0 +1 @@
+7
diff --git a/test/files/continuations-run/function1.scala b/test/files/continuations-run/function1.scala
new file mode 100644
index 0000000000..3b39722e3a
--- /dev/null
+++ b/test/files/continuations-run/function1.scala
@@ -0,0 +1,16 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+ val f = () => shift { k: (Int=>Int) => k(7) }
+ val g: () => Int @cps[Int] = f
+
+ println(reset(g()))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/function4.check b/test/files/continuations-run/function4.check
new file mode 100644
index 0000000000..c7930257df
--- /dev/null
+++ b/test/files/continuations-run/function4.check
@@ -0,0 +1 @@
+7 \ No newline at end of file
diff --git a/test/files/continuations-run/function4.scala b/test/files/continuations-run/function4.scala
new file mode 100644
index 0000000000..b73eedb02c
--- /dev/null
+++ b/test/files/continuations-run/function4.scala
@@ -0,0 +1,15 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+ val g: () => Int @cps[Int] = () => shift { k: (Int=>Int) => k(7) }
+
+ println(reset(g()))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/function5.check b/test/files/continuations-run/function5.check
new file mode 100644
index 0000000000..c7930257df
--- /dev/null
+++ b/test/files/continuations-run/function5.check
@@ -0,0 +1 @@
+7 \ No newline at end of file
diff --git a/test/files/continuations-run/function5.scala b/test/files/continuations-run/function5.scala
new file mode 100644
index 0000000000..a689ccf243
--- /dev/null
+++ b/test/files/continuations-run/function5.scala
@@ -0,0 +1,15 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+ val g: () => Int @cps[Int] = () => 7
+
+ println(reset(g()))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/ifelse0.check b/test/files/continuations-run/ifelse0.check
new file mode 100644
index 0000000000..f8bc79860d
--- /dev/null
+++ b/test/files/continuations-run/ifelse0.check
@@ -0,0 +1,2 @@
+10
+9 \ No newline at end of file
diff --git a/test/files/continuations-run/ifelse0.scala b/test/files/continuations-run/ifelse0.scala
new file mode 100644
index 0000000000..e34b86ee84
--- /dev/null
+++ b/test/files/continuations-run/ifelse0.scala
@@ -0,0 +1,18 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test(x:Int) = if (x <= 7)
+ shift { k: (Int=>Int) => k(k(k(x))) }
+ else
+ shift { k: (Int=>Int) => k(x) }
+
+ def main(args: Array[String]): Any = {
+ println(reset(1 + test(7)))
+ println(reset(1 + test(8)))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/ifelse1.check b/test/files/continuations-run/ifelse1.check
new file mode 100644
index 0000000000..86a3fbc0c1
--- /dev/null
+++ b/test/files/continuations-run/ifelse1.check
@@ -0,0 +1,4 @@
+10
+9
+8
+11 \ No newline at end of file
diff --git a/test/files/continuations-run/ifelse1.scala b/test/files/continuations-run/ifelse1.scala
new file mode 100644
index 0000000000..2ccc1ed730
--- /dev/null
+++ b/test/files/continuations-run/ifelse1.scala
@@ -0,0 +1,25 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test1(x:Int) = if (x <= 7)
+ shift { k: (Int=>Int) => k(k(k(x))) }
+ else
+ x
+
+ def test2(x:Int) = if (x <= 7)
+ x
+ else
+ shift { k: (Int=>Int) => k(k(k(x))) }
+
+ def main(args: Array[String]): Any = {
+ println(reset(1 + test1(7)))
+ println(reset(1 + test1(8)))
+ println(reset(1 + test2(7)))
+ println(reset(1 + test2(8)))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/ifelse2.check b/test/files/continuations-run/ifelse2.check
new file mode 100644
index 0000000000..f97a95b08d
--- /dev/null
+++ b/test/files/continuations-run/ifelse2.check
@@ -0,0 +1,4 @@
+abort
+()
+alive
+()
diff --git a/test/files/continuations-run/ifelse2.scala b/test/files/continuations-run/ifelse2.scala
new file mode 100644
index 0000000000..536e350190
--- /dev/null
+++ b/test/files/continuations-run/ifelse2.scala
@@ -0,0 +1,16 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test(x:Int) = if (x <= 7)
+ shift { k: (Unit=>Unit) => println("abort") }
+
+ def main(args: Array[String]): Any = {
+ println(reset{ test(7); println("alive") })
+ println(reset{ test(8); println("alive") })
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/ifelse3.check b/test/files/continuations-run/ifelse3.check
new file mode 100644
index 0000000000..95b562c8e6
--- /dev/null
+++ b/test/files/continuations-run/ifelse3.check
@@ -0,0 +1,2 @@
+6
+9
diff --git a/test/files/continuations-run/ifelse3.scala b/test/files/continuations-run/ifelse3.scala
new file mode 100644
index 0000000000..5dbd079d1c
--- /dev/null
+++ b/test/files/continuations-run/ifelse3.scala
@@ -0,0 +1,21 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def util(x: Boolean) = shift { k: (Boolean=>Int) => k(x) }
+
+ def test(x:Int) = if (util(x <= 7))
+ x - 1
+ else
+ x + 1
+
+
+ def main(args: Array[String]): Any = {
+ println(reset(test(7)))
+ println(reset(test(8)))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/infer1.scala b/test/files/continuations-run/infer1.scala
new file mode 100644
index 0000000000..a6c6c07215
--- /dev/null
+++ b/test/files/continuations-run/infer1.scala
@@ -0,0 +1,33 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test(x: => Int @cpsParam[String,Int]) = 7
+
+ def test2() = {
+ val x = shift { k: (Int => String) => 9 }
+ x
+ }
+
+ def test3(x: => Int @cpsParam[Int,Int]) = 7
+
+
+ def util() = shift { k: (String => String) => "7" }
+
+ def main(args: Array[String]): Any = {
+ test { shift { k: (Int => String) => 9 } }
+ test { shift { k: (Int => String) => 9 }; 2 }
+// test { shift { k: (Int => String) => 9 }; util() } <-- doesn't work
+ test { shift { k: (Int => String) => 9 }; util(); 2 }
+
+
+ test { shift { k: (Int => String) => 9 }; { test3(0); 2 } }
+
+ test3 { { test3(0); 2 } }
+
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/match0.check b/test/files/continuations-run/match0.check
new file mode 100644
index 0000000000..f8bc79860d
--- /dev/null
+++ b/test/files/continuations-run/match0.check
@@ -0,0 +1,2 @@
+10
+9 \ No newline at end of file
diff --git a/test/files/continuations-run/match0.scala b/test/files/continuations-run/match0.scala
new file mode 100644
index 0000000000..bd36238d7f
--- /dev/null
+++ b/test/files/continuations-run/match0.scala
@@ -0,0 +1,18 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test(x:Int) = x match {
+ case 7 => shift { k: (Int=>Int) => k(k(k(x))) }
+ case 8 => shift { k: (Int=>Int) => k(x) }
+ }
+
+ def main(args: Array[String]): Any = {
+ println(reset(1 + test(7)))
+ println(reset(1 + test(8)))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/match1.check b/test/files/continuations-run/match1.check
new file mode 100644
index 0000000000..73053d3f4f
--- /dev/null
+++ b/test/files/continuations-run/match1.check
@@ -0,0 +1,2 @@
+10
+9
diff --git a/test/files/continuations-run/match1.scala b/test/files/continuations-run/match1.scala
new file mode 100644
index 0000000000..ea4e219666
--- /dev/null
+++ b/test/files/continuations-run/match1.scala
@@ -0,0 +1,18 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test(x:Int) = x match {
+ case 7 => shift { k: (Int=>Int) => k(k(k(x))) }
+ case _ => x
+ }
+
+ def main(args: Array[String]): Any = {
+ println(reset(1 + test(7)))
+ println(reset(1 + test(8)))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/match2.check b/test/files/continuations-run/match2.check
new file mode 100644
index 0000000000..cbf91349cc
--- /dev/null
+++ b/test/files/continuations-run/match2.check
@@ -0,0 +1,2 @@
+B
+B
diff --git a/test/files/continuations-run/match2.scala b/test/files/continuations-run/match2.scala
new file mode 100644
index 0000000000..8d4f04870f
--- /dev/null
+++ b/test/files/continuations-run/match2.scala
@@ -0,0 +1,26 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def test1() = {
+ val (a, b) = shift { k: (((String,String)) => String) => k("A","B") }
+ b
+ }
+
+ case class Elem[T,U](a: T, b: U)
+
+ def test2() = {
+ val Elem(a,b) = shift { k: (Elem[String,String] => String) => k(Elem("A","B")) }
+ b
+ }
+
+
+ def main(args: Array[String]): Any = {
+ println(reset(test1()))
+ println(reset(test2()))
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/t1807.check b/test/files/continuations-run/t1807.check
new file mode 100644
index 0000000000..56a6051ca2
--- /dev/null
+++ b/test/files/continuations-run/t1807.check
@@ -0,0 +1 @@
+1 \ No newline at end of file
diff --git a/test/files/continuations-run/t1807.scala b/test/files/continuations-run/t1807.scala
new file mode 100644
index 0000000000..278b3a9936
--- /dev/null
+++ b/test/files/continuations-run/t1807.scala
@@ -0,0 +1,14 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ val z = reset {
+ val f: (() => Int @cps[Int]) = () => 1
+ f()
+ }
+ println(z)
+ }
+} \ No newline at end of file
diff --git a/test/files/continuations-run/t1808.scala b/test/files/continuations-run/t1808.scala
new file mode 100644
index 0000000000..125c7c1cdf
--- /dev/null
+++ b/test/files/continuations-run/t1808.scala
@@ -0,0 +1,10 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ reset0 { 0 }
+ }
+} \ No newline at end of file
diff --git a/test/files/continuations-run/t1820.scala b/test/files/continuations-run/t1820.scala
new file mode 100644
index 0000000000..893ddab6d1
--- /dev/null
+++ b/test/files/continuations-run/t1820.scala
@@ -0,0 +1,14 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+ def shifted: Unit @suspendable = shift { (k: Unit => Unit) => () }
+ def test1(b: => Boolean) = {
+ reset {
+ if (b) shifted
+ }
+ }
+ def main(args: Array[String]) = test1(true)
+} \ No newline at end of file
diff --git a/test/files/continuations-run/t1821.check b/test/files/continuations-run/t1821.check
new file mode 100644
index 0000000000..f7b76115db
--- /dev/null
+++ b/test/files/continuations-run/t1821.check
@@ -0,0 +1,4 @@
+()
+()
+()
+() \ No newline at end of file
diff --git a/test/files/continuations-run/t1821.scala b/test/files/continuations-run/t1821.scala
new file mode 100644
index 0000000000..0d5fb553be
--- /dev/null
+++ b/test/files/continuations-run/t1821.scala
@@ -0,0 +1,20 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+ def suspended[A](x: A): A @suspendable = x
+ def test1[A](x: A): A @suspendable = suspended(x) match { case x => x }
+ def test2[A](x: List[A]): A @suspendable = suspended(x) match { case List(x) => x }
+
+ def test3[A](x: A): A @suspendable = x match { case x => x }
+ def test4[A](x: List[A]): A @suspendable = x match { case List(x) => x }
+
+ def main(args: Array[String]) = {
+ println(reset(test1()))
+ println(reset(test2(List(()))))
+ println(reset(test3()))
+ println(reset(test4(List(()))))
+ }
+} \ No newline at end of file
diff --git a/test/files/continuations-run/while0.check b/test/files/continuations-run/while0.check
new file mode 100644
index 0000000000..d58c55a31d
--- /dev/null
+++ b/test/files/continuations-run/while0.check
@@ -0,0 +1 @@
+9000
diff --git a/test/files/continuations-run/while0.scala b/test/files/continuations-run/while0.scala
new file mode 100644
index 0000000000..9735f9d2c3
--- /dev/null
+++ b/test/files/continuations-run/while0.scala
@@ -0,0 +1,22 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def foo(): Int @cps[Unit] = 2
+
+ def test(): Unit @cps[Unit] = {
+ var x = 0
+ while (x < 9000) { // pick number large enough to require tail-call opt
+ x += foo()
+ }
+ println(x)
+ }
+
+ def main(args: Array[String]): Any = {
+ reset(test())
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/while1.check b/test/files/continuations-run/while1.check
new file mode 100644
index 0000000000..3d5f0b9a46
--- /dev/null
+++ b/test/files/continuations-run/while1.check
@@ -0,0 +1,11 @@
+up
+up
+up
+up
+up
+10
+down
+down
+down
+down
+down
diff --git a/test/files/continuations-run/while1.scala b/test/files/continuations-run/while1.scala
new file mode 100644
index 0000000000..fb5dc0079a
--- /dev/null
+++ b/test/files/continuations-run/while1.scala
@@ -0,0 +1,22 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def foo(): Int @cps[Unit] = shift { k => println("up"); k(2); println("down") }
+
+ def test(): Unit @cps[Unit] = {
+ var x = 0
+ while (x < 9) {
+ x += foo()
+ }
+ println(x)
+ }
+
+ def main(args: Array[String]): Any = {
+ reset(test())
+ }
+
+} \ No newline at end of file
diff --git a/test/files/continuations-run/while2.check b/test/files/continuations-run/while2.check
new file mode 100644
index 0000000000..9fe515181b
--- /dev/null
+++ b/test/files/continuations-run/while2.check
@@ -0,0 +1,19 @@
+up
+up
+up
+up
+up
+up
+up
+up
+up
+9000
+down
+down
+down
+down
+down
+down
+down
+down
+down
diff --git a/test/files/continuations-run/while2.scala b/test/files/continuations-run/while2.scala
new file mode 100644
index 0000000000..f36288929e
--- /dev/null
+++ b/test/files/continuations-run/while2.scala
@@ -0,0 +1,23 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def foo1(): Int @cps[Unit] = 2
+ def foo2(): Int @cps[Unit] = shift { k => println("up"); k(2); println("down") }
+
+ def test(): Unit @cps[Unit] = {
+ var x = 0
+ while (x < 9000) { // pick number large enough to require tail-call opt
+ x += (if (x % 1000 != 0) foo1() else foo2())
+ }
+ println(x)
+ }
+
+ def main(args: Array[String]): Any = {
+ reset(test())
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example0.scala b/test/pending/continuations-run/example0.scala
new file mode 100644
index 0000000000..44b1331339
--- /dev/null
+++ b/test/pending/continuations-run/example0.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test0.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example1.scala b/test/pending/continuations-run/example1.scala
new file mode 100644
index 0000000000..195a98e59f
--- /dev/null
+++ b/test/pending/continuations-run/example1.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test1.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example16.scala b/test/pending/continuations-run/example16.scala
new file mode 100644
index 0000000000..5eb64046ed
--- /dev/null
+++ b/test/pending/continuations-run/example16.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test16Printf.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example2.scala b/test/pending/continuations-run/example2.scala
new file mode 100644
index 0000000000..0d96257c40
--- /dev/null
+++ b/test/pending/continuations-run/example2.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test2.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example3.scala b/test/pending/continuations-run/example3.scala
new file mode 100644
index 0000000000..3f5052a4ad
--- /dev/null
+++ b/test/pending/continuations-run/example3.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test3.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example4.scala b/test/pending/continuations-run/example4.scala
new file mode 100644
index 0000000000..66c6774791
--- /dev/null
+++ b/test/pending/continuations-run/example4.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test4.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example5.scala b/test/pending/continuations-run/example5.scala
new file mode 100644
index 0000000000..0994bdee8a
--- /dev/null
+++ b/test/pending/continuations-run/example5.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test5.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example6.scala b/test/pending/continuations-run/example6.scala
new file mode 100644
index 0000000000..5207e3fc68
--- /dev/null
+++ b/test/pending/continuations-run/example6.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test6.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example7.scala b/test/pending/continuations-run/example7.scala
new file mode 100644
index 0000000000..fb22387dac
--- /dev/null
+++ b/test/pending/continuations-run/example7.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test7.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example8.scala b/test/pending/continuations-run/example8.scala
new file mode 100644
index 0000000000..8e21e6c674
--- /dev/null
+++ b/test/pending/continuations-run/example8.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test8.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/example9.scala b/test/pending/continuations-run/example9.scala
new file mode 100644
index 0000000000..0f27c686f7
--- /dev/null
+++ b/test/pending/continuations-run/example9.scala
@@ -0,0 +1,9 @@
+// $Id$
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+ examples.continuations.Test9Monads.main(args)
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/foreach.check b/test/pending/continuations-run/foreach.check
new file mode 100644
index 0000000000..9bab7a2eed
--- /dev/null
+++ b/test/pending/continuations-run/foreach.check
@@ -0,0 +1,4 @@
+1
+2
+3
+enough is enough \ No newline at end of file
diff --git a/test/pending/continuations-run/foreach.scala b/test/pending/continuations-run/foreach.scala
new file mode 100644
index 0000000000..4daade452c
--- /dev/null
+++ b/test/pending/continuations-run/foreach.scala
@@ -0,0 +1,33 @@
+// $Id$
+
+import scala.util.continuations._
+
+import scala.util.continuations.Loops._
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+
+ reset {
+
+ val list = List(1,2,3,4,5)
+
+ for (x <- list.suspendable) {
+
+ shift { k: (Unit => Unit) =>
+ println(x)
+ if (x < 3)
+ k()
+ else
+ println("enough is enough")
+ }
+
+ }
+
+ }
+
+
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/function6.scala b/test/pending/continuations-run/function6.scala
new file mode 100644
index 0000000000..f1296ae410
--- /dev/null
+++ b/test/pending/continuations-run/function6.scala
@@ -0,0 +1,15 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args: Array[String]): Any = {
+
+ val g: PartialFunction[Int, Int @cps[Int,Int]] = { case x => 7 }
+
+ println(reset(g(2)))
+ }
+
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/select-run.log b/test/pending/continuations-run/select-run.log
new file mode 100644
index 0000000000..6faa868ce3
--- /dev/null
+++ b/test/pending/continuations-run/select-run.log
@@ -0,0 +1,20 @@
+8
+java.lang.ClassCastException: scala.util.continuations.ControlContext cannot be cast to java.lang.Integer
+ at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
+ at Test$$anonfun$main$2.apply(select.scala:18)
+ at Test$$anonfun$main$2.apply(select.scala:18)
+ at scala.util.continuations.ControlContext$.reset(ControlContext.scala:65)
+ at Test$.main(select.scala:18)
+ at Test.main(select.scala)
+ at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+ at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+ at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+ at java.lang.reflect.Method.invoke(Method.java:597)
+ at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:55)
+ at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:22)
+ at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:61)
+ at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:55)
+ at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:61)
+ at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:33)
+ at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:153)
+ at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
diff --git a/test/pending/continuations-run/select.check b/test/pending/continuations-run/select.check
new file mode 100644
index 0000000000..620ce84217
--- /dev/null
+++ b/test/pending/continuations-run/select.check
@@ -0,0 +1,2 @@
+8
+8 \ No newline at end of file
diff --git a/test/pending/continuations-run/select.scala b/test/pending/continuations-run/select.scala
new file mode 100644
index 0000000000..faf5842329
--- /dev/null
+++ b/test/pending/continuations-run/select.scala
@@ -0,0 +1,21 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ class Bla {
+ val x = 8
+ }
+
+ def bla = shift { k:(Bla=>Bla) => k(new Bla) }
+
+ // TODO: check whether this also applies to a::shift { k => ... }
+
+ def main(args: Array[String]) = {
+ println(reset(bla).x)
+ println(reset(bla.x))
+ }
+
+}
diff --git a/test/pending/continuations-run/t2864.scala b/test/pending/continuations-run/t2864.scala
new file mode 100644
index 0000000000..291e739332
--- /dev/null
+++ b/test/pending/continuations-run/t2864.scala
@@ -0,0 +1,18 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+
+object Test {
+
+ def double[B](n : Int)(k : Int => B) : B = k(n * 2)
+
+ def main(args : Array[String]) {
+ reset {
+ val result1 = shift(double[Unit](100))
+ val result2 = shift(double[Unit](result1))
+ println(result2)
+ }
+ }
+} \ No newline at end of file
diff --git a/test/pending/continuations-run/t2934.scala b/test/pending/continuations-run/t2934.scala
new file mode 100644
index 0000000000..6089355bcf
--- /dev/null
+++ b/test/pending/continuations-run/t2934.scala
@@ -0,0 +1,14 @@
+// $Id$
+
+import scala.util.continuations._
+
+
+object Test {
+
+ def main(args : Array[String]) {
+ println(reset {
+ val x = shift(List(1,2,3).flatMap[Int, List[Int]])
+ List(x + 2)
+ })
+ }
+} \ No newline at end of file