Skip to content

Commit 307bf0e

Browse files
committed
Use compiler integrated async phase under -Xasync
1 parent 96af417 commit 307bf0e

File tree

4 files changed

+66
-2
lines changed

4 files changed

+66
-2
lines changed

build.sbt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ ScalaModulePlugin.scalaModuleSettingsJVM
33

44
name := "scala-async"
55

6+
scalaVersion := "2.12.11-bin-fd83137-SNAPSHOT"
67
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided"
78
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % "test" // for ToolBox
89
libraryDependencies += "junit" % "junit" % "4.12" % "test"
@@ -11,6 +12,7 @@ libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
1112
ScalaModulePlugin.enableOptimizer
1213
testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-v", "-s")
1314
scalacOptions in Test ++= Seq("-Yrangepos")
15+
scalacOptions in Test ++= (if (scalaVersion.value == "2.12.11-bin-fd83137-SNAPSHOT") List("-Xasync") else Nil)
1416

1517
parallelExecution in Global := false
1618

src/main/scala/scala/async/internal/AsyncTransform.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ trait AsyncTransform {
5656
val apply0DefDef: DefDef = {
5757
// We extend () => Unit so we can pass this class as the by-name argument to `Future.apply`.
5858
// See SI-1247 for the the optimization that avoids creation.
59-
DefDef(NoMods, name.apply, Nil, Nil, TypeTree(definitions.UnitTpe), Apply(Ident(name.apply), literalNull :: Nil))
59+
DefDef(NoMods, name.apply, Nil, List(Nil), TypeTree(definitions.UnitTpe), Apply(Ident(name.apply), literalNull :: Nil))
6060
}
6161
List(emptyConstructor, stateVar) ++ resultAndAccessors ++ List(execContextValDef) ++ List(applyDefDefDummyBody, apply0DefDef)
6262
}

src/main/scala/scala/async/internal/ExprBuilder.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,9 @@ trait ExprBuilder {
536536
Block(toList(complete), Return(literalUnit))
537537
}
538538
If(Apply(Ident(defn.NonFatalClass), List(Ident(name.t))), thenn, Throw(Ident(name.t)))
539+
// TODO scala-async accidentally started catching NonFatal exceptions in:
540+
// https://github.com/scala/scala-async/commit/e3ff0382ae4e015fc69da8335450718951714982#diff-136ab0b6ecaee5d240cd109e2b17ccb2R411
541+
// Should we restore the old behavior ?!?!
539542
thenn
540543
})), EmptyTree)
541544
}

src/main/scala/scala/async/internal/ScalaConcurrentAsync.scala

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,65 @@ object ScalaConcurrentAsync extends AsyncBase {
2424
override def asyncImpl[T: c.WeakTypeTag](c: whitebox.Context)
2525
(body: c.Expr[T])
2626
(execContext: c.Expr[futureSystem.ExecContext]): c.Expr[Future[T]] = {
27-
super.asyncImpl[T](c)(body)(execContext)
27+
import c.universe._
28+
if (!c.compilerSettings.contains("-Xasync")) {
29+
super.asyncImpl[T](c)(body)(execContext)
30+
} else try {
31+
val awaitSym = typeOf[Async.type].decl(TermName("await"))
32+
def mark(t: DefDef): Tree = {
33+
import language.reflectiveCalls
34+
c.internal.asInstanceOf[{
35+
def markForAsyncTransform(owner: Symbol, method: DefDef, awaitSymbol: Symbol, config: Map[String, AnyRef]): DefDef
36+
}].markForAsyncTransform(c.internal.enclosingOwner, t, awaitSym, Map.empty)
37+
}
38+
val name = TypeName("stateMachine$async_" + body.tree.pos.line)
39+
// TODO all but the `apply` method code could live in a abstract base class shipped as runtime support.
40+
41+
// TODO scala-async accidentally started catching NonFatal exceptions in:
42+
// https://github.com/scala/scala-async/commit/e3ff0382ae4e015fc69da8335450718951714982#diff-136ab0b6ecaee5d240cd109e2b17ccb2R411
43+
// This follows the new behaviour but should we fix the regression?
44+
45+
val tree = q"""
46+
47+
final class $name extends _root_.scala.Function1[_root_.scala.util.Try[_root_.scala.AnyRef], _root_.scala.Unit] {
48+
private val execContext$$async = ${execContext.tree}
49+
private val result$$async: _root_.scala.concurrent.Promise[_root_.scala.AnyRef] = _root_.scala.concurrent.Promise[_root_.scala.AnyRef]();
50+
51+
// FSM translated method
52+
${mark(q"""override def apply(tr$$async: _root_.scala.util.Try[_root_.scala.AnyRef]) = ${body.tree}""")}
53+
54+
// Required methods
55+
protected var state$$async: _root_.scala.Int = 0
56+
57+
protected def completeFailure(t: _root_.scala.Throwable): _root_.scala.Unit = result$$async.complete(_root_.scala.util.Failure(t))
58+
59+
protected def completeSuccess(value: _root_.scala.AnyRef): _root_.scala.Unit = result$$async.complete(_root_.scala.util.Success(value))
60+
protected def onComplete(f: _root_.scala.concurrent.Future[_root_.scala.AnyRef]): _root_.scala.Unit = f.onComplete(this)(execContext$$async)
61+
protected def getCompleted(f: _root_.scala.concurrent.Future[_root_.scala.AnyRef]): _root_.scala.util.Try[_root_.scala.AnyRef] = {
62+
if (f.isCompleted) {
63+
f.value.get
64+
} else null
65+
}
66+
protected def tryGet(tr: _root_.scala.util.Try[_root_.scala.AnyRef]): _root_.scala.AnyRef = tr match {
67+
case _root_.scala.util.Success(value) =>
68+
value.asInstanceOf[_root_.scala.AnyRef]
69+
case _root_.scala.util.Failure(throwable) =>
70+
completeFailure(throwable)
71+
this // sentinel value to indicate the dispatch loop should exit.
72+
}
73+
def start(): _root_.scala.concurrent.Future[_root_.scala.AnyRef] = {
74+
// This cast is safe because we know that `def apply` does not consult its argument when `state == 0`.
75+
_root_.scala.concurrent.Future.unit.asInstanceOf[_root_.scala.concurrent.Future[_root_.scala.AnyRef]].onComplete(this)(execContext$$async)
76+
result$$async.future
77+
}
78+
79+
}
80+
new $name().start().asInstanceOf[${c.macroApplication.tpe}]
81+
"""
82+
c.Expr[Future[T]](tree)
83+
} catch {
84+
case e: ReflectiveOperationException =>
85+
c.abort(c.macroApplication.pos, "-Xasync is provided as a Scala compiler option, but the async macro is unable to call c.internal.markForAsyncTransform. " + e.getClass.getName + " " + e.getMessage)
86+
}
2887
}
2988
}

0 commit comments

Comments
 (0)