From d76bd53b4a86d748883e3c82e749eaebbc16892c Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 10 Mar 2014 09:35:01 +0100 Subject: [PATCH 01/14] only build 2.10.x on this branch --- release.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/release.sh b/release.sh index ceee2871..28bef269 100755 --- a/release.sh +++ b/release.sh @@ -12,10 +12,6 @@ # % cat ~/.sbt/0.13/plugins/gpg.sbt # addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.1") -function sbt211() { - sbt 'set scalaVersion := "2.11.0-M6"' 'set scalaBinaryVersion := scalaVersion.value' $@ - return $? -} die () { echo "$@" exit 1 @@ -26,10 +22,8 @@ RELEASE=";clean;test;publishSigned" VERSION=`gsed -rn 's/version :=.*"(.+).*"/\1/p' build.sbt` [[ -n "$(git status --porcelain)" ]] && die "working directory is not clean!" -sbt211 $CHECK sbt $CHECK sbt $RELEASE -sbt211 $RELEASE cat < Date: Mon, 10 Mar 2014 09:36:24 +0100 Subject: [PATCH 02/14] 0.9.0-M5 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 3095d044..09a8130a 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ organization := "org.scala-lang.modules" name := "scala-async" -version := "0.9.0-SNAPSHOT" +version := "0.9.0-M5" libraryDependencies <++= (scalaVersion) { sv => Seq( From 6b344faa028f3b84f908bce0ef8e474a496181a6 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 10 Mar 2014 09:40:22 +0100 Subject: [PATCH 03/14] 0.9.0-SNAPSHOT --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 09a8130a..3095d044 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ organization := "org.scala-lang.modules" name := "scala-async" -version := "0.9.0-M5" +version := "0.9.0-SNAPSHOT" libraryDependencies <++= (scalaVersion) { sv => Seq( From e3027b1cb599fe6e4ad3dbd9729803e188c8de10 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 10 Mar 2014 09:42:09 +0100 Subject: [PATCH 04/14] Update version in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2aa8885..3da756e6 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Add a dependency: ```scala // SBT -libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.0-M4" +libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.0-M5" ``` Write your first `async` block: From c3384d87e986e14f3557b449a400539cb1097003 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 10 Mar 2014 09:42:58 +0100 Subject: [PATCH 05/14] Update release script for _2.10 tagging convention --- release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.sh b/release.sh index 28bef269..44e17247 100755 --- a/release.sh +++ b/release.sh @@ -27,8 +27,8 @@ sbt $RELEASE cat < Date: Mon, 10 Mar 2014 09:52:56 +0100 Subject: [PATCH 06/14] Fix substitution in release script message --- release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.sh b/release.sh index 44e17247..89964ac1 100755 --- a/release.sh +++ b/release.sh @@ -27,8 +27,8 @@ sbt $RELEASE cat < Date: Wed, 12 Mar 2014 10:36:36 +0100 Subject: [PATCH 07/14] [backport] Test case for "not a class" crasher in live variable Works on the 2.10.x branch, so just backprting the test. Cherry picked from 6f6546ebfc26564843621e79d840209a5103d3c8. --- .../scala/async/run/toughtype/ToughType.scala | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/test/scala/scala/async/run/toughtype/ToughType.scala b/src/test/scala/scala/async/run/toughtype/ToughType.scala index 6159f7b1..458157c3 100644 --- a/src/test/scala/scala/async/run/toughtype/ToughType.scala +++ b/src/test/scala/scala/async/run/toughtype/ToughType.scala @@ -184,6 +184,34 @@ class ToughTypeSpec { } Bippy } + + @Test + def ticket63(): Unit = { + import scala.async.Async._ + import scala.concurrent.{ ExecutionContext, Future } + + object SomeExecutionContext extends ExecutionContext { + def reportFailure(t: Throwable): Unit = ??? + def execute(runnable: Runnable): Unit = ??? + } + + trait FunDep[W, S, R] { + def method(w: W, s: S): Future[R] + } + + object FunDep { + implicit def `Something to do with List`[W, S, R](implicit funDep: FunDep[W, S, R]) = + new FunDep[W, List[S], W] { + def method(w: W, l: List[S]) = async { + val it = l.iterator + while (it.hasNext) { + await(funDep.method(w, it.next())) + } + w + }(SomeExecutionContext) + } + } + } } trait A From 5c83eb1bc06c065c699b4d0dc9ef0bb2f190390b Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 12 Mar 2014 10:51:39 +0100 Subject: [PATCH 08/14] 0.9.0-M6 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 3095d044..c472a7e8 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ organization := "org.scala-lang.modules" name := "scala-async" -version := "0.9.0-SNAPSHOT" +version := "0.9.0-M6" libraryDependencies <++= (scalaVersion) { sv => Seq( From b489738ebe838b772fc4fdad300c57ea571386ab Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 12 Mar 2014 10:55:40 +0100 Subject: [PATCH 09/14] 0.9.0-SNAPSHOT --- README.md | 2 +- build.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3da756e6..dea4ae94 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Add a dependency: ```scala // SBT -libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.0-M5" +libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.0-M6" ``` Write your first `async` block: diff --git a/build.sbt b/build.sbt index c472a7e8..3095d044 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ organization := "org.scala-lang.modules" name := "scala-async" -version := "0.9.0-M6" +version := "0.9.0-SNAPSHOT" libraryDependencies <++= (scalaVersion) { sv => Seq( From 8f9a0b10d743410d94f8c1d2419c769fff58fb83 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 21 Mar 2014 08:46:52 +0100 Subject: [PATCH 10/14] Update to Scala 2.10.4 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 3095d044..04758c3c 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -scalaVersion := "2.10.3" +scalaVersion := "2.10.4" // Uncomment to test with a locally built copy of Scala. // scalaHome := Some(file("/code/scala2/build/pack")) From a37cf65c3f5c93957b09741307b6f31c711bdd73 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 21 Mar 2014 08:47:26 +0100 Subject: [PATCH 11/14] 0.9.0 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 04758c3c..a59b1492 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ organization := "org.scala-lang.modules" name := "scala-async" -version := "0.9.0-SNAPSHOT" +version := "0.9.0" libraryDependencies <++= (scalaVersion) { sv => Seq( From 6808ce4a4023becd984ebe06805d1eeb844694e1 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 21 Mar 2014 08:51:07 +0100 Subject: [PATCH 12/14] 0.9.1-SNAPSHOT --- README.md | 2 +- build.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dea4ae94..9f456d88 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Add a dependency: ```scala // SBT -libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.0-M6" +libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.0" ``` Write your first `async` block: diff --git a/build.sbt b/build.sbt index a59b1492..245d335d 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ organization := "org.scala-lang.modules" name := "scala-async" -version := "0.9.0" +version := "0.9.1-SNAPSHOT" libraryDependencies <++= (scalaVersion) { sv => Seq( From ee66e081ad0dec0e0c20fbb7e40fd20122bba238 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 27 Mar 2014 14:39:39 +0100 Subject: [PATCH 13/14] [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 cc4587b1985519f7049d0feb0783d8e22c10f792) Conflicts: src/main/scala/scala/async/internal/AsyncAnalysis.scala src/main/scala/scala/async/internal/AsyncTransform.scala --- .../scala/async/internal/AsyncAnalysis.scala | 5 ++- .../scala/async/internal/AsyncTransform.scala | 10 ++++-- .../scala/scala/async/neg/NakedAwait.scala | 4 +-- .../scala/async/run/lazyval/LazyValSpec.scala | 34 +++++++++++++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 src/test/scala/scala/async/run/lazyval/LazyValSpec.scala diff --git a/src/main/scala/scala/async/internal/AsyncAnalysis.scala b/src/main/scala/scala/async/internal/AsyncAnalysis.scala index 76c2dbac..274603fd 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(_) => abort(tree.pos, "return is illegal within a async block") - case ValDef(mods, _, _, _) if mods.hasFlag(Flag.LAZY) => - // TODO lift this restriction - 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 ca53ff11..beb828df 100644 --- a/src/main/scala/scala/async/internal/AsyncTransform.scala +++ b/src/main/scala/scala/async/internal/AsyncTransform.scala @@ -52,11 +52,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) - callSiteTyper.typedPos(macroPos)(Block(t :: Nil, Literal(Constant(())))) - t + typecheckClassDef(t) } val stateMachineClass = stateMachine.symbol @@ -218,4 +217,9 @@ trait AsyncTransform { } result } + + def typecheckClassDef(cd: ClassDef): ClassDef = { + val Block(cd1 :: Nil, _) = callSiteTyper.typedPos(macroPos)(Block(cd :: Nil, Literal(Constant(())))) + cd1.asInstanceOf[ClassDef] + } } diff --git a/src/test/scala/scala/async/neg/NakedAwait.scala b/src/test/scala/scala/async/neg/NakedAwait.scala index 9778c723..f9264c71 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 00000000..6214b43b --- /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 + } +} + From cf19f02966fc427be26404b7f8ddd5d05bfe3127 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 27 Mar 2014 15:44:34 +0100 Subject: [PATCH 14/14] Incorporate pull request feedback - remove unneeded `setType(NoType)`, which was leftover from my first attempts to find this bug. - fix typo in error message - optimize imports (cherry picked from commit 5c6ea29966fa80faae13892da50fc68ed1bf9ae7) --- src/main/scala/scala/async/internal/AsyncAnalysis.scala | 2 +- src/main/scala/scala/async/internal/AsyncTransform.scala | 2 +- src/test/scala/scala/async/neg/NakedAwait.scala | 2 +- src/test/scala/scala/async/run/lazyval/LazyValSpec.scala | 5 ----- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/scala/scala/async/internal/AsyncAnalysis.scala b/src/main/scala/scala/async/internal/AsyncAnalysis.scala index 274603fd..15ddf48c 100644 --- a/src/main/scala/scala/async/internal/AsyncAnalysis.scala +++ b/src/main/scala/scala/async/internal/AsyncAnalysis.scala @@ -61,7 +61,7 @@ trait AsyncAnalysis { case Return(_) => abort(tree.pos, "return is illegal within a async block") case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flag.LAZY) && containsAwait => - reportUnsupportedAwait(tree, "lazy val initalizer") + reportUnsupportedAwait(tree, "lazy val initializer") 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 beb828df..c8d22343 100644 --- a/src/main/scala/scala/async/internal/AsyncTransform.scala +++ b/src/main/scala/scala/async/internal/AsyncTransform.scala @@ -52,7 +52,7 @@ trait AsyncTransform { } val tryToUnit = appliedType(definitions.FunctionClass(1), futureSystemOps.tryType[Any], typeOf[Unit]) - val template = Template(List(tryToUnit, typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body).setType(NoType) + val template = Template(List(tryToUnit, typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body) val t = ClassDef(NoMods, name.stateMachineT, Nil, template) typecheckClassDef(t) diff --git a/src/test/scala/scala/async/neg/NakedAwait.scala b/src/test/scala/scala/async/neg/NakedAwait.scala index f9264c71..2af691ff 100644 --- a/src/test/scala/scala/async/neg/NakedAwait.scala +++ b/src/test/scala/scala/async/neg/NakedAwait.scala @@ -163,7 +163,7 @@ class NakedAwait { @Test def lazyValIllegal() { - expectError("await must not be used under a lazy val initalizer") { + expectError("await must not be used under a lazy val initializer") { """ | import _root_.scala.async.internal.AsyncId._ | def foo(): Any = async { val x = { lazy val y = await(0); y } } diff --git a/src/test/scala/scala/async/run/lazyval/LazyValSpec.scala b/src/test/scala/scala/async/run/lazyval/LazyValSpec.scala index 6214b43b..701d5d6c 100644 --- a/src/test/scala/scala/async/run/lazyval/LazyValSpec.scala +++ b/src/test/scala/scala/async/run/lazyval/LazyValSpec.scala @@ -6,11 +6,6 @@ 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._