summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-06-28 12:09:21 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-06-28 14:23:15 -0700
commite320a920d4c32752157cf73be5a5fbbd3b9cc6e7 (patch)
tree04d2e78365d257c055c56ea93aaf88f9c0b9f3f9
parent397a057fd242833098562e48f12e11621b4ab7ae (diff)
parentd22e8d282c538a711f6fdd33793c06cd7676959d (diff)
downloadscala-e320a920d4c32752157cf73be5a5fbbd3b9cc6e7.tar.gz
scala-e320a920d4c32752157cf73be5a5fbbd3b9cc6e7.tar.bz2
scala-e320a920d4c32752157cf73be5a5fbbd3b9cc6e7.zip
Merge 2.10.x into master
Conflicts: src/compiler/scala/tools/nsc/typechecker/Duplicators.scala src/library/scala/concurrent/Future.scala test/files/jvm/scala-concurrent-tck.scala
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala32
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala4
-rw-r--r--src/library/scala/concurrent/Future.scala177
-rw-r--r--test/files/jvm/scala-concurrent-tck.check3
-rw-r--r--test/files/jvm/scala-concurrent-tck.scala562
-rw-r--r--test/files/neg/valueclasses-impl-restrictions.check8
-rw-r--r--test/files/neg/valueclasses-impl-restrictions.scala4
-rw-r--r--test/files/run/t7571.scala12
-rw-r--r--test/files/specialized/SI-7343.scala55
-rw-r--r--test/files/specialized/spec-ame.check2
-rw-r--r--test/files/specialized/spec-ame.scala3
13 files changed, 307 insertions, 560 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index eafe03d5cd..5f8bec1cd2 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -1057,6 +1057,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
@inline final def enteringMixin[T](op: => T): T = enteringPhase(currentRun.mixinPhase)(op)
@inline final def enteringPickler[T](op: => T): T = enteringPhase(currentRun.picklerPhase)(op)
@inline final def enteringRefchecks[T](op: => T): T = enteringPhase(currentRun.refchecksPhase)(op)
+ @inline final def enteringSpecialize[T](op: => T): T = enteringPhase(currentRun.specializePhase)(op)
@inline final def enteringTyper[T](op: => T): T = enteringPhase(currentRun.typerPhase)(op)
@inline final def enteringUncurry[T](op: => T): T = enteringPhase(currentRun.uncurryPhase)(op)
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index d14fcb3eb1..4bc4e06fa7 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -1267,7 +1267,35 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context)
+ }
+ /** Introduced to fix SI-7343: Phase ordering problem between Duplicators and Specialization.
+ * brief explanation: specialization rewires class parents during info transformation, and
+ * the new info then guides the tree changes. But if a symbol is created during duplication,
+ * which runs after specialization, its info is not visited and thus the corresponding tree
+ * is not specialized. One manifestation is the following:
+ * ```
+ * object Test {
+ * class Parent[@specialized(Int) T]
+ *
+ * def spec_method[@specialized(Int) T](t: T, expectedXSuper: String) = {
+ * class X extends Parent[T]()
+ * // even in the specialized variant, the local X class
+ * // doesn't extend Parent$mcI$sp, since its symbol has
+ * // been created after specialization and was not seen
+ * // by specialzation's info transformer.
+ * ...
+ * }
+ * }
+ * ```
+ * We fix this by forcing duplication to take place before specialization.
+ *
+ * Note: The constructors phase (which also uses duplication) comes after erasure and uses the
+ * post-erasure typer => we must protect it from the beforeSpecialization phase shifting.
+ */
+ class SpecializationDuplicator(casts: Map[Symbol, Type]) extends Duplicator(casts) {
+ override def retyped(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol, env: scala.collection.Map[Symbol, Type]): Tree =
+ enteringSpecialize(super.retyped(context, tree, oldThis, newThis, env))
}
/** A tree symbol substituter that substitutes on type skolems.
@@ -1664,7 +1692,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate)
debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName)
- val d = new Duplicator(emptyEnv)
+ val d = new SpecializationDuplicator(emptyEnv)
val newValDef = d.retyped(
localTyper.context1.asInstanceOf[d.Context],
tree1,
@@ -1723,7 +1751,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val symbol = tree.symbol
val meth = addBody(tree, source)
- val d = new Duplicator(castmap)
+ val d = new SpecializationDuplicator(castmap)
debuglog("-->d DUPLICATING: " + meth)
d.retyped(
localTyper.context1.asInstanceOf[d.Context],
diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
index 95b771a8a5..0a2628b482 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
@@ -32,6 +32,7 @@ abstract class Duplicators extends Analyzer {
envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList)
debuglog("retyped with env: " + env)
+
newBodyDuplicator(context).typed(tree)
}
@@ -365,7 +366,8 @@ abstract class Duplicators extends Analyzer {
tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==)
}
val ntree = castType(tree, pt)
- super.typed(ntree, mode, pt)
+ val res = super.typed(ntree, mode, pt)
+ res
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index cb3a12b60d..353e8e4810 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1431,8 +1431,8 @@ trait Typers extends Adaptations with Tags {
implRestriction(tree, "nested object")
//see https://issues.scala-lang.org/browse/SI-6444
//see https://issues.scala-lang.org/browse/SI-6463
- case _: ClassDef =>
- implRestriction(tree, "nested class")
+ case cd: ClassDef if !cd.symbol.isAnonymousClass => // Don't warn about partial functions, etc. SI-7571
+ implRestriction(tree, "nested class") // avoiding Type Tests that might check the $outer pointer.
case Select(sup @ Super(qual, mix), selector) if selector != nme.CONSTRUCTOR && qual.symbol == clazz && mix != tpnme.EMPTY =>
//see https://issues.scala-lang.org/browse/SI-6483
implRestriction(sup, "qualified super reference")
diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala
index 2e4c72cd71..b072cd653b 100644
--- a/src/library/scala/concurrent/Future.scala
+++ b/src/library/scala/concurrent/Future.scala
@@ -14,7 +14,7 @@ import java.util.concurrent.{ ConcurrentLinkedQueue, TimeUnit, Callable }
import java.util.concurrent.TimeUnit.{ NANOSECONDS => NANOS, MILLISECONDS ⇒ MILLIS }
import java.lang.{ Iterable => JIterable }
import java.util.{ LinkedList => JLinkedList }
-import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean }
+import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicLong, AtomicBoolean }
import scala.util.control.NonFatal
import scala.Option
@@ -101,7 +101,7 @@ trait Future[+T] extends Awaitable[T] {
// that also have an executor parameter, which
// keeps us from accidentally forgetting to use
// the executor parameter.
- private implicit def internalExecutor: ExecutionContext = Future.InternalCallbackExecutor
+ private def internalExecutor = Future.InternalCallbackExecutor
/* Callbacks */
@@ -116,9 +116,10 @@ trait Future[+T] extends Awaitable[T] {
* $callbackInContext
*/
def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit = onComplete {
- case Success(v) if pf isDefinedAt v => pf(v)
+ case Success(v) =>
+ pf.applyOrElse[T, Any](v, Predef.conforms[T]) // Exploiting the cached function to avoid MatchError
case _ =>
- }(executor)
+ }
/** When this future is completed with a failure (i.e. with a throwable),
* apply the provided callback to the throwable.
@@ -134,9 +135,10 @@ trait Future[+T] extends Awaitable[T] {
* $callbackInContext
*/
def onFailure[U](@deprecatedName('callback) pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit = onComplete {
- case Failure(t) if NonFatal(t) && pf.isDefinedAt(t) => pf(t)
+ case Failure(t) =>
+ pf.applyOrElse[Throwable, Any](t, Predef.conforms[Throwable]) // Exploiting the cached function to avoid MatchError
case _ =>
- }(executor)
+ }
/** When this future is completed, either through an exception, or a value,
* apply the provided function.
@@ -186,13 +188,12 @@ trait Future[+T] extends Awaitable[T] {
* and throws a corresponding exception if the original future fails.
*/
def failed: Future[Throwable] = {
+ implicit val ec = internalExecutor
val p = Promise[Throwable]()
-
onComplete {
case Failure(t) => p success t
case Success(v) => p failure (new NoSuchElementException("Future.failed not completed with a throwable."))
}
-
p.future
}
@@ -203,10 +204,7 @@ trait Future[+T] extends Awaitable[T] {
*
* Will not be called if the future fails.
*/
- def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = onComplete {
- case Success(r) => f(r)
- case _ => // do nothing
- }(executor)
+ def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = onComplete { _ foreach f }
/** Creates a new future by applying the 's' function to the successful result of
* this future, or the 'f' function to the failed result. If there is any non-fatal
@@ -221,19 +219,11 @@ trait Future[+T] extends Awaitable[T] {
*/
def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S] = {
val p = Promise[S]()
-
+ // transform on Try has the wrong shape for us here
onComplete {
- case result =>
- try {
- result match {
- case Failure(t) => p failure f(t)
- case Success(r) => p success s(r)
- }
- } catch {
- case NonFatal(t) => p failure t
- }
- }(executor)
-
+ case Success(r) => p complete Try(s(r))
+ case Failure(t) => p complete Try(throw f(t)) // will throw fatal errors!
+ }
p.future
}
@@ -245,19 +235,7 @@ trait Future[+T] extends Awaitable[T] {
*/
def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { // transform(f, identity)
val p = Promise[S]()
-
- onComplete {
- case result =>
- try {
- result match {
- case Success(r) => p success f(r)
- case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]
- }
- } catch {
- case NonFatal(t) => p failure t
- }
- }(executor)
-
+ onComplete { v => p complete (v map f) }
p.future
}
@@ -270,20 +248,10 @@ trait Future[+T] extends Awaitable[T] {
*/
def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = {
val p = Promise[S]()
-
onComplete {
case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]
- case Success(v) =>
- try {
- f(v).onComplete({
- case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]
- case Success(v) => p success v
- })(internalExecutor)
- } catch {
- case NonFatal(t) => p failure t
- }
- }(executor)
-
+ case Success(v) => try f(v) onComplete p.complete catch { case NonFatal(t) => p failure t }
+ }
p.future
}
@@ -303,34 +271,14 @@ trait Future[+T] extends Awaitable[T] {
* Await.result(h, Duration.Zero) // throw a NoSuchElementException
* }}}
*/
- def filter(@deprecatedName('pred) p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = {
- val promise = Promise[T]()
-
- onComplete {
- case f: Failure[_] => promise complete f.asInstanceOf[Failure[T]]
- case Success(v) =>
- try {
- if (p(v)) promise success v
- else promise failure new NoSuchElementException("Future.filter predicate is not satisfied")
- } catch {
- case NonFatal(t) => promise failure t
- }
- }(executor)
-
- promise.future
- }
+ def filter(@deprecatedName('pred) p: T => Boolean)(implicit executor: ExecutionContext): Future[T] =
+ map {
+ r => if (p(r)) r else throw new NoSuchElementException("Future.filter predicate is not satisfied")
+ }
/** Used by for-comprehensions.
*/
final def withFilter(p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = filter(p)(executor)
- // final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p)
-
- // final class FutureWithFilter[+S](self: Future[S], p: S => Boolean) {
- // def foreach(f: S => Unit): Unit = self filter p foreach f
- // def map[R](f: S => R) = self filter p map f
- // def flatMap[R](f: S => Future[R]) = self filter p flatMap f
- // def withFilter(q: S => Boolean): FutureWithFilter[S] = new FutureWithFilter[S](self, x => p(x) && q(x))
- // }
/** Creates a new future by mapping the value of the current future, if the given partial function is defined at that value.
*
@@ -352,22 +300,10 @@ trait Future[+T] extends Awaitable[T] {
* Await.result(h, Duration.Zero) // throw a NoSuchElementException
* }}}
*/
- def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] = {
- val p = Promise[S]()
-
- onComplete {
- case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]
- case Success(v) =>
- try {
- if (pf.isDefinedAt(v)) p success pf(v)
- else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v)
- } catch {
- case NonFatal(t) => p failure t
- }
- }(executor)
-
- p.future
- }
+ def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] =
+ map {
+ r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
+ }
/** Creates a new future that will handle any matching throwable that this
* future might contain. If there is no match, or if this future contains
@@ -383,9 +319,7 @@ trait Future[+T] extends Awaitable[T] {
*/
def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = {
val p = Promise[U]()
-
- onComplete { case tr => p.complete(tr recover pf) }(executor)
-
+ onComplete { v => p complete (v recover pf) }
p.future
}
@@ -404,17 +338,10 @@ trait Future[+T] extends Awaitable[T] {
*/
def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = {
val p = Promise[U]()
-
onComplete {
- case Failure(t) if pf isDefinedAt t =>
- try {
- p completeWith pf(t)
- } catch {
- case NonFatal(t) => p failure t
- }
- case otherwise => p complete otherwise
- }(executor)
-
+ case Failure(t) => try pf.applyOrElse(t, (_: Throwable) => this) onComplete p.complete catch { case NonFatal(t) => p failure t }
+ case other => p complete other
+ }
p.future
}
@@ -427,19 +354,12 @@ trait Future[+T] extends Awaitable[T] {
* with the throwable stored in `that`.
*/
def zip[U](that: Future[U]): Future[(T, U)] = {
+ implicit val ec = internalExecutor
val p = Promise[(T, U)]()
-
- this onComplete {
+ onComplete {
case f: Failure[_] => p complete f.asInstanceOf[Failure[(T, U)]]
- case Success(r) =>
- that onSuccess {
- case r2 => p success ((r, r2))
- }
- that onFailure {
- case f => p failure f
- }
+ case Success(s) => that onComplete { c => p.complete(c map { s2 => (s, s2) }) }
}
-
p.future
}
@@ -458,6 +378,7 @@ trait Future[+T] extends Awaitable[T] {
* }}}
*/
def fallbackTo[U >: T](that: Future[U]): Future[U] = {
+ implicit val ec = internalExecutor
val p = Promise[U]()
onComplete {
case s @ Success(_) => p complete s
@@ -470,23 +391,13 @@ trait Future[+T] extends Awaitable[T] {
* that conforms to `S`'s erased type or a `ClassCastException` otherwise.
*/
def mapTo[S](implicit tag: ClassTag[S]): Future[S] = {
- def boxedType(c: Class[_]): Class[_] = {
+ implicit val ec = internalExecutor
+ val boxedClass = {
+ val c = tag.runtimeClass
if (c.isPrimitive) Future.toBoxed(c) else c
}
-
- val p = Promise[S]()
-
- onComplete {
- case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]
- case Success(t) =>
- p complete (try {
- Success(boxedType(tag.runtimeClass).cast(t).asInstanceOf[S])
- } catch {
- case e: ClassCastException => Failure(e)
- })
- }
-
- p.future
+ require(boxedClass ne null)
+ map(s => boxedClass.cast(s).asInstanceOf[S])
}
/** Applies the side-effecting function to the result of this future, and returns
@@ -514,11 +425,9 @@ trait Future[+T] extends Awaitable[T] {
*/
def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] = {
val p = Promise[T]()
-
onComplete {
- case r => try if (pf isDefinedAt r) pf(r) finally p complete r
- }(executor)
-
+ case r => try pf.applyOrElse[Try[T], Any](r, Predef.conforms[Try[T]]) finally p complete r
+ }
p.future
}
@@ -579,14 +488,12 @@ object Future {
} map (_.result())
}
- /** Returns a `Future` to the result of the first future in the list that is completed.
+ /** Returns a new `Future` to the result of the first future in the list that is completed.
*/
def firstCompletedOf[T](futures: TraversableOnce[Future[T]])(implicit executor: ExecutionContext): Future[T] = {
val p = Promise[T]()
-
val completeFirst: Try[T] => Unit = p tryComplete _
- futures.foreach(_ onComplete completeFirst)
-
+ futures foreach { _ onComplete completeFirst }
p.future
}
diff --git a/test/files/jvm/scala-concurrent-tck.check b/test/files/jvm/scala-concurrent-tck.check
deleted file mode 100644
index 18fc456acb..0000000000
--- a/test/files/jvm/scala-concurrent-tck.check
+++ /dev/null
@@ -1,3 +0,0 @@
-scala-concurrent-tck.scala:429: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
- f
- ^
diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala
index 438ee310e6..6e2b8ca033 100644
--- a/test/files/jvm/scala-concurrent-tck.scala
+++ b/test/files/jvm/scala-concurrent-tck.scala
@@ -15,21 +15,17 @@ import scala.reflect.{ classTag, ClassTag }
import scala.tools.partest.TestUtil.intercept
trait TestBase {
-
- def once(body: (() => Unit) => Unit) {
- val sv = new SyncVar[Boolean]
- body(() => sv put true)
- sv.take(2000)
+ trait Done { def apply(proof: => Boolean): Unit }
+ def once(body: Done => Unit) {
+ import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit }
+ val q = new LinkedBlockingQueue[Try[Boolean]]
+ body(new Done {
+ def apply(proof: => Boolean): Unit = q offer Try(proof)
+ })
+ assert(q.poll(2000, TimeUnit.MILLISECONDS).get)
+ // Check that we don't get more than one completion
+ assert(q.poll(50, TimeUnit.MILLISECONDS) eq null)
}
-
- // def assert(cond: => Boolean) {
- // try {
- // Predef.assert(cond)
- // } catch {
- // case e => e.printStackTrace()
- // }
- // }
-
}
@@ -39,99 +35,52 @@ trait FutureCallbacks extends TestBase {
def testOnSuccess(): Unit = once {
done =>
var x = 0
- val f = future {
- x = 1
- }
- f onSuccess {
- case _ =>
- done()
- assert(x == 1)
- }
+ val f = future { x = 1 }
+ f onSuccess { case _ => done(x == 1) }
}
def testOnSuccessWhenCompleted(): Unit = once {
done =>
var x = 0
- val f = future {
- x = 1
- }
+ val f = future { x = 1 }
f onSuccess {
- case _ =>
- assert(x == 1)
+ case _ if x == 1 =>
x = 2
- f onSuccess {
- case _ =>
- assert(x == 2)
- done()
- }
+ f onSuccess { case _ => done(x == 2) }
}
}
def testOnSuccessWhenFailed(): Unit = once {
done =>
- val f = future[Unit] {
- done()
- throw new Exception
- }
- f onSuccess {
- case _ => assert(false)
- }
+ val f = future[Unit] { throw new Exception }
+ f onSuccess { case _ => done(false) }
+ f onFailure { case _ => done(true) }
}
def testOnFailure(): Unit = once {
done =>
- var x = 0
- val f = future[Unit] {
- x = 1
- throw new Exception
- }
- f onSuccess {
- case _ =>
- done()
- assert(false)
- }
- f onFailure {
- case _ =>
- done()
- assert(x == 1)
- }
+ val f = future[Unit] { throw new Exception }
+ f onSuccess { case _ => done(false) }
+ f onFailure { case _ => done(true) }
}
def testOnFailureWhenSpecialThrowable(num: Int, cause: Throwable): Unit = once {
done =>
- val f = future[Unit] {
- throw cause
- }
- f onSuccess {
- case _ =>
- done()
- assert(false)
- }
+ val f = future[Unit] { throw cause }
+ f onSuccess { case _ => done(false) }
f onFailure {
- case e: ExecutionException if (e.getCause == cause) =>
- done()
- case _ =>
- done()
- assert(false)
+ case e: ExecutionException if e.getCause == cause => done(true)
+ case _ => done(false)
}
}
def testOnFailureWhenTimeoutException(): Unit = once {
done =>
- val f = future[Unit] {
- throw new TimeoutException()
- }
- f onSuccess {
- case _ =>
- done()
- assert(false)
- }
+ val f = future[Unit] { throw new TimeoutException() }
+ f onSuccess { case _ => done(false) }
f onFailure {
- case e: TimeoutException =>
- done()
- case other =>
- done()
- assert(false)
+ case e: TimeoutException => done(true)
+ case _ => done(false)
}
}
@@ -151,7 +100,6 @@ trait FutureCallbacks extends TestBase {
//testOnFailureWhenSpecialThrowable(7, new InterruptedException)
testThatNestedCallbacksDoNotYieldStackOverflow()
testOnFailureWhenTimeoutException()
-
}
@@ -162,207 +110,120 @@ trait FutureCombinators extends TestBase {
done =>
val f = future { 5 }
val g = f map { x => "result: " + x }
- g onSuccess {
- case s =>
- done()
- assert(s == "result: 5")
- }
- g onFailure {
- case _ =>
- done()
- assert(false)
- }
+ g onSuccess { case s => done(s == "result: 5") }
+ g onFailure { case _ => done(false) }
}
def testMapFailure(): Unit = once {
done =>
- val f = future {
- throw new Exception("exception message")
- }
+ val f = future[Unit] { throw new Exception("exception message") }
val g = f map { x => "result: " + x }
- g onSuccess {
- case _ =>
- done()
- assert(false)
- }
- g onFailure {
- case t =>
- done()
- assert(t.getMessage() == "exception message")
- }
+ g onSuccess { case _ => done(false) }
+ g onFailure { case t => done(t.getMessage() == "exception message") }
}
def testMapSuccessPF(): Unit = once {
done =>
val f = future { 5 }
val g = f map { case r => "result: " + r }
- g onSuccess {
- case s =>
- done()
- assert(s == "result: 5")
- }
- g onFailure {
- case _ =>
- done()
- assert(false)
- }
+ g onSuccess { case s => done(s == "result: 5") }
+ g onFailure { case _ => done(false) }
}
def testTransformSuccess(): Unit = once {
done =>
val f = future { 5 }
val g = f.transform(r => "result: " + r, identity)
- g onSuccess {
- case s =>
- done()
- assert(s == "result: 5")
- }
- g onFailure {
- case _ =>
- done()
- assert(false)
- }
+ g onSuccess { case s => done(s == "result: 5") }
+ g onFailure { case _ => done(false) }
}
def testTransformSuccessPF(): Unit = once {
done =>
val f = future { 5 }
val g = f.transform( { case r => "result: " + r }, identity)
- g onSuccess {
- case s =>
- done()
- assert(s == "result: 5")
- }
- g onFailure {
- case _ =>
- done()
- assert(false)
- }
+ g onSuccess { case s => done(s == "result: 5") }
+ g onFailure { case _ => done(false) }
+ }
+
+def testTransformFailure(): Unit = once {
+ done =>
+ val transformed = new Exception("transformed")
+ val f = future { throw new Exception("expected") }
+ val g = f.transform(identity, _ => transformed)
+ g onSuccess { case _ => done(false) }
+ g onFailure { case e => done(e eq transformed) }
+ }
+
+ def testTransformFailurePF(): Unit = once {
+ done =>
+ val e = new Exception("expected")
+ val transformed = new Exception("transformed")
+ val f = future[Unit] { throw e }
+ val g = f.transform(identity, { case `e` => transformed })
+ g onSuccess { case _ => done(false) }
+ g onFailure { case e => done(e eq transformed) }
}
def testFoldFailure(): Unit = once {
done =>
- val f = future {
- throw new Exception("exception message")
- }
+ val f = future[Unit] { throw new Exception("expected") }
val g = f.transform(r => "result: " + r, identity)
- g onSuccess {
- case _ =>
- done()
- assert(false)
- }
- g onFailure {
- case t =>
- done()
- assert(t.getMessage() == "exception message")
- }
+ g onSuccess { case _ => done(false) }
+ g onFailure { case t => done(t.getMessage() == "expected") }
}
def testFlatMapSuccess(): Unit = once {
done =>
val f = future { 5 }
val g = f flatMap { _ => future { 10 } }
- g onSuccess {
- case x =>
- done()
- assert(x == 10)
- }
- g onFailure {
- case _ =>
- done()
- assert(false)
- }
+ g onSuccess { case x => done(x == 10) }
+ g onFailure { case _ => done(false) }
}
def testFlatMapFailure(): Unit = once {
done =>
- val f = future {
- throw new Exception("exception message")
- }
+ val f = future[Unit] { throw new Exception("expected") }
val g = f flatMap { _ => future { 10 } }
- g onSuccess {
- case _ =>
- done()
- assert(false)
- }
- g onFailure {
- case t =>
- done()
- assert(t.getMessage() == "exception message")
- }
+ g onSuccess { case _ => done(false) }
+ g onFailure { case t => done(t.getMessage() == "expected") }
}
def testFilterSuccess(): Unit = once {
done =>
val f = future { 4 }
val g = f filter { _ % 2 == 0 }
- g onSuccess {
- case x: Int =>
- done()
- assert(x == 4)
- }
- g onFailure {
- case _ =>
- done()
- assert(false)
- }
+ g onSuccess { case x: Int => done(x == 4) }
+ g onFailure { case _ => done(false) }
}
def testFilterFailure(): Unit = once {
done =>
val f = future { 4 }
val g = f filter { _ % 2 == 1 }
- g onSuccess {
- case x: Int =>
- done()
- assert(false)
- }
+ g onSuccess { case x: Int => done(false) }
g onFailure {
- case e: NoSuchElementException =>
- done()
- assert(true)
- case _ =>
- done()
- assert(false)
+ case e: NoSuchElementException => done(true)
+ case _ => done(false)
}
}
def testCollectSuccess(): Unit = once {
done =>
val f = future { -5 }
- val g = f collect {
- case x if x < 0 => -x
- }
- g onSuccess {
- case x: Int =>
- done()
- assert(x == 5)
- }
- g onFailure {
- case _ =>
- done()
- assert(false)
- }
+ val g = f collect { case x if x < 0 => -x }
+ g onSuccess { case x: Int => done(x == 5) }
+ g onFailure { case _ => done(false) }
}
def testCollectFailure(): Unit = once {
done =>
val f = future { -5 }
- val g = f collect {
- case x if x > 0 => x * 2
- }
- g onSuccess {
- case _ =>
- done()
- assert(false)
- }
+ val g = f collect { case x if x > 0 => x * 2 }
+ g onSuccess { case _ => done(false) }
g onFailure {
- case e: NoSuchElementException =>
- done()
- assert(true)
- case _ =>
- done()
- assert(false)
+ case e: NoSuchElementException => done(true)
+ case _ => done(false)
}
}
@@ -376,16 +237,8 @@ trait FutureCombinators extends TestBase {
f foreach { x => p.success(x * 2) }
val g = p.future
- g.onSuccess {
- case res: Int =>
- done()
- assert(res == 10)
- }
- g.onFailure {
- case _ =>
- done()
- assert(false)
- }
+ g.onSuccess { case res: Int => done(res == 10) }
+ g.onFailure { case _ => done(false) }
}
def testForeachFailure(): Unit = once {
@@ -396,16 +249,8 @@ trait FutureCombinators extends TestBase {
f onFailure { case _ => p.failure(new Exception) }
val g = p.future
- g.onSuccess {
- case _ =>
- done()
- assert(false)
- }
- g.onFailure {
- case _ =>
- done()
- assert(true)
- }
+ g.onSuccess { case _ => done(false) }
+ g.onFailure { case _ => done(true) }
}
def testRecoverSuccess(): Unit = once {
@@ -415,18 +260,9 @@ trait FutureCombinators extends TestBase {
throw cause
} recover {
case re: RuntimeException =>
- "recovered"
- }
- f onSuccess {
- case x =>
- done()
- assert(x == "recovered")
- }
- f onFailure { case any =>
- done()
- assert(false)
- }
- f
+ "recovered" }
+ f onSuccess { case x => done(x == "recovered") }
+ f onFailure { case any => done(false) }
}
def testRecoverFailure(): Unit = once {
@@ -437,15 +273,8 @@ trait FutureCombinators extends TestBase {
} recover {
case te: TimeoutException => "timeout"
}
- f onSuccess {
- case x =>
- done()
- assert(false)
- }
- f onFailure { case any =>
- done()
- assert(any == cause)
- }
+ f onSuccess { case _ => done(false) }
+ f onFailure { case any => done(any == cause) }
}
def testRecoverWithSuccess(): Unit = once {
@@ -457,15 +286,8 @@ trait FutureCombinators extends TestBase {
case re: RuntimeException =>
future { "recovered" }
}
- f onSuccess {
- case x =>
- done()
- assert(x == "recovered")
- }
- f onFailure { case any =>
- done()
- assert(false)
- }
+ f onSuccess { case x => done(x == "recovered") }
+ f onFailure { case any => done(false) }
}
def testRecoverWithFailure(): Unit = once {
@@ -477,15 +299,8 @@ trait FutureCombinators extends TestBase {
case te: TimeoutException =>
future { "timeout" }
}
- f onSuccess {
- case x =>
- done()
- assert(false)
- }
- f onFailure { case any =>
- done()
- assert(any == cause)
- }
+ f onSuccess { case x => done(false) }
+ f onFailure { case any => done(any == cause) }
}
def testZipSuccess(): Unit = once {
@@ -493,52 +308,28 @@ trait FutureCombinators extends TestBase {
val f = future { 5 }
val g = future { 6 }
val h = f zip g
- h onSuccess {
- case (l: Int, r: Int) =>
- done()
- assert(l+r == 11)
- }
- h onFailure {
- case _ =>
- done()
- assert(false)
- }
+ h onSuccess { case (l: Int, r: Int) => done(l+r == 11) }
+ h onFailure { case _ => done(false) }
}
def testZipFailureLeft(): Unit = once {
done =>
- val cause = new Exception("exception message")
+ val cause = new Exception("expected")
val f = future { throw cause }
val g = future { 6 }
val h = f zip g
- h onSuccess {
- case _ =>
- done()
- assert(false)
- }
- h onFailure {
- case e: Exception =>
- done()
- assert(e.getMessage == "exception message")
- }
+ h onSuccess { case _ => done(false) }
+ h onFailure { case e: Exception => done(e.getMessage == "expected") }
}
def testZipFailureRight(): Unit = once {
done =>
- val cause = new Exception("exception message")
+ val cause = new Exception("expected")
val f = future { 5 }
val g = future { throw cause }
val h = f zip g
- h onSuccess {
- case _ =>
- done()
- assert(false)
- }
- h onFailure {
- case e: Exception =>
- done()
- assert(e.getMessage == "exception message")
- }
+ h onSuccess { case _ => done(false) }
+ h onFailure { case e: Exception => done(e.getMessage == "expected") }
}
def testFallbackTo(): Unit = once {
@@ -546,17 +337,8 @@ trait FutureCombinators extends TestBase {
val f = future { sys.error("failed") }
val g = future { 5 }
val h = f fallbackTo g
-
- h onSuccess {
- case x: Int =>
- done()
- assert(x == 5)
- }
- h onFailure {
- case _ =>
- done()
- assert(false)
- }
+ h onSuccess { case x: Int => done(x == 5) }
+ h onFailure { case _ => done(false) }
}
def testFallbackToFailure(): Unit = once {
@@ -566,16 +348,8 @@ trait FutureCombinators extends TestBase {
val g = future { throw cause }
val h = f fallbackTo g
- h onSuccess {
- case _ =>
- done()
- assert(false)
- }
- h onFailure {
- case e: Exception =>
- done()
- assert(e == cause)
- }
+ h onSuccess { case _ => done(false) }
+ h onFailure { case e => done(e eq cause) }
}
testMapSuccess()
@@ -597,6 +371,8 @@ trait FutureCombinators extends TestBase {
testZipFailureRight()
testFallbackTo()
testFallbackToFailure()
+ testTransformSuccess()
+ testTransformSuccessPF()
}
@@ -606,40 +382,26 @@ trait FutureProjections extends TestBase {
def testFailedFailureOnComplete(): Unit = once {
done =>
val cause = new RuntimeException
- val f = future {
- throw cause
- }
+ val f = future { throw cause }
f.failed onComplete {
- case Success(t) =>
- assert(t == cause)
- done()
- case Failure(t) =>
- assert(false)
+ case Success(t) => done(t == cause)
+ case Failure(t) => done(false)
}
}
def testFailedFailureOnSuccess(): Unit = once {
done =>
val cause = new RuntimeException
- val f = future {
- throw cause
- }
- f.failed onSuccess {
- case t =>
- assert(t == cause)
- done()
- }
+ val f = future { throw cause }
+ f.failed onSuccess { case t => done(t == cause) }
}
def testFailedSuccessOnComplete(): Unit = once {
done =>
val f = future { 0 }
f.failed onComplete {
- case Success(t) =>
- assert(false)
- case Failure(t) =>
- assert(t.isInstanceOf[NoSuchElementException])
- done()
+ case Failure(_: NoSuchElementException) => done(true)
+ case _ => done(false)
}
}
@@ -647,19 +409,17 @@ trait FutureProjections extends TestBase {
done =>
val f = future { 0 }
f.failed onFailure {
- case nsee: NoSuchElementException =>
- done()
+ case e: NoSuchElementException => done(true)
+ case _ => done(false)
}
+ f.failed onSuccess { case _ => done(false) }
}
def testFailedFailureAwait(): Unit = once {
done =>
val cause = new RuntimeException
- val f = future {
- throw cause
- }
- assert(Await.result(f.failed, Duration(500, "ms")) == cause)
- done()
+ val f = future { throw cause }
+ done(Await.result(f.failed, Duration(500, "ms")) == cause)
}
def testFailedSuccessAwait(): Unit = once {
@@ -667,9 +427,10 @@ trait FutureProjections extends TestBase {
val f = future { 0 }
try {
Await.result(f.failed, Duration(500, "ms"))
- assert(false)
+ done(false)
} catch {
- case nsee: NoSuchElementException => done()
+ case nsee: NoSuchElementException => done(true)
+ case _: Throwable => done(false)
}
}
@@ -682,8 +443,8 @@ trait FutureProjections extends TestBase {
Await.ready(f, Duration.Zero)
Await.ready(f, Duration(500, "ms"))
Await.ready(f, Duration.Inf)
- done()
- } onFailure { case x => throw x }
+ done(true)
+ } onFailure { case x => done(throw x) }
}
def testAwaitNegativeDuration(): Unit = once { done =>
@@ -692,8 +453,8 @@ trait FutureProjections extends TestBase {
intercept[TimeoutException] { Await.ready(f, Duration.Zero) }
intercept[TimeoutException] { Await.ready(f, Duration.MinusInf) }
intercept[TimeoutException] { Await.ready(f, Duration(-500, "ms")) }
- done()
- } onFailure { case x => throw x }
+ done(true)
+ } onFailure { case x => done(throw x) }
}
testFailedFailureOnComplete()
@@ -704,7 +465,6 @@ trait FutureProjections extends TestBase {
testFailedSuccessAwait()
testAwaitPositiveDuration()
testAwaitNegativeDuration()
-
}
@@ -714,33 +474,25 @@ trait Blocking extends TestBase {
def testAwaitSuccess(): Unit = once {
done =>
val f = future { 0 }
- Await.result(f, Duration(500, "ms"))
- done()
+ done(Await.result(f, Duration(500, "ms")) == 0)
}
def testAwaitFailure(): Unit = once {
done =>
val cause = new RuntimeException
- val f = future {
- throw cause
- }
+ val f = future { throw cause }
try {
Await.result(f, Duration(500, "ms"))
- assert(false)
+ done(false)
} catch {
- case t: Throwable =>
- assert(t == cause)
- done()
+ case t: Throwable => done(t == cause)
}
}
def testFQCNForAwaitAPI(): Unit = once {
done =>
-
- assert(classOf[CanAwait].getName == "scala.concurrent.CanAwait")
- assert(Await.getClass.getName == "scala.concurrent.Await")
-
- done()
+ done(classOf[CanAwait].getName == "scala.concurrent.CanAwait" &&
+ Await.getClass.getName == "scala.concurrent.Await")
}
testAwaitSuccess()
@@ -813,22 +565,26 @@ trait Promises extends TestBase {
val p = promise[Int]()
val f = p.future
- f onSuccess {
- case x =>
- done()
- assert(x == 5)
- }
- f onFailure {
- case any =>
- done()
- assert(false)
- }
+ f onSuccess { case x => done(x == 5) }
+ f onFailure { case any => done(false) }
p.success(5)
}
- testSuccess()
+ def testFailure(): Unit = once {
+ done =>
+ val e = new Exception("expected")
+ val p = promise[Int]()
+ val f = p.future
+
+ f onSuccess { case x => done(false) }
+ f onFailure { case any => done(any eq e) }
+
+ p.failure(e)
+ }
+ testSuccess()
+ testFailure()
}
@@ -888,11 +644,11 @@ trait CustomExecutionContext extends TestBase {
val count = countExecs { implicit ec =>
blocking {
once { done =>
- val f = future({ assertNoEC() })(defaultEC)
+ val f = future(assertNoEC())(defaultEC)
f onSuccess {
case _ =>
assertEC()
- done()
+ done(true)
}
assertNoEC()
}
@@ -911,7 +667,7 @@ trait CustomExecutionContext extends TestBase {
f onSuccess {
case _ =>
assertEC()
- done()
+ done(true)
}
}
}
@@ -935,15 +691,10 @@ trait CustomExecutionContext extends TestBase {
Promise.successful(x + 1).future.map(addOne).map(addOne)
} onComplete {
case Failure(t) =>
- try {
- throw new AssertionError("error in test: " + t.getMessage, t)
- } finally {
- done()
- }
+ done(throw new AssertionError("error in test: " + t.getMessage, t))
case Success(x) =>
assertEC()
- assert(x == 14)
- done()
+ done(x == 14)
}
assertNoEC()
}
@@ -999,21 +750,14 @@ trait ExecutionContextPrepare extends TestBase {
done =>
theLocal.set("secret")
val fut = future { 42 }
- fut onComplete {
- case _ =>
- assert(theLocal.get == "secret")
- done()
- }
+ fut onComplete { case _ => done(theLocal.get == "secret") }
}
def testMap(): Unit = once {
done =>
theLocal.set("secret2")
val fut = future { 42 }
- fut map { x =>
- assert(theLocal.get == "secret2")
- done()
- }
+ fut map { x => done(theLocal.get == "secret2") }
}
testOnComplete()
diff --git a/test/files/neg/valueclasses-impl-restrictions.check b/test/files/neg/valueclasses-impl-restrictions.check
index 63924493aa..0af9173f74 100644
--- a/test/files/neg/valueclasses-impl-restrictions.check
+++ b/test/files/neg/valueclasses-impl-restrictions.check
@@ -6,12 +6,8 @@ valueclasses-impl-restrictions.scala:9: error: implementation restriction: neste
This restriction is planned to be removed in subsequent releases.
trait I2 {
^
-valueclasses-impl-restrictions.scala:15: error: implementation restriction: nested class is not allowed in value class
-This restriction is planned to be removed in subsequent releases.
- val i2 = new I2 { val q = x.s }
- ^
-valueclasses-impl-restrictions.scala:21: error: implementation restriction: nested class is not allowed in value class
+valueclasses-impl-restrictions.scala:23: error: implementation restriction: nested class is not allowed in value class
This restriction is planned to be removed in subsequent releases.
private[this] class I2(val q: String)
^
-four errors found
+three errors found
diff --git a/test/files/neg/valueclasses-impl-restrictions.scala b/test/files/neg/valueclasses-impl-restrictions.scala
index 137f3f854c..f0577a94aa 100644
--- a/test/files/neg/valueclasses-impl-restrictions.scala
+++ b/test/files/neg/valueclasses-impl-restrictions.scala
@@ -12,8 +12,10 @@ class X1(val s: String) extends AnyVal {
}
def y(x: X1) = {
- val i2 = new I2 { val q = x.s }
+ val i2 = new I2 { val q = x.s } // allowed as of SI-7571
i2.z
+
+ { case x => x } : PartialFunction[Int, Int] // allowed
}
}
diff --git a/test/files/run/t7571.scala b/test/files/run/t7571.scala
new file mode 100644
index 0000000000..00b9695168
--- /dev/null
+++ b/test/files/run/t7571.scala
@@ -0,0 +1,12 @@
+class Foo(val a: Int) extends AnyVal {
+ def foo = { {case x => x + a}: PartialFunction[Int, Int]}
+
+ def bar = (new {}).toString
+}
+
+object Test extends App {
+ val x = new Foo(1).foo.apply(2)
+ assert(x == 3, x)
+ val s = new Foo(1).bar
+ assert(s.nonEmpty, s)
+}
diff --git a/test/files/specialized/SI-7343.scala b/test/files/specialized/SI-7343.scala
new file mode 100644
index 0000000000..5ee683064c
--- /dev/null
+++ b/test/files/specialized/SI-7343.scala
@@ -0,0 +1,55 @@
+class Parent[@specialized(Int) T]
+
+object Test extends App {
+
+ /**
+ * This method will check if specialization is correctly rewiring parents
+ * for classes defined inside methods. The pattern is important since this
+ * is how closures are currently represented: as locally-defined anonymous
+ * classes, which usually end up inside methods. For these closures we do
+ * want their parents rewired correctly:
+ *
+ * ```
+ * def checkSuperClass$mIc$sp[T](t: T, ...) = {
+ * class X extends Parent$mcI$sp // instead of just Parent
+ * ...
+ * }
+ */
+ def checkSuperClass[@specialized(Int) T](t: T, expectedXSuper: String) = {
+ // test target:
+ // - in checkSuperClass, X should extend Parent
+ // - in checkSuperClass$mIc$sp, X should extend Parent$mcI$sp
+ class X extends Parent[T]()
+
+ // get the superclass for X and make sure it's correct
+ val actualXSuper = (new X).getClass().getSuperclass().getSimpleName()
+ assert(actualXSuper == expectedXSuper, actualXSuper + " != " + expectedXSuper)
+ }
+
+ checkSuperClass("x", "Parent")
+ checkSuperClass(101, "Parent$mcI$sp")
+
+ /**
+ * This is the same check, but in value. It should work exactly the same
+ * as its method counterpart.
+ */
+ class Val[@specialized(Int) T](t: T, expectedXSuper: String) {
+ val check: T = {
+ class X extends Parent[T]()
+
+ // get the superclass for X and make sure it's correct
+ val actualXSuper = (new X).getClass().getSuperclass().getSimpleName()
+ assert(actualXSuper == expectedXSuper, actualXSuper + " != " + expectedXSuper)
+ t
+ }
+ }
+
+ new Val("x", "Parent")
+ new Val(101, "Parent$mcI$sp")
+
+ /**
+ * NOTE: The the same check, only modified to affect constructors, won't
+ * work since the class X definition will always be lifted to become a
+ * member of the class, making it impossible to force its duplication.
+ */
+}
diff --git a/test/files/specialized/spec-ame.check b/test/files/specialized/spec-ame.check
index 9c1713cc8a..cf18c01191 100644
--- a/test/files/specialized/spec-ame.check
+++ b/test/files/specialized/spec-ame.check
@@ -1,3 +1,3 @@
abc
10
-3 \ No newline at end of file
+2
diff --git a/test/files/specialized/spec-ame.scala b/test/files/specialized/spec-ame.scala
index 79ee4217ed..129fb9f447 100644
--- a/test/files/specialized/spec-ame.scala
+++ b/test/files/specialized/spec-ame.scala
@@ -13,6 +13,9 @@ object Test {
def main(args: Array[String]) {
println((new A("abc")).foo.value)
println((new A(10)).foo.value)
+ // before fixing SI-7343, this was printing 3. Now it's printing 2,
+ // since the anonymous class created by doing new B[T] { ... } when
+ // T = Int is now rewired to B$mcI$sp instead of just B[Int]
println(runtime.BoxesRunTime.integerBoxCount)
}
}