aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2015-07-30 15:54:22 +1000
committerJason Zaugg <jzaugg@gmail.com>2015-07-30 15:54:22 +1000
commitb295d74433a7f4372995e034de305be5608d5ddf (patch)
tree572cd403c5e8aac7b1010f6d472e700558331ceb
parenta82fa0e95e3fba6eb1aee03080471097f69a33a8 (diff)
parentf9e170e3187f83c5d00e9ea8128fe2edf2bd371c (diff)
downloadscala-async-2.10.x.tar.gz
scala-async-2.10.x.tar.bz2
scala-async-2.10.x.zip
Merge pull request #133 from retronym/backport/1502.10.x
Backport fix for #150 to 2.10.x
-rw-r--r--src/main/scala/scala/async/Async.scala2
-rw-r--r--src/main/scala/scala/async/internal/AsyncId.scala2
-rw-r--r--src/main/scala/scala/async/internal/AsyncTransform.scala12
-rw-r--r--src/main/scala/scala/async/internal/ExprBuilder.scala57
-rw-r--r--src/test/scala/scala/async/package.scala12
-rw-r--r--src/test/scala/scala/async/run/WarningsSpec.scala48
6 files changed, 109 insertions, 24 deletions
diff --git a/src/main/scala/scala/async/Async.scala b/src/main/scala/scala/async/Async.scala
index 17a63a4..5e2d62d 100644
--- a/src/main/scala/scala/async/Async.scala
+++ b/src/main/scala/scala/async/Async.scala
@@ -42,7 +42,7 @@ object Async {
* Run the block of code `body` asynchronously. `body` may contain calls to `await` when the results of
* a `Future` are needed; this is translated into non-blocking code.
*/
- def async[T](body: T)(implicit execContext: ExecutionContext): Future[T] = macro internal.ScalaConcurrentAsync.asyncImpl[T]
+ def async[T](body: => T)(implicit execContext: ExecutionContext): Future[T] = macro internal.ScalaConcurrentAsync.asyncImpl[T]
/**
* Non-blocking await the on result of `awaitable`. This may only be used directly within an enclosing `async` block.
diff --git a/src/main/scala/scala/async/internal/AsyncId.scala b/src/main/scala/scala/async/internal/AsyncId.scala
index f9d8c0b..c92ebf4 100644
--- a/src/main/scala/scala/async/internal/AsyncId.scala
+++ b/src/main/scala/scala/async/internal/AsyncId.scala
@@ -13,7 +13,7 @@ object AsyncId extends AsyncBase {
lazy val futureSystem = IdentityFutureSystem
type FS = IdentityFutureSystem.type
- def async[T](body: T) = macro asyncIdImpl[T]
+ def async[T](body: => T) = macro asyncIdImpl[T]
def asyncIdImpl[T: c.WeakTypeTag](c: Context)(body: c.Expr[T]): c.Expr[T] = asyncImpl[T](c)(body)(c.literalUnit)
}
diff --git a/src/main/scala/scala/async/internal/AsyncTransform.scala b/src/main/scala/scala/async/internal/AsyncTransform.scala
index 47a2704..b894e61 100644
--- a/src/main/scala/scala/async/internal/AsyncTransform.scala
+++ b/src/main/scala/scala/async/internal/AsyncTransform.scala
@@ -68,12 +68,12 @@ trait AsyncTransform {
for ((state, flds) <- assignsOf) {
val assigns = flds.map { fld =>
val fieldSym = fld.symbol
- Block(
- List(
- asyncBase.nullOut(global)(Expr[String](Literal(Constant(fieldSym.name.toString))), Expr[Any](Ident(fieldSym))).tree
- ),
- Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), mkZero(fieldSym.info))
- )
+ val assign = Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), mkZero(fieldSym.info))
+ asyncBase.nullOut(global)(Expr[String](Literal(Constant(fieldSym.name.toString))), Expr[Any](Ident(fieldSym))).tree
+ match {
+ case Literal(Constant(value: Unit)) => assign
+ case x => Block(x :: Nil, assign)
+ }
}
val asyncState = asyncBlock.asyncStates.find(_.state == state).get
asyncState.stats = assigns ++ asyncState.stats
diff --git a/src/main/scala/scala/async/internal/ExprBuilder.scala b/src/main/scala/scala/async/internal/ExprBuilder.scala
index 2dd485d..ae20642 100644
--- a/src/main/scala/scala/async/internal/ExprBuilder.scala
+++ b/src/main/scala/scala/async/internal/ExprBuilder.scala
@@ -33,8 +33,18 @@ trait ExprBuilder {
var stats: List[Tree]
+ def statsAnd(trees: List[Tree]): List[Tree] = {
+ val body = stats match {
+ case init :+ last if tpeOf(last) =:= definitions.NothingTpe =>
+ adaptToUnit(init :+ Typed(last, TypeTree(definitions.AnyTpe)))
+ case _ =>
+ adaptToUnit(stats)
+ }
+ Try(body, Nil, adaptToUnit(trees)) :: Nil
+ }
+
final def allStats: List[Tree] = this match {
- case a: AsyncStateWithAwait => stats :+ a.awaitable.resultValDef
+ case a: AsyncStateWithAwait => statsAnd(a.awaitable.resultValDef :: Nil)
case _ => stats
}
@@ -51,8 +61,9 @@ trait ExprBuilder {
def nextStates: List[Int] =
List(nextState)
- def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef =
- mkHandlerCase(state, stats :+ mkStateTree(nextState, symLookup))
+ def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef = {
+ mkHandlerCase(state, statsAnd(mkStateTree(nextState, symLookup) :: Nil))
+ }
override val toString: String =
s"AsyncState #$state, next = $nextState"
@@ -86,10 +97,10 @@ trait ExprBuilder {
val tryGetOrCallOnComplete =
if (futureSystemOps.continueCompletedFutureOnSameThread)
If(futureSystemOps.isCompleted(Expr[futureSystem.Fut[_]](awaitable.expr)).tree,
- Block(ifIsFailureTree[T](futureSystemOps.getCompleted[Any](Expr[futureSystem.Fut[Any]](awaitable.expr)).tree) :: Nil, literalUnit),
- Block(callOnComplete :: Nil, Return(literalUnit)))
+ adaptToUnit(ifIsFailureTree[T](futureSystemOps.getCompleted[Any](Expr[futureSystem.Fut[Any]](awaitable.expr)).tree) :: Nil),
+ Block(toList(callOnComplete), Return(literalUnit)))
else
- Block(callOnComplete :: Nil, Return(literalUnit))
+ Block(toList(callOnComplete), Return(literalUnit))
mkHandlerCase(state, stats ++ List(mkStateTree(onCompleteState, symLookup), tryGetOrCallOnComplete))
}
@@ -109,11 +120,11 @@ trait ExprBuilder {
*/
def ifIsFailureTree[T: WeakTypeTag](tryReference: => Tree) =
If(futureSystemOps.tryyIsFailure(Expr[futureSystem.Tryy[T]](tryReference)).tree,
- Block(futureSystemOps.completeProm[T](
+ Block(toList(futureSystemOps.completeProm[T](
Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)),
Expr[futureSystem.Tryy[T]](
TypeApply(Select(tryReference, newTermName("asInstanceOf")),
- List(TypeTree(futureSystemOps.tryType[T]))))).tree :: Nil,
+ List(TypeTree(futureSystemOps.tryType[T]))))).tree),
Return(literalUnit)),
Block(List(tryGetTree(tryReference)), mkStateTree(nextState, symLookup))
)
@@ -381,12 +392,12 @@ trait ExprBuilder {
val t = Expr[Throwable](Ident(name.t))
val complete = futureSystemOps.completeProm[T](
Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), futureSystemOps.tryyFailure[T](t)).tree
- Block(complete :: Nil, Return(literalUnit))
+ Block(toList(complete), Return(literalUnit))
})), EmptyTree)
def forever(t: Tree): Tree = {
val labelName = name.fresh("while$")
- LabelDef(labelName, Nil, Block(t :: Nil, Apply(Ident(labelName), Nil)))
+ LabelDef(labelName, Nil, Block(toList(t), Apply(Ident(labelName), Nil)))
}
/**
@@ -404,7 +415,7 @@ trait ExprBuilder {
def onCompleteHandler[T: WeakTypeTag]: Tree = {
val onCompletes = initStates.flatMap(_.mkOnCompleteHandler[T]).toList
forever {
- Block(resumeFunTree :: Nil, literalUnit)
+ adaptToUnit(toList(resumeFunTree))
}
}
}
@@ -421,12 +432,32 @@ trait ExprBuilder {
Assign(symLookup.memberRef(name.state), Literal(Constant(nextState)))
private def mkHandlerCase(num: Int, rhs: List[Tree]): CaseDef =
- mkHandlerCase(num, Block(rhs, literalUnit))
+ mkHandlerCase(num, adaptToUnit(rhs))
+
+ private def tpeOf(t: Tree): Type = t match {
+ case _ if t.tpe != null => t.tpe
+ case Try(body, Nil, _) => tpeOf(body)
+ case _ => NoType
+ }
+
+ private def adaptToUnit(rhs: List[Tree]): Block = {
+ rhs match {
+ case init :+ last if tpeOf(last) <:< definitions.UnitTpe =>
+ Block(init, last)
+ case _ =>
+ Block(rhs, literalUnit)
+ }
+ }
private def mkHandlerCase(num: Int, rhs: Tree): CaseDef =
CaseDef(Literal(Constant(num)), EmptyTree, rhs)
- def literalUnit = Literal(Constant(()))
+ def literalUnit = Literal(Constant(())) // a def to avoid sharing trees
+
+ def toList(tree: Tree): List[Tree] = tree match {
+ case Block(stats, Literal(Constant(value))) if value == () => stats
+ case _ => tree :: Nil
+ }
def literalNull = Literal(Constant(null))
}
diff --git a/src/test/scala/scala/async/package.scala b/src/test/scala/scala/async/package.scala
index 974a989..b355825 100644
--- a/src/test/scala/scala/async/package.scala
+++ b/src/test/scala/scala/async/package.scala
@@ -42,6 +42,18 @@ package object async {
m.mkToolBox(options = compileOptions)
}
+ import scala.tools.nsc._, reporters._
+ def mkGlobal(compileOptions: String = ""): Global = {
+ val settings = new Settings()
+ settings.processArgumentString(compileOptions)
+ val initClassPath = settings.classpath.value
+ settings.embeddedDefaults(getClass.getClassLoader)
+ if (initClassPath == settings.classpath.value)
+ settings.usejavacp.value = true // not running under SBT, try to use the Java claspath instead
+ val reporter = new StoreReporter
+ new Global(settings, reporter)
+ }
+
def scalaBinaryVersion: String = {
val PreReleasePattern = """.*-(M|RC).*""".r
val Pattern = """(\d+\.\d+)\..*""".r
diff --git a/src/test/scala/scala/async/run/WarningsSpec.scala b/src/test/scala/scala/async/run/WarningsSpec.scala
index 3a7843a..00c6466 100644
--- a/src/test/scala/scala/async/run/WarningsSpec.scala
+++ b/src/test/scala/scala/async/run/WarningsSpec.scala
@@ -7,10 +7,8 @@ package run
import org.junit.Test
-import scala.async.internal.AsyncId
-import scala.concurrent.Await
-import scala.concurrent.duration._
import scala.language.{postfixOps, reflectiveCalls}
+import scala.tools.nsc.reporters.StoreReporter
class WarningsSpec {
@@ -32,4 +30,48 @@ class WarningsSpec {
""".stripMargin
})
}
+
+ @Test
+ // https://github.com/scala/async/issues/74
+ def noDeadCodeWarningForAsyncThrow() {
+ val global = mkGlobal("-cp ${toolboxClasspath} -Yrangepos -Ywarn-dead-code -Xfatal-warnings -Ystop-after:refchecks")
+ // was: "a pure expression does nothing in statement position; you may be omitting necessary parentheses"
+ val source =
+ """
+ | class Test {
+ | import scala.async.Async._
+ | import scala.concurrent.ExecutionContext.Implicits.global
+ | async { throw new Error() }
+ | }
+ """.stripMargin
+ val run = new global.Run
+ val sourceFile = global.newSourceFile(source)
+ run.compileSources(sourceFile :: Nil)
+ assert(!global.reporter.hasErrors, global.reporter.asInstanceOf[StoreReporter].infos)
+ }
+
+ @Test
+ def noDeadCodeWarningInMacroExpansion() {
+ val global = mkGlobal("-cp ${toolboxClasspath} -Yrangepos -Ywarn-dead-code -Xfatal-warnings -Ystop-after:refchecks")
+ val source = """
+ | class Test {
+ | def test = {
+ | import scala.async.Async._, scala.concurrent._, ExecutionContext.Implicits.global
+ | async {
+ | val opt = await(async(Option.empty[String => Future[Unit]]))
+ | opt match {
+ | case None =>
+ | throw new RuntimeException("case a")
+ | case Some(f) =>
+ | await(f("case b"))
+ | }
+ | }
+ | }
+ |}
+ """.stripMargin
+ val run = new global.Run
+ val sourceFile = global.newSourceFile(source)
+ run.compileSources(sourceFile :: Nil)
+ assert(!global.reporter.hasErrors, global.reporter.asInstanceOf[StoreReporter].infos)
+ }
}