From f38a2f78243b4cc481d7150e78adef2b2f8afc8d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 27 Mar 2014 14:39:39 +0100 Subject: 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. Fixes #52 --- .../scala/scala/async/internal/AsyncAnalysis.scala | 5 ++-- .../scala/async/internal/AsyncTransform.scala | 13 +++++++-- src/test/scala/scala/async/neg/NakedAwait.scala | 4 +-- .../scala/async/run/lazyval/LazyValSpec.scala | 34 ++++++++++++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 src/test/scala/scala/async/run/lazyval/LazyValSpec.scala (limited to 'src') diff --git a/src/main/scala/scala/async/internal/AsyncAnalysis.scala b/src/main/scala/scala/async/internal/AsyncAnalysis.scala index ffbc04d..7be6b43 100644 --- a/src/main/scala/scala/async/internal/AsyncAnalysis.scala +++ b/src/main/scala/scala/async/internal/AsyncAnalysis.scala @@ -60,9 +60,8 @@ trait AsyncAnalysis { super.traverse(tree) case Return(_) => c.abort(tree.pos, "return is illegal within a async block") - case ValDef(mods, _, _, _) if mods.hasFlag(Flag.LAZY) => - // TODO lift this restriction - c.abort(tree.pos, "lazy vals are illegal within an async block") + case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flag.LAZY) && containsAwait => + reportUnsupportedAwait(tree, "lazy val initalizer") case CaseDef(_, guard, _) if guard exists isAwait => // TODO lift this restriction reportUnsupportedAwait(tree, "pattern guard") diff --git a/src/main/scala/scala/async/internal/AsyncTransform.scala b/src/main/scala/scala/async/internal/AsyncTransform.scala index cf9dd1c..aae9dea 100644 --- a/src/main/scala/scala/async/internal/AsyncTransform.scala +++ b/src/main/scala/scala/async/internal/AsyncTransform.scala @@ -54,11 +54,10 @@ trait AsyncTransform { } val tryToUnit = appliedType(definitions.FunctionClass(1), futureSystemOps.tryType[Any], typeOf[Unit]) - val template = Template(List(tryToUnit, typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body) + val template = Template(List(tryToUnit, typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body).setType(NoType) val t = ClassDef(NoMods, name.stateMachineT, Nil, template) - typingTransform(atPos(macroPos)(Block(t :: Nil, Literal(Constant(())))))((tree, api) => api.typecheck(tree)) - t + typecheckClassDef(t) } val stateMachineClass = stateMachine.symbol @@ -211,4 +210,12 @@ trait AsyncTransform { } result } + + def typecheckClassDef(cd: ClassDef): ClassDef = { + val Block(cd1 :: Nil, _) = typingTransform(atPos(macroPos)(Block(cd :: Nil, Literal(Constant(())))))( + (tree, api) => + api.typecheck(tree) + ) + cd1.asInstanceOf[ClassDef] + } } diff --git a/src/test/scala/scala/async/neg/NakedAwait.scala b/src/test/scala/scala/async/neg/NakedAwait.scala index 9778c72..f9264c7 100644 --- a/src/test/scala/scala/async/neg/NakedAwait.scala +++ b/src/test/scala/scala/async/neg/NakedAwait.scala @@ -163,10 +163,10 @@ class NakedAwait { @Test def lazyValIllegal() { - expectError("lazy vals are illegal") { + expectError("await must not be used under a lazy val initalizer") { """ | import _root_.scala.async.internal.AsyncId._ - | def foo(): Any = async { val x = { lazy val y = 0; y } } + | def foo(): Any = async { val x = { lazy val y = await(0); y } } | () | |""".stripMargin diff --git a/src/test/scala/scala/async/run/lazyval/LazyValSpec.scala b/src/test/scala/scala/async/run/lazyval/LazyValSpec.scala new file mode 100644 index 0000000..6214b43 --- /dev/null +++ b/src/test/scala/scala/async/run/lazyval/LazyValSpec.scala @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012-2014 Typesafe Inc. + */ + +package scala.async +package run +package lazyval + +import scala.async.run.noawait + +import scala.async.internal.AsyncId +import scala.async.internal.AsyncId +import AsyncId._ +import org.junit.Test +import scala.async.internal.AsyncId._ + +class LazyValSpec { + @Test + def lazyValAllowed() { + val result = async { + var x = 0 + lazy val y = { x += 1; 42 } + assert(x == 0, x) + val z = await(1) + val result = y + x + assert(x == 1, x) + identity(y) + assert(x == 1, x) + result + } + result mustBe 43 + } +} + -- cgit v1.2.3