diff options
Diffstat (limited to 'src')
7 files changed, 93 insertions, 145 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index dd94a8cdaf..603f9af1b4 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1065,6 +1065,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) @inline final def enteringMixin[T](op: => T): T = enteringPhase(currentRun.mixinPhase)(op) @inline final def enteringPickler[T](op: => T): T = enteringPhase(currentRun.picklerPhase)(op) @inline final def enteringRefchecks[T](op: => T): T = enteringPhase(currentRun.refchecksPhase)(op) + @inline final def enteringSpecialize[T](op: => T): T = enteringPhase(currentRun.specializePhase)(op) @inline final def enteringTyper[T](op: => T): T = enteringPhase(currentRun.typerPhase)(op) @inline final def enteringUncurry[T](op: => T): T = enteringPhase(currentRun.uncurryPhase)(op) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index d14fcb3eb1..4bc4e06fa7 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1267,7 +1267,35 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context) + } + /** Introduced to fix SI-7343: Phase ordering problem between Duplicators and Specialization. + * brief explanation: specialization rewires class parents during info transformation, and + * the new info then guides the tree changes. But if a symbol is created during duplication, + * which runs after specialization, its info is not visited and thus the corresponding tree + * is not specialized. One manifestation is the following: + * ``` + * object Test { + * class Parent[@specialized(Int) T] + * + * def spec_method[@specialized(Int) T](t: T, expectedXSuper: String) = { + * class X extends Parent[T]() + * // even in the specialized variant, the local X class + * // doesn't extend Parent$mcI$sp, since its symbol has + * // been created after specialization and was not seen + * // by specialzation's info transformer. + * ... + * } + * } + * ``` + * We fix this by forcing duplication to take place before specialization. + * + * Note: The constructors phase (which also uses duplication) comes after erasure and uses the + * post-erasure typer => we must protect it from the beforeSpecialization phase shifting. + */ + class SpecializationDuplicator(casts: Map[Symbol, Type]) extends Duplicator(casts) { + override def retyped(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol, env: scala.collection.Map[Symbol, Type]): Tree = + enteringSpecialize(super.retyped(context, tree, oldThis, newThis, env)) } /** A tree symbol substituter that substitutes on type skolems. @@ -1664,7 +1692,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate) debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName) - val d = new Duplicator(emptyEnv) + val d = new SpecializationDuplicator(emptyEnv) val newValDef = d.retyped( localTyper.context1.asInstanceOf[d.Context], tree1, @@ -1723,7 +1751,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val symbol = tree.symbol val meth = addBody(tree, source) - val d = new Duplicator(castmap) + val d = new SpecializationDuplicator(castmap) debuglog("-->d DUPLICATING: " + meth) d.retyped( localTyper.context1.asInstanceOf[d.Context], diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 95b771a8a5..0a2628b482 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -32,6 +32,7 @@ abstract class Duplicators extends Analyzer { envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList) debuglog("retyped with env: " + env) + newBodyDuplicator(context).typed(tree) } @@ -365,7 +366,8 @@ abstract class Duplicators extends Analyzer { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } val ntree = castType(tree, pt) - super.typed(ntree, mode, pt) + val res = super.typed(ntree, mode, pt) + res } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 0305aab844..1282cfb416 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1408,11 +1408,20 @@ trait Namers extends MethodSynthesis { if (!annotated.isInitialized) tree match { case defn: MemberDef => val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => + val ctx = typer.context + val annCtx = ctx.make(ann) + annCtx.setReportErrors() // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. AnnotationInfo lazily { - val context1 = typer.context.make(ann) - context1.setReportErrors() - enteringTyper(newTyper(context1) typedAnnotation ann) + if (typer.context ne ctx) + log(sm"""|The var `typer.context` in ${Namer.this} was mutated before the annotation ${ann} was forced. + | + |current value = ${typer.context} + |original value = $ctx + | + |This confirms the hypothesis for the cause of SI-7603. If you see this message, please comment on that ticket.""") + + enteringTyper(newTyper(annCtx) typedAnnotation ann) } } if (ainfos.nonEmpty) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cb3a12b60d..353e8e4810 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1431,8 +1431,8 @@ trait Typers extends Adaptations with Tags { implRestriction(tree, "nested object") //see https://issues.scala-lang.org/browse/SI-6444 //see https://issues.scala-lang.org/browse/SI-6463 - case _: ClassDef => - implRestriction(tree, "nested class") + case cd: ClassDef if !cd.symbol.isAnonymousClass => // Don't warn about partial functions, etc. SI-7571 + implRestriction(tree, "nested class") // avoiding Type Tests that might check the $outer pointer. case Select(sup @ Super(qual, mix), selector) if selector != nme.CONSTRUCTOR && qual.symbol == clazz && mix != tpnme.EMPTY => //see https://issues.scala-lang.org/browse/SI-6483 implRestriction(sup, "qualified super reference") diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 2e4c72cd71..b072cd653b 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -14,7 +14,7 @@ import java.util.concurrent.{ ConcurrentLinkedQueue, TimeUnit, Callable } import java.util.concurrent.TimeUnit.{ NANOSECONDS => NANOS, MILLISECONDS ⇒ MILLIS } import java.lang.{ Iterable => JIterable } import java.util.{ LinkedList => JLinkedList } -import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } +import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicLong, AtomicBoolean } import scala.util.control.NonFatal import scala.Option @@ -101,7 +101,7 @@ trait Future[+T] extends Awaitable[T] { // that also have an executor parameter, which // keeps us from accidentally forgetting to use // the executor parameter. - private implicit def internalExecutor: ExecutionContext = Future.InternalCallbackExecutor + private def internalExecutor = Future.InternalCallbackExecutor /* Callbacks */ @@ -116,9 +116,10 @@ trait Future[+T] extends Awaitable[T] { * $callbackInContext */ def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit = onComplete { - case Success(v) if pf isDefinedAt v => pf(v) + case Success(v) => + pf.applyOrElse[T, Any](v, Predef.conforms[T]) // Exploiting the cached function to avoid MatchError case _ => - }(executor) + } /** When this future is completed with a failure (i.e. with a throwable), * apply the provided callback to the throwable. @@ -134,9 +135,10 @@ trait Future[+T] extends Awaitable[T] { * $callbackInContext */ def onFailure[U](@deprecatedName('callback) pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit = onComplete { - case Failure(t) if NonFatal(t) && pf.isDefinedAt(t) => pf(t) + case Failure(t) => + pf.applyOrElse[Throwable, Any](t, Predef.conforms[Throwable]) // Exploiting the cached function to avoid MatchError case _ => - }(executor) + } /** When this future is completed, either through an exception, or a value, * apply the provided function. @@ -186,13 +188,12 @@ trait Future[+T] extends Awaitable[T] { * and throws a corresponding exception if the original future fails. */ def failed: Future[Throwable] = { + implicit val ec = internalExecutor val p = Promise[Throwable]() - onComplete { case Failure(t) => p success t case Success(v) => p failure (new NoSuchElementException("Future.failed not completed with a throwable.")) } - p.future } @@ -203,10 +204,7 @@ trait Future[+T] extends Awaitable[T] { * * Will not be called if the future fails. */ - def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = onComplete { - case Success(r) => f(r) - case _ => // do nothing - }(executor) + def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = onComplete { _ foreach f } /** Creates a new future by applying the 's' function to the successful result of * this future, or the 'f' function to the failed result. If there is any non-fatal @@ -221,19 +219,11 @@ trait Future[+T] extends Awaitable[T] { */ def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S] = { val p = Promise[S]() - + // transform on Try has the wrong shape for us here onComplete { - case result => - try { - result match { - case Failure(t) => p failure f(t) - case Success(r) => p success s(r) - } - } catch { - case NonFatal(t) => p failure t - } - }(executor) - + case Success(r) => p complete Try(s(r)) + case Failure(t) => p complete Try(throw f(t)) // will throw fatal errors! + } p.future } @@ -245,19 +235,7 @@ trait Future[+T] extends Awaitable[T] { */ def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { // transform(f, identity) val p = Promise[S]() - - onComplete { - case result => - try { - result match { - case Success(r) => p success f(r) - case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] - } - } catch { - case NonFatal(t) => p failure t - } - }(executor) - + onComplete { v => p complete (v map f) } p.future } @@ -270,20 +248,10 @@ trait Future[+T] extends Awaitable[T] { */ def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = { val p = Promise[S]() - onComplete { case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] - case Success(v) => - try { - f(v).onComplete({ - case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] - case Success(v) => p success v - })(internalExecutor) - } catch { - case NonFatal(t) => p failure t - } - }(executor) - + case Success(v) => try f(v) onComplete p.complete catch { case NonFatal(t) => p failure t } + } p.future } @@ -303,34 +271,14 @@ trait Future[+T] extends Awaitable[T] { * Await.result(h, Duration.Zero) // throw a NoSuchElementException * }}} */ - def filter(@deprecatedName('pred) p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = { - val promise = Promise[T]() - - onComplete { - case f: Failure[_] => promise complete f.asInstanceOf[Failure[T]] - case Success(v) => - try { - if (p(v)) promise success v - else promise failure new NoSuchElementException("Future.filter predicate is not satisfied") - } catch { - case NonFatal(t) => promise failure t - } - }(executor) - - promise.future - } + def filter(@deprecatedName('pred) p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = + map { + r => if (p(r)) r else throw new NoSuchElementException("Future.filter predicate is not satisfied") + } /** Used by for-comprehensions. */ final def withFilter(p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = filter(p)(executor) - // final def withFilter(p: T => Boolean) = new FutureWithFilter[T](this, p) - - // final class FutureWithFilter[+S](self: Future[S], p: S => Boolean) { - // def foreach(f: S => Unit): Unit = self filter p foreach f - // def map[R](f: S => R) = self filter p map f - // def flatMap[R](f: S => Future[R]) = self filter p flatMap f - // def withFilter(q: S => Boolean): FutureWithFilter[S] = new FutureWithFilter[S](self, x => p(x) && q(x)) - // } /** Creates a new future by mapping the value of the current future, if the given partial function is defined at that value. * @@ -352,22 +300,10 @@ trait Future[+T] extends Awaitable[T] { * Await.result(h, Duration.Zero) // throw a NoSuchElementException * }}} */ - def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] = { - val p = Promise[S]() - - onComplete { - case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] - case Success(v) => - try { - if (pf.isDefinedAt(v)) p success pf(v) - else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) - } catch { - case NonFatal(t) => p failure t - } - }(executor) - - p.future - } + def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] = + map { + r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t)) + } /** Creates a new future that will handle any matching throwable that this * future might contain. If there is no match, or if this future contains @@ -383,9 +319,7 @@ trait Future[+T] extends Awaitable[T] { */ def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = { val p = Promise[U]() - - onComplete { case tr => p.complete(tr recover pf) }(executor) - + onComplete { v => p complete (v recover pf) } p.future } @@ -404,17 +338,10 @@ trait Future[+T] extends Awaitable[T] { */ def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = { val p = Promise[U]() - onComplete { - case Failure(t) if pf isDefinedAt t => - try { - p completeWith pf(t) - } catch { - case NonFatal(t) => p failure t - } - case otherwise => p complete otherwise - }(executor) - + case Failure(t) => try pf.applyOrElse(t, (_: Throwable) => this) onComplete p.complete catch { case NonFatal(t) => p failure t } + case other => p complete other + } p.future } @@ -427,19 +354,12 @@ trait Future[+T] extends Awaitable[T] { * with the throwable stored in `that`. */ def zip[U](that: Future[U]): Future[(T, U)] = { + implicit val ec = internalExecutor val p = Promise[(T, U)]() - - this onComplete { + onComplete { case f: Failure[_] => p complete f.asInstanceOf[Failure[(T, U)]] - case Success(r) => - that onSuccess { - case r2 => p success ((r, r2)) - } - that onFailure { - case f => p failure f - } + case Success(s) => that onComplete { c => p.complete(c map { s2 => (s, s2) }) } } - p.future } @@ -458,6 +378,7 @@ trait Future[+T] extends Awaitable[T] { * }}} */ def fallbackTo[U >: T](that: Future[U]): Future[U] = { + implicit val ec = internalExecutor val p = Promise[U]() onComplete { case s @ Success(_) => p complete s @@ -470,23 +391,13 @@ trait Future[+T] extends Awaitable[T] { * that conforms to `S`'s erased type or a `ClassCastException` otherwise. */ def mapTo[S](implicit tag: ClassTag[S]): Future[S] = { - def boxedType(c: Class[_]): Class[_] = { + implicit val ec = internalExecutor + val boxedClass = { + val c = tag.runtimeClass if (c.isPrimitive) Future.toBoxed(c) else c } - - val p = Promise[S]() - - onComplete { - case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] - case Success(t) => - p complete (try { - Success(boxedType(tag.runtimeClass).cast(t).asInstanceOf[S]) - } catch { - case e: ClassCastException => Failure(e) - }) - } - - p.future + require(boxedClass ne null) + map(s => boxedClass.cast(s).asInstanceOf[S]) } /** Applies the side-effecting function to the result of this future, and returns @@ -514,11 +425,9 @@ trait Future[+T] extends Awaitable[T] { */ def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() - onComplete { - case r => try if (pf isDefinedAt r) pf(r) finally p complete r - }(executor) - + case r => try pf.applyOrElse[Try[T], Any](r, Predef.conforms[Try[T]]) finally p complete r + } p.future } @@ -579,14 +488,12 @@ object Future { } map (_.result()) } - /** Returns a `Future` to the result of the first future in the list that is completed. + /** Returns a new `Future` to the result of the first future in the list that is completed. */ def firstCompletedOf[T](futures: TraversableOnce[Future[T]])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() - val completeFirst: Try[T] => Unit = p tryComplete _ - futures.foreach(_ onComplete completeFirst) - + futures foreach { _ onComplete completeFirst } p.future } diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala index b5b09a753a..8b88021dbf 100644 --- a/src/partest/scala/tools/partest/PartestTask.scala +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -15,6 +15,7 @@ import java.lang.reflect.Method import org.apache.tools.ant.Task import org.apache.tools.ant.types.{ Reference, FileSet} import org.apache.tools.ant.types.Commandline.Argument +import scala.tools.ant.ScalaTask /** An Ant task to execute the Scala test suite (NSC). * @@ -34,7 +35,7 @@ import org.apache.tools.ant.types.Commandline.Argument * * @author Philippe Haller */ -class PartestTask extends Task with CompilationPathProperty { +class PartestTask extends Task with CompilationPathProperty with ScalaTask { type Path = org.apache.tools.ant.types.Path private var kinds: List[String] = Nil @@ -178,7 +179,7 @@ class PartestTask extends Task with CompilationPathProperty { val allFailures = _results map (_._2) sum val allFailedPaths = _results flatMap (_._3) - def f = if (errorOnFailed && allFailures > 0) (sys error _) else log(_: String) + def f = if (errorOnFailed && allFailures > 0) buildError(_: String) else log(_: String) def s = if (allFailures > 1) "s" else "" val msg = if (allFailures > 0) |