Skip to content

Commit b295d74

Browse files
committed
Merge pull request #133 from retronym/backport/150
Backport fix for #150 to 2.10.x
2 parents a82fa0e + f9e170e commit b295d74

File tree

6 files changed

+109
-24
lines changed

6 files changed

+109
-24
lines changed

src/main/scala/scala/async/Async.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ object Async {
4242
* Run the block of code `body` asynchronously. `body` may contain calls to `await` when the results of
4343
* a `Future` are needed; this is translated into non-blocking code.
4444
*/
45-
def async[T](body: T)(implicit execContext: ExecutionContext): Future[T] = macro internal.ScalaConcurrentAsync.asyncImpl[T]
45+
def async[T](body: => T)(implicit execContext: ExecutionContext): Future[T] = macro internal.ScalaConcurrentAsync.asyncImpl[T]
4646

4747
/**
4848
* Non-blocking await the on result of `awaitable`. This may only be used directly within an enclosing `async` block.

src/main/scala/scala/async/internal/AsyncId.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object AsyncId extends AsyncBase {
1313
lazy val futureSystem = IdentityFutureSystem
1414
type FS = IdentityFutureSystem.type
1515

16-
def async[T](body: T) = macro asyncIdImpl[T]
16+
def async[T](body: => T) = macro asyncIdImpl[T]
1717

1818
def asyncIdImpl[T: c.WeakTypeTag](c: Context)(body: c.Expr[T]): c.Expr[T] = asyncImpl[T](c)(body)(c.literalUnit)
1919
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ trait AsyncTransform {
6868
for ((state, flds) <- assignsOf) {
6969
val assigns = flds.map { fld =>
7070
val fieldSym = fld.symbol
71-
Block(
72-
List(
73-
asyncBase.nullOut(global)(Expr[String](Literal(Constant(fieldSym.name.toString))), Expr[Any](Ident(fieldSym))).tree
74-
),
75-
Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), mkZero(fieldSym.info))
76-
)
71+
val assign = Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), mkZero(fieldSym.info))
72+
asyncBase.nullOut(global)(Expr[String](Literal(Constant(fieldSym.name.toString))), Expr[Any](Ident(fieldSym))).tree
73+
match {
74+
case Literal(Constant(value: Unit)) => assign
75+
case x => Block(x :: Nil, assign)
76+
}
7777
}
7878
val asyncState = asyncBlock.asyncStates.find(_.state == state).get
7979
asyncState.stats = assigns ++ asyncState.stats

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

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,18 @@ trait ExprBuilder {
3333

3434
var stats: List[Tree]
3535

36+
def statsAnd(trees: List[Tree]): List[Tree] = {
37+
val body = stats match {
38+
case init :+ last if tpeOf(last) =:= definitions.NothingTpe =>
39+
adaptToUnit(init :+ Typed(last, TypeTree(definitions.AnyTpe)))
40+
case _ =>
41+
adaptToUnit(stats)
42+
}
43+
Try(body, Nil, adaptToUnit(trees)) :: Nil
44+
}
45+
3646
final def allStats: List[Tree] = this match {
37-
case a: AsyncStateWithAwait => stats :+ a.awaitable.resultValDef
47+
case a: AsyncStateWithAwait => statsAnd(a.awaitable.resultValDef :: Nil)
3848
case _ => stats
3949
}
4050

@@ -51,8 +61,9 @@ trait ExprBuilder {
5161
def nextStates: List[Int] =
5262
List(nextState)
5363

54-
def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef =
55-
mkHandlerCase(state, stats :+ mkStateTree(nextState, symLookup))
64+
def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef = {
65+
mkHandlerCase(state, statsAnd(mkStateTree(nextState, symLookup) :: Nil))
66+
}
5667

5768
override val toString: String =
5869
s"AsyncState #$state, next = $nextState"
@@ -86,10 +97,10 @@ trait ExprBuilder {
8697
val tryGetOrCallOnComplete =
8798
if (futureSystemOps.continueCompletedFutureOnSameThread)
8899
If(futureSystemOps.isCompleted(Expr[futureSystem.Fut[_]](awaitable.expr)).tree,
89-
Block(ifIsFailureTree[T](futureSystemOps.getCompleted[Any](Expr[futureSystem.Fut[Any]](awaitable.expr)).tree) :: Nil, literalUnit),
90-
Block(callOnComplete :: Nil, Return(literalUnit)))
100+
adaptToUnit(ifIsFailureTree[T](futureSystemOps.getCompleted[Any](Expr[futureSystem.Fut[Any]](awaitable.expr)).tree) :: Nil),
101+
Block(toList(callOnComplete), Return(literalUnit)))
91102
else
92-
Block(callOnComplete :: Nil, Return(literalUnit))
103+
Block(toList(callOnComplete), Return(literalUnit))
93104
mkHandlerCase(state, stats ++ List(mkStateTree(onCompleteState, symLookup), tryGetOrCallOnComplete))
94105
}
95106

@@ -109,11 +120,11 @@ trait ExprBuilder {
109120
*/
110121
def ifIsFailureTree[T: WeakTypeTag](tryReference: => Tree) =
111122
If(futureSystemOps.tryyIsFailure(Expr[futureSystem.Tryy[T]](tryReference)).tree,
112-
Block(futureSystemOps.completeProm[T](
123+
Block(toList(futureSystemOps.completeProm[T](
113124
Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)),
114125
Expr[futureSystem.Tryy[T]](
115126
TypeApply(Select(tryReference, newTermName("asInstanceOf")),
116-
List(TypeTree(futureSystemOps.tryType[T]))))).tree :: Nil,
127+
List(TypeTree(futureSystemOps.tryType[T]))))).tree),
117128
Return(literalUnit)),
118129
Block(List(tryGetTree(tryReference)), mkStateTree(nextState, symLookup))
119130
)
@@ -381,12 +392,12 @@ trait ExprBuilder {
381392
val t = Expr[Throwable](Ident(name.t))
382393
val complete = futureSystemOps.completeProm[T](
383394
Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), futureSystemOps.tryyFailure[T](t)).tree
384-
Block(complete :: Nil, Return(literalUnit))
395+
Block(toList(complete), Return(literalUnit))
385396
})), EmptyTree)
386397

387398
def forever(t: Tree): Tree = {
388399
val labelName = name.fresh("while$")
389-
LabelDef(labelName, Nil, Block(t :: Nil, Apply(Ident(labelName), Nil)))
400+
LabelDef(labelName, Nil, Block(toList(t), Apply(Ident(labelName), Nil)))
390401
}
391402

392403
/**
@@ -404,7 +415,7 @@ trait ExprBuilder {
404415
def onCompleteHandler[T: WeakTypeTag]: Tree = {
405416
val onCompletes = initStates.flatMap(_.mkOnCompleteHandler[T]).toList
406417
forever {
407-
Block(resumeFunTree :: Nil, literalUnit)
418+
adaptToUnit(toList(resumeFunTree))
408419
}
409420
}
410421
}
@@ -421,12 +432,32 @@ trait ExprBuilder {
421432
Assign(symLookup.memberRef(name.state), Literal(Constant(nextState)))
422433

423434
private def mkHandlerCase(num: Int, rhs: List[Tree]): CaseDef =
424-
mkHandlerCase(num, Block(rhs, literalUnit))
435+
mkHandlerCase(num, adaptToUnit(rhs))
436+
437+
private def tpeOf(t: Tree): Type = t match {
438+
case _ if t.tpe != null => t.tpe
439+
case Try(body, Nil, _) => tpeOf(body)
440+
case _ => NoType
441+
}
442+
443+
private def adaptToUnit(rhs: List[Tree]): Block = {
444+
rhs match {
445+
case init :+ last if tpeOf(last) <:< definitions.UnitTpe =>
446+
Block(init, last)
447+
case _ =>
448+
Block(rhs, literalUnit)
449+
}
450+
}
425451

426452
private def mkHandlerCase(num: Int, rhs: Tree): CaseDef =
427453
CaseDef(Literal(Constant(num)), EmptyTree, rhs)
428454

429-
def literalUnit = Literal(Constant(()))
455+
def literalUnit = Literal(Constant(())) // a def to avoid sharing trees
456+
457+
def toList(tree: Tree): List[Tree] = tree match {
458+
case Block(stats, Literal(Constant(value))) if value == () => stats
459+
case _ => tree :: Nil
460+
}
430461

431462
def literalNull = Literal(Constant(null))
432463
}

src/test/scala/scala/async/package.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ package object async {
4242
m.mkToolBox(options = compileOptions)
4343
}
4444

45+
import scala.tools.nsc._, reporters._
46+
def mkGlobal(compileOptions: String = ""): Global = {
47+
val settings = new Settings()
48+
settings.processArgumentString(compileOptions)
49+
val initClassPath = settings.classpath.value
50+
settings.embeddedDefaults(getClass.getClassLoader)
51+
if (initClassPath == settings.classpath.value)
52+
settings.usejavacp.value = true // not running under SBT, try to use the Java claspath instead
53+
val reporter = new StoreReporter
54+
new Global(settings, reporter)
55+
}
56+
4557
def scalaBinaryVersion: String = {
4658
val PreReleasePattern = """.*-(M|RC).*""".r
4759
val Pattern = """(\d+\.\d+)\..*""".r

src/test/scala/scala/async/run/WarningsSpec.scala

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ package run
77

88
import org.junit.Test
99

10-
import scala.async.internal.AsyncId
11-
import scala.concurrent.Await
12-
import scala.concurrent.duration._
1310
import scala.language.{postfixOps, reflectiveCalls}
11+
import scala.tools.nsc.reporters.StoreReporter
1412

1513

1614
class WarningsSpec {
@@ -32,4 +30,48 @@ class WarningsSpec {
3230
""".stripMargin
3331
})
3432
}
33+
34+
@Test
35+
// https://github.com/scala/async/issues/74
36+
def noDeadCodeWarningForAsyncThrow() {
37+
val global = mkGlobal("-cp ${toolboxClasspath} -Yrangepos -Ywarn-dead-code -Xfatal-warnings -Ystop-after:refchecks")
38+
// was: "a pure expression does nothing in statement position; you may be omitting necessary parentheses"
39+
val source =
40+
"""
41+
| class Test {
42+
| import scala.async.Async._
43+
| import scala.concurrent.ExecutionContext.Implicits.global
44+
| async { throw new Error() }
45+
| }
46+
""".stripMargin
47+
val run = new global.Run
48+
val sourceFile = global.newSourceFile(source)
49+
run.compileSources(sourceFile :: Nil)
50+
assert(!global.reporter.hasErrors, global.reporter.asInstanceOf[StoreReporter].infos)
51+
}
52+
53+
@Test
54+
def noDeadCodeWarningInMacroExpansion() {
55+
val global = mkGlobal("-cp ${toolboxClasspath} -Yrangepos -Ywarn-dead-code -Xfatal-warnings -Ystop-after:refchecks")
56+
val source = """
57+
| class Test {
58+
| def test = {
59+
| import scala.async.Async._, scala.concurrent._, ExecutionContext.Implicits.global
60+
| async {
61+
| val opt = await(async(Option.empty[String => Future[Unit]]))
62+
| opt match {
63+
| case None =>
64+
| throw new RuntimeException("case a")
65+
| case Some(f) =>
66+
| await(f("case b"))
67+
| }
68+
| }
69+
| }
70+
|}
71+
""".stripMargin
72+
val run = new global.Run
73+
val sourceFile = global.newSourceFile(source)
74+
run.compileSources(sourceFile :: Nil)
75+
assert(!global.reporter.hasErrors, global.reporter.asInstanceOf[StoreReporter].infos)
76+
}
3577
}

0 commit comments

Comments
 (0)