From c14766c1affe968788c77fea45ae042d71038472 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 28 Mar 2012 14:46:46 +0200 Subject: Add JavaConversions and JavaConverters concurrent.Map support. This required deprecating several old methods for converting ConcurrentMap. Also, several new wrappers were introduced for concurrent.Map. Once we remove ConcurrentMap in 2.11, we can remove its corresponding wrappers and conversions in JavaConversions and JavaConverters. --- src/library/scala/collection/JavaConversions.scala | 86 +++++++++++++++++++--- src/library/scala/collection/JavaConverters.scala | 34 +++++---- 2 files changed, 96 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/library/scala/collection/JavaConversions.scala b/src/library/scala/collection/JavaConversions.scala index 50919e506a..03f1fe07ab 100644 --- a/src/library/scala/collection/JavaConversions.scala +++ b/src/library/scala/collection/JavaConversions.scala @@ -293,24 +293,30 @@ object JavaConversions { @deprecated("use mapAsJavaMap instead", "2.9.0") def asJavaMap[A, B](m : Map[A, B]): ju.Map[A, B] = mapAsJavaMap[A, B](m) + @deprecated("Use `concurrent.Map` instead of `mutable.ConcurrentMap`", "2.10.0") + def asJavaConcurrentMap[A, B](m: mutable.ConcurrentMap[A, B]): juc.ConcurrentMap[A, B] = m match { + case JConcurrentMapWrapper(wrapped) => wrapped + case _ => new ConcurrentMapWrapper(m) + } + /** - * Implicitly converts a Scala mutable `ConcurrentMap` to a Java + * Implicitly converts a Scala mutable `concurrent.Map` to a Java * `ConcurrentMap`. * * The returned Java `ConcurrentMap` is backed by the provided Scala - * `ConcurrentMap` and any side-effects of using it via the Java interface + * `concurrent.Map` and any side-effects of using it via the Java interface * will be visible via the Scala interface and vice versa. * - * If the Scala `ConcurrentMap` was previously obtained from an implicit or - * explicit call of `asConcurrentMap(java.util.concurrect.ConcurrentMap)` + * If the Scala `concurrent.Map` was previously obtained from an implicit or + * explicit call of `javaConcurrentMapAsScalaConcurrentMap(java.util.concurrect.ConcurrentMap)` * then the original Java ConcurrentMap will be returned. * - * @param m The `ConcurrentMap` to be converted. + * @param m The `concurrent.Map` to be converted. * @return A Java `ConcurrentMap` view of the argument. */ - implicit def asJavaConcurrentMap[A, B](m: mutable.ConcurrentMap[A, B]): juc.ConcurrentMap[A, B] = m match { - case JConcurrentMapWrapper(wrapped) => wrapped - case _ => new ConcurrentMapWrapper(m) + implicit def asJavaConcurrentMap[A, B](m: concurrent.Map[A, B]): juc.ConcurrentMap[A, B] = m match { + case JavaConcurrentMapWrapper(wrapped) => wrapped + case _ => new ScalaConcurrentMapWrapper(m) } // Java => Scala @@ -451,6 +457,12 @@ object JavaConversions { @deprecated("use mapAsScalaMap instead", "2.9.0") def asScalaMap[A, B](m: ju.Map[A, B]): mutable.Map[A, B] = mapAsScalaMap[A, B](m) + @deprecated("Use `javaConcurrentMapAsScalaConcurrentMap` instead.", "2.10.0") + def asScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): mutable.ConcurrentMap[A, B] = m match { + case cmw: ConcurrentMapWrapper[a, b] => cmw.underlying + case _ => new JConcurrentMapWrapper(m) + } + /** * Implicitly converts a Java ConcurrentMap to a Scala mutable ConcurrentMap. * The returned Scala ConcurrentMap is backed by the provided Java @@ -464,11 +476,12 @@ object JavaConversions { * @param m The ConcurrentMap to be converted. * @return A Scala mutable ConcurrentMap view of the argument. */ - implicit def asScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): mutable.ConcurrentMap[A, B] = m match { - case cmw: ConcurrentMapWrapper[a, b] => cmw.underlying - case _ => new JConcurrentMapWrapper(m) + implicit def javaConcurrentMapAsScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = m match { + case cmw: ScalaConcurrentMapWrapper[a, b] => cmw.underlying + case _ => new JavaConcurrentMapWrapper(m) } + /** * Implicitly converts a Java `Dictionary` to a Scala mutable * `Map[String, String]`. @@ -765,6 +778,7 @@ object JavaConversions { override def empty = JMapWrapper(new ju.HashMap[A, B]) } + // will be removed in 2.11 class ConcurrentMapWrapper[A, B](override val underlying: mutable.ConcurrentMap[A, B]) extends MutableMapWrapper[A, B](underlying) with juc.ConcurrentMap[A, B] { @@ -788,6 +802,30 @@ object JavaConversions { def replace(k: A, oldval: B, newval: B) = underlying.replace(k, oldval, newval) } + class ScalaConcurrentMapWrapper[A, B](override val underlying: concurrent.Map[A, B]) + extends MutableMapWrapper[A, B](underlying) with juc.ConcurrentMap[A, B] { + + def putIfAbsent(k: A, v: B) = underlying.putIfAbsent(k, v) match { + case Some(v) => v + case None => null.asInstanceOf[B] + } + + def remove(k: AnyRef, v: AnyRef) = try { + underlying.remove(k.asInstanceOf[A], v.asInstanceOf[B]) + } catch { + case ex: ClassCastException => + false + } + + def replace(k: A, v: B): B = underlying.replace(k, v) match { + case Some(v) => v + case None => null.asInstanceOf[B] + } + + def replace(k: A, oldval: B, newval: B) = underlying.replace(k, oldval, newval) + } + + // will be removed in 2.11 case class JConcurrentMapWrapper[A, B](val underlying: juc.ConcurrentMap[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]] with mutable.ConcurrentMap[A, B] { override def get(k: A) = { @@ -814,6 +852,32 @@ object JavaConversions { underlying.replace(k, oldvalue, newvalue) } + case class JavaConcurrentMapWrapper[A, B](val underlying: juc.ConcurrentMap[A, B]) + extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]] with concurrent.Map[A, B] { + override def get(k: A) = { + val v = underlying get k + if (v != null) Some(v) + else None + } + + override def empty = new JConcurrentMapWrapper(new juc.ConcurrentHashMap[A, B]) + + def putIfAbsent(k: A, v: B): Option[B] = { + val r = underlying.putIfAbsent(k, v) + if (r != null) Some(r) else None + } + + def remove(k: A, v: B): Boolean = underlying.remove(k, v) + + def replace(k: A, v: B): Option[B] = { + val prev = underlying.replace(k, v) + if (prev != null) Some(prev) else None + } + + def replace(k: A, oldvalue: B, newvalue: B): Boolean = + underlying.replace(k, oldvalue, newvalue) + } + case class DictionaryWrapper[A, B](underlying: mutable.Map[A, B]) extends ju.Dictionary[A, B] { def size: Int = underlying.size diff --git a/src/library/scala/collection/JavaConverters.scala b/src/library/scala/collection/JavaConverters.scala index d213e60112..d56a6baff6 100755 --- a/src/library/scala/collection/JavaConverters.scala +++ b/src/library/scala/collection/JavaConverters.scala @@ -331,23 +331,27 @@ trait JavaConverters { @deprecated("Use mapAsJavaMapConverter instead", "2.9.0") def asJavaMapConverter[A, B](m : Map[A, B]): AsJava[ju.Map[A, B]] = mapAsJavaMapConverter(m) + @deprecated("Use `concurrent.Map` instead of `mutable.ConcurrentMap`", "2.10.0") + implicit def asJavaConcurrentMapConverter[A, B](m: mutable.ConcurrentMap[A, B]): AsJava[juc.ConcurrentMap[A, B]] = + new AsJava(asJavaConcurrentMap(m)) + /** * Adds an `asJava` method that implicitly converts a Scala mutable - * `ConcurrentMap` to a Java `ConcurrentMap`. + * `concurrent.Map` to a Java `ConcurrentMap`. * * The returned Java `ConcurrentMap` is backed by the provided Scala - * `ConcurrentMap` and any side-effects of using it via the Java interface + * `concurrent.Map` and any side-effects of using it via the Java interface * will be visible via the Scala interface and vice versa. * - * If the Scala `ConcurrentMap` was previously obtained from an implicit or - * explicit call of `asConcurrentMap(java.util.concurrect.ConcurrentMap)` + * If the Scala `concurrent.Map` was previously obtained from an implicit or + * explicit call of `asConcurrentMap(java.util.concurrent.ConcurrentMap)` * then the original Java `ConcurrentMap` will be returned. * - * @param m The `ConcurrentMap` to be converted. + * @param m The `concurrent.Map` to be converted. * @return An object with an `asJava` method that returns a Java * `ConcurrentMap` view of the argument. */ - implicit def asJavaConcurrentMapConverter[A, B](m: mutable.ConcurrentMap[A, B]): AsJava[juc.ConcurrentMap[A, B]] = + implicit def asJavaConcurrentMapConverter[A, B](m: concurrent.Map[A, B]): AsJava[juc.ConcurrentMap[A, B]] = new AsJava(asJavaConcurrentMap(m)) /** @@ -486,24 +490,28 @@ trait JavaConverters { @deprecated("Use mapAsScalaMapConverter instead", "2.9.0") def asScalaMapConverter[A, B](m : ju.Map[A, B]): AsScala[mutable.Map[A, B]] = mapAsScalaMapConverter(m) + @deprecated("Use `javaConcurrentMapAsScalaConcurrentMapConverter` instead, and use `concurrent.Map` instead of `mutable.ConcurrentMap`", "2.10.0") + def asScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[mutable.ConcurrentMap[A, B]] = + new AsScala(asScalaConcurrentMap(m)) + /** * Adds an `asScala` method that implicitly converts a Java `ConcurrentMap` - * to a Scala mutable `ConcurrentMap`. The returned Scala `ConcurrentMap` is + * to a Scala mutable `concurrent.Map`. The returned Scala `concurrent.Map` is * backed by the provided Java `ConcurrentMap` and any side-effects of using * it via the Scala interface will be visible via the Java interface and * vice versa. * * If the Java `ConcurrentMap` was previously obtained from an implicit or * explicit call of `asConcurrentMap(scala.collection.mutable.ConcurrentMap)` - * then the original Scala `ConcurrentMap` will be returned. + * then the original Scala `concurrent.Map` will be returned. * * @param m The `ConcurrentMap` to be converted. * @return An object with an `asScala` method that returns a Scala mutable - * `ConcurrentMap` view of the argument. + * `concurrent.Map` view of the argument. */ - implicit def asScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[mutable.ConcurrentMap[A, B]] = - new AsScala(asScalaConcurrentMap(m)) - + implicit def javaConcurrentMapAsScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[concurrent.Map[A, B]] = + new AsScala(javaConcurrentMapAsScalaConcurrentMap(m)) + /** * Adds an `asScala` method that implicitly converts a Java `Dictionary` * to a Scala mutable `Map[String, String]`. The returned Scala @@ -538,4 +546,4 @@ trait JavaConverters { } -object JavaConverters extends JavaConverters \ No newline at end of file +object JavaConverters extends JavaConverters -- cgit v1.2.3 From 7c84f1fc32e99a309f9b47b7359764aecbfc21e6 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 29 Mar 2012 17:52:08 +0200 Subject: Add withFilter and mapTo to futures. --- src/library/scala/concurrent/Future.scala | 33 +++++++++++++++++++++++++- src/library/scala/concurrent/impl/Future.scala | 30 ----------------------- 2 files changed, 32 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index d73801aa90..8cecadc605 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -274,6 +274,18 @@ self => p.future } + /** Used by for-comprehensions. + */ + final def withFilter(p: T => Boolean): Future[T] = filter(p) + // 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. * * If the current future contains a value for which the partial function is defined, the new future will also hold that value. @@ -417,7 +429,26 @@ self => p.future } - + + /** Creates a new `Future[S]` which is completed with this `Future`'s result if + * that conforms to `S`'s erased type or a `ClassCastException` otherwise. + */ + def mapTo[S](implicit m: Manifest[S]): Future[S] = { + val p = newPromise[S] + + onComplete { + case l: Failure[_] => p complete l.asInstanceOf[Try[S]] + case Success(t) => + p complete (try { + Success(impl.Future.boxedType(m.erasure).cast(t).asInstanceOf[S]) + } catch { + case e: ClassCastException => Failure(e) + }) + } + + p.future + } + /** Applies the side-effecting function to the result of this future, and returns * a new future with the result of this future. * diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 6833b2467f..1111aa4753 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -40,36 +40,6 @@ private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awa def onComplete[U](func: Try[T] => U): this.type - /** Creates a new Future[A] which is completed with this Future's result if - * that conforms to A's erased type or a ClassCastException otherwise. - */ - final def mapTo[T](implicit m: Manifest[T]) = { - val p = new Promise.DefaultPromise[T] - - onComplete { - case f @ Failure(t) => p complete f.asInstanceOf[Try[T]] - case Success(v) => - p complete (try { - Success(Future.boxedType(m.erasure).cast(v).asInstanceOf[T]) - } catch { - case e: ClassCastException => Failure(e) - }) - } - - p.future - } - - /** Used by for-comprehensions. - */ - final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p) - - final class FutureWithFilter[+A](self: Future[A], p: A => Boolean) { - def foreach(f: A => Unit): Unit = self filter p foreach f - def map[B](f: A => B) = self filter p map f - def flatMap[B](f: A => Future[B]) = self filter p flatMap f - def withFilter(q: A => Boolean): FutureWithFilter[A] = new FutureWithFilter[A](self, x ⇒ p(x) && q(x)) - } - } object Future { -- cgit v1.2.3 From 44551b2e9fb3ba76278dc6c4b56195cc65a1dff8 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Fri, 30 Mar 2012 14:52:10 +0200 Subject: Remove blocking from execution contexts. --- .../scala/concurrent/ConcurrentPackageObject.scala | 30 ++++++++-------- .../scala/concurrent/ExecutionContext.scala | 6 ++-- .../concurrent/impl/ExecutionContextImpl.scala | 42 +++++++++++++++++++--- src/library/scala/concurrent/impl/Promise.scala | 2 +- 4 files changed, 54 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala index ba98757906..f4744a8757 100644 --- a/src/library/scala/concurrent/ConcurrentPackageObject.scala +++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala @@ -10,8 +10,8 @@ package scala.concurrent -import java.util.concurrent.{ Executors, ExecutorService } -import scala.concurrent.forkjoin.ForkJoinPool +import java.util.concurrent.{ Executors, ExecutorService, ThreadFactory } +import scala.concurrent.forkjoin.{ ForkJoinPool, ForkJoinWorkerThread } import scala.util.{ Try, Success, Failure } import scala.concurrent.util.Duration import ConcurrentPackageObject._ @@ -24,14 +24,9 @@ abstract class ConcurrentPackageObject { /** A global execution environment for executing lightweight tasks. */ lazy val executionContext = - new impl.ExecutionContextImpl(getExecutorService) - - private[concurrent] def getExecutorService: AnyRef = - if (scala.util.Properties.isJavaAtLeast("1.6")) { - val vendor = scala.util.Properties.javaVmVendor - if ((vendor contains "Oracle") || (vendor contains "Sun") || (vendor contains "Apple")) new ForkJoinPool - else Executors.newCachedThreadPool() - } else Executors.newCachedThreadPool() + new impl.ExecutionContextImpl() + + private val currentExecutionContext = new ThreadLocal[ExecutionContext] val handledFutureException: PartialFunction[Throwable, Throwable] = { case t: Throwable if isFutureThrowable(t) => t @@ -58,10 +53,10 @@ abstract class ConcurrentPackageObject { /* concurrency constructs */ - def future[T](body: =>T)(implicit execCtx: ExecutionContext = executionContext): Future[T] = + def future[T](body: =>T)(implicit execctx: ExecutionContext = executionContext): Future[T] = Future[T](body) - def promise[T]()(implicit execCtx: ExecutionContext = executionContext): Promise[T] = + def promise[T]()(implicit execctx: ExecutionContext = executionContext): Promise[T] = Promise[T]() /** Wraps a block of code into an awaitable object. */ @@ -82,8 +77,8 @@ abstract class ConcurrentPackageObject { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def blocking[T](body: =>T)(implicit execCtx: ExecutionContext): T = - executionContext.blocking(body) + def blocking[T](body: =>T): T = + blocking(body2awaitable(body), Duration.fromNanos(0)) /** Blocks on an awaitable object. * @@ -94,8 +89,11 @@ abstract class ConcurrentPackageObject { * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def blocking[T](awaitable: Awaitable[T], atMost: Duration)(implicit execCtx: ExecutionContext = executionContext): T = - executionContext.blocking(awaitable, atMost) + def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = + currentExecutionContext.get match { + case null => Await.result(awaitable, atMost) + case ec => ec.internalBlockingCall(awaitable, atMost) + } @inline implicit final def int2durationops(x: Int): DurationOps = new DurationOps(x) } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index a206a2d4ea..f639f76dc9 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -26,10 +26,8 @@ trait ExecutionContext { def execute[U](body: () => U): Unit - def blocking[T](body: =>T): T - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T - + def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T + def reportFailure(t: Throwable): Unit /* implementations follow */ diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 5dc440f42b..2cfd6f22cd 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -10,7 +10,7 @@ package scala.concurrent.impl -import java.util.concurrent.{Callable, ExecutorService} +import java.util.concurrent.{Callable, ExecutorService, Executors, ThreadFactory} import scala.concurrent.forkjoin._ import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} import scala.util.{ Try, Success, Failure } @@ -18,8 +18,42 @@ import scala.concurrent.util.{ Duration } -private[scala] class ExecutionContextImpl(val executorService: AnyRef) extends ExecutionContext { +private[scala] class ExecutionContextImpl() extends ExecutionContext { import ExecutionContextImpl._ + + val executorService: AnyRef = getExecutorService + + // to ensure that the current execution context thread local is properly set + private def executorsThreadFactory = new ThreadFactory { + def newThread(r: Runnable) = new Thread(new Runnable { + override def run() { + currentExecutionContext.set(ExecutionContextImpl.this) + r.run() + } + }) + } + + // to ensure that the current execution context thread local is properly set + private def forkJoinPoolThreadFactory = new ForkJoinPool.ForkJoinWorkerThreadFactory { + def newThread(fjp: ForkJoinPool) = new ForkJoinWorkerThread(fjp) { + override def onStart() { + currentExecutionContext.set(ExecutionContextImpl.this) + } + } + } + + private def getExecutorService: AnyRef = + if (scala.util.Properties.isJavaAtLeast("1.6")) { + val vendor = scala.util.Properties.javaVmVendor + if ((vendor contains "Oracle") || (vendor contains "Sun") || (vendor contains "Apple")) + new ForkJoinPool( + Runtime.getRuntime.availableProcessors(), + forkJoinPoolThreadFactory, + null, + false) + else + Executors.newCachedThreadPool(executorsThreadFactory) + } else Executors.newCachedThreadPool(executorsThreadFactory) def execute(runnable: Runnable): Unit = executorService match { case fj: ForkJoinPool => @@ -37,9 +71,7 @@ private[scala] class ExecutionContextImpl(val executorService: AnyRef) extends E def run() = body() }) - def blocking[T](body: =>T): T = blocking(body2awaitable(body), Duration.fromNanos(0)) - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { + def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = { Future.releaseStack(this) awaitable.result(atMost)(scala.concurrent.Await.canAwaitEvidence) diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index c79b0d02cc..f05e306088 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -127,7 +127,7 @@ object Promise { value.isDefined } - executor.blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) + blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) } def ready(atMost: Duration)(implicit permit: CanAwait): this.type = -- cgit v1.2.3 From 88bc33301a1d1994a7a60f91ff7603c8e902b6a8 Mon Sep 17 00:00:00 2001 From: David Hall Date: Fri, 30 Mar 2012 18:26:46 -0700 Subject: Fixes SI-5632 (serialization of large HashTables) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converts HashTable threshold to long before multiplying by a large value. Test is very slow and requires giving partest more RAM. Rather than committing it, I'm attaching it as a gist. Whoever does the merge is more than welcome to commit it along with this patch… Test: https://gist.github.com/2257703 --- src/library/scala/collection/mutable/HashTable.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/library/scala/collection/mutable/HashTable.scala b/src/library/scala/collection/mutable/HashTable.scala index cc0aed6963..06b7d40bfc 100644 --- a/src/library/scala/collection/mutable/HashTable.scala +++ b/src/library/scala/collection/mutable/HashTable.scala @@ -366,7 +366,7 @@ private[collection] object HashTable { private[collection] final def newThreshold(_loadFactor: Int, size: Int) = ((size.toLong * _loadFactor) / loadFactorDenum).toInt - private[collection] final def sizeForThreshold(_loadFactor: Int, thr: Int) = thr * loadFactorDenum / _loadFactor + private[collection] final def sizeForThreshold(_loadFactor: Int, thr: Int) = ((thr.toLong * loadFactorDenum) / _loadFactor).toInt private[collection] final def capacity(expectedSize: Int) = if (expectedSize == 0) 1 else powerOfTwo(expectedSize) -- cgit v1.2.3 From 69d2999a4b7c8b5e002404437d066fd418b5c1fa Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Sat, 31 Mar 2012 09:57:28 -0400 Subject: Maven repository dependencies are now added to distributions. --- build.xml | 7 ++++++- src/build/maven/scala-library-pom.xml | 29 ++++++++++++++++++----------- 2 files changed, 24 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/build.xml b/build.xml index c5850e2000..c1ab09c4e4 100644 --- a/build.xml +++ b/build.xml @@ -246,7 +246,7 @@ INITIALISATION - + @@ -1016,6 +1016,11 @@ PACKED QUICK BUILD (PACK) + + + + + diff --git a/src/build/maven/scala-library-pom.xml b/src/build/maven/scala-library-pom.xml index 673c3dfada..c3f8a4531c 100644 --- a/src/build/maven/scala-library-pom.xml +++ b/src/build/maven/scala-library-pom.xml @@ -30,17 +30,24 @@ JIRA https://issues.scala-lang.org/ - - - scala-tools.org - @RELEASE_REPOSITORY@ - - - scala-tools.org - @SNAPSHOT_REPOSITORY@ - false - - + + + org.skife.com.typesafe.config + typesafe-config + 0.3.0 + + + + + scala-tools.org + @RELEASE_REPOSITORY@ + + + scala-tools.org + @SNAPSHOT_REPOSITORY@ + false + + lamp -- cgit v1.2.3 From 883ca63361a039f27e412eb0cb1bee7e9742a0bb Mon Sep 17 00:00:00 2001 From: Dmitry Nadezhin Date: Mon, 2 Apr 2012 10:29:25 +0400 Subject: SI-5627 BigInt.equals(Number) and BigDecimal.equals(Number) should implement equality in mathematical sense --- src/library/scala/math/BigDecimal.scala | 15 ++++++- src/library/scala/math/BigInt.scala | 38 ++++++++++++++++ test/files/run/is-valid-num.scala | 77 +++++++++++++++++++++++++++++++++ test/files/run/numbereq.scala | 35 ++++++++++++++- 4 files changed, 162 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala index c1f45eccfb..cb42b76b51 100644 --- a/src/library/scala/math/BigDecimal.scala +++ b/src/library/scala/math/BigDecimal.scala @@ -183,7 +183,8 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { override def equals (that: Any): Boolean = that match { case that: BigDecimal => this equals that case that: BigInt => this.toBigIntExact exists (that equals _) - case _: Float | _: Double => unifiedPrimitiveEquals(that) + case that: Double => isValidDouble && toDouble == that + case that: Float => isValidFloat && toFloat == that case _ => isValidLong && unifiedPrimitiveEquals(that) } override def isValidByte = noArithmeticException(toByteExact) @@ -191,6 +192,18 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { override def isValidChar = isValidInt && toIntExact >= Char.MinValue && toIntExact <= Char.MaxValue override def isValidInt = noArithmeticException(toIntExact) def isValidLong = noArithmeticException(toLongExact) + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + */ + def isValidFloat = { + val f = toFloat + !f.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(f)) == 0 + } + /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + */ + def isValidDouble = { + val d = toDouble + !d.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(d)) == 0 + } private def noArithmeticException(body: => Unit): Boolean = { try { body ; true } diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index 8a53afaa62..dbec30b2fe 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -20,6 +20,7 @@ object BigInt { private val minCached = -1024 private val maxCached = 1024 private val cache = new Array[BigInt](maxCached - minCached + 1) + private val minusOne = BigInteger.valueOf(-1) @deprecated("Use Long.MinValue", "2.9.0") val MinLong = BigInt(Long.MinValue) @@ -122,6 +123,8 @@ class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericCo override def equals(that: Any): Boolean = that match { case that: BigInt => this equals that case that: BigDecimal => that.toBigIntExact exists (this equals _) + case that: Double => isValidDouble && toDouble == that + case that: Float => isValidFloat && toFloat == that case x => isValidLong && unifiedPrimitiveEquals(x) } override def isValidByte = this >= Byte.MinValue && this <= Byte.MaxValue @@ -129,6 +132,41 @@ class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericCo override def isValidChar = this >= Char.MinValue && this <= Char.MaxValue override def isValidInt = this >= Int.MinValue && this <= Int.MaxValue def isValidLong = this >= Long.MinValue && this <= Long.MaxValue + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + */ + def isValidFloat = { + val bitLen = bitLength + (bitLen <= 24 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Float.MAX_EXPONENT + 1 && // exclude this < -2^128 && this >= 2^128 + lowest >= bitLen - 24 && + lowest < java.lang.Float.MAX_EXPONENT + 1 // exclude this == -2^128 + } + ) && !bitLengthOverflow + } + /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + */ + def isValidDouble = { + val bitLen = bitLength + (bitLen <= 53 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Double.MAX_EXPONENT + 1 && // exclude this < -2^1024 && this >= 2^1024 + lowest >= bitLen - 53 && + lowest < java.lang.Double.MAX_EXPONENT + 1 // exclude this == -2^1024 + } + ) && !bitLengthOverflow + } + /** Some implementations of java.math.BigInteger allow huge values with bit length greater than Int.MaxValue . + * The BigInteger.bitLength method returns truncated bit length in this case . + * This method tests if result of bitLength is valid. + * This method will become unnecessary if BigInt constructors reject huge BigIntegers. + */ + private def bitLengthOverflow = { + val shifted = bigInteger.shiftRight(Int.MaxValue) + (shifted.signum != 0) && !(shifted equals BigInt.minusOne) + } protected[math] def isWhole = true def underlying = bigInteger diff --git a/test/files/run/is-valid-num.scala b/test/files/run/is-valid-num.scala index f919a21dee..9c43e98911 100644 --- a/test/files/run/is-valid-num.scala +++ b/test/files/run/is-valid-num.scala @@ -1,11 +1,15 @@ object Test { def x = BigInt("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") def y = BigDecimal("" + (Short.MaxValue + 1) + ".0") + def y1 = BigDecimal("0.1") + def y2 = BigDecimal("0.5") def l1 = Int.MaxValue.toLong + 1 def l2 = Int.MinValue.toLong - 1 def main(args: Array[String]): Unit = { + assert(!x.isValidDouble, x) + assert(!x.isValidFloat, x) assert(!x.isValidLong, x) assert(!x.isValidInt, x) assert(!x.isValidChar, x) @@ -13,8 +17,81 @@ object Test { assert(!y.isValidShort, y) assert(y.isValidChar, y) assert(y.isValidInt, y) + assert(y.isValidFloat, y) + assert(y.isValidDouble, y) + assert(!y1.isValidLong, y1) + assert(!y1.isValidFloat, y1) + assert(!y1.isValidDouble, y1) + assert(!y2.isValidLong, y2) + assert(y2.isValidFloat, y2) + assert(y2.isValidDouble, y2) + + testBigIntIsFloat() + testBigIntIsDouble() assert(!l1.isValidInt && (l1 - 1).isValidInt, l1) assert(!l2.isValidInt && (l2 + 1).isValidInt, l2) } + + def biExp2(e: Int) = BigInt(1) << e + + def testBigIntIsFloat() { + val prec = 24 + def checkFloatT(x: BigInt) = { + assert(x.isValidFloat, x) + assert((-x).isValidFloat, -x) + } + def checkFloatF(x: BigInt) = { + assert(!x.isValidFloat, x) + assert(!(-x).isValidFloat, -x) + } + checkFloatT(biExp2(prec) - 1) + checkFloatT(biExp2(prec)) + checkFloatF(biExp2(prec) + 1) + checkFloatT(biExp2(prec) + 2) + checkFloatT(biExp2(prec) - 2) + checkFloatF(biExp2(prec + 1) - 1) + checkFloatT(biExp2(prec + 1)) + checkFloatF(biExp2(prec + 1) + 1) + checkFloatF(biExp2(prec + 1) + 2) + checkFloatF(biExp2(prec + 1) + 3) + checkFloatT(biExp2(prec + 1) + 4) + checkFloatT(biExp2(64)) + checkFloatF(biExp2(64) + biExp2(64 - prec)) + checkFloatT(biExp2(64) + biExp2(64 - prec + 1)) + checkFloatT(biExp2(127)) + checkFloatT(biExp2(128) - biExp2(128 - prec)) + checkFloatF(biExp2(128) - biExp2(128 - prec - 1)) + checkFloatF(biExp2(128)) + } + + def testBigIntIsDouble() { + val prec = 53 + def checkDoubleT(x: BigInt) = { + assert(x.isValidDouble, x) + assert((-x).isValidDouble, -x) + } + def checkDoubleF(x: BigInt) = { + assert(!x.isValidDouble, x) + assert(!(-x).isValidDouble, -x) + } + checkDoubleT(biExp2(prec) - 1) + checkDoubleT(biExp2(prec)) + checkDoubleF(biExp2(prec) + 1) + checkDoubleT(biExp2(prec) + 2) + checkDoubleT(biExp2(prec + 1) - 2) + checkDoubleF(biExp2(prec + 1) - 1) + checkDoubleT(biExp2(prec + 1)) + checkDoubleF(biExp2(prec + 1) + 1) + checkDoubleF(biExp2(prec + 1) + 2) + checkDoubleF(biExp2(prec + 1) + 3) + checkDoubleT(biExp2(prec + 1) + 4) + checkDoubleT(biExp2(64)) + checkDoubleF(biExp2(64) + biExp2(64 - prec)) + checkDoubleT(biExp2(64) + biExp2(64 - prec + 1)) + checkDoubleT(biExp2(1023)) + checkDoubleT(biExp2(1024) - biExp2(1024 - prec)) + checkDoubleF(biExp2(1024) - biExp2(1024 - prec - 1)) + checkDoubleF(biExp2(1024)) + } } diff --git a/test/files/run/numbereq.scala b/test/files/run/numbereq.scala index 77a217df36..a1f11da205 100644 --- a/test/files/run/numbereq.scala +++ b/test/files/run/numbereq.scala @@ -16,7 +16,20 @@ object Test { base ::: extras } - + + def mkNumbers(x: BigInt): List[AnyRef] = { + List( + List(BigDecimal(x, java.math.MathContext.UNLIMITED)), + List(x), + if (x.isValidDouble) List(new java.lang.Double(x.toDouble)) else Nil, + if (x.isValidFloat) List(new java.lang.Float(x.toFloat)) else Nil, + if (x.isValidLong) List(new java.lang.Long(x.toLong)) else Nil, + if (x.isValidInt) List(new java.lang.Integer(x.toInt)) else Nil, + if (x.isValidShort) List(new java.lang.Short(x.toShort)) else Nil, + if (x.isValidByte) List(new java.lang.Byte(x.toByte)) else Nil, + if (x.isValidChar) List(new java.lang.Character(x.toChar)) else Nil + ).flatten + } def main(args: Array[String]): Unit = { val ints = (0 to 15).toList map (Short.MinValue >> _) @@ -37,5 +50,23 @@ object Test { assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) assert(x.## == y.##, "%s != %s".format(x.getClass, y.getClass)) } + + val bigInts = (0 to 1024).toList map (BigInt(-1) << _) + val bigInts2 = bigInts map (x => -x) + val bigInts3 = bigInts map (_ + 1) + val bigInts4 = bigInts2 map (_ - 1) + + val setneg1b = bigInts map mkNumbers + val setneg2b = bigInts3 map mkNumbers + val setpos1b = bigInts2 map mkNumbers + val setpos2b = bigInts4 map mkNumbers + + val sets2 = setneg1 ++ setneg1b ++ setneg2 ++ setneg2b ++ List(zero) ++ setpos1 ++ setpos1b ++ setpos2 ++ setpos2b + + for (set <- sets2 ; x <- set ; y <- set) { +// println("'%s' == '%s' (%s == %s) (%s == %s)".format(x, y, x.hashCode, y.hashCode, x.##, y.##)) + assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) +// assert(x.## == y.##, "%s != %s".format(x.getClass, y.getClass)) Disable until Double.## is fixed (SI-5640) + } } -} \ No newline at end of file +} -- cgit v1.2.3 From 03f32bd22f85bf3a6f8824dbe4a7a989e717c071 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 2 Apr 2012 08:23:57 -0700 Subject: Fix for SI-3272. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "References to the type parameters in object-private or object-protected values, variables, or methods (§5.2) of the class are not checked for their variance position." Review by @odersky. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 15 ++++++----- test/files/neg/variances.check | 19 ++++++++------ test/files/neg/variances.scala | 29 +++++++++++++++------- test/files/pos/t3272.scala | 8 ++++++ 4 files changed, 48 insertions(+), 23 deletions(-) create mode 100644 test/files/pos/t3272.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 045614e773..54b711cebc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -951,17 +951,20 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R override def traverse(tree: Tree) { tree match { - case ClassDef(_, _, _, _) | - TypeDef(_, _, _, _) => + case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => validateVariance(tree.symbol) super.traverse(tree) // ModuleDefs need not be considered because they have been eliminated already case ValDef(_, _, _, _) => - validateVariance(tree.symbol) + if (!tree.symbol.hasLocalFlag) + validateVariance(tree.symbol) case DefDef(_, _, tparams, vparamss, _, _) => - validateVariance(tree.symbol) - traverseTrees(tparams) - traverseTreess(vparamss) + // No variance check for object-private/protected methods/values. + if (!tree.symbol.hasLocalFlag) { + validateVariance(tree.symbol) + traverseTrees(tparams) + traverseTreess(vparamss) + } case Template(_, _, _) => super.traverse(tree) case _ => diff --git a/test/files/neg/variances.check b/test/files/neg/variances.check index dc72b05e1e..0643e533b7 100644 --- a/test/files/neg/variances.check +++ b/test/files/neg/variances.check @@ -1,16 +1,19 @@ variances.scala:4: error: covariant type A occurs in contravariant position in type test.Vector[A] of value x - def append(x: Vector[A]): Vector[A] + def append(x: Vector[A]): Vector[A] ^ -variances.scala:14: error: covariant type A occurs in contravariant position in type A of value a - private[this] def setA(a : A) = this.a = a - ^ -variances.scala:16: error: covariant type A occurs in invariant position in supertype test.C[A] of object Baz +variances.scala:18: error: covariant type A occurs in contravariant position in type A of value a + private def setA3(a : A) = this.a = a + ^ +variances.scala:19: error: covariant type A occurs in contravariant position in type A of value a + protected def setA4(a : A) = this.a = a + ^ +variances.scala:21: error: covariant type A occurs in invariant position in supertype test.C[A] of object Baz object Baz extends C[A] ^ -variances.scala:63: error: covariant type A occurs in contravariant position in type => test.Covariant.T[A]{val m: A => A} of value x +variances.scala:74: error: covariant type A occurs in contravariant position in type => test.Covariant.T[A]{val m: A => A} of value x val x: T[A] { ^ -variances.scala:79: error: covariant type T occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo +variances.scala:90: error: covariant type T occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo def foo: B[A] ^ -5 errors found +6 errors found diff --git a/test/files/neg/variances.scala b/test/files/neg/variances.scala index 726bc35270..10ca111cd0 100644 --- a/test/files/neg/variances.scala +++ b/test/files/neg/variances.scala @@ -1,7 +1,7 @@ package test trait Vector[+A] { - def append(x: Vector[A]): Vector[A] + def append(x: Vector[A]): Vector[A] private[this] def append3(x: Vector[A]): Vector[A] = append(x) } @@ -11,19 +11,27 @@ object Covariant { class Foo[+A] { private[this] var a : A = _ def getA : A = a - private[this] def setA(a : A) = this.a = a - + // allowed + private[this] def setA1(a : A) = this.a = a + protected[this] def setA2(a : A) = this.a = a + // forbidden + private def setA3(a : A) = this.a = a + protected def setA4(a : A) = this.a = a + object Baz extends C[A] trait Convert[B] { def b2a(b : B) : A - def doit(b : B) = setA(b2a(b)) + def doit1(b : B) = setA1(b2a(b)) + def doit2(b : B) = setA2(b2a(b)) + def doit3(b : B) = setA3(b2a(b)) + def doit4(b : B) = setA4(b2a(b)) } } class Foo2[+A] { private[this] var a : A = _ def getA : A = a - private[this] def setA(a : A) = this.a = a - + private[this] def setA(a : A) = this.a = a + { trait Convert[B] { def b2a(b : B) : A @@ -35,8 +43,8 @@ object Covariant { class Foo3[+A] { private[this] var a : A = _ def getA : A = a - private[this] def setA(a : A) = this.a = a - + private[this] def setA(a : A) = this.a = a + private[this] trait Convert[B] { def b2a(b : B) : A def doit(b : B) = setA(b2a(b)) @@ -54,7 +62,10 @@ object Covariant { } def main(args : Array[String]) { val test = new Test - test.c.doit(test.b) + test.c.doit1(test.b) + test.c.doit2(test.b) + test.c.doit3(test.b) + test.c.doit4(test.b) val x : java.lang.Character = test.a.getA Console.println("XXX " + x) } diff --git a/test/files/pos/t3272.scala b/test/files/pos/t3272.scala new file mode 100644 index 0000000000..8efd5ded2c --- /dev/null +++ b/test/files/pos/t3272.scala @@ -0,0 +1,8 @@ +trait A { + trait C[+T] { + protected[this] def f(t: T) {} + } + trait D[T] extends C[T] { + def g(t: T) { f(t) } + } +} -- cgit v1.2.3 From 457172dca3f3fea505f2421a99f86976141c7e75 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Tue, 3 Apr 2012 10:17:33 +0200 Subject: Remedies Try/Either signature disparity for source compat. w/ Akka --- .../scala/concurrent/ConcurrentPackageObject.scala | 27 ++++---- .../scala/concurrent/ExecutionContext.scala | 1 - src/library/scala/concurrent/Future.scala | 81 +++++++++++----------- src/library/scala/concurrent/Promise.scala | 17 ++--- .../concurrent/impl/ExecutionContextImpl.scala | 1 - src/library/scala/concurrent/impl/Future.scala | 9 +-- src/library/scala/concurrent/impl/Promise.scala | 46 ++++++------ src/library/scala/util/Try.scala | 53 +++++++++----- test/files/jvm/scala-concurrent-tck.scala | 80 ++++++++++++++++++++- 9 files changed, 197 insertions(+), 118 deletions(-) (limited to 'src') diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala index f4744a8757..0f882540f2 100644 --- a/src/library/scala/concurrent/ConcurrentPackageObject.scala +++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala @@ -8,11 +8,8 @@ package scala.concurrent - - import java.util.concurrent.{ Executors, ExecutorService, ThreadFactory } import scala.concurrent.forkjoin.{ ForkJoinPool, ForkJoinWorkerThread } -import scala.util.{ Try, Success, Failure } import scala.concurrent.util.Duration import ConcurrentPackageObject._ @@ -40,16 +37,16 @@ abstract class ConcurrentPackageObject { case _ => true } - private[concurrent] def resolve[T](source: Try[T]): Try[T] = source match { - case Failure(t: scala.runtime.NonLocalReturnControl[_]) => Success(t.value.asInstanceOf[T]) - case Failure(t: scala.util.control.ControlThrowable) => Failure(new ExecutionException("Boxed ControlThrowable", t)) - case Failure(t: InterruptedException) => Failure(new ExecutionException("Boxed InterruptedException", t)) - case Failure(e: Error) => Failure(new ExecutionException("Boxed Error", e)) + private[concurrent] def resolve[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { + case Left(t: scala.runtime.NonLocalReturnControl[_]) => Right(t.value.asInstanceOf[T]) + case Left(t: scala.util.control.ControlThrowable) => Left(new ExecutionException("Boxed ControlThrowable", t)) + case Left(t: InterruptedException) => Left(new ExecutionException("Boxed InterruptedException", t)) + case Left(e: Error) => Left(new ExecutionException("Boxed Error", e)) case _ => source } private[concurrent] def resolver[T] = - resolverFunction.asInstanceOf[PartialFunction[Throwable, Try[T]]] + resolverFunction.asInstanceOf[PartialFunction[Throwable, Either[Throwable, T]]] /* concurrency constructs */ @@ -104,11 +101,11 @@ private[concurrent] object ConcurrentPackageObject { // compiling a subset of sources; it seems that the wildcard is not // properly handled, and you get messages like "type _$1 defined twice". // This is consistent with other package object breakdowns. - private val resolverFunction: PartialFunction[Throwable, Try[_]] = { - case t: scala.runtime.NonLocalReturnControl[_] => Success(t.value) - case t: scala.util.control.ControlThrowable => Failure(new ExecutionException("Boxed ControlThrowable", t)) - case t: InterruptedException => Failure(new ExecutionException("Boxed InterruptedException", t)) - case e: Error => Failure(new ExecutionException("Boxed Error", e)) - case t => Failure(t) + private val resolverFunction: PartialFunction[Throwable, Either[Throwable, _]] = { + case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value) + case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) + case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) + case e: Error => Left(new ExecutionException("Boxed Error", e)) + case t => Left(t) } } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index f639f76dc9..16d9a1f980 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -13,7 +13,6 @@ package scala.concurrent import java.util.concurrent.atomic.{ AtomicInteger } import java.util.concurrent.{ Executors, Future => JFuture, Callable } import scala.concurrent.util.Duration -import scala.util.{ Try, Success, Failure } import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveTask => FJTask, RecursiveAction, ForkJoinWorkerThread } import scala.collection.generic.CanBuildFrom import collection._ diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 8cecadc605..5f703ac23b 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -17,7 +17,6 @@ import java.util.{ LinkedList => JLinkedList } import java.{ lang => jl } import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } -import scala.util.{ Try, Success, Failure } import scala.concurrent.util.Duration import scala.Option @@ -97,8 +96,8 @@ self => * $multipleCallbacks */ def onSuccess[U](pf: PartialFunction[T, U]): this.type = onComplete { - case Failure(t) => // do nothing - case Success(v) => if (pf isDefinedAt v) pf(v) else { /*do nothing*/ } + case Left(t) => // do nothing + case Right(v) => if (pf isDefinedAt v) pf(v) else { /*do nothing*/ } } /** When this future is completed with a failure (i.e. with a throwable), @@ -114,8 +113,8 @@ self => * $multipleCallbacks */ def onFailure[U](callback: PartialFunction[Throwable, U]): this.type = onComplete { - case Failure(t) => if (isFutureThrowable(t) && callback.isDefinedAt(t)) callback(t) else { /*do nothing*/ } - case Success(v) => // do nothing + case Left(t) => if (isFutureThrowable(t) && callback.isDefinedAt(t)) callback(t) else { /*do nothing*/ } + case Right(v) => // do nothing } /** When this future is completed, either through an exception, a timeout, or a value, @@ -126,7 +125,7 @@ self => * * $multipleCallbacks */ - def onComplete[U](func: Try[T] => U): this.type + def onComplete[U](func: Either[Throwable, T] => U): this.type /* Miscellaneous */ @@ -151,7 +150,7 @@ self => * if it contains a valid result, or `Some(Failure(error))` if it contains * an exception. */ - def value: Option[Try[T]] + def value: Option[Either[Throwable, T]] /* Projections */ @@ -175,8 +174,8 @@ self => val p = newPromise[Throwable] onComplete { - case Failure(t) => p success t - case Success(v) => p failure noSuchElem(v) + case Left(t) => p success t + case Right(v) => p failure noSuchElem(v) } p.future @@ -190,8 +189,8 @@ self => * Will not be called if the future fails. */ def foreach[U](f: T => U): Unit = onComplete { - case Success(r) => f(r) - case Failure(_) => // do nothing + case Right(r) => f(r) + case Left(_) => // do nothing } /** Creates a new future by applying a function to the successful result of @@ -204,8 +203,8 @@ self => val p = newPromise[S] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try p success f(v) catch { case t => p complete resolver(t) @@ -226,12 +225,12 @@ self => val p = newPromise[S] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try { f(v) onComplete { - case Failure(t) => p failure t - case Success(v) => p success v + case Left(t) => p failure t + case Right(v) => p success v } } catch { case t: Throwable => p complete resolver(t) @@ -261,8 +260,8 @@ self => val p = newPromise[T] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try { if (pred(v)) p success v else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) @@ -310,8 +309,8 @@ self => val p = newPromise[S] onComplete { - case Failure(t) => p failure t - case Success(v) => + case Left(t) => p failure t + case Right(v) => try { if (pf.isDefinedAt(v)) p success pf(v) else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) @@ -339,7 +338,7 @@ self => val p = newPromise[U] onComplete { - case Failure(t) if pf isDefinedAt t => + case Left(t) if pf isDefinedAt t => try { p success pf(t) } catch { case t: Throwable => p complete resolver(t) } case otherwise => p complete otherwise @@ -365,7 +364,7 @@ self => val p = newPromise[U] onComplete { - case Failure(t) if pf isDefinedAt t => + case Left(t) if pf isDefinedAt t => try { p completeWith pf(t) } catch { @@ -389,8 +388,8 @@ self => val p = newPromise[(T, U)] this onComplete { - case Failure(t) => p failure t - case Success(r) => that onSuccess { + case Left(t) => p failure t + case Right(r) => that onSuccess { case r2 => p success ((r, r2)) } } @@ -420,11 +419,11 @@ self => val p = newPromise[U] onComplete { - case Failure(t) => that onComplete { - case Failure(_) => p failure t - case Success(v) => p success v + case Left(t) => that onComplete { + case Left(_) => p failure t + case Right(v) => p success v } - case Success(v) => p success v + case Right(v) => p success v } p.future @@ -437,12 +436,12 @@ self => val p = newPromise[S] onComplete { - case l: Failure[_] => p complete l.asInstanceOf[Try[S]] - case Success(t) => + case l: Left[Throwable, _] => p complete l.asInstanceOf[Either[Throwable, S]] + case Right(t) => p complete (try { - Success(impl.Future.boxedType(m.erasure).cast(t).asInstanceOf[S]) + Right(impl.Future.boxedType(m.erasure).cast(t).asInstanceOf[S]) } catch { - case e: ClassCastException => Failure(e) + case e: ClassCastException => Left(e) }) } @@ -472,7 +471,7 @@ self => * } * }}} */ - def andThen[U](pf: PartialFunction[Try[T], U]): Future[T] = { + def andThen[U](pf: PartialFunction[Either[Throwable, T], U]): Future[T] = { val p = newPromise[T] onComplete { @@ -500,9 +499,9 @@ self => def either[U >: T](that: Future[U]): Future[U] = { val p = self.newPromise[U] - val completePromise: PartialFunction[Try[U], _] = { - case Failure(t) => p tryFailure t - case Success(v) => p trySuccess v + val completePromise: PartialFunction[Either[Throwable, U], _] = { + case Left(t) => p tryFailure t + case Right(v) => p trySuccess v } self onComplete completePromise @@ -541,7 +540,7 @@ object Future { def firstCompletedOf[T](futures: Traversable[Future[T]])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() - val completeFirst: Try[T] => Unit = p tryComplete _ + val completeFirst: Either[Throwable, T] => Unit = p tryComplete _ futures.foreach(_ onComplete completeFirst) p.future @@ -554,14 +553,14 @@ object Future { else { val result = Promise[Option[T]]() val ref = new AtomicInteger(futures.size) - val search: Try[T] => Unit = v => try { + val search: Either[Throwable, T] => Unit = v => try { v match { - case Success(r) => if (predicate(r)) result tryComplete Success(Some(r)) + case Right(r) => if (predicate(r)) result tryComplete Right(Some(r)) case _ => } } finally { if (ref.decrementAndGet == 0) - result tryComplete Success(None) + result tryComplete Right(None) } futures.foreach(_ onComplete search) diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 61e21606e6..8f2bce5d1a 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -8,11 +8,6 @@ package scala.concurrent -import scala.util.{ Try, Success, Failure } - - - - /** Promise is an object which can be completed with a value or failed * with an exception. * @@ -40,7 +35,7 @@ trait Promise[T] { * * $promiseCompletion */ - def complete(result:Try[T]): this.type = if (tryComplete(result)) this else throwCompleted + def complete(result: Either[Throwable, T]): this.type = if (tryComplete(result)) this else throwCompleted /** Tries to complete the promise with either a value or the exception. * @@ -48,7 +43,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryComplete(result: Try[T]): Boolean + def tryComplete(result: Either[Throwable, T]): Boolean /** Completes this promise with the specified future, once that future is completed. * @@ -75,7 +70,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def trySuccess(value: T): Boolean = tryComplete(Success(value)) + def trySuccess(value: T): Boolean = tryComplete(Right(value)) /** Completes the promise with an exception. * @@ -93,7 +88,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryFailure(t: Throwable): Boolean = tryComplete(Failure(t)) + def tryFailure(t: Throwable): Boolean = tryComplete(Left(t)) /** Wraps a `Throwable` in an `ExecutionException` if necessary. TODO replace with `resolver` from scala.concurrent * @@ -118,11 +113,11 @@ object Promise { /** Creates an already completed Promise with the specified exception */ - def failed[T](exception: Throwable)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Failure(exception)) + def failed[T](exception: Throwable)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Left(exception)) /** Creates an already completed Promise with the specified result */ - def successful[T](result: T)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Success(result)) + def successful[T](result: T)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Right(result)) } diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 2cfd6f22cd..9a94bfca4f 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -13,7 +13,6 @@ package scala.concurrent.impl import java.util.concurrent.{Callable, ExecutorService, Executors, ThreadFactory} import scala.concurrent.forkjoin._ import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} -import scala.util.{ Try, Success, Failure } import scala.concurrent.util.{ Duration } diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 1111aa4753..615ab061a5 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -11,11 +11,8 @@ package scala.concurrent.impl import scala.concurrent.{Awaitable, ExecutionContext} -import scala.util.{ Try, Success, Failure } import scala.collection.mutable.Stack - - private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { implicit def executor: ExecutionContext @@ -36,9 +33,9 @@ private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awa * if it contains a valid result, or Some(Left(error)) if it contains * an exception. */ - def value: Option[Try[T]] + def value: Option[Either[Throwable, T]] - def onComplete[U](func: Try[T] => U): this.type + def onComplete[U](func: Either[Throwable, T] => U): this.type } @@ -67,7 +64,7 @@ object Future { def run = { promise complete { try { - Success(body) + Right(body) } catch { case e => scala.concurrent.resolver(e) } diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index f05e306088..f7e073cb78 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -15,7 +15,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} //import scala.util.continuations._ import scala.concurrent.util.Duration -import scala.util.Try import scala.util import scala.annotation.tailrec //import scala.concurrent.NonDeterministic @@ -84,18 +83,18 @@ object Promise { * * [adriaan] it's unsound to make FState covariant (tryComplete won't type check) */ - sealed trait FState[T] { def value: Option[Try[T]] } + sealed trait FState[T] { def value: Option[Either[Throwable, T]] } - case class Pending[T](listeners: List[Try[T] => Any] = Nil) extends FState[T] { - def value: Option[Try[T]] = None + case class Pending[T](listeners: List[Either[Throwable, T] => Any] = Nil) extends FState[T] { + def value: Option[Either[Throwable, T]] = None } - case class Success[T](value: Option[util.Success[T]] = None) extends FState[T] { - def result: T = value.get.get + case class Success[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def result: T = value.get.right.get } - case class Failure[T](value: Option[util.Failure[T]] = None) extends FState[T] { - def exception: Throwable = value.get.exception + case class Failure[T](value: Option[Either[Throwable, T]] = None) extends FState[T] { + def exception: Throwable = value.get.left.get } private val emptyPendingValue = Pending[Nothing](Nil) @@ -136,11 +135,11 @@ object Promise { def result(atMost: Duration)(implicit permit: CanAwait): T = ready(atMost).value.get match { - case util.Failure(e) => throw e - case util.Success(r) => r + case Left(e) => throw e + case Right(r) => r } - def value: Option[Try[T]] = getState.value + def value: Option[Either[Throwable, T]] = getState.value @inline private[this] final def updater = AbstractPromise.updater.asInstanceOf[AtomicReferenceFieldUpdater[AbstractPromise, FState[T]]] @@ -151,16 +150,16 @@ object Promise { @inline protected final def getState: FState[T] = updater.get(this) - def tryComplete(value: Try[T]): Boolean = { - val callbacks: List[Try[T] => Any] = { + def tryComplete(value: Either[Throwable, T]): Boolean = { + val callbacks: List[Either[Throwable, T] => Any] = { try { @tailrec - def tryComplete(v: Try[T]): List[Try[T] => Any] = { + def tryComplete(v: Either[Throwable, T]): List[Either[Throwable, T] => Any] = { getState match { case cur @ Pending(listeners) => val newState = - if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) - else Success(Some(v.asInstanceOf[util.Success[T]])) + if (v.isLeft) Failure(Some(v.asInstanceOf[Left[Throwable, T]])) + else Success(Some(v.asInstanceOf[Right[Throwable, T]])) if (updateState(cur, newState)) listeners else tryComplete(v) @@ -184,7 +183,7 @@ object Promise { } } - def onComplete[U](func: Try[T] => U): this.type = { + def onComplete[U](func: Either[Throwable, T] => U): this.type = { @tailrec // Returns whether the future has already been completed or not def tryAddCallback(): Boolean = { val cur = getState @@ -206,7 +205,7 @@ object Promise { this } - private final def notifyCompleted(func: Try[T] => Any, result: Try[T]) { + private final def notifyCompleted(func: Either[Throwable, T] => Any, result: Either[Throwable, T]) { try { func(result) } catch { @@ -219,12 +218,13 @@ object Promise { * * Useful in Future-composition when a value to contribute is already available. */ - final class KeptPromise[T](suppliedValue: Try[T])(implicit val executor: ExecutionContext) extends Promise[T] { + final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContext) extends Promise[T] { + val value = Some(resolve(suppliedValue)) - def tryComplete(value: Try[T]): Boolean = false + def tryComplete(value: Either[Throwable, T]): Boolean = false - def onComplete[U](func: Try[T] => U): this.type = { + def onComplete[U](func: Either[Throwable, T] => U): this.type = { val completedAs = value.get Future.dispatchFuture(executor, { () => func(completedAs) @@ -235,8 +235,8 @@ object Promise { def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this def result(atMost: Duration)(implicit permit: CanAwait): T = value.get match { - case util.Failure(e) => throw e - case util.Success(r) => r + case Left(e) => throw e + case Right(r) => r } } diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index c9bde81317..efa2fcabb8 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -101,9 +101,9 @@ sealed abstract class Try[+T] { } -final case class Failure[+T](val exception: Throwable) extends Try[T] { - def isFailure = true - def isSuccess = false +final class Failure[+T](val exception: Throwable) extends Try[T] { + def isFailure: Boolean = true + def isSuccess: Boolean = false def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = { try { if (rescueException.isDefinedAt(exception)) rescueException(exception) else this @@ -129,30 +129,49 @@ final case class Failure[+T](val exception: Throwable) extends Try[T] { } -final case class Success[+T](r: T) extends Try[T] { - def isFailure = false - def isSuccess = true - def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(r) - def get = r +final class Success[+T](value: T) extends Try[T] { + def isFailure: Boolean = false + def isSuccess: Boolean = true + def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value) + def get = value def flatMap[U](f: T => Try[U]): Try[U] = - try f(r) + try f(value) catch { case e => Failure(e) } - def flatten[U](implicit ev: T <:< Try[U]): Try[U] = r - def foreach[U](f: T => U): Unit = f(r) - def map[U](f: T => U): Try[U] = Try[U](f(r)) + def flatten[U](implicit ev: T <:< Try[U]): Try[U] = value + def foreach[U](f: T => U): Unit = f(value) + def map[U](f: T => U): Try[U] = Try[U](f(value)) def collect[U](pf: PartialFunction[T, U]): Try[U] = - if (pf isDefinedAt r) Success(pf(r)) - else Failure[U](new NoSuchElementException("Partial function not defined at " + r)) + if (pf isDefinedAt value) Success(pf(value)) + else Failure[U](new NoSuchElementException("Partial function not defined at " + value)) def filter(p: T => Boolean): Try[T] = - if (p(r)) this - else Failure(new NoSuchElementException("Predicate does not hold for " + r)) + if (p(value)) this + else Failure(new NoSuchElementException("Predicate does not hold for " + value)) def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this - def exists(p: T => Boolean): Boolean = p(r) + def exists(p: T => Boolean): Boolean = p(value) def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed")) } +object Failure { + def apply[T](e: Throwable): Failure[T] = new Failure(e) + def unapply(scrutinizee: Any): Option[Throwable] = scrutinizee match { + case Right(_) => None + case Left(e) => Some(e.asInstanceOf[Throwable]) + case s: Success[_] => None + case f: Failure[_] => Some(f.exception) + } +} + +object Success { + def apply[T](value: T): Success[T] = new Success(value) + def unapply[T](scrutinizee: Any): Option[T] = scrutinizee match { + case Right(v) => Some(v.asInstanceOf[T]) + case Left(_) => None + case s: Success[_] => Some(s.get.asInstanceOf[T]) + case f: Failure[Throwable] => None + } +} object Try { diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 75e2b92ff6..b3470d275d 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -1,6 +1,3 @@ - - - import scala.concurrent.{ Future, Promise, @@ -398,6 +395,80 @@ trait Exceptions extends TestBase { } +trait TryEitherExtractor extends TestBase { + + import scala.util.{Try, Success, Failure} + + def testSuccessMatch(): Unit = once { + done => + val thisIsASuccess = Success(42) + thisIsASuccess match { + case Success(v) => + done() + assert(v == 42) + case Failure(e) => + done() + assert(false) + case other => + done() + assert(false) + } + } + + def testRightMatch(): Unit = once { + done => + val thisIsNotASuccess: Right[Throwable, Int] = Right(43) + thisIsNotASuccess match { + case Success(v) => + done() + assert(v == 43) + case Failure(e) => + done() + assert(false) + case other => + done() + assert(false) + } + } + + def testFailureMatch(): Unit = once { + done => + val thisIsAFailure = Failure(new Exception("I'm an exception")) + thisIsAFailure match { + case Success(v) => + done() + assert(false) + case Failure(e) => + done() + assert(e.getMessage == "I'm an exception") + case other => + done() + assert(false) + } + } + + def testLeftMatch(): Unit = once { + done => + val thisIsNotAFailure: Left[Throwable, Int] = Left(new Exception("I'm an exception")) + thisIsNotAFailure match { + case Success(v) => + done() + assert(false) + case Failure(e) => + done() + assert(e.getMessage == "I'm an exception") + case other => + done() + assert(false) + } + + } + + testSuccessMatch() + testRightMatch() + testFailureMatch() + testLeftMatch() +} object Test extends App @@ -406,8 +477,11 @@ with FutureCombinators with FutureProjections with Promises with Exceptions +with TryEitherExtractor { System.exit(0) } + + -- cgit v1.2.3 From a81c907bd3b6d5e57572a652ee7865a90da9a76e Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Mon, 2 Apr 2012 23:24:58 +0200 Subject: Added presentation memory leak test. --- .../nsc/interactive/tests/InteractiveTest.scala | 1 + .../nsc/interactive/tests/core/AskCommand.scala | 4 +- test/files/presentation/memory-leaks.check | 14 +++ .../memory-leaks/MemoryLeaksTest.scala | 125 +++++++++++++++++++++ 4 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 test/files/presentation/memory-leaks.check create mode 100644 test/files/presentation/memory-leaks/MemoryLeaksTest.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala index 7be115e777..9dc2a8de10 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -55,6 +55,7 @@ abstract class InteractiveTest with AskShutdown with AskReload with AskLoadedTyped + with AskType with PresentationCompilerInstance with CoreTestDefs with InteractiveTestSettings { self => diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala index 35d6723818..657ef23eed 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala @@ -101,13 +101,13 @@ trait AskTypeAt extends AskCommand { trait AskType extends AskCommand { import compiler.Tree - private[tests] def askType(source: SourceFile, forceReload: Boolean)(implicit reporter: Reporter): Response[Tree] = { + protected def askType(source: SourceFile, forceReload: Boolean)(implicit reporter: Reporter): Response[Tree] = { ask { compiler.askType(source, forceReload, _) } } - private[tests] def askType(sources: Seq[SourceFile], forceReload: Boolean)(implicit reporter: Reporter): Seq[Response[Tree]] = { + protected def askType(sources: Seq[SourceFile], forceReload: Boolean)(implicit reporter: Reporter): Seq[Response[Tree]] = { for(source <- sources) yield askType(source, forceReload) } diff --git a/test/files/presentation/memory-leaks.check b/test/files/presentation/memory-leaks.check new file mode 100644 index 0000000000..9d8cbb4da4 --- /dev/null +++ b/test/files/presentation/memory-leaks.check @@ -0,0 +1,14 @@ +reload: Trees.scala, Typers.scala, Types.scala +reload: Trees.scala +reload: Types.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +reload: Typers.scala +No leaks detected. diff --git a/test/files/presentation/memory-leaks/MemoryLeaksTest.scala b/test/files/presentation/memory-leaks/MemoryLeaksTest.scala new file mode 100644 index 0000000000..e24f36d7b6 --- /dev/null +++ b/test/files/presentation/memory-leaks/MemoryLeaksTest.scala @@ -0,0 +1,125 @@ +import java.io.PrintWriter +import java.io.FileOutputStream +import java.util.Calendar + +import scala.tools.nsc.interactive.tests._ +import scala.tools.nsc.util._ +import scala.tools.nsc.io._ + +/** This test runs the presentation compiler on the Scala compiler project itself and records memory consumption. + * + * The test scenario is to open Typers, Trees and Types, then repeatedly add and remove one character + * in Typers.scala. Each step causes the parser, namer, and type checker to run. + * + * At each step we record the memory usage after the GC has run. At the end of the test, + * simple linear regression is used to compute the straight line that best fits the + * curve, and if the slope is higher than 1 (meaning a leak of 1MB/run), we fail the test. + * + * The Scala compiler sources are assumed to be under 'basedir/src/compiler'. + * + * The individual data points are saved under 'usedMem-.txt', under the test project + * directory. Use the cool graph-it.R (https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core.tests/graph-it.R) + * script to see the memory curve for the given test run. + */ +object Test extends InteractiveTest { + final val mega = 1024 * 1024 + + override def main(args: Array[String]) { + memoryConsumptionTest() + compiler.askShutdown() + } + + def batchSource(name: String) = + new BatchSourceFile(AbstractFile.getFile(name)) + + def memoryConsumptionTest() { + val N = 10 + val filename = "usedmem-%tF.txt".format(Calendar.getInstance.getTime) + + val typerUnit = AbstractFile.getFile(baseDir.parent.parent.parent.parent / "src/compiler/scala/tools/nsc/typechecker/Typers.scala") + val typesUnit = AbstractFile.getFile(baseDir.parent.parent.parent.parent / "src/compiler/scala/reflect/internal/Types.scala") + val treesUnit = AbstractFile.getFile(baseDir.parent.parent.parent.parent / "src/compiler/scala/reflect/internal/Trees.scala") + + askReload(Seq(new BatchSourceFile(typerUnit), new BatchSourceFile(typesUnit), new BatchSourceFile(treesUnit))) + typeCheckWith(treesUnit, new String(treesUnit.toCharArray)) + typeCheckWith(typesUnit, new String(typesUnit.toCharArray)) + + val originalTyper = new String(typerUnit.toCharArray) + + val (prefix, postfix) = originalTyper.splitAt(originalTyper.indexOf("import global._")) + val changedTyper = prefix + " a\n " + postfix + + val usedMem = for (i <- 1 to N) yield { + val src = if (i % 2 == 0) originalTyper else changedTyper + + val usedMem = withGC { + typeCheckWith(typerUnit, src) + } + +// println("UsedMem:\t%d\t%d".format(i, usedMem / mega)) + usedMem / mega // report size in MB + } + + // println("=" * 80) + + val outputFile = new PrintWriter(new FileOutputStream(filename)) + outputFile.println("\tusedMem") + for ((dataPoint, i) <- usedMem.zipWithIndex) { + outputFile.println("%d\t%d".format(i, dataPoint)) + } + outputFile.close() + // drop the first two measurements, since the compiler needs some memory when initializing + val (a, b) = linearModel((3L to N).toSeq, usedMem.drop(2)) + //println("LinearModel: constant: %.4f\tslope:%.4f".format(a, b)) + + if (b > 1.0) + println("Rate of memory consumption is alarming! %.4f MB/run".format(b)) + else + println("No leaks detected.") + } + + private def typeCheckWith(file: AbstractFile, src: String) = { + val sourceFile = new BatchSourceFile(file, src.toCharArray) + askReload(Seq(sourceFile)) + askLoadedTyped(sourceFile).get // block until it's here + } + + + /** Return the linear model of these values, (a, b). First value is the constant factor, + * second value is the slope, i.e. `y = a + bx` + * + * The linear model of a set of points is a straight line that minimizes the square distance + * between the each point and the line. + * + * See: http://en.wikipedia.org/wiki/Simple_linear_regression + */ + def linearModel(xs: Seq[Long], ys: Seq[Long]): (Double, Double) = { + require(xs.length == ys.length) + + def mean(v: Seq[Long]): Double = v.sum.toDouble / v.length + + val meanXs = mean(xs) + val meanYs = mean(ys) + + val beta = (mean((xs, ys).zipped.map(_ * _)) - meanXs * meanYs) / (mean(xs.map(x => x * x)) - meanXs * meanXs) + val alfa = meanYs - beta * meanXs + + (alfa, beta) + } + + /** Run the given closure and return the amount of used memory at the end of its execution. + * + * Runs the GC before and after the execution of `f'. + */ + def withGC(f: => Unit): Long = { + val r = Runtime.getRuntime + System.gc() + + f; + + System.gc() + + r.totalMemory() - r.freeMemory() + } + +} \ No newline at end of file -- cgit v1.2.3 From 19b6ad5ee41d8a68612a58b925397280f08bc931 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 3 Apr 2012 12:18:46 +0200 Subject: avoid memory leak by avoiding default arg --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2aff00f6a5..09fb39125e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2199,7 +2199,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { vparams map {p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe} } - def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = { + def mkParams(methodSym: Symbol, formals: List[Type]/* = deriveFormals*/) = { selOverride match { case None if targs.isEmpty => MissingParameterTypeAnonMatchError(tree, pt); (Nil, EmptyTree) case None => @@ -2225,7 +2225,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // rig the show so we can get started typing the method body -- later we'll correct the infos... anonClass setInfo ClassInfoType(List(ObjectClass.tpe, pt, SerializableClass.tpe), newScope, anonClass) val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym) + val (paramSyms, selector) = mkParams(methodSym, deriveFormals) if (selector eq EmptyTree) EmptyTree else { @@ -2289,7 +2289,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def isDefinedAtMethod = { val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym) + val (paramSyms, selector) = mkParams(methodSym, deriveFormals) if (selector eq EmptyTree) EmptyTree else { val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) -- cgit v1.2.3 From a7f68ce32c35c73af855eab26635251249ac171a Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sun, 1 Apr 2012 08:40:39 -0700 Subject: Unify "object Foo" and "Foo.type". The source of many bugs over the years is that the first is represented as a TypeRef and the second a SingleType. Over a great period of time I figured out how to shield us from the more obvious bug manifestations, but a recent comment by adriaan jarred me into realizing that we can fix it at the source. This commit changes <:< and =:= to recognize when those two representations are being compared and to treat them as equivalent regardless of which is on the left. The reason I don't quash one representation entirely is that a fair bit of code depends on singleton types having an underlying type which is not the same, and regardless of that it would entail more changes and more risk. The change allows removing the type inference conditions which worried about this, and also fixes SI-4910. scala> val t1 = typeRef(ScalaPackageClass.thisType, NoneModule.moduleClass, Nil) t1: $r.intp.global.Type = None.type scala> val t2 = t1.narrow t2: $r.intp.global.Type = None.type scala> (t1.getClass, t2.getClass) res20: (Class[?0], Class[?0]) forSome { type ?0 <: $r.intp.global.Type; type ?0 <: $r.intp.global.Type } = (class scala.reflect.internal.Types$ModuleTypeRef,class scala.reflect.internal.Types$UniqueSingleType) scala> ((t1 =:= t2, t2 =:= t1, t1 <:< t2, t2 <:< t1)) res21: (Boolean, Boolean, Boolean, Boolean) = (true,true,true,true) --- src/compiler/scala/reflect/internal/Types.scala | 10 +++++++--- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 3 +-- src/compiler/scala/tools/nsc/typechecker/Namers.scala | 5 +---- test/files/pos/t4910.scala | 6 ++++++ 4 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 test/files/pos/t4910.scala (limited to 'src') diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 5afa5343ed..786b680ff8 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -5059,6 +5059,8 @@ trait Types extends api.Types { self: SymbolTable => case (_, tv @ TypeVar(_,_)) => tv.registerTypeSelection(tr2.sym, tr1) case _ => false }) + case _: SingleType => + return isSameType2(tp2, tp1) // put singleton type on the left, caught below case _ => } case tt1: ThisType => @@ -5071,6 +5073,8 @@ trait Types extends api.Types { self: SymbolTable => tp2 match { case st2: SingleType => if (equalSymsAndPrefixes(st1.sym, st1.pre, st2.sym, st2.pre)) return true + case TypeRef(pre2, sym2, Nil) => + if (sym2.isModuleClass && equalSymsAndPrefixes(st1.sym, st1.pre, sym2.sourceModule, pre2)) return true case _ => } case ct1: ConstantType => @@ -5481,7 +5485,7 @@ trait Types extends api.Types { self: SymbolTable => * - handle typerefs, refined types, notnull and singleton types. */ def fourthTry = tp1 match { - case tr1 @ TypeRef(_, sym1, _) => + case tr1 @ TypeRef(pre1, sym1, _) => sym1 match { case NothingClass => true case NullClass => @@ -5495,8 +5499,8 @@ trait Types extends api.Types { self: SymbolTable => if (isRaw(sym1, tr1.args)) isSubType(rawToExistential(tp1), tp2, depth) else if (sym1.isModuleClass) tp2 match { - case SingleType(_, sym2) => sym1 == sym2 - case _ => false + case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1.sourceModule, pre1, sym2, pre2) + case _ => false } else if (sym1.isRefinementClass) isSubType(sym1.info, tp2, depth) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index a1ca4904f4..dba31f7bca 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -484,8 +484,7 @@ trait Infer { else Some( if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass) else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass) - // this infers Foo.type instead of "object Foo" (see also widenIfNecessary) - else if (targ.typeSymbol.isModuleClass || ((opt.experimental || opt.virtPatmat) && tvar.constr.avoidWiden)) targ + else if ((opt.experimental || opt.virtPatmat) && tvar.constr.avoidWiden) targ else targ.widen ) )) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 8604366bf2..1b505d1e5d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -793,10 +793,7 @@ trait Namers extends MethodSynthesis { val tpe1 = dropRepeatedParamType(tpe.deconst) val tpe2 = tpe1.widen - // This infers Foo.type instead of "object Foo" - // See Infer#adjustTypeArgs for the polymorphic case. - if (tpe.typeSymbolDirect.isModuleClass) tpe1 - else if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag) + if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag) if (tpe2 <:< pt) tpe2 else tpe1 else if (isHidden(tpe)) tpe2 // In an attempt to make pattern matches involving method local vals diff --git a/test/files/pos/t4910.scala b/test/files/pos/t4910.scala new file mode 100644 index 0000000000..c66fd523f5 --- /dev/null +++ b/test/files/pos/t4910.scala @@ -0,0 +1,6 @@ +class A { + implicit object foo + // it compiles if we uncomment this + // implicit val bar = foo + implicitly[foo.type] +} -- cgit v1.2.3 From bb4935e92c26778a1d1096cd5cd66812a9122f66 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 4 Apr 2012 10:01:47 -0700 Subject: Added Option#fold. --- src/library/scala/Option.scala | 12 ++++++++++++ test/files/run/option-fold.check | 5 +++++ test/files/run/option-fold.scala | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 test/files/run/option-fold.check create mode 100644 test/files/run/option-fold.scala (limited to 'src') diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index 6db4904b93..2d87ccb261 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -130,6 +130,18 @@ sealed abstract class Option[+A] extends Product with Serializable { @inline final def map[B](f: A => B): Option[B] = if (isEmpty) None else Some(f(this.get)) + /** Returns the result of applying $f to this $option's + * value if the $option is nonempty. Otherwise, evaluates + * expression $ifEmpty. + * + * @note This is equivalent to `$option map f getOrElse ifEmpty`. + * + * @param ifEmpty the expression to evaluate if empty. + * @param f the function to apply if nonempty. + */ + @inline final def fold[B](ifEmpty: => B)(f: A => B): B = + if (isEmpty) ifEmpty else f(this.get) + /** Returns the result of applying $f to this $option's value if * this $option is nonempty. * Returns $none if this $option is empty. diff --git a/test/files/run/option-fold.check b/test/files/run/option-fold.check new file mode 100644 index 0000000000..4e3fe99f98 --- /dev/null +++ b/test/files/run/option-fold.check @@ -0,0 +1,5 @@ +List() +List(5) +-1 +0 +1 diff --git a/test/files/run/option-fold.scala b/test/files/run/option-fold.scala new file mode 100644 index 0000000000..d554ba4f9b --- /dev/null +++ b/test/files/run/option-fold.scala @@ -0,0 +1,19 @@ +object Test { + sealed class A + case object B extends A + case class C(x: Int) extends A + + def f[T](x: Option[T]) = x.fold(List.empty[T])(List(_)) + def g(x: Option[A]) = x.fold(-1) { + case B => 0 + case C(x) => x + } + + def main(args: Array[String]): Unit = { + println(f(None)) + println(f(Some(5))) + println(g(None)) + println(g(Some(B))) + println(g(Some(C(1)))) + } +} -- cgit v1.2.3 From 37eabf615afe3de9733ea41cc9c522df3e2a6b87 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 5 Apr 2012 10:31:22 -0700 Subject: Fix for continuations issue. Avoid explicit type arguments which don't conform to bounds where they could be successfully inferred. I had to disable one "neg" test which is no longer neg. Can anyone clue me in as to whether it is important? --- .../plugin/scala/tools/selectivecps/CPSUtils.scala | 2 ++ .../tools/selectivecps/SelectiveANFTransform.scala | 26 ++++++++++++------- test/disabled/continuations-neg/infer0.check | 4 +++ test/disabled/continuations-neg/infer0.scala | 12 +++++++++ test/files/continuations-neg/infer0.check | 4 --- test/files/continuations-neg/infer0.scala | 14 ---------- test/files/continuations-run/shift-pct.check | 25 ++++++++++++++++++ test/files/continuations-run/shift-pct.scala | 30 ++++++++++++++++++++++ 8 files changed, 90 insertions(+), 27 deletions(-) create mode 100644 test/disabled/continuations-neg/infer0.check create mode 100644 test/disabled/continuations-neg/infer0.scala delete mode 100644 test/files/continuations-neg/infer0.check delete mode 100644 test/files/continuations-neg/infer0.scala create mode 100644 test/files/continuations-run/shift-pct.check create mode 100644 test/files/continuations-run/shift-pct.scala (limited to 'src') diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 075009ce5e..67ea6e15f0 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -24,6 +24,7 @@ trait CPSUtils { val shift = newTermName("shift") val shiftR = newTermName("shiftR") val shiftSuffix = newTermName("$shift") + val shiftUnit0 = newTermName("shiftUnit0") val shiftUnit = newTermName("shiftUnit") val shiftUnitR = newTermName("shiftUnitR") } @@ -38,6 +39,7 @@ trait CPSUtils { lazy val ModCPS = definitions.getRequiredModule("scala.util.continuations") lazy val MethShiftUnit = definitions.getMember(ModCPS, cpsNames.shiftUnit) + lazy val MethShiftUnit0 = definitions.getMember(ModCPS, cpsNames.shiftUnit0) lazy val MethShiftUnitR = definitions.getMember(ModCPS, cpsNames.shiftUnitR) lazy val MethShift = definitions.getMember(ModCPS, cpsNames.shift) lazy val MethShiftR = definitions.getMember(ModCPS, cpsNames.shiftR) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 1189cc2e38..a6737573ea 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -310,15 +310,23 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with try { val Some((a, b)) = cpsR - - val res = localTyper.typed(atPos(tree.pos) { - Apply(TypeApply(gen.mkAttributedRef(MethShiftUnit), - List(TypeTree(plainTpe), TypeTree(a), TypeTree(b))), - List(expr)) - }) - return (stms, res) - - } catch { + /** Since shiftUnit is bounded [A,B,C>:B] this may not typecheck + * if C is overly specific. So if !(B <:< C), call shiftUnit0 + * instead, which takes only two type arguments. + */ + val conforms = a <:< b + val call = localTyper.typedPos(tree.pos)( + Apply( + TypeApply( + gen.mkAttributedRef( if (conforms) MethShiftUnit else MethShiftUnit0 ), + List(TypeTree(plainTpe), TypeTree(a)) ++ ( if (conforms) List(TypeTree(b)) else Nil ) + ), + List(expr) + ) + ) + return ((stms, call)) + } + catch { case ex:TypeError => unit.error(ex.pos, "cannot cps-transform expression " + tree + ": " + ex.msg) } diff --git a/test/disabled/continuations-neg/infer0.check b/test/disabled/continuations-neg/infer0.check new file mode 100644 index 0000000000..1dd072ef09 --- /dev/null +++ b/test/disabled/continuations-neg/infer0.check @@ -0,0 +1,4 @@ +infer0.scala:11: error: cannot cps-transform expression 8: type arguments [Int(8),String,Int] do not conform to method shiftUnit's type parameter bounds [A,B,C >: B] + test(8) + ^ +one error found diff --git a/test/disabled/continuations-neg/infer0.scala b/test/disabled/continuations-neg/infer0.scala new file mode 100644 index 0000000000..6d97d7504d --- /dev/null +++ b/test/disabled/continuations-neg/infer0.scala @@ -0,0 +1,12 @@ +// $Id$ + +import scala.util.continuations._ + + +object Test { + def test(x: => Int @cpsParam[String,Int]) = 7 + + def main(args: Array[String]) { + test(8) + } +} diff --git a/test/files/continuations-neg/infer0.check b/test/files/continuations-neg/infer0.check deleted file mode 100644 index 1dd072ef09..0000000000 --- a/test/files/continuations-neg/infer0.check +++ /dev/null @@ -1,4 +0,0 @@ -infer0.scala:11: error: cannot cps-transform expression 8: type arguments [Int(8),String,Int] do not conform to method shiftUnit's type parameter bounds [A,B,C >: B] - test(8) - ^ -one error found diff --git a/test/files/continuations-neg/infer0.scala b/test/files/continuations-neg/infer0.scala deleted file mode 100644 index 9cf69c5d35..0000000000 --- a/test/files/continuations-neg/infer0.scala +++ /dev/null @@ -1,14 +0,0 @@ -// $Id$ - -import scala.util.continuations._ - - -object Test { - - def test(x: => Int @cpsParam[String,Int]) = 7 - - def main(args: Array[String]): Any = { - test(8) - } - -} \ No newline at end of file diff --git a/test/files/continuations-run/shift-pct.check b/test/files/continuations-run/shift-pct.check new file mode 100644 index 0000000000..fb190e770a --- /dev/null +++ b/test/files/continuations-run/shift-pct.check @@ -0,0 +1,25 @@ +d = 1, d2 = 1.0, pct = 1.000 +d = 2, d2 = 4.0, pct = 0.500 +d = 3, d2 = 9.0, pct = 0.333 +d = 4, d2 = 16.0, pct = 0.250 +d = 5, d2 = 25.0, pct = 0.200 +d = 6, d2 = 36.0, pct = 0.167 +d = 7, d2 = 49.0, pct = 0.143 +d = 8, d2 = 64.0, pct = 0.125 +d = 9, d2 = 81.0, pct = 0.111 +d = 10, d2 = 100.0, pct = 0.100 +d = 11, d2 = 121.0, pct = 0.091 +d = 12, d2 = 144.0, pct = 0.083 +d = 13, d2 = 169.0, pct = 0.077 +d = 14, d2 = 196.0, pct = 0.071 +d = 15, d2 = 225.0, pct = 0.067 +d = 16, d2 = 256.0, pct = 0.063 +d = 17, d2 = 289.0, pct = 0.059 +d = 18, d2 = 324.0, pct = 0.056 +d = 19, d2 = 361.0, pct = 0.053 +d = 20, d2 = 400.0, pct = 0.050 +d = 21, d2 = 441.0, pct = 0.048 +d = 22, d2 = 484.0, pct = 0.045 +d = 23, d2 = 529.0, pct = 0.043 +d = 24, d2 = 576.0, pct = 0.042 +d = 25, d2 = 625.0, pct = 0.040 diff --git a/test/files/continuations-run/shift-pct.scala b/test/files/continuations-run/shift-pct.scala new file mode 100644 index 0000000000..7ef9922168 --- /dev/null +++ b/test/files/continuations-run/shift-pct.scala @@ -0,0 +1,30 @@ +import scala.util.continuations._ + +object Test { + abstract class IfReturnRepro { + def s1: Double @cpsParam[Any, Unit] + def s2: Double @cpsParam[Any, Unit] + + def p(i: Int): Double @cpsParam[Unit, Any] = { + val px = s1 + val pct = if (px > 100) px else px / s2 + println("pct = %.3f".format(pct)) + pct + } + } + + def main(args: Array[String]) : Unit = { + var d: Double = 0d + def d2 = d * d + + val irr = new IfReturnRepro { + def s1 = shift(f => f(d)) + def s2 = shift(f => f(d2)) + } + 1 to 25 foreach { i => + d = i + print("d = " + i + ", d2 = " + d2 + ", ") + run(irr p i) + } + } +} -- cgit v1.2.3 From 62acd249052f889e9519213fa91bcf212429bc8e Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 5 Apr 2012 12:44:29 -0700 Subject: Fix for continuations issue with match blocks. Don't type pattern trees with annotations still attached. --- .../scala/tools/nsc/typechecker/Typers.scala | 5 +++- test/files/continuations-run/z1673.check | 0 test/files/continuations-run/z1673.scala | 31 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/files/continuations-run/z1673.check create mode 100644 test/files/continuations-run/z1673.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 09fb39125e..36c81b09cd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2089,7 +2089,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x) StarPositionInPatternError(x) - val pat1 = typedPattern(cdef.pat, pattpe) + // withoutAnnotations - see continuations-run/z1673.scala + // This adjustment is awfully specific to continuations, but AFAICS the + // whole AnnotationChecker framework is. + val pat1 = typedPattern(cdef.pat, pattpe.withoutAnnotations) // When case classes have more than two parameter lists, the pattern ends // up typed as a method. We only pattern match on the first parameter // list, so substitute the final result type of the method, i.e. the type diff --git a/test/files/continuations-run/z1673.check b/test/files/continuations-run/z1673.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/files/continuations-run/z1673.scala b/test/files/continuations-run/z1673.scala new file mode 100644 index 0000000000..716b374860 --- /dev/null +++ b/test/files/continuations-run/z1673.scala @@ -0,0 +1,31 @@ +import scala.util.continuations._ + +class MatchRepro { + def s: String @cps[Any] = shift { k => k("foo") } + + def p = { + val k = s + s match { case lit0 => } + } + + def q = { + val k = s + k match { case lit1 => } + } + + def r = { + s match { case "FOO" => } + } + + def t = { + val k = s + k match { case "FOO" => } + } +} + +object Test { + def main(args: Array[String]): Unit = { + val m = new MatchRepro + () + } +} -- cgit v1.2.3 From 2b89c3459d1d9a9aa9dd5aba75847274d6f74129 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 5 Apr 2012 14:04:12 -0700 Subject: Symbol activity tracer. I just can't shake the feeling more people should see the things that I see. scalac -Dscalac.debug.syms foo.scala --- .../scala/reflect/internal/SymbolTable.scala | 5 + src/compiler/scala/reflect/internal/Symbols.scala | 7 +- .../internal/util/TraceSymbolActivity.scala | 168 +++++++++++++++++++++ src/compiler/scala/tools/nsc/Global.scala | 3 + 4 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/compiler/scala/reflect/internal/util/TraceSymbolActivity.scala (limited to 'src') diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index bb11ca634a..256c1a6ced 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -62,6 +62,11 @@ abstract class SymbolTable extends api.Universe result } + final val traceSymbolActivity = sys.props contains "scalac.debug.syms" + object traceSymbols extends { + val global: SymbolTable.this.type = SymbolTable.this + } with util.TraceSymbolActivity + /** Are we compiling for Java SE? */ // def forJVM: Boolean diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 4473d63f5f..5c43047046 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -3,7 +3,6 @@ * @author Martin Odersky */ - package scala.reflect package internal @@ -99,9 +98,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => private var rawpos = initPos val id = nextId() // identity displayed when -uniqid - private[this] var _validTo: Period = NoPeriod + if (traceSymbolActivity) + traceSymbols.recordNewSymbol(this) + def validTo = _validTo def validTo_=(x: Period) { _validTo = x} @@ -796,6 +797,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => else originalOwner(this) = rawowner } assert(!inReflexiveMirror, "owner_= is not thread-safe; cannot be run in reflexive code") + if (traceSymbolActivity) + traceSymbols.recordNewSymbolOwner(this, owner) _rawowner = owner } diff --git a/src/compiler/scala/reflect/internal/util/TraceSymbolActivity.scala b/src/compiler/scala/reflect/internal/util/TraceSymbolActivity.scala new file mode 100644 index 0000000000..eb384f9a85 --- /dev/null +++ b/src/compiler/scala/reflect/internal/util/TraceSymbolActivity.scala @@ -0,0 +1,168 @@ +package scala.reflect.internal +package util + +import scala.collection.{ mutable, immutable } + +trait TraceSymbolActivity { + val global: SymbolTable + import global._ + + if (traceSymbolActivity) + scala.sys addShutdownHook showAllSymbols() + + private type Set[T] = scala.collection.immutable.Set[T] + private val Set = scala.collection.immutable.Set + + val allSymbols = mutable.Map[Int, Symbol]() + val allChildren = mutable.Map[Int, List[Int]]() withDefaultValue Nil + val prevOwners = mutable.Map[Int, List[(Int, Phase)]]() withDefaultValue Nil + val symsCaused = mutable.Map[Int, Int]() withDefaultValue 0 + val allTrees = mutable.Set[Tree]() + + def recordSymbolsInTree(tree: Tree) { + allTrees += tree + } + + def recordNewSymbol(sym: Symbol) { + if (sym.id > 1) { + allSymbols(sym.id) = sym + allChildren(sym.owner.id) ::= sym.id + } + } + def recordNewSymbolOwner(sym: Symbol, newOwner: Symbol) { + val sid = sym.id + val oid = sym.owner.id + val nid = newOwner.id + + prevOwners(sid) ::= (oid -> phase) + allChildren(oid) = allChildren(oid) filterNot (_ == sid) + allChildren(nid) ::= sid + } + + /** TODO. + */ + private def reachableDirectlyFromSymbol(sym: Symbol): List[Symbol] = ( + List(sym.owner, sym.alias, sym.thisSym) + ++ sym.children + ++ sym.info.parents.map(_.typeSymbol) + ++ sym.typeParams + ++ sym.paramss.flatten + ) + private def reachable[T](inputs: Traversable[T], mkSymbol: T => Symbol): Set[Symbol] = { + def loop(seen: Set[Symbol], remaining: List[Symbol]): Set[Symbol] = { + remaining match { + case Nil => seen + case head :: rest => + if ((head eq null) || (head eq NoSymbol) || seen(head)) loop(seen, rest) + else loop(seen + head, rest ++ reachableDirectlyFromSymbol(head).filterNot(seen)) + } + } + loop(immutable.Set(), inputs.toList map mkSymbol filterNot (_ eq null) distinct) + } + private def treeList(t: Tree) = { + val buf = mutable.ListBuffer[Tree]() + t foreach (buf += _) + buf.toList + } + + private def reachableFromSymbol(root: Symbol): Set[Symbol] = + reachable[Symbol](List(root, root.info.typeSymbol), x => x) + + private def reachableFromTree(tree: Tree): Set[Symbol] = + reachable[Tree](treeList(tree), _.symbol) + + private def signature(id: Int) = runBeforeErasure(allSymbols(id).defString) + + private def dashes(s: Any): String = ("" + s) map (_ => '-') + private def show(s1: Any, ss: Any*) { + println("%-12s".format(s1) +: ss mkString " ") + } + private def showHeader(s1: Any, ss: Any*) { + show(s1, ss: _*) + show(dashes(s1), ss map dashes: _*) + } + private def showSym(sym: Symbol) { + def prefix = (" " * (sym.ownerChain.length - 1)) + sym.id + try println("%s#%s %s".format(prefix, sym.accurateKindString, sym.name.decode)) + catch { + case x => println(prefix + " failed: " + x) + } + allChildren(sym.id).sorted foreach showIdAndRemove + } + private def showIdAndRemove(id: Int) { + allSymbols remove id foreach showSym + } + private def symbolStr(id: Int): String = { + if (id == 1) "NoSymbol" else { + val sym = allSymbols(id) + sym.accurateKindString + " " + sym.name.decode + } + } + private def ownerStr(id: Int): String = { + val sym = allSymbols(id) + sym.name.decode + "#" + sym.id + } + + private def freq[T, U](xs: collection.Traversable[T])(fn: T => U): List[(U, Int)] = { + val ys = xs groupBy fn mapValues (_.size) + ys.toList sortBy (-_._2) + } + + private def showMapFreq[T](xs: collection.Map[T, Traversable[_]])(showFn: T => String) { + xs.mapValues(_.size).toList.sortBy(-_._2) take 100 foreach { case (k, size) => + show(size, showFn(k)) + } + println("\n") + } + private def showFreq[T, U](xs: Traversable[T])(groupFn: T => U, showFn: U => String = (x: U) => "" + x) = { + showMapFreq(xs.toList groupBy groupFn)(showFn) + } + private lazy val findErasurePhase: Phase = { + var ph = phase + while (ph != NoPhase && ph.name != "erasure") { + ph = ph.prev + } + ph + } + private def runBeforeErasure[T](body: => T): T = atPhase(findErasurePhase)(body) + + def showAllSymbols() { + if (!traceSymbolActivity) return + allSymbols(1) = NoSymbol + + println("" + allSymbols.size + " symbols created.") + println("") + + showHeader("descendants", "symbol") + showFreq(allSymbols.values flatMap (_.ownerChain drop 1))(_.id, symbolStr) + + showHeader("children", "symbol") + showMapFreq(allChildren)(symbolStr) + + if (prevOwners.nonEmpty) { + showHeader("prev owners", "symbol") + showMapFreq(prevOwners) { k => + val owners = (((allSymbols(k).owner.id, NoPhase)) :: prevOwners(k)) map { + case (oid, NoPhase) => "-> owned by " + ownerStr(oid) + case (oid, ph) => "-> owned by %s (until %s)".format(ownerStr(oid), ph) + } + signature(k) :: owners mkString "\n " + } + } + + val nameFreq = allSymbols.values.toList groupBy (_.name) + showHeader("frequency", "%-15s".format("name"), "owners") + showMapFreq(nameFreq) { name => + "%-15s %s".format(name.decode, { + val owners = freq(nameFreq(name))(_.owner) + + "%4s owners (%s)".format( + owners.size, + owners.take(3).map({ case (k, v) => v + "/" + k }).mkString(", ") + ", ..." + ) + }) + } + + allSymbols.keys.toList.sorted foreach showIdAndRemove + } +} diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 9ccd0c28db..6a30dc2a0f 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1356,6 +1356,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb inform(unitTimingsFormatted) } + if (traceSymbolActivity) + units map (_.body) foreach (traceSymbols recordSymbolsInTree _) + // In case no phase was specified for -Xshow-class/object, show it now for sure. if (opt.noShow) showMembers() -- cgit v1.2.3