aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-07-21 18:47:40 +0200
committerJason Zaugg <jzaugg@gmail.com>2014-07-21 18:47:40 +0200
commit5342427303c1e91aeb0cd82007dcac4fb4249c9e (patch)
tree88338e5ada77918fc9cb251e626aec406722779f
parenta08a822df9987bf34ec804f0e5a537da75fd3294 (diff)
parentde102789917e4b9aeae2836bde70c2a8c29a7e0e (diff)
downloadscala-async-5342427303c1e91aeb0cd82007dcac4fb4249c9e.tar.gz
scala-async-5342427303c1e91aeb0cd82007dcac4fb4249c9e.tar.bz2
scala-async-5342427303c1e91aeb0cd82007dcac4fb4249c9e.zip
Merge pull request #84 from retronym/merge/2.10.x-to-master-20140721
Merge 2.10.x to master
-rw-r--r--src/main/scala/scala/async/internal/AsyncTransform.scala7
-rw-r--r--src/main/scala/scala/async/internal/Lifter.scala3
-rw-r--r--src/main/scala/scala/async/internal/LiveVariables.scala71
-rw-r--r--src/main/scala/scala/async/internal/TransformUtils.scala8
-rw-r--r--src/test/scala/scala/async/run/ifelse1/IfElse1.scala77
-rw-r--r--src/test/scala/scala/async/run/ifelse4/IfElse4.scala63
-rw-r--r--src/test/scala/scala/async/run/toughtype/ToughType.scala16
7 files changed, 211 insertions, 34 deletions
diff --git a/src/main/scala/scala/async/internal/AsyncTransform.scala b/src/main/scala/scala/async/internal/AsyncTransform.scala
index aca8c7e..66cfb87 100644
--- a/src/main/scala/scala/async/internal/AsyncTransform.scala
+++ b/src/main/scala/scala/async/internal/AsyncTransform.scala
@@ -157,9 +157,10 @@ trait AsyncTransform {
case ValDef(_, _, _, rhs) if liftedSyms(tree.symbol) =>
api.atOwner(api.currentOwner) {
val fieldSym = tree.symbol
- val set = Assign(gen.mkAttributedStableRef(thisType(fieldSym.owner.asClass), fieldSym), api.recur(rhs))
- set.changeOwner(tree.symbol, api.currentOwner)
- api.typecheck(atPos(tree.pos)(set))
+ val lhs = atPos(tree.pos) {
+ gen.mkAttributedStableRef(thisType(fieldSym.owner.asClass), fieldSym)
+ }
+ treeCopy.Assign(tree, lhs, api.recur(rhs)).setType(definitions.UnitTpe).changeOwner(fieldSym, api.currentOwner)
}
case _: DefTree if liftedSyms(tree.symbol) =>
EmptyTree
diff --git a/src/main/scala/scala/async/internal/Lifter.scala b/src/main/scala/scala/async/internal/Lifter.scala
index bf7bdbd..4242a8e 100644
--- a/src/main/scala/scala/async/internal/Lifter.scala
+++ b/src/main/scala/scala/async/internal/Lifter.scala
@@ -115,8 +115,7 @@ trait Lifter {
sym.setFlag(MUTABLE | STABLE | PRIVATE | LOCAL)
sym.setName(name.fresh(sym.name.toTermName))
sym.setInfo(deconst(sym.info))
- val zeroRhs = atPos(t.pos)(gen.mkZero(vd.symbol.info))
- treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(tpe(sym)).setPos(t.pos), zeroRhs)
+ treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(tpe(sym)).setPos(t.pos), EmptyTree)
case dd@DefDef(_, _, tparams, vparamss, tpt, rhs) =>
sym.setName(this.name.fresh(sym.name.toTermName))
sym.setFlag(PRIVATE | LOCAL)
diff --git a/src/main/scala/scala/async/internal/LiveVariables.scala b/src/main/scala/scala/async/internal/LiveVariables.scala
index 7f386c8..5b49398 100644
--- a/src/main/scala/scala/async/internal/LiveVariables.scala
+++ b/src/main/scala/scala/async/internal/LiveVariables.scala
@@ -126,14 +126,22 @@ trait LiveVariables {
/** Tests if `state1` is a predecessor of `state2`.
*/
- def isPred(state1: Int, state2: Int, seen: Set[Int] = Set()): Boolean =
- if (seen(state1)) false // breaks cycles in the CFG
- else cfg get state1 match {
- case Some(nextStates) =>
- nextStates.contains(state2) || nextStates.exists(isPred(_, state2, seen + state1))
- case None =>
- false
- }
+ def isPred(state1: Int, state2: Int): Boolean = {
+ val seen = scala.collection.mutable.HashSet[Int]()
+
+ def isPred0(state1: Int, state2: Int): Boolean =
+ if(state1 == state2) false
+ else if (seen(state1)) false // breaks cycles in the CFG
+ else cfg get state1 match {
+ case Some(nextStates) =>
+ seen += state1
+ nextStates.contains(state2) || nextStates.exists(isPred0(_, state2))
+ case None =>
+ false
+ }
+
+ isPred0(state1, state2)
+ }
val finalState = asyncStates.find(as => !asyncStates.exists(other => isPred(as.state, other.state))).get
@@ -162,12 +170,10 @@ trait LiveVariables {
LVexit = LVexit + (finalState.state -> noNull)
var currStates = List(finalState) // start at final state
- var pred = List[AsyncState]() // current predecessor states
- var hasChanged = true // if something has changed we need to continue iterating
var captured: Set[Symbol] = Set()
- while (hasChanged) {
- hasChanged = false
+ while (!currStates.isEmpty) {
+ var entryChanged: List[AsyncState] = Nil
for (cs <- currStates) {
val LVentryOld = LVentry(cs.state)
@@ -176,22 +182,23 @@ trait LiveVariables {
val LVentryNew = LVexit(cs.state) ++ referenced.used
if (!LVentryNew.sameElements(LVentryOld)) {
LVentry = LVentry + (cs.state -> LVentryNew)
- hasChanged = true
+ entryChanged ::= cs
}
}
- pred = currStates.flatMap(cs => asyncStates.filter(_.nextStates.contains(cs.state)))
+ val pred = entryChanged.flatMap(cs => asyncStates.filter(_.nextStates.contains(cs.state)))
+ var exitChanged: List[AsyncState] = Nil
for (p <- pred) {
val LVexitOld = LVexit(p.state)
val LVexitNew = p.nextStates.flatMap(succ => LVentry(succ)).toSet
if (!LVexitNew.sameElements(LVexitOld)) {
LVexit = LVexit + (p.state -> LVexitNew)
- hasChanged = true
+ exitChanged ::= p
}
}
- currStates = pred
+ currStates = exitChanged
}
for (as <- asyncStates) {
@@ -199,21 +206,29 @@ trait LiveVariables {
AsyncUtils.vprintln(s"LVexit at state #${as.state}: ${LVexit(as.state).mkString(", ")}")
}
- def lastUsagesOf(field: Tree, at: AsyncState, avoid: Set[AsyncState]): Set[Int] =
- if (avoid(at)) Set()
- else if (captured(field.symbol)) {
- Set()
- }
- else LVentry get at.state match {
- case Some(fields) if fields.exists(_ == field.symbol) =>
- Set(at.state)
- case _ =>
- val preds = asyncStates.filter(_.nextStates.contains(at.state)).toSet
- preds.flatMap(p => lastUsagesOf(field, p, avoid + at))
+ def lastUsagesOf(field: Tree, at: AsyncState): Set[Int] = {
+ val avoid = scala.collection.mutable.HashSet[AsyncState]()
+
+ def lastUsagesOf0(field: Tree, at: AsyncState): Set[Int] = {
+ if (avoid(at)) Set()
+ else if (captured(field.symbol)) {
+ Set()
+ }
+ else LVentry get at.state match {
+ case Some(fields) if fields.exists(_ == field.symbol) =>
+ Set(at.state)
+ case _ =>
+ avoid += at
+ val preds = asyncStates.filter(_.nextStates.contains(at.state)).toSet
+ preds.flatMap(p => lastUsagesOf0(field, p))
+ }
}
+ lastUsagesOf0(field, at)
+ }
+
val lastUsages: Map[Tree, Set[Int]] =
- liftables.map(fld => (fld -> lastUsagesOf(fld, finalState, Set()))).toMap
+ liftables.map(fld => (fld -> lastUsagesOf(fld, finalState))).toMap
for ((fld, lastStates) <- lastUsages)
AsyncUtils.vprintln(s"field ${fld.symbol.name} is last used in states ${lastStates.mkString(", ")}")
diff --git a/src/main/scala/scala/async/internal/TransformUtils.scala b/src/main/scala/scala/async/internal/TransformUtils.scala
index a01a801..665dead 100644
--- a/src/main/scala/scala/async/internal/TransformUtils.scala
+++ b/src/main/scala/scala/async/internal/TransformUtils.scala
@@ -164,11 +164,17 @@ private[async] trait TransformUtils {
case ld: LabelDef => ld.symbol
}.toSet
t.exists {
- case rt: RefTree => !(labelDefs contains rt.symbol)
+ case rt: RefTree => rt.symbol != null && isLabel(rt.symbol) && !(labelDefs contains rt.symbol)
case _ => false
}
}
+ private def isLabel(sym: Symbol): Boolean = {
+ val LABEL = 1L << 17 // not in the public reflection API.
+ (internal.flags(sym).asInstanceOf[Long] & LABEL) != 0L
+ }
+
+
/** Map a list of arguments to:
* - A list of argument Trees
* - A list of auxillary results.
diff --git a/src/test/scala/scala/async/run/ifelse1/IfElse1.scala b/src/test/scala/scala/async/run/ifelse1/IfElse1.scala
index 587aaac..6cbe910 100644
--- a/src/test/scala/scala/async/run/ifelse1/IfElse1.scala
+++ b/src/test/scala/scala/async/run/ifelse1/IfElse1.scala
@@ -87,6 +87,75 @@ class TestIfElse1Class {
}
z
}
+
+ def pred: Future[Boolean] = async(true)
+
+ def m5: Future[Boolean] = async {
+ if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(if(await(pred))
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false)
+ await(pred)
+ else
+ false
+ }
}
class IfElse1Spec {
@@ -124,4 +193,12 @@ class IfElse1Spec {
val res = Await.result(fut, 2 seconds)
res mustBe (14)
}
+
+ @Test
+ def `await in deeply-nested if-else conditions`() {
+ val o = new TestIfElse1Class
+ val fut = o.m5
+ val res = Await.result(fut, 2 seconds)
+ res mustBe true
+ }
}
diff --git a/src/test/scala/scala/async/run/ifelse4/IfElse4.scala b/src/test/scala/scala/async/run/ifelse4/IfElse4.scala
new file mode 100644
index 0000000..b0ecf13
--- /dev/null
+++ b/src/test/scala/scala/async/run/ifelse4/IfElse4.scala
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012-2014 Typesafe Inc. <http://www.typesafe.com>
+ */
+
+package scala.async
+package run
+package ifelse4
+
+import language.{reflectiveCalls, postfixOps}
+import scala.concurrent.{Future, ExecutionContext, future, Await}
+import scala.concurrent.duration._
+import scala.async.Async.{async, await}
+import org.junit.Test
+
+
+class TestIfElse4Class {
+
+ import ExecutionContext.Implicits.global
+
+ class F[A]
+ class S[A](val id: String)
+ trait P
+
+ case class K(f: F[_])
+
+ def result[A](f: F[A]) = async {
+ new S[A with P]("foo")
+ }
+
+ def run(k: K) = async {
+ val res = await(result(k.f))
+ // these triggered a crash with mismatched existential skolems
+ // found : S#10272[_$1#10308 with String#137] where type _$1#10308
+ // required: S#10272[_$1#10311 with String#137] forSome { type _$1#10311 }
+
+ // This variation of the crash could be avoided by fixing the over-eager
+ // generation of states in `If` nodes, which was caused by a bug in label
+ // detection code.
+ if(true) {
+ identity(res)
+ }
+
+ // This variation remained after the aforementioned fix, however.
+ // It was fixed by manually typing the `Assign(liftedField, rhs)` AST,
+ // which is how we avoid these problems through the rest of the ANF transform.
+ if(true) {
+ identity(res)
+ await(result(k.f))
+ }
+ res
+ }
+}
+
+class IfElse4Spec {
+
+ @Test
+ def `await result with complex type containing skolem`() {
+ val o = new TestIfElse4Class
+ val fut = o.run(new o.K(null))
+ val res = Await.result(fut, 2 seconds)
+ res.id mustBe ("foo")
+ }
+}
diff --git a/src/test/scala/scala/async/run/toughtype/ToughType.scala b/src/test/scala/scala/async/run/toughtype/ToughType.scala
index 458157c..54a53c8 100644
--- a/src/test/scala/scala/async/run/toughtype/ToughType.scala
+++ b/src/test/scala/scala/async/run/toughtype/ToughType.scala
@@ -211,6 +211,22 @@ class ToughTypeSpec {
}(SomeExecutionContext)
}
}
+
+ }
+
+ @Test def ticket66Nothing() {
+ import scala.concurrent.Future
+ import scala.concurrent.ExecutionContext.Implicits.global
+ val e = new Exception()
+ val f: Future[Nothing] = Future.failed(e)
+ val f1 = async {
+ await(f)
+ }
+ try {
+ Await.result(f1, 5.seconds)
+ } catch {
+ case `e` =>
+ }
}
}