diff options
author | Philipp Haller <hallerp@gmail.com> | 2013-11-22 11:44:02 -0800 |
---|---|---|
committer | Philipp Haller <hallerp@gmail.com> | 2013-11-22 11:44:02 -0800 |
commit | adbe51ef1050ec78eafcce7e45f848c4de8adf83 (patch) | |
tree | b96e3df9cf89d6af8603b6a2726e16ca6efc0b68 /src | |
parent | 93ab62488f4b75c45578a620785656e1faf48bd7 (diff) | |
parent | 4fc54635380eca4beb13678f24ba0a8e4d592bec (diff) | |
download | scala-async-adbe51ef1050ec78eafcce7e45f848c4de8adf83.tar.gz scala-async-adbe51ef1050ec78eafcce7e45f848c4de8adf83.tar.bz2 scala-async-adbe51ef1050ec78eafcce7e45f848c4de8adf83.zip |
Merge pull request #50 from retronym/ticket/48
Fix crashers in do/while and while(await(..))
Diffstat (limited to 'src')
5 files changed, 73 insertions, 34 deletions
diff --git a/src/main/scala/scala/async/internal/AnfTransform.scala b/src/main/scala/scala/async/internal/AnfTransform.scala index f19a87a..8518cf5 100644 --- a/src/main/scala/scala/async/internal/AnfTransform.scala +++ b/src/main/scala/scala/async/internal/AnfTransform.scala @@ -162,7 +162,14 @@ private[async] trait AnfTransform { def _transformToList(tree: Tree): List[Tree] = trace(tree) { val containsAwait = tree exists isAwait if (!containsAwait) { - List(tree) + tree match { + case Block(stats, expr) => + // avoids nested block in `while(await(false)) ...`. + // TODO I think `containsAwait` really should return true if the code contains a label jump to an enclosing + // while/doWhile and there is an await *anywhere* inside that construct. + stats :+ expr + case _ => List(tree) + } } else tree match { case Select(qual, sel) => val stats :+ expr = linearize.transformToList(qual) diff --git a/src/main/scala/scala/async/internal/ExprBuilder.scala b/src/main/scala/scala/async/internal/ExprBuilder.scala index 85e0953..b0cd914 100644 --- a/src/main/scala/scala/async/internal/ExprBuilder.scala +++ b/src/main/scala/scala/async/internal/ExprBuilder.scala @@ -127,7 +127,11 @@ trait ExprBuilder { private var nextJumpState: Option[Int] = None def +=(stat: Tree): this.type = { - assert(nextJumpState.isEmpty, s"statement appeared after a label jump: $stat") + stat match { + case Literal(Constant(())) => // This case occurs in do/while + case _ => + assert(nextJumpState.isEmpty, s"statement appeared after a label jump: $stat") + } def addStat() = stats += stat stat match { case Apply(fun, Nil) => @@ -228,7 +232,7 @@ trait ExprBuilder { currState = afterAwaitState stateBuilder = new AsyncStateBuilder(currState, symLookup) - case If(cond, thenp, elsep) if stat exists isAwait => + case If(cond, thenp, elsep) if (stat exists isAwait) || containsForiegnLabelJump(stat) => checkForUnsupportedAwait(cond) val thenStartState = nextState() diff --git a/src/main/scala/scala/async/internal/TransformUtils.scala b/src/main/scala/scala/async/internal/TransformUtils.scala index 71fddaa..e382c62 100644 --- a/src/main/scala/scala/async/internal/TransformUtils.scala +++ b/src/main/scala/scala/async/internal/TransformUtils.scala @@ -96,6 +96,19 @@ private[async] trait TransformUtils { treeInfo.isExprSafeToInline(tree) } + // `while(await(x))` ... or `do { await(x); ... } while(...)` contain an `If` that loops; + // we must break that `If` into states so that it convert the label jump into a state machine + // transition + final def containsForiegnLabelJump(t: Tree): Boolean = { + val labelDefs = t.collect { + case ld: LabelDef => ld.symbol + }.toSet + t.exists { + case rt: RefTree => !(labelDefs contains rt.symbol) + case _ => false + } + } + /** Map a list of arguments to: * - A list of argument Trees * - A list of auxillary results. diff --git a/src/test/scala/scala/async/TreeInterrogation.scala b/src/test/scala/scala/async/TreeInterrogation.scala index c8fe2d6..b42726b 100644 --- a/src/test/scala/scala/async/TreeInterrogation.scala +++ b/src/test/scala/scala/async/TreeInterrogation.scala @@ -66,43 +66,18 @@ object TreeInterrogation extends App { withDebug { val cm = reflect.runtime.currentMirror val tb = mkToolbox("-cp ${toolboxClasspath} -Xprint:typer -uniqid") - import scala.async.internal.AsyncTestLV._ + import scala.async.internal.AsyncId._ val tree = tb.parse( """ - | import scala.async.internal.AsyncTestLV._ - | import scala.async.internal.AsyncTestLV - | - | case class MCell[T](var v: T) - | val f = async { MCell(1) } - | - | def m1(x: MCell[Int], y: Int): Int = - | async { x.v + y } - | case class Cell[T](v: T) - | + | import scala.async.internal.AsyncId._ | async { - | // state #1 - | val a: MCell[Int] = await(f) // await$13$1 - | // state #2 - | var y = MCell(0) - | - | while (a.v < 10) { - | // state #4 - | a.v = a.v + 1 - | y = MCell(await(a).v + 1) // await$14$1 - | // state #7 + | var b = true + | while(await(b)) { + | b = false | } - | - | // state #3 - | assert(AsyncTestLV.log.exists(entry => entry._1 == "await$14$1")) - | - | val b = await(m1(a, y.v)) // await$15$1 - | // state #8 - | assert(AsyncTestLV.log.exists(_ == ("a$1" -> MCell(10)))) - | assert(AsyncTestLV.log.exists(_ == ("y$1" -> MCell(11)))) - | b + | await(b) | } | - | | """.stripMargin) println(tree) val tree1 = tb.typeCheck(tree.duplicate) diff --git a/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala b/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala index 4b3c2aa..76e6b1e 100644 --- a/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala +++ b/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala @@ -76,4 +76,44 @@ class WhileSpec { } result mustBe () } + + @Test def doWhile() { + import AsyncId._ + val result = async { + var b = 0 + var x = "" + await(do { + x += "1" + x += await("2") + x += "3" + b += await(1) + } while (b < 2)) + await(x) + } + result mustBe "123123" + } + + @Test def whileAwaitCondition() { + import AsyncId._ + val result = async { + var b = true + while(await(b)) { + b = false + } + await(b) + } + result mustBe false + } + + @Test def doWhileAwaitCondition() { + import AsyncId._ + val result = async { + var b = true + do { + b = false + } while(await(b)) + b + } + result mustBe false + } } |