From 4ddf4fad87de6470fd4c04c25384817e97ffe135 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 15 Aug 2013 18:22:34 +0200 Subject: [PATCH 1/4] Remove @RunWith annotations from test cases. These are are at odds with the junit-interface SBT test framework under SBT 0.13.+, and appear to be superfluous. --- src/test/scala/scala/async/TreeInterrogation.scala | 3 --- src/test/scala/scala/async/neg/LocalClasses0Spec.scala | 3 --- src/test/scala/scala/async/neg/NakedAwait.scala | 3 --- src/test/scala/scala/async/neg/SampleNegSpec.scala | 3 --- src/test/scala/scala/async/run/anf/AnfTransformSpec.scala | 3 --- src/test/scala/scala/async/run/await0/Await0Spec.scala | 3 --- src/test/scala/scala/async/run/block0/AsyncSpec.scala | 3 --- src/test/scala/scala/async/run/block1/block1.scala | 3 --- src/test/scala/scala/async/run/cps/CPSSpec.scala | 3 --- src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala | 3 --- src/test/scala/scala/async/run/futures/FutureSpec.scala | 3 --- src/test/scala/scala/async/run/hygiene/Hygiene.scala | 3 --- src/test/scala/scala/async/run/ifelse0/IfElse0.scala | 3 --- src/test/scala/scala/async/run/ifelse0/WhileSpec.scala | 3 --- src/test/scala/scala/async/run/ifelse1/IfElse1.scala | 3 --- src/test/scala/scala/async/run/ifelse2/ifelse2.scala | 3 --- src/test/scala/scala/async/run/ifelse3/IfElse3.scala | 3 --- src/test/scala/scala/async/run/match0/Match0.scala | 3 --- src/test/scala/scala/async/run/nesteddef/NestedDef.scala | 3 --- src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala | 3 --- src/test/scala/scala/async/run/toughtype/ToughType.scala | 3 --- 21 files changed, 63 deletions(-) diff --git a/src/test/scala/scala/async/TreeInterrogation.scala b/src/test/scala/scala/async/TreeInterrogation.scala index 770c0f9e..b62f7238 100644 --- a/src/test/scala/scala/async/TreeInterrogation.scala +++ b/src/test/scala/scala/async/TreeInterrogation.scala @@ -4,14 +4,11 @@ package scala.async -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test import scala.async.internal.AsyncId import AsyncId._ import tools.reflect.ToolBox -@RunWith(classOf[JUnit4]) class TreeInterrogation { @Test def `a minimal set of vals are lifted to vars`() { diff --git a/src/test/scala/scala/async/neg/LocalClasses0Spec.scala b/src/test/scala/scala/async/neg/LocalClasses0Spec.scala index 6ebc9caf..ae346a2f 100644 --- a/src/test/scala/scala/async/neg/LocalClasses0Spec.scala +++ b/src/test/scala/scala/async/neg/LocalClasses0Spec.scala @@ -5,12 +5,9 @@ package scala.async package neg -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test import scala.async.internal.AsyncId -@RunWith(classOf[JUnit4]) class LocalClasses0Spec { @Test def localClassCrashIssue16() { diff --git a/src/test/scala/scala/async/neg/NakedAwait.scala b/src/test/scala/scala/async/neg/NakedAwait.scala index ba388c5e..deac0697 100644 --- a/src/test/scala/scala/async/neg/NakedAwait.scala +++ b/src/test/scala/scala/async/neg/NakedAwait.scala @@ -5,11 +5,8 @@ package scala.async package neg -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test -@RunWith(classOf[JUnit4]) class NakedAwait { @Test def `await only allowed in async neg`() { diff --git a/src/test/scala/scala/async/neg/SampleNegSpec.scala b/src/test/scala/scala/async/neg/SampleNegSpec.scala index 76f9c3ec..e57dae98 100644 --- a/src/test/scala/scala/async/neg/SampleNegSpec.scala +++ b/src/test/scala/scala/async/neg/SampleNegSpec.scala @@ -5,11 +5,8 @@ package scala.async package neg -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test -@RunWith(classOf[JUnit4]) class SampleNegSpec { @Test def `missing symbol`() { diff --git a/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala b/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala index c8cec287..728f33ba 100644 --- a/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala +++ b/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala @@ -11,8 +11,6 @@ import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import scala.async.internal.AsyncId @@ -70,7 +68,6 @@ object State { @volatile var result: Int = 0 } -@RunWith(classOf[JUnit4]) class AnfTransformSpec { @Test diff --git a/src/test/scala/scala/async/run/await0/Await0Spec.scala b/src/test/scala/scala/async/run/await0/Await0Spec.scala index 111602a4..2adaa09a 100644 --- a/src/test/scala/scala/async/run/await0/Await0Spec.scala +++ b/src/test/scala/scala/async/run/await0/Await0Spec.scala @@ -15,8 +15,6 @@ import language.{reflectiveCalls, postfixOps} import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test class Await0Class { @@ -63,7 +61,6 @@ class Await0Class { } } -@RunWith(classOf[JUnit4]) class Await0Spec { @Test diff --git a/src/test/scala/scala/async/run/block0/AsyncSpec.scala b/src/test/scala/scala/async/run/block0/AsyncSpec.scala index 5f38086f..677cce8d 100644 --- a/src/test/scala/scala/async/run/block0/AsyncSpec.scala +++ b/src/test/scala/scala/async/run/block0/AsyncSpec.scala @@ -11,8 +11,6 @@ import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 class Test1Class { @@ -39,7 +37,6 @@ class Test1Class { } -@RunWith(classOf[JUnit4]) class AsyncSpec { @Test diff --git a/src/test/scala/scala/async/run/block1/block1.scala b/src/test/scala/scala/async/run/block1/block1.scala index bf9b56f0..f42b0730 100644 --- a/src/test/scala/scala/async/run/block1/block1.scala +++ b/src/test/scala/scala/async/run/block1/block1.scala @@ -11,8 +11,6 @@ import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 class Test1Class { @@ -32,7 +30,6 @@ class Test1Class { } } -@RunWith(classOf[JUnit4]) class Block1Spec { @Test def `support a simple await`() { diff --git a/src/test/scala/scala/async/run/cps/CPSSpec.scala b/src/test/scala/scala/async/run/cps/CPSSpec.scala index b56c6ad9..9476b227 100644 --- a/src/test/scala/scala/async/run/cps/CPSSpec.scala +++ b/src/test/scala/scala/async/run/cps/CPSSpec.scala @@ -11,11 +11,8 @@ import scala.concurrent.duration._ import scala.async.continuations.CPSBasedAsync._ import scala.util.continuations._ -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test -@RunWith(classOf[JUnit4]) class CPSSpec { import ExecutionContext.Implicits.global diff --git a/src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala b/src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala index 733ea018..b417dd65 100644 --- a/src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala +++ b/src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala @@ -14,10 +14,7 @@ import scala.concurrent.duration._ import scala.reflect.ClassTag import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -@RunWith(classOf[JUnit4]) class ExceptionsSpec { @Test diff --git a/src/test/scala/scala/async/run/futures/FutureSpec.scala b/src/test/scala/scala/async/run/futures/FutureSpec.scala index 01c86203..491b43fe 100644 --- a/src/test/scala/scala/async/run/futures/FutureSpec.scala +++ b/src/test/scala/scala/async/run/futures/FutureSpec.scala @@ -18,10 +18,7 @@ import scala.util.{Try,Success,Failure} import scala.async.Async.{async, await} import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -@RunWith(classOf[JUnit4]) class FutureSpec { /* some utils */ diff --git a/src/test/scala/scala/async/run/hygiene/Hygiene.scala b/src/test/scala/scala/async/run/hygiene/Hygiene.scala index 8081ee70..f11d21e6 100644 --- a/src/test/scala/scala/async/run/hygiene/Hygiene.scala +++ b/src/test/scala/scala/async/run/hygiene/Hygiene.scala @@ -7,11 +7,8 @@ package run package hygiene import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import scala.async.internal.AsyncId -@RunWith(classOf[JUnit4]) class HygieneSpec { import AsyncId.{async, await} diff --git a/src/test/scala/scala/async/run/ifelse0/IfElse0.scala b/src/test/scala/scala/async/run/ifelse0/IfElse0.scala index fc438a1a..4a86d822 100644 --- a/src/test/scala/scala/async/run/ifelse0/IfElse0.scala +++ b/src/test/scala/scala/async/run/ifelse0/IfElse0.scala @@ -10,8 +10,6 @@ import language.{reflectiveCalls, postfixOps} import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test import scala.async.internal.AsyncId @@ -39,7 +37,6 @@ class TestIfElseClass { } -@RunWith(classOf[JUnit4]) class IfElseSpec { @Test def `support await in a simple if-else expression`() { diff --git a/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala b/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala index b8d88fb6..666c3739 100644 --- a/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala +++ b/src/test/scala/scala/async/run/ifelse0/WhileSpec.scala @@ -6,12 +6,9 @@ package scala.async package run package ifelse0 -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test import scala.async.internal.AsyncId -@RunWith(classOf[JUnit4]) class WhileSpec { @Test diff --git a/src/test/scala/scala/async/run/ifelse1/IfElse1.scala b/src/test/scala/scala/async/run/ifelse1/IfElse1.scala index b567ee66..41b81a46 100644 --- a/src/test/scala/scala/async/run/ifelse1/IfElse1.scala +++ b/src/test/scala/scala/async/run/ifelse1/IfElse1.scala @@ -10,8 +10,6 @@ import language.{reflectiveCalls, postfixOps} import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test @@ -91,7 +89,6 @@ class TestIfElse1Class { } } -@RunWith(classOf[JUnit4]) class IfElse1Spec { @Test diff --git a/src/test/scala/scala/async/run/ifelse2/ifelse2.scala b/src/test/scala/scala/async/run/ifelse2/ifelse2.scala index 92a76e43..3fc4d3b4 100644 --- a/src/test/scala/scala/async/run/ifelse2/ifelse2.scala +++ b/src/test/scala/scala/async/run/ifelse2/ifelse2.scala @@ -10,8 +10,6 @@ import language.{reflectiveCalls, postfixOps} import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test @@ -37,7 +35,6 @@ class TestIfElse2Class { } } -@RunWith(classOf[JUnit4]) class IfElse2Spec { @Test diff --git a/src/test/scala/scala/async/run/ifelse3/IfElse3.scala b/src/test/scala/scala/async/run/ifelse3/IfElse3.scala index 8a2ab13a..8e6e1bbf 100644 --- a/src/test/scala/scala/async/run/ifelse3/IfElse3.scala +++ b/src/test/scala/scala/async/run/ifelse3/IfElse3.scala @@ -10,8 +10,6 @@ import language.{reflectiveCalls, postfixOps} import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test @@ -40,7 +38,6 @@ class TestIfElse3Class { } -@RunWith(classOf[JUnit4]) class IfElse3Spec { @Test diff --git a/src/test/scala/scala/async/run/match0/Match0.scala b/src/test/scala/scala/async/run/match0/Match0.scala index 7c392ab5..418275ee 100644 --- a/src/test/scala/scala/async/run/match0/Match0.scala +++ b/src/test/scala/scala/async/run/match0/Match0.scala @@ -10,8 +10,6 @@ import language.{reflectiveCalls, postfixOps} import scala.concurrent.{Future, ExecutionContext, future, Await} import scala.concurrent.duration._ import scala.async.Async.{async, await} -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test import scala.async.internal.AsyncId @@ -54,7 +52,6 @@ class TestMatchClass { } -@RunWith(classOf[JUnit4]) class MatchSpec { @Test def `support await in a simple match expression`() { diff --git a/src/test/scala/scala/async/run/nesteddef/NestedDef.scala b/src/test/scala/scala/async/run/nesteddef/NestedDef.scala index 409f70ac..69e741dc 100644 --- a/src/test/scala/scala/async/run/nesteddef/NestedDef.scala +++ b/src/test/scala/scala/async/run/nesteddef/NestedDef.scala @@ -2,12 +2,9 @@ package scala.async package run package nesteddef -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.junit.Test import scala.async.internal.AsyncId -@RunWith(classOf[JUnit4]) class NestedDef { @Test diff --git a/src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala b/src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala index ba9c9bed..0adb5066 100644 --- a/src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala +++ b/src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala @@ -9,10 +9,7 @@ package noawait import scala.async.internal.AsyncId import AsyncId._ import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -@RunWith(classOf[JUnit4]) class NoAwaitSpec { @Test def `async block without await`() { diff --git a/src/test/scala/scala/async/run/toughtype/ToughType.scala b/src/test/scala/scala/async/run/toughtype/ToughType.scala index ec2278f6..202a2880 100644 --- a/src/test/scala/scala/async/run/toughtype/ToughType.scala +++ b/src/test/scala/scala/async/run/toughtype/ToughType.scala @@ -11,8 +11,6 @@ import scala.concurrent._ import scala.concurrent.duration._ import scala.async.Async._ import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import scala.async.internal.AsyncId @@ -29,7 +27,6 @@ object ToughTypeObject { } } -@RunWith(classOf[JUnit4]) class ToughTypeSpec { @Test def `propogates tough types`() { From 74681a0e1e3b2488386ca5b5dd12f5b0682393a3 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 15 Aug 2013 18:26:39 +0200 Subject: [PATCH 2/4] Update to SBT 0.13.0-M5 Which correctly substitutes scala-reflect.jar when using scalaHome to test out the plugin with a local version of Scala. Also updates to a released version of the junit-interface SBT TestFramework. --- build.sbt | 2 +- project/build.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index d6dc3bbd..7eba7cd8 100644 --- a/build.sbt +++ b/build.sbt @@ -15,7 +15,7 @@ libraryDependencies <++= (scalaVersion) { libraryDependencies += "junit" % "junit-dep" % "4.10" % "test" -libraryDependencies += "com.novocode" % "junit-interface" % "0.10-M2" % "test" +libraryDependencies += "com.novocode" % "junit-interface" % "0.10" % "test" testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-v", "-s") diff --git a/project/build.properties b/project/build.properties index 5e96e967..87d87d88 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.12.4 +sbt.version=0.13.0-RC5 From 8371f480ca6783ea52aa76f4990eed101938c224 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 15 Aug 2013 18:41:39 +0200 Subject: [PATCH 3/4] Better support for testing with a local instance of Scala. SBT's scalaHome handling doesn't pick up the local continuations plugin. --- build.sbt | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 7eba7cd8..2ee12356 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,8 @@ scalaVersion := "2.10.2" +// Uncomment to test with a locally built copy of Scala. +// scalaHome := Some(file("/code/scala2/build/pack")) + organization := "org.typesafe.async" // TODO new org name under scala-lang. name := "scala-async" @@ -23,10 +26,22 @@ parallelExecution in Global := false autoCompilerPlugins := true -libraryDependencies <<= (scalaVersion, libraryDependencies) { - (ver, deps) => - deps :+ compilerPlugin("org.scala-lang.plugins" % "continuations" % ver) -} +scalacOptions ++= (scalaHome.value match { + case Some(sh) => + // Use continuations plugin from the local scala instance + val continuationsJar = sh / "misc" / "scala-devel" / "plugins" / "continuations.jar" + ("-Xplugin:" + continuationsJar.getAbsolutePath) :: Nil + case None => + Nil +}) + +libraryDependencies ++= (scalaHome.value match { + case Some(sh) => + Nil + case None => + // Use continuations plugin from the published artifact. + compilerPlugin("org.scala-lang.plugins" % "continuations" % scalaVersion.value) :: Nil +}) scalacOptions += "-P:continuations:enable" From 98e2f26000aaaf5abb527f776426c4759b95cde8 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 21 Aug 2013 23:29:11 +0200 Subject: [PATCH 4/4] Use @uncheckedBounds to avoid introducing refchecks errors ... in code that would otherwise have smuggled through these slack LUBs in the types of trees but never in a TypeTree. More details in SI-7694. Fixes #29 --- build.sbt | 6 +- .../scala/async/internal/AnfTransform.scala | 6 +- .../scala/scala/async/internal/AsyncId.scala | 2 +- .../scala/async/internal/AsyncTransform.scala | 11 +++- .../scala/async/internal/ExprBuilder.scala | 8 +-- .../scala/async/internal/TransformUtils.scala | 13 ++++- .../scala/async/run/toughtype/ToughType.scala | 9 ++- .../uncheckedBounds/UncheckedBoundsSpec.scala | 57 +++++++++++++++++++ 8 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 src/test/scala/scala/async/run/uncheckedBounds/UncheckedBoundsSpec.scala diff --git a/build.sbt b/build.sbt index 2ee12356..a013bb25 100644 --- a/build.sbt +++ b/build.sbt @@ -1,10 +1,10 @@ -scalaVersion := "2.10.2" +scalaVersion := "2.10.3-RC1" + +organization := "org.typesafe.async" // TODO new org name under scala-lang. // Uncomment to test with a locally built copy of Scala. // scalaHome := Some(file("/code/scala2/build/pack")) -organization := "org.typesafe.async" // TODO new org name under scala-lang. - name := "scala-async" version := "1.0.0-SNAPSHOT" diff --git a/src/main/scala/scala/async/internal/AnfTransform.scala b/src/main/scala/scala/async/internal/AnfTransform.scala index 6aeaba3d..0f8bc67a 100644 --- a/src/main/scala/scala/async/internal/AnfTransform.scala +++ b/src/main/scala/scala/async/internal/AnfTransform.scala @@ -119,8 +119,8 @@ private[async] trait AnfTransform { } private def defineVar(prefix: String, tp: Type, pos: Position): ValDef = { - val sym = currOwner.newTermSymbol(name.fresh(prefix), pos, MUTABLE | SYNTHETIC).setInfo(tp) - ValDef(sym, gen.mkZero(tp)).setType(NoType).setPos(pos) + val sym = currOwner.newTermSymbol(name.fresh(prefix), pos, MUTABLE | SYNTHETIC).setInfo(uncheckedBounds(tp)) + ValDef(sym, gen.mkZero(uncheckedBounds(tp))).setType(NoType).setPos(pos) } } @@ -145,7 +145,7 @@ private[async] trait AnfTransform { } private def defineVal(prefix: String, lhs: Tree, pos: Position): ValDef = { - val sym = currOwner.newTermSymbol(name.fresh(prefix), pos, SYNTHETIC).setInfo(lhs.tpe) + val sym = currOwner.newTermSymbol(name.fresh(prefix), pos, SYNTHETIC).setInfo(uncheckedBounds(lhs.tpe)) changeOwner(lhs, currentOwner, sym) ValDef(sym, changeOwner(lhs, currentOwner, sym)).setType(NoType).setPos(pos) } diff --git a/src/main/scala/scala/async/internal/AsyncId.scala b/src/main/scala/scala/async/internal/AsyncId.scala index 43340886..b9d82e2b 100644 --- a/src/main/scala/scala/async/internal/AsyncId.scala +++ b/src/main/scala/scala/async/internal/AsyncId.scala @@ -41,7 +41,7 @@ object IdentityFutureSystem extends FutureSystem { def execContextType: Type = weakTypeOf[Unit] def createProm[A: WeakTypeTag]: Expr[Prom[A]] = reify { - new Prom() + new Prom[A]() } def promiseToFuture[A: WeakTypeTag](prom: Expr[Prom[A]]) = reify { diff --git a/src/main/scala/scala/async/internal/AsyncTransform.scala b/src/main/scala/scala/async/internal/AsyncTransform.scala index c755c874..78a0876e 100644 --- a/src/main/scala/scala/async/internal/AsyncTransform.scala +++ b/src/main/scala/scala/async/internal/AsyncTransform.scala @@ -6,7 +6,12 @@ trait AsyncTransform { import global._ def asyncTransform[T](body: Tree, execContext: Tree, cpsFallbackEnabled: Boolean) - (implicit resultType: WeakTypeTag[T]): Tree = { + (resultType: WeakTypeTag[T]): Tree = { + + // We annotate the type of the whole expression as `T @uncheckedBounds` so as not to introduce + // warnings about non-conformant LUBs. See SI-7694 + // This implicit propatages the annotated type in the type tag. + implicit val uncheckedBoundsResultTag: WeakTypeTag[T] = WeakTypeTag[T](rootMirror, FixedMirrorTypeCreator(rootMirror, uncheckedBounds(resultType.tpe))) reportUnsupportedAwaits(body, report = !cpsFallbackEnabled) @@ -22,12 +27,12 @@ trait AsyncTransform { DefDef(NoMods, name.apply, Nil, applyVParamss, TypeTree(definitions.UnitTpe), Literal(Constant(()))) } - val stateMachineType = applied("scala.async.StateMachine", List(futureSystemOps.promType[T], futureSystemOps.execContextType)) + val stateMachineType = applied("scala.async.StateMachine", List(futureSystemOps.promType[T](uncheckedBoundsResultTag), futureSystemOps.execContextType)) val stateMachine: ClassDef = { val body: List[Tree] = { val stateVar = ValDef(Modifiers(Flag.MUTABLE | Flag.PRIVATE | Flag.LOCAL), name.state, TypeTree(definitions.IntTpe), Literal(Constant(0))) - val result = ValDef(NoMods, name.result, TypeTree(futureSystemOps.promType[T]), futureSystemOps.createProm[T].tree) + val result = ValDef(NoMods, name.result, TypeTree(futureSystemOps.promType[T](uncheckedBoundsResultTag)), futureSystemOps.createProm[T](uncheckedBoundsResultTag).tree) val execContextValDef = ValDef(NoMods, name.execContext, TypeTree(), execContext) val apply0DefDef: DefDef = { diff --git a/src/main/scala/scala/async/internal/ExprBuilder.scala b/src/main/scala/scala/async/internal/ExprBuilder.scala index e0da8741..f43d1cbc 100644 --- a/src/main/scala/scala/async/internal/ExprBuilder.scala +++ b/src/main/scala/scala/async/internal/ExprBuilder.scala @@ -284,7 +284,7 @@ trait ExprBuilder { def onCompleteHandler[T: WeakTypeTag]: Tree - def resumeFunTree[T]: DefDef + def resumeFunTree[T: WeakTypeTag]: DefDef } case class SymLookup(stateMachineClass: Symbol, applyTrParam: Symbol) { @@ -303,12 +303,12 @@ trait ExprBuilder { new AsyncBlock { def asyncStates = blockBuilder.asyncStates.toList - def mkCombinedHandlerCases[T]: List[CaseDef] = { + def mkCombinedHandlerCases[T: WeakTypeTag]: List[CaseDef] = { val caseForLastState: CaseDef = { val lastState = asyncStates.last val lastStateBody = Expr[T](lastState.body) val rhs = futureSystemOps.completeProm( - Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), reify(scala.util.Success(lastStateBody.splice))) + Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), reify(scala.util.Success[T](lastStateBody.splice))) mkHandlerCase(lastState.state, rhs.tree) } asyncStates.toList match { @@ -337,7 +337,7 @@ trait ExprBuilder { * } * } */ - def resumeFunTree[T]: DefDef = + def resumeFunTree[T: WeakTypeTag]: DefDef = DefDef(Modifiers(), name.resume, Nil, List(Nil), Ident(definitions.UnitClass), Try( Match(symLookup.memberRef(name.state), mkCombinedHandlerCases[T]), diff --git a/src/main/scala/scala/async/internal/TransformUtils.scala b/src/main/scala/scala/async/internal/TransformUtils.scala index 70237bcb..663ca45c 100644 --- a/src/main/scala/scala/async/internal/TransformUtils.scala +++ b/src/main/scala/scala/async/internal/TransformUtils.scala @@ -244,8 +244,19 @@ private[async] trait TransformUtils { // Attributed version of `TreeGen#mkCastPreservingAnnotations` def mkAttributedCastPreservingAnnotations(tree: Tree, tp: Type): Tree = { atPos(tree.pos) { - val casted = gen.mkAttributedCast(tree, tp.withoutAnnotations.dealias) + val casted = gen.mkAttributedCast(tree, uncheckedBounds(tp.withoutAnnotations).dealias) Typed(casted, TypeTree(tp)).setType(tp) } } + + // ===================================== + // Copy/Pasted from Scala 2.10.3. See SI-7694. + private lazy val UncheckedBoundsClass = { + global.rootMirror.getClassIfDefined("scala.reflect.internal.annotations.uncheckedBounds") + } + final def uncheckedBounds(tp: Type): Type = { + if (tp.typeArgs.isEmpty || UncheckedBoundsClass == NoSymbol) tp + else tp.withAnnotation(AnnotationInfo marker UncheckedBoundsClass.tpe) + } + // ===================================== } diff --git a/src/test/scala/scala/async/run/toughtype/ToughType.scala b/src/test/scala/scala/async/run/toughtype/ToughType.scala index 202a2880..b342a004 100644 --- a/src/test/scala/scala/async/run/toughtype/ToughType.scala +++ b/src/test/scala/scala/async/run/toughtype/ToughType.scala @@ -10,7 +10,7 @@ import language.{reflectiveCalls, postfixOps} import scala.concurrent._ import scala.concurrent.duration._ import scala.async.Async._ -import org.junit.Test +import org.junit.{Assert, Test} import scala.async.internal.AsyncId @@ -136,3 +136,10 @@ class ToughTypeSpec { foo } } + +trait A +trait B + +trait L[A2, B2 <: A2] { + def bar(a: Any, b: Any) = 0 +} diff --git a/src/test/scala/scala/async/run/uncheckedBounds/UncheckedBoundsSpec.scala b/src/test/scala/scala/async/run/uncheckedBounds/UncheckedBoundsSpec.scala new file mode 100644 index 00000000..5eb1f32a --- /dev/null +++ b/src/test/scala/scala/async/run/uncheckedBounds/UncheckedBoundsSpec.scala @@ -0,0 +1,57 @@ +package scala.async +package run +package uncheckedBounds + +import org.junit.{Test, Assert} +import scala.async.TreeInterrogation + +class UncheckedBoundsSpec { + @Test def insufficientLub_SI_7694() { + suppressingFailureBefore2_10_3 { + eval( s""" + object Test { + import _root_.scala.async.run.toughtype._ + import _root_.scala.async.internal.AsyncId.{async, await} + async { + (if (true) await(null: L[A, A]) else await(null: L[B, B])) + } + } + """, compileOptions = s"-cp ${toolboxClasspath} ") + } + } + + @Test def insufficientLub_SI_7694_ScalaConcurrent() { + suppressingFailureBefore2_10_3 { + eval( s""" + object Test { + import _root_.scala.async.run.toughtype._ + import _root_.scala.async.Async.{async, await} + import scala.concurrent._ + import scala.concurrent.ExecutionContext.Implicits.global + async { + (if (true) await(null: Future[L[A, A]]) else await(null: Future[L[B, B]])) + } + } + """, compileOptions = s"-cp ${toolboxClasspath} ") + } + } + + private def suppressingFailureBefore2_10_3(body: => Any) { + try { + body + } catch { + case x: Throwable => + // @uncheckedBounds was only introduced in 2.10.3/ 2.11.0-M5, so avoid reporting this test failure in those cases. + scala.util.Properties.versionNumberString match { + case "2.10.0" | "2.10.1" | "2.10.2" | "2.11.0-M4" => // ignore, the @uncheckedBounds doesn't exist yet + case _ => + val annotationExists = + reflect.runtime.currentMirror.staticClass("scala.reflect.internal.annotations.uncheckedBounds") == reflect.runtime.universe.NoSymbol + if (annotationExists) + Assert.fail("@uncheckedBounds not found in scala-reflect.jar") + else + Assert.fail(s"@uncheckedBounds exists, but it didn't prevent this failure: $x") + } + } + } +}