Skip to content

Commit ee66e08

Browse files
committed
[backport] Allow lazy vals without await in the initializer
We were incorrectly typechecking the `ClassDef` of the state machine in the macro in a way that discarded the resulting trees, and only kept around the symbol. The led to the the macro engine retypechecking that node, which somehow led to duplicated lazy val initiaializer `DefDef`-s in the template, which manifest as a `VerifyError`. This commit: - rescues the typechecked `ClassDef` node from the eager typechecking by the macro - loosens the restriction on lazy vals in async blocks. They are still prohibited if they contain an await on the RHS - Adds a test that shows evalution is indeed lazy. (cherry picked from commit cc4587b) Conflicts: src/main/scala/scala/async/internal/AsyncAnalysis.scala src/main/scala/scala/async/internal/AsyncTransform.scala
1 parent 6808ce4 commit ee66e08

File tree

4 files changed

+45
-8
lines changed

4 files changed

+45
-8
lines changed

src/main/scala/scala/async/internal/AsyncAnalysis.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,8 @@ trait AsyncAnalysis {
6060
super.traverse(tree)
6161
case Return(_) =>
6262
abort(tree.pos, "return is illegal within a async block")
63-
case ValDef(mods, _, _, _) if mods.hasFlag(Flag.LAZY) =>
64-
// TODO lift this restriction
65-
abort(tree.pos, "lazy vals are illegal within an async block")
63+
case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flag.LAZY) && containsAwait =>
64+
reportUnsupportedAwait(tree, "lazy val initalizer")
6665
case CaseDef(_, guard, _) if guard exists isAwait =>
6766
// TODO lift this restriction
6867
reportUnsupportedAwait(tree, "pattern guard")

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,10 @@ trait AsyncTransform {
5252
}
5353

5454
val tryToUnit = appliedType(definitions.FunctionClass(1), futureSystemOps.tryType[Any], typeOf[Unit])
55-
val template = Template(List(tryToUnit, typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body)
55+
val template = Template(List(tryToUnit, typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body).setType(NoType)
5656

5757
val t = ClassDef(NoMods, name.stateMachineT, Nil, template)
58-
callSiteTyper.typedPos(macroPos)(Block(t :: Nil, Literal(Constant(()))))
59-
t
58+
typecheckClassDef(t)
6059
}
6160

6261
val stateMachineClass = stateMachine.symbol
@@ -218,4 +217,9 @@ trait AsyncTransform {
218217
}
219218
result
220219
}
220+
221+
def typecheckClassDef(cd: ClassDef): ClassDef = {
222+
val Block(cd1 :: Nil, _) = callSiteTyper.typedPos(macroPos)(Block(cd :: Nil, Literal(Constant(()))))
223+
cd1.asInstanceOf[ClassDef]
224+
}
221225
}

src/test/scala/scala/async/neg/NakedAwait.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ class NakedAwait {
163163

164164
@Test
165165
def lazyValIllegal() {
166-
expectError("lazy vals are illegal") {
166+
expectError("await must not be used under a lazy val initalizer") {
167167
"""
168168
| import _root_.scala.async.internal.AsyncId._
169-
| def foo(): Any = async { val x = { lazy val y = 0; y } }
169+
| def foo(): Any = async { val x = { lazy val y = await(0); y } }
170170
| ()
171171
|
172172
|""".stripMargin
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (C) 2012-2014 Typesafe Inc. <http://www.typesafe.com>
3+
*/
4+
5+
package scala.async
6+
package run
7+
package lazyval
8+
9+
import scala.async.run.noawait
10+
11+
import scala.async.internal.AsyncId
12+
import scala.async.internal.AsyncId
13+
import AsyncId._
14+
import org.junit.Test
15+
import scala.async.internal.AsyncId._
16+
17+
class LazyValSpec {
18+
@Test
19+
def lazyValAllowed() {
20+
val result = async {
21+
var x = 0
22+
lazy val y = { x += 1; 42 }
23+
assert(x == 0, x)
24+
val z = await(1)
25+
val result = y + x
26+
assert(x == 1, x)
27+
identity(y)
28+
assert(x == 1, x)
29+
result
30+
}
31+
result mustBe 43
32+
}
33+
}
34+

0 commit comments

Comments
 (0)