diff options
Diffstat (limited to 'src')
68 files changed, 2241 insertions, 1709 deletions
diff --git a/src/actors/scala/actors/Actor.scala b/src/actors/scala/actors/Actor.scala index b746f68268..aab533ae8d 100644 --- a/src/actors/scala/actors/Actor.scala +++ b/src/actors/scala/actors/Actor.scala @@ -46,7 +46,7 @@ object Actor extends Combinators { Terminated = Value } - private[actors] val tl = new ThreadLocal[ReplyReactor] + private[actors] val tl = new ThreadLocal[InternalReplyReactor] // timer thread runs as daemon private[actors] val timer = new Timer(true) @@ -59,15 +59,15 @@ object Actor extends Combinators { * * @return returns the currently executing actor. */ - def self: Actor = self(Scheduler) + def self: Actor = self(Scheduler).asInstanceOf[Actor] - private[actors] def self(sched: IScheduler): Actor = - rawSelf(sched).asInstanceOf[Actor] + private[actors] def self(sched: IScheduler): InternalActor = + rawSelf(sched).asInstanceOf[InternalActor] - private[actors] def rawSelf: ReplyReactor = + private[actors] def rawSelf: InternalReplyReactor = rawSelf(Scheduler) - private[actors] def rawSelf(sched: IScheduler): ReplyReactor = { + private[actors] def rawSelf(sched: IScheduler): InternalReplyReactor = { val s = tl.get if (s eq null) { val r = new ActorProxy(Thread.currentThread, sched) @@ -245,7 +245,7 @@ object Actor extends Combinators { def eventloop(f: PartialFunction[Any, Unit]): Nothing = rawSelf.react(new RecursiveProxyHandler(rawSelf, f)) - private class RecursiveProxyHandler(a: ReplyReactor, f: PartialFunction[Any, Unit]) + private class RecursiveProxyHandler(a: InternalReplyReactor, f: PartialFunction[Any, Unit]) extends scala.runtime.AbstractPartialFunction[Any, Unit] { def _isDefinedAt(m: Any): Boolean = true // events are immediately removed from the mailbox @@ -259,7 +259,7 @@ object Actor extends Combinators { * Returns the actor which sent the last received message. */ def sender: OutputChannel[Any] = - rawSelf.sender + rawSelf.internalSender /** * Sends `msg` to the actor waiting in a call to `!?`. @@ -302,7 +302,7 @@ object Actor extends Combinators { def andThen[b](other: => b): Unit } - implicit def mkBody[a](body: => a) = new Body[a] { + implicit def mkBody[a](body: => a) = new InternalActor.Body[a] { def andThen[b](other: => b): Unit = rawSelf.seq(body, other) } @@ -397,476 +397,12 @@ object Actor extends Combinators { * @define channel actor's mailbox */ @SerialVersionUID(-781154067877019505L) -trait Actor extends AbstractActor with ReplyReactor with ActorCanReply with InputChannel[Any] with Serializable { - - /* The following two fields are only used when the actor - * suspends by blocking its underlying thread, for example, - * when waiting in a receive or synchronous send. - */ - @volatile - private var isSuspended = false - - /* This field is used to communicate the received message from - * the invocation of send to the place where the thread of - * the receiving actor resumes inside receive/receiveWithin. - */ - @volatile - private var received: Option[Any] = None - - protected[actors] override def scheduler: IScheduler = Scheduler - - private[actors] override def startSearch(msg: Any, replyTo: OutputChannel[Any], handler: PartialFunction[Any, Any]) = - if (isSuspended) { - () => synchronized { - mailbox.append(msg, replyTo) - resumeActor() - } - } else super.startSearch(msg, replyTo, handler) - - // we override this method to check `shouldExit` before suspending - private[actors] override def searchMailbox(startMbox: MQueue[Any], - handler: PartialFunction[Any, Any], - resumeOnSameThread: Boolean) { - var tmpMbox = startMbox - var done = false - while (!done) { - val qel = tmpMbox.extractFirst((msg: Any, replyTo: OutputChannel[Any]) => { - senders = List(replyTo) - handler.isDefinedAt(msg) - }) - if (tmpMbox ne mailbox) - tmpMbox.foreach((m, s) => mailbox.append(m, s)) - if (null eq qel) { - synchronized { - // in mean time new stuff might have arrived - if (!sendBuffer.isEmpty) { - tmpMbox = new MQueue[Any]("Temp") - drainSendBuffer(tmpMbox) - // keep going - } else { - // very important to check for `shouldExit` at this point - // since linked actors might have set it after we checked - // last time (e.g., at the beginning of `react`) - if (shouldExit) exit() - waitingFor = handler - // see Reactor.searchMailbox - throw Actor.suspendException - } - } - } else { - resumeReceiver((qel.msg, qel.session), handler, resumeOnSameThread) - done = true - } - } - } - - private[actors] override def makeReaction(fun: () => Unit, handler: PartialFunction[Any, Any], msg: Any): Runnable = - new ActorTask(this, fun, handler, msg) - - /** See the companion object's `receive` method. */ - def receive[R](f: PartialFunction[Any, R]): R = { - assert(Actor.self(scheduler) == this, "receive from channel belonging to other actor") - - synchronized { - if (shouldExit) exit() // links - drainSendBuffer(mailbox) - } - - var done = false - while (!done) { - val qel = mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => { - senders = replyTo :: senders - val matches = f.isDefinedAt(m) - senders = senders.tail - matches - }) - if (null eq qel) { - synchronized { - // in mean time new stuff might have arrived - if (!sendBuffer.isEmpty) { - drainSendBuffer(mailbox) - // keep going - } else { - waitingFor = f - isSuspended = true - scheduler.managedBlock(blocker) - drainSendBuffer(mailbox) - // keep going - } - } - } else { - received = Some(qel.msg) - senders = qel.session :: senders - done = true - } - } - - val result = f(received.get) - received = None - senders = senders.tail - result - } - - /** See the companion object's `receiveWithin` method. */ - def receiveWithin[R](msec: Long)(f: PartialFunction[Any, R]): R = { - assert(Actor.self(scheduler) == this, "receive from channel belonging to other actor") - - synchronized { - if (shouldExit) exit() // links - drainSendBuffer(mailbox) - } - - // first, remove spurious TIMEOUT message from mailbox if any - mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => m == TIMEOUT) - - val receiveTimeout = () => { - if (f.isDefinedAt(TIMEOUT)) { - received = Some(TIMEOUT) - senders = this :: senders - } else - sys.error("unhandled timeout") - } - - var done = false - while (!done) { - val qel = mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => { - senders = replyTo :: senders - val matches = f.isDefinedAt(m) - senders = senders.tail - matches - }) - if (null eq qel) { - val todo = synchronized { - // in mean time new stuff might have arrived - if (!sendBuffer.isEmpty) { - drainSendBuffer(mailbox) - // keep going - () => {} - } else if (msec == 0L) { - done = true - receiveTimeout - } else { - if (onTimeout.isEmpty) { - if (!f.isDefinedAt(TIMEOUT)) - sys.error("unhandled timeout") - - val thisActor = this - onTimeout = Some(new TimerTask { - def run() { - thisActor.send(TIMEOUT, thisActor) - } - }) - Actor.timer.schedule(onTimeout.get, msec) - } - - // It is possible that !onTimeout.isEmpty, but TIMEOUT is not yet in mailbox - // See SI-4759 - waitingFor = f - received = None - isSuspended = true - scheduler.managedBlock(blocker) - drainSendBuffer(mailbox) - // keep going - () => {} - } - } - todo() - } else { - synchronized { - if (!onTimeout.isEmpty) { - onTimeout.get.cancel() - onTimeout = None - } - } - received = Some(qel.msg) - senders = qel.session :: senders - done = true - } - } - - val result = f(received.get) - received = None - senders = senders.tail - result - } - - /** See the companion object's `react` method. */ - override def react(handler: PartialFunction[Any, Unit]): Nothing = { - synchronized { - if (shouldExit) exit() - } - super.react(handler) - } - - /** See the companion object's `reactWithin` method. */ - override def reactWithin(msec: Long)(handler: PartialFunction[Any, Unit]): Nothing = { - synchronized { - if (shouldExit) exit() - } - super.reactWithin(msec)(handler) - } - - /** Receives the next message from the mailbox */ - def ? : Any = receive { - case x => x - } - - // guarded by lock of this - // never throws SuspendActorControl - private[actors] override def scheduleActor(f: PartialFunction[Any, Any], msg: Any) = - if (f eq null) { - // do nothing (timeout is handled instead) - } - else { - val task = new ActorTask(this, null, f, msg) - scheduler executeFromActor task - } - - /* Used for notifying scheduler when blocking inside receive/receiveWithin. */ - private object blocker extends scala.concurrent.ManagedBlocker { - def block() = { - Actor.this.suspendActor() - true - } - def isReleasable = - !Actor.this.isSuspended - } - - private def suspendActor() = synchronized { - while (isSuspended) { - try { - wait() - } catch { - case _: InterruptedException => - } - } - // links: check if we should exit - if (shouldExit) exit() - } - - private def resumeActor() { - isSuspended = false - notify() - } - - private[actors] override def exiting = synchronized { - _state == Actor.State.Terminated - } - - // guarded by this - private[actors] override def dostart() { - // Reset various flags. - // - // Note that we do *not* reset `trapExit`. The reason is that - // users should be able to set the field in the constructor - // and before `act` is called. - exitReason = 'normal - shouldExit = false - - super.dostart() - } +trait Actor extends InternalActor with ReplyReactor { override def start(): Actor = synchronized { super.start() this } - /** State of this actor */ - override def getState: Actor.State.Value = synchronized { - if (isSuspended) { - if (onTimeout.isEmpty) - Actor.State.Blocked - else - Actor.State.TimedBlocked - } else - super.getState - } - - // guarded by this - private[actors] var links: List[AbstractActor] = Nil - - /** - * Links `self` to actor `to`. - * - * @param to the actor to link to - * @return the parameter actor - */ - def link(to: AbstractActor): AbstractActor = { - assert(Actor.self(scheduler) == this, "link called on actor different from self") - this linkTo to - to linkTo this - to - } - - /** - * Links `self` to the actor defined by `body`. - * - * @param body the body of the actor to link to - * @return the parameter actor - */ - def link(body: => Unit): Actor = { - assert(Actor.self(scheduler) == this, "link called on actor different from self") - val a = new Actor { - def act() = body - override final val scheduler: IScheduler = Actor.this.scheduler - } - link(a) - a.start() - a - } - - private[actors] def linkTo(to: AbstractActor) = synchronized { - links = to :: links - } - - /** - * Unlinks `self` from actor `from`. - */ - def unlink(from: AbstractActor) { - assert(Actor.self(scheduler) == this, "unlink called on actor different from self") - this unlinkFrom from - from unlinkFrom this - } - - private[actors] def unlinkFrom(from: AbstractActor) = synchronized { - links = links.filterNot(from.==) - } - - @volatile - var trapExit = false - // guarded by this - private var exitReason: AnyRef = 'normal - // guarded by this - private[actors] var shouldExit = false - - /** - * Terminates execution of `self` with the following effect on - * linked actors: - * - * For each linked actor `a` with `trapExit` set to `'''true'''`, - * send message `Exit(self, reason)` to `a`. - * - * For each linked actor `a` with `trapExit` set to `'''false'''` - * (default), call `a.exit(reason)` if `reason != 'normal`. - */ - protected[actors] def exit(reason: AnyRef): Nothing = { - synchronized { - exitReason = reason - } - exit() - } - - /** - * Terminates with exit reason `'normal`. - */ - protected[actors] override def exit(): Nothing = { - val todo = synchronized { - if (!links.isEmpty) - exitLinked() - else - () => {} - } - todo() - super.exit() - } - - // Assume !links.isEmpty - // guarded by this - private[actors] def exitLinked(): () => Unit = { - _state = Actor.State.Terminated - // reset waitingFor, otherwise getState returns Suspended - waitingFor = Reactor.waitingForNone - // remove this from links - val mylinks = links.filterNot(this.==) - // unlink actors - mylinks.foreach(unlinkFrom(_)) - // return closure that locks linked actors - () => { - mylinks.foreach((linked: AbstractActor) => { - linked.synchronized { - if (!linked.exiting) { - linked.unlinkFrom(this) - linked.exit(this, exitReason) - } - } - }) - } - } - - // Assume !links.isEmpty - // guarded by this - private[actors] def exitLinked(reason: AnyRef): () => Unit = { - exitReason = reason - exitLinked() - } - - // Assume !this.exiting - private[actors] def exit(from: AbstractActor, reason: AnyRef) { - if (trapExit) { - this ! Exit(from, reason) - } - else if (reason != 'normal) - synchronized { - shouldExit = true - exitReason = reason - // resume this Actor in a way that - // causes it to exit - // (because shouldExit == true) - if (isSuspended) - resumeActor() - else if (waitingFor ne Reactor.waitingForNone) { - waitingFor = Reactor.waitingForNone - // it doesn't matter what partial function we are passing here - scheduleActor(waitingFor, null) - /* Here we should not throw a SuspendActorControl, - since the current method is called from an actor that - is in the process of exiting. - - Therefore, the contract for scheduleActor is that - it never throws a SuspendActorControl. - */ - } - } - } - - /** Requires qualified private, because `RemoteActor` must - * register a termination handler. - */ - private[actors] def onTerminate(f: => Unit) { - scheduler.onTerminate(this) { f } - } } - -/** - * Used as the timeout pattern in - * <a href="Actor.html#receiveWithin(Long)" target="contentFrame"> - * <code>receiveWithin</code></a> and - * <a href="Actor.html#reactWithin(Long)" target="contentFrame"> - * <code>reactWithin</code></a>. - * - * @example {{{ - * receiveWithin(500) { - * case (x, y) => ... - * case TIMEOUT => ... - * } - * }}} - * - * @author Philipp Haller - */ -case object TIMEOUT - - -/** Sent to an actor with `trapExit` set to `'''true'''` whenever one of its - * linked actors terminates. - * - * @param from the actor that terminated - * @param reason the reason that caused the actor to terminate - */ -case class Exit(from: AbstractActor, reason: AnyRef) - -/** Manages control flow of actor executions. - * - * @author Philipp Haller - */ -private[actors] class SuspendActorControl extends ControlThrowable diff --git a/src/actors/scala/actors/ActorCanReply.scala b/src/actors/scala/actors/ActorCanReply.scala index b307aafa57..d92fb183c0 100644 --- a/src/actors/scala/actors/ActorCanReply.scala +++ b/src/actors/scala/actors/ActorCanReply.scala @@ -18,7 +18,7 @@ import scala.concurrent.SyncVar * @author Philipp Haller */ private[actors] trait ActorCanReply extends ReactorCanReply { - this: AbstractActor with ReplyReactor => + this: AbstractActor with InternalReplyReactor => override def !?(msg: Any): Any = { val replyCh = new Channel[Any](Actor.self(scheduler)) diff --git a/src/actors/scala/actors/ActorTask.scala b/src/actors/scala/actors/ActorTask.scala index 090d0448f0..bb04302238 100644 --- a/src/actors/scala/actors/ActorTask.scala +++ b/src/actors/scala/actors/ActorTask.scala @@ -17,7 +17,7 @@ package scala.actors * changes to the underlying var invisible.) I can't figure out what's supposed * to happen, so I renamed the constructor parameter to at least be less confusing. */ -private[actors] class ActorTask(actor: Actor, +private[actors] class ActorTask(actor: InternalActor, fun: () => Unit, handler: PartialFunction[Any, Any], initialMsg: Any) @@ -32,7 +32,7 @@ private[actors] class ActorTask(actor: Actor, } protected override def terminateExecution(e: Throwable) { - val senderInfo = try { Some(actor.sender) } catch { + val senderInfo = try { Some(actor.internalSender) } catch { case _: Exception => None } // !!! If this is supposed to be setting the current contents of the @@ -45,13 +45,16 @@ private[actors] class ActorTask(actor: Actor, e) val todo = actor.synchronized { - if (!actor.links.isEmpty) + val res = if (!actor.links.isEmpty) actor.exitLinked(uncaught) else { super.terminateExecution(e) () => {} } + actor.internalPostStop + res } + todo() } diff --git a/src/actors/scala/actors/Channel.scala b/src/actors/scala/actors/Channel.scala index 62331239e8..36cee66b42 100644 --- a/src/actors/scala/actors/Channel.scala +++ b/src/actors/scala/actors/Channel.scala @@ -34,7 +34,7 @@ case class ! [a](ch: Channel[a], msg: a) * @define actor channel * @define channel channel */ -class Channel[Msg](val receiver: Actor) extends InputChannel[Msg] with OutputChannel[Msg] with CanReply[Msg, Any] { +class Channel[Msg](val receiver: InternalActor) extends InputChannel[Msg] with OutputChannel[Msg] with CanReply[Msg, Any] { type Future[+P] = scala.actors.Future[P] diff --git a/src/actors/scala/actors/Combinators.scala b/src/actors/scala/actors/Combinators.scala index 5276c7843e..c1a9095614 100644 --- a/src/actors/scala/actors/Combinators.scala +++ b/src/actors/scala/actors/Combinators.scala @@ -16,7 +16,7 @@ private[actors] trait Combinators { * Enables the composition of suspendable closures using `andThen`, * `loop`, `loopWhile`, etc. */ - implicit def mkBody[a](body: => a): Actor.Body[a] + implicit def mkBody[a](body: => a): InternalActor.Body[a] /** * Repeatedly executes `body`. diff --git a/src/actors/scala/actors/InternalActor.scala b/src/actors/scala/actors/InternalActor.scala new file mode 100644 index 0000000000..c94da5b9fd --- /dev/null +++ b/src/actors/scala/actors/InternalActor.scala @@ -0,0 +1,509 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2005-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.actors +import java.util.TimerTask +import scala.util.control.ControlThrowable + +private[actors] object InternalActor { + private[actors] trait Body[a] { + def andThen[b](other: => b): Unit + } +} + +private[actors] trait InternalActor extends AbstractActor with InternalReplyReactor with ActorCanReply with InputChannel[Any] with Serializable { + + /* The following two fields are only used when the actor + * suspends by blocking its underlying thread, for example, + * when waiting in a receive or synchronous send. + */ + @volatile + private[actors] var isSuspended = false + + /* This field is used to communicate the received message from + * the invocation of send to the place where the thread of + * the receiving actor resumes inside receive/receiveWithin. + */ + @volatile + private var received: Option[Any] = None + + protected[actors] override def scheduler: IScheduler = Scheduler + + private[actors] override def startSearch(msg: Any, replyTo: OutputChannel[Any], handler: PartialFunction[Any, Any]) = + if (isSuspended) { + () => + synchronized { + mailbox.append(msg, replyTo) + resumeActor() + } + } else super.startSearch(msg, replyTo, handler) + + // we override this method to check `shouldExit` before suspending + private[actors] override def searchMailbox(startMbox: MQueue[Any], + handler: PartialFunction[Any, Any], + resumeOnSameThread: Boolean) { + var tmpMbox = startMbox + var done = false + while (!done) { + val qel = tmpMbox.extractFirst((msg: Any, replyTo: OutputChannel[Any]) => { + senders = List(replyTo) + handler.isDefinedAt(msg) + }) + if (tmpMbox ne mailbox) + tmpMbox.foreach((m, s) => mailbox.append(m, s)) + if (null eq qel) { + synchronized { + // in mean time new stuff might have arrived + if (!sendBuffer.isEmpty) { + tmpMbox = new MQueue[Any]("Temp") + drainSendBuffer(tmpMbox) + // keep going + } else { + // very important to check for `shouldExit` at this point + // since linked actors might have set it after we checked + // last time (e.g., at the beginning of `react`) + if (shouldExit) exit() + waitingFor = handler + // see Reactor.searchMailbox + throw Actor.suspendException + } + } + } else { + resumeReceiver((qel.msg, qel.session), handler, resumeOnSameThread) + done = true + } + } + } + + private[actors] override def makeReaction(fun: () => Unit, handler: PartialFunction[Any, Any], msg: Any): Runnable = + new ActorTask(this, fun, handler, msg) + + /** See the companion object's `receive` method. */ + def receive[R](f: PartialFunction[Any, R]): R = { + assert(Actor.self(scheduler) == this, "receive from channel belonging to other actor") + + synchronized { + if (shouldExit) exit() // links + drainSendBuffer(mailbox) + } + + var done = false + while (!done) { + val qel = mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => { + senders = replyTo :: senders + val matches = f.isDefinedAt(m) + senders = senders.tail + matches + }) + if (null eq qel) { + synchronized { + // in mean time new stuff might have arrived + if (!sendBuffer.isEmpty) { + drainSendBuffer(mailbox) + // keep going + } else { + waitingFor = f + isSuspended = true + scheduler.managedBlock(blocker) + drainSendBuffer(mailbox) + // keep going + } + } + } else { + received = Some(qel.msg) + senders = qel.session :: senders + done = true + } + } + + val result = f(received.get) + received = None + senders = senders.tail + result + } + + /** See the companion object's `receiveWithin` method. */ + def receiveWithin[R](msec: Long)(f: PartialFunction[Any, R]): R = { + assert(Actor.self(scheduler) == this, "receive from channel belonging to other actor") + + synchronized { + if (shouldExit) exit() // links + drainSendBuffer(mailbox) + } + + // first, remove spurious TIMEOUT message from mailbox if any + mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => m == TIMEOUT) + + val receiveTimeout = () => { + if (f.isDefinedAt(TIMEOUT)) { + received = Some(TIMEOUT) + senders = this :: senders + } else + sys.error("unhandled timeout") + } + + var done = false + while (!done) { + val qel = mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => { + senders = replyTo :: senders + val matches = f.isDefinedAt(m) + senders = senders.tail + matches + }) + if (null eq qel) { + val todo = synchronized { + // in mean time new stuff might have arrived + if (!sendBuffer.isEmpty) { + drainSendBuffer(mailbox) + // keep going + () => {} + } else if (msec == 0L) { + done = true + receiveTimeout + } else { + if (onTimeout.isEmpty) { + if (!f.isDefinedAt(TIMEOUT)) + sys.error("unhandled timeout") + + val thisActor = this + onTimeout = Some(new TimerTask { + def run() { + thisActor.send(TIMEOUT, thisActor) + } + }) + Actor.timer.schedule(onTimeout.get, msec) + } + + // It is possible that !onTimeout.isEmpty, but TIMEOUT is not yet in mailbox + // See SI-4759 + waitingFor = f + received = None + isSuspended = true + scheduler.managedBlock(blocker) + drainSendBuffer(mailbox) + // keep going + () => {} + } + } + todo() + } else { + synchronized { + if (!onTimeout.isEmpty) { + onTimeout.get.cancel() + onTimeout = None + } + } + received = Some(qel.msg) + senders = qel.session :: senders + done = true + } + } + + val result = f(received.get) + received = None + senders = senders.tail + result + } + + /** See the companion object's `react` method. */ + override def react(handler: PartialFunction[Any, Unit]): Nothing = { + synchronized { + if (shouldExit) exit() + } + super.react(handler) + } + + /** See the companion object's `reactWithin` method. */ + override def reactWithin(msec: Long)(handler: PartialFunction[Any, Unit]): Nothing = { + synchronized { + if (shouldExit) exit() + } + super.reactWithin(msec)(handler) + } + + /** Receives the next message from the mailbox */ + def ? : Any = receive { + case x => x + } + + // guarded by lock of this + // never throws SuspendActorControl + private[actors] override def scheduleActor(f: PartialFunction[Any, Any], msg: Any) = + if (f eq null) { + // do nothing (timeout is handled instead) + } else { + val task = new ActorTask(this, null, f, msg) + scheduler executeFromActor task + } + + /* Used for notifying scheduler when blocking inside receive/receiveWithin. */ + private object blocker extends scala.concurrent.ManagedBlocker { + def block() = { + InternalActor.this.suspendActor() + true + } + def isReleasable = + !InternalActor.this.isSuspended + } + + private def suspendActor() = synchronized { + while (isSuspended) { + try { + wait() + } catch { + case _: InterruptedException => + } + } + // links: check if we should exit + if (shouldExit) exit() + } + + private def resumeActor() { + isSuspended = false + notify() + } + + private[actors] override def exiting = synchronized { + _state == Actor.State.Terminated + } + + // guarded by this + private[actors] override def dostart() { + // Reset various flags. + // + // Note that we do *not* reset `trapExit`. The reason is that + // users should be able to set the field in the constructor + // and before `act` is called. + exitReason = 'normal + shouldExit = false + + super.dostart() + } + + override def start(): InternalActor = synchronized { + super.start() + this + } + + /** State of this actor */ + override def getState: Actor.State.Value = synchronized { + if (isSuspended) { + if (onTimeout.isEmpty) + Actor.State.Blocked + else + Actor.State.TimedBlocked + } else + super.getState + } + + // guarded by this + private[actors] var links: List[AbstractActor] = Nil + + /** + * Links <code>self</code> to actor <code>to</code>. + * + * @param to the actor to link to + * @return the parameter actor + */ + def link(to: AbstractActor): AbstractActor = { + assert(Actor.self(scheduler) == this, "link called on actor different from self") + this linkTo to + to linkTo this + to + } + + /** + * Links <code>self</code> to the actor defined by <code>body</code>. + * + * @param body the body of the actor to link to + * @return the parameter actor + */ + def link(body: => Unit): Actor = { + assert(Actor.self(scheduler) == this, "link called on actor different from self") + val a = new Actor { + def act() = body + override final val scheduler: IScheduler = InternalActor.this.scheduler + } + link(a) + a.start() + a + } + + private[actors] def linkTo(to: AbstractActor) = synchronized { + links = to :: links + } + + /** + * Unlinks <code>self</code> from actor <code>from</code>. + */ + def unlink(from: AbstractActor) { + assert(Actor.self(scheduler) == this, "unlink called on actor different from self") + this unlinkFrom from + from unlinkFrom this + } + + private[actors] def unlinkFrom(from: AbstractActor) = synchronized { + links = links.filterNot(from.==) + } + + @volatile + private[actors] var _trapExit = false + + def trapExit = _trapExit + + def trapExit_=(value: Boolean) = _trapExit = value + + // guarded by this + private var exitReason: AnyRef = 'normal + // guarded by this + private[actors] var shouldExit = false + + /** + * <p> + * Terminates execution of <code>self</code> with the following + * effect on linked actors: + * </p> + * <p> + * For each linked actor <code>a</code> with + * <code>trapExit</code> set to <code>true</code>, send message + * <code>Exit(self, reason)</code> to <code>a</code>. + * </p> + * <p> + * For each linked actor <code>a</code> with + * <code>trapExit</code> set to <code>false</code> (default), + * call <code>a.exit(reason)</code> if + * <code>reason != 'normal</code>. + * </p> + */ + protected[actors] def exit(reason: AnyRef): Nothing = { + synchronized { + exitReason = reason + } + exit() + } + + /** + * Terminates with exit reason <code>'normal</code>. + */ + protected[actors] override def exit(): Nothing = { + val todo = synchronized { + if (!links.isEmpty) + exitLinked() + else + () => {} + } + todo() + super.exit() + } + + // Assume !links.isEmpty + // guarded by this + private[actors] def exitLinked(): () => Unit = { + _state = Actor.State.Terminated + // reset waitingFor, otherwise getState returns Suspended + waitingFor = Reactor.waitingForNone + // remove this from links + val mylinks = links.filterNot(this.==) + // unlink actors + mylinks.foreach(unlinkFrom(_)) + // return closure that locks linked actors + () => { + mylinks.foreach((linked: AbstractActor) => { + linked.synchronized { + if (!linked.exiting) { + linked.unlinkFrom(this) + linked.exit(this, exitReason) + } + } + }) + } + } + + // Assume !links.isEmpty + // guarded by this + private[actors] def exitLinked(reason: AnyRef): () => Unit = { + exitReason = reason + exitLinked() + } + + // Assume !this.exiting + private[actors] def exit(from: AbstractActor, reason: AnyRef) { + if (trapExit) { + this ! Exit(from, reason) + } else if (reason != 'normal) + stop(reason) + } + + /* Requires qualified private, because <code>RemoteActor</code> must + * register a termination handler. + */ + private[actors] def onTerminate(f: => Unit) { + scheduler.onTerminate(this) { f } + } + + private[actors] def internalPostStop() = {} + + private[actors] def stop(reason: AnyRef): Unit = { + synchronized { + shouldExit = true + exitReason = reason + // resume this Actor in a way that + // causes it to exit + // (because shouldExit == true) + if (isSuspended) + resumeActor() + else if (waitingFor ne Reactor.waitingForNone) { + waitingFor = Reactor.waitingForNone + // it doesn't matter what partial function we are passing here + val task = new ActorTask(this, null, waitingFor, null) + scheduler execute task + /* Here we should not throw a SuspendActorControl, + since the current method is called from an actor that + is in the process of exiting. + + Therefore, the contract for scheduleActor is that + it never throws a SuspendActorControl. + */ + } + } + } +} + +/** + * Used as the timeout pattern in + * <a href="Actor.html#receiveWithin(Long)" target="contentFrame"> + * <code>receiveWithin</code></a> and + * <a href="Actor.html#reactWithin(Long)" target="contentFrame"> + * <code>reactWithin</code></a>. + * + * @example {{{ + * receiveWithin(500) { + * case (x, y) => ... + * case TIMEOUT => ... + * } + * }}} + * + * @author Philipp Haller + */ +case object TIMEOUT + +/** + * Sent to an actor + * with `trapExit` set to `true` whenever one of its linked actors + * terminates. + * + * @param from the actor that terminated + * @param reason the reason that caused the actor to terminate + */ +case class Exit(from: AbstractActor, reason: AnyRef) + +/** + * Manages control flow of actor executions. + * + * @author Philipp Haller + */ +private[actors] class SuspendActorControl extends ControlThrowable diff --git a/src/actors/scala/actors/InternalReplyReactor.scala b/src/actors/scala/actors/InternalReplyReactor.scala new file mode 100644 index 0000000000..38295138d4 --- /dev/null +++ b/src/actors/scala/actors/InternalReplyReactor.scala @@ -0,0 +1,161 @@ +package scala.actors + +import java.util.{TimerTask} + +/** + * Extends the [[scala.actors.Reactor]] + * trait with methods to reply to the sender of a message. + * Sending a message to a <code>ReplyReactor</code> implicitly + * passes a reference to the sender together with the message. + * + * @author Philipp Haller + * + * @define actor `ReplyReactor` + */ +trait InternalReplyReactor extends Reactor[Any] with ReactorCanReply { + + /* A list of the current senders. The head of the list is + * the sender of the message that was received last. + */ + @volatile + private[actors] var senders: List[OutputChannel[Any]] = List() + + /* This option holds a TimerTask when the actor waits in a + * reactWithin. The TimerTask is cancelled when the actor + * resumes. + * + * guarded by this + */ + private[actors] var onTimeout: Option[TimerTask] = None + + /** + * Returns the $actor which sent the last received message. + */ + protected[actors] def internalSender: OutputChannel[Any] = senders.head + + /** + * Replies with <code>msg</code> to the sender. + */ + protected[actors] def reply(msg: Any) { + internalSender ! msg + } + + override def !(msg: Any) { + send(msg, Actor.rawSelf(scheduler)) + } + + override def forward(msg: Any) { + send(msg, Actor.sender) + } + + private[actors] override def resumeReceiver(item: (Any, OutputChannel[Any]), handler: PartialFunction[Any, Any], onSameThread: Boolean) { + synchronized { + if (!onTimeout.isEmpty) { + onTimeout.get.cancel() + onTimeout = None + } + } + senders = List(item._2) + super.resumeReceiver(item, handler, onSameThread) + } + + private[actors] override def searchMailbox(startMbox: MQueue[Any], + handler: PartialFunction[Any, Any], + resumeOnSameThread: Boolean) { + var tmpMbox = startMbox + var done = false + while (!done) { + val qel = tmpMbox.extractFirst((msg: Any, replyTo: OutputChannel[Any]) => { + senders = List(replyTo) + handler.isDefinedAt(msg) + }) + if (tmpMbox ne mailbox) + tmpMbox.foreach((m, s) => mailbox.append(m, s)) + if (null eq qel) { + synchronized { + // in mean time new stuff might have arrived + if (!sendBuffer.isEmpty) { + tmpMbox = new MQueue[Any]("Temp") + drainSendBuffer(tmpMbox) + // keep going + } else { + waitingFor = handler + // see Reactor.searchMailbox + throw Actor.suspendException + } + } + } else { + resumeReceiver((qel.msg, qel.session), handler, resumeOnSameThread) + done = true + } + } + } + + private[actors] override def makeReaction(fun: () => Unit, handler: PartialFunction[Any, Any], msg: Any): Runnable = + new ReplyReactorTask(this, fun, handler, msg) + + protected[actors] override def react(handler: PartialFunction[Any, Unit]): Nothing = { + assert(Actor.rawSelf(scheduler) == this, "react on channel belonging to other actor") + super.react(handler) + } + + + /** + * Receives a message from this $actor's mailbox within a certain + * time span. + * + * This method never returns. Therefore, the rest of the computation + * has to be contained in the actions of the partial function. + * + * @param msec the time span before timeout + * @param handler a partial function with message patterns and actions + */ + protected[actors] def reactWithin(msec: Long)(handler: PartialFunction[Any, Unit]): Nothing = { + assert(Actor.rawSelf(scheduler) == this, "react on channel belonging to other actor") + + synchronized { drainSendBuffer(mailbox) } + + // first, remove spurious TIMEOUT message from mailbox if any + mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => m == TIMEOUT) + + while (true) { + val qel = mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => { + senders = List(replyTo) + handler isDefinedAt m + }) + if (null eq qel) { + synchronized { + // in mean time new messages might have arrived + if (!sendBuffer.isEmpty) { + drainSendBuffer(mailbox) + // keep going + } else if (msec == 0L) { + // throws Actor.suspendException + resumeReceiver((TIMEOUT, this), handler, false) + } else { + waitingFor = handler + val thisActor = this + onTimeout = Some(new TimerTask { + def run() { thisActor.send(TIMEOUT, thisActor) } + }) + Actor.timer.schedule(onTimeout.get, msec) + throw Actor.suspendException + } + } + } else + resumeReceiver((qel.msg, qel.session), handler, false) + } + throw Actor.suspendException + } + + override def getState: Actor.State.Value = synchronized { + if (waitingFor ne Reactor.waitingForNone) { + if (onTimeout.isEmpty) + Actor.State.Suspended + else + Actor.State.TimedSuspended + } else + _state + } + +} diff --git a/src/actors/scala/actors/OutputChannel.scala b/src/actors/scala/actors/OutputChannel.scala index 089b3d0981..1fba684975 100644 --- a/src/actors/scala/actors/OutputChannel.scala +++ b/src/actors/scala/actors/OutputChannel.scala @@ -43,5 +43,5 @@ trait OutputChannel[-Msg] { /** * Returns the `Actor` that is receiving from this $actor. */ - def receiver: Actor + def receiver: InternalActor } diff --git a/src/actors/scala/actors/ReactChannel.scala b/src/actors/scala/actors/ReactChannel.scala index fccde34272..81a166c1a4 100644 --- a/src/actors/scala/actors/ReactChannel.scala +++ b/src/actors/scala/actors/ReactChannel.scala @@ -12,7 +12,7 @@ package scala.actors /** * @author Philipp Haller */ -private[actors] class ReactChannel[Msg](receiver: ReplyReactor) extends InputChannel[Msg] { +private[actors] class ReactChannel[Msg](receiver: InternalReplyReactor) extends InputChannel[Msg] { private case class SendToReactor(channel: ReactChannel[Msg], msg: Msg) diff --git a/src/actors/scala/actors/Reactor.scala b/src/actors/scala/actors/Reactor.scala index 7d21e9f91e..8fc7578344 100644 --- a/src/actors/scala/actors/Reactor.scala +++ b/src/actors/scala/actors/Reactor.scala @@ -253,7 +253,7 @@ trait Reactor[Msg >: Null] extends OutputChannel[Msg] with Combinators { _state } - implicit def mkBody[A](body: => A) = new Actor.Body[A] { + implicit def mkBody[A](body: => A) = new InternalActor.Body[A] { def andThen[B](other: => B): Unit = Reactor.this.seq(body, other) } diff --git a/src/actors/scala/actors/ReactorCanReply.scala b/src/actors/scala/actors/ReactorCanReply.scala index 68f9999776..dabd0832f0 100644 --- a/src/actors/scala/actors/ReactorCanReply.scala +++ b/src/actors/scala/actors/ReactorCanReply.scala @@ -16,7 +16,7 @@ package scala.actors * @author Philipp Haller */ private[actors] trait ReactorCanReply extends CanReply[Any, Any] { - _: ReplyReactor => + _: InternalReplyReactor => type Future[+P] = scala.actors.Future[P] diff --git a/src/actors/scala/actors/ReplyReactor.scala b/src/actors/scala/actors/ReplyReactor.scala index 0e5ce00c91..0ffbbd3cce 100644 --- a/src/actors/scala/actors/ReplyReactor.scala +++ b/src/actors/scala/actors/ReplyReactor.scala @@ -5,165 +5,12 @@ ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ - package scala.actors -import java.util.{Timer, TimerTask} - -/** - * Extends the [[scala.actors.Reactor]] trait with methods to reply to the - * sender of a message. - * - * Sending a message to a `ReplyReactor` implicitly passes a reference to - * the sender together with the message. - * - * @author Philipp Haller - * - * @define actor `ReplyReactor` - */ -trait ReplyReactor extends Reactor[Any] with ReactorCanReply { - - /* A list of the current senders. The head of the list is - * the sender of the message that was received last. - */ - @volatile - private[actors] var senders: List[OutputChannel[Any]] = List() - - /* This option holds a TimerTask when the actor waits in a - * reactWithin. The TimerTask is cancelled when the actor - * resumes. - * - * guarded by this - */ - private[actors] var onTimeout: Option[TimerTask] = None - - /** - * Returns the $actor which sent the last received message. - */ - protected[actors] def sender: OutputChannel[Any] = senders.head - - /** - * Replies with `msg` to the sender. - */ - protected[actors] def reply(msg: Any) { - sender ! msg - } - - override def !(msg: Any) { - send(msg, Actor.rawSelf(scheduler)) - } - - override def forward(msg: Any) { - send(msg, Actor.sender) - } - - private[actors] override def resumeReceiver(item: (Any, OutputChannel[Any]), handler: PartialFunction[Any, Any], onSameThread: Boolean) { - synchronized { - if (!onTimeout.isEmpty) { - onTimeout.get.cancel() - onTimeout = None - } - } - senders = List(item._2) - super.resumeReceiver(item, handler, onSameThread) - } - - private[actors] override def searchMailbox(startMbox: MQueue[Any], - handler: PartialFunction[Any, Any], - resumeOnSameThread: Boolean) { - var tmpMbox = startMbox - var done = false - while (!done) { - val qel = tmpMbox.extractFirst((msg: Any, replyTo: OutputChannel[Any]) => { - senders = List(replyTo) - handler.isDefinedAt(msg) - }) - if (tmpMbox ne mailbox) - tmpMbox.foreach((m, s) => mailbox.append(m, s)) - if (null eq qel) { - synchronized { - // in mean time new stuff might have arrived - if (!sendBuffer.isEmpty) { - tmpMbox = new MQueue[Any]("Temp") - drainSendBuffer(tmpMbox) - // keep going - } else { - waitingFor = handler - // see Reactor.searchMailbox - throw Actor.suspendException - } - } - } else { - resumeReceiver((qel.msg, qel.session), handler, resumeOnSameThread) - done = true - } - } - } - - private[actors] override def makeReaction(fun: () => Unit, handler: PartialFunction[Any, Any], msg: Any): Runnable = - new ReplyReactorTask(this, fun, handler, msg) - - protected[actors] override def react(handler: PartialFunction[Any, Unit]): Nothing = { - assert(Actor.rawSelf(scheduler) == this, "react on channel belonging to other actor") - super.react(handler) - } - - /** - * Receives a message from this $actor's mailbox within a certain - * time span. - * - * This method never returns. Therefore, the rest of the computation - * has to be contained in the actions of the partial function. - * - * @param msec the time span before timeout - * @param handler a partial function with message patterns and actions - */ - protected[actors] def reactWithin(msec: Long)(handler: PartialFunction[Any, Unit]): Nothing = { - assert(Actor.rawSelf(scheduler) == this, "react on channel belonging to other actor") - - synchronized { drainSendBuffer(mailbox) } - - // first, remove spurious TIMEOUT message from mailbox if any - mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => m == TIMEOUT) - - while (true) { - val qel = mailbox.extractFirst((m: Any, replyTo: OutputChannel[Any]) => { - senders = List(replyTo) - handler isDefinedAt m - }) - if (null eq qel) { - synchronized { - // in mean time new messages might have arrived - if (!sendBuffer.isEmpty) { - drainSendBuffer(mailbox) - // keep going - } else if (msec == 0L) { - // throws Actor.suspendException - resumeReceiver((TIMEOUT, this), handler, false) - } else { - waitingFor = handler - val thisActor = this - onTimeout = Some(new TimerTask { - def run() { thisActor.send(TIMEOUT, thisActor) } - }) - Actor.timer.schedule(onTimeout.get, msec) - throw Actor.suspendException - } - } - } else - resumeReceiver((qel.msg, qel.session), handler, false) - } - throw Actor.suspendException - } - - override def getState: Actor.State.Value = synchronized { - if (waitingFor ne Reactor.waitingForNone) { - if (onTimeout.isEmpty) - Actor.State.Suspended - else - Actor.State.TimedSuspended - } else - _state - } - +@deprecated("Scala Actors are beeing removed from the standard library. Please refer to the migration guide.", "2.10") +trait ReplyReactor extends InternalReplyReactor { + + protected[actors] def sender: OutputChannel[Any] = super.internalSender + } + diff --git a/src/actors/scala/actors/ReplyReactorTask.scala b/src/actors/scala/actors/ReplyReactorTask.scala index cb63d7e000..d38eb50381 100644 --- a/src/actors/scala/actors/ReplyReactorTask.scala +++ b/src/actors/scala/actors/ReplyReactorTask.scala @@ -17,13 +17,13 @@ package scala.actors * changes to the underlying var invisible.) I can't figure out what's supposed * to happen, so I renamed the constructor parameter to at least be less confusing. */ -private[actors] class ReplyReactorTask(replyReactor: ReplyReactor, +private[actors] class ReplyReactorTask(replyReactor: InternalReplyReactor, fun: () => Unit, handler: PartialFunction[Any, Any], msg: Any) extends ReactorTask(replyReactor, fun, handler, msg) { - var saved: ReplyReactor = _ + var saved: InternalReplyReactor = _ protected override def beginExecution() { saved = Actor.tl.get diff --git a/src/actors/scala/actors/UncaughtException.scala b/src/actors/scala/actors/UncaughtException.scala index 3e6efe3b7c..a3e7f795f1 100644 --- a/src/actors/scala/actors/UncaughtException.scala +++ b/src/actors/scala/actors/UncaughtException.scala @@ -20,7 +20,7 @@ package scala.actors * @author Philipp Haller * @author Erik Engbrecht */ -case class UncaughtException(actor: Actor, +case class UncaughtException(actor: InternalActor, message: Option[Any], sender: Option[OutputChannel[Any]], thread: Thread, diff --git a/src/build/maven/maven-deploy.xml b/src/build/maven/maven-deploy.xml index e0f31a5db2..7f8343a84e 100644 --- a/src/build/maven/maven-deploy.xml +++ b/src/build/maven/maven-deploy.xml @@ -111,11 +111,12 @@ <deploy-local name="scala-library" version="@{version}" repository="@{repository}" /> <deploy-local name="scala-compiler" version="@{version}" repository="@{repository}" /> <deploy-local-plugin name="continuations" version="@{version}" repository="@{repository}"/> + <deploy-local name="scala-actors" version="@{version}" repository="@{repository}" /> <deploy-local name="scala-dbc" version="@{version}" repository="@{repository}" /> <deploy-local name="scala-swing" version="@{version}" repository="@{repository}"/> - <deploy-local name="scalap" version="@{version}" repository="@{repository}"/> - <deploy-local name="scala-partest" version="@{version}" repository="@{repository}"/> - <deploy-local name="jline" version="@{version}" repository="@{repository}"/> + <deploy-local name="scalap" version="@{version}" repository="@{repository}"/> + <deploy-local name="scala-partest" version="@{version}" repository="@{repository}"/> + <deploy-local name="jline" version="@{version}" repository="@{repository}"/> </sequential> </macrodef> </target> @@ -168,12 +169,13 @@ <artifact:attach type="jar" file="scala-library/scala-library-docs.jar" classifier="javadoc" /> </extra-attachments> </deploy-remote> - <deploy-remote name="jline" version="@{version}" repository="@{repository}"/> + <deploy-remote name="jline" version="@{version}" repository="@{repository}"/> <deploy-remote name="scala-compiler" version="@{version}" repository="@{repository}" /> <deploy-remote name="scala-dbc" version="@{version}" repository="@{repository}" /> <deploy-remote name="scala-swing" version="@{version}" repository="@{repository}"/> - <deploy-remote name="scalap" version="@{version}" repository="@{repository}"/> - <deploy-remote name="scala-partest" version="@{version}" repository="@{repository}"/> + <deploy-remote name="scala-actors" version="@{version}" repository="@{repository}"/> + <deploy-remote name="scalap" version="@{version}" repository="@{repository}"/> + <deploy-remote name="scala-partest" version="@{version}" repository="@{repository}"/> <deploy-remote-plugin name="continuations" version="@{version}" repository="@{repository}"/> </sequential> </macrodef> @@ -234,13 +236,14 @@ <attribute name="version" /> <sequential> <deploy-remote-plugin-signed name="continuations" version="@{version}" repository="@{repository}"/> - <deploy-remote-signed name="scala-library" version="@{version}" repository="@{repository}"/> - <deploy-remote-signed name="jline" version="@{version}" repository="@{repository}"/> + <deploy-remote-signed name="scala-library" version="@{version}" repository="@{repository}"/> + <deploy-remote-signed name="jline" version="@{version}" repository="@{repository}"/> <deploy-remote-signed name="scala-compiler" version="@{version}" repository="@{repository}" /> <deploy-remote-signed name="scala-dbc" version="@{version}" repository="@{repository}" /> <deploy-remote-signed name="scala-swing" version="@{version}" repository="@{repository}"/> - <deploy-remote-signed name="scalap" version="@{version}" repository="@{repository}"/> - <deploy-remote-signed name="scala-partest" version="@{version}" repository="@{repository}"/> + <deploy-remote-signed name="scala-actors" version="@{version}" repository="@{repository}"/> + <deploy-remote-signed name="scalap" version="@{version}" repository="@{repository}"/> + <deploy-remote-signed name="scala-partest" version="@{version}" repository="@{repository}"/> </sequential> </macrodef> </target> diff --git a/src/build/maven/scala-actors-pom.xml b/src/build/maven/scala-actors-pom.xml new file mode 100644 index 0000000000..12bae2a23d --- /dev/null +++ b/src/build/maven/scala-actors-pom.xml @@ -0,0 +1,62 @@ +<project + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.scala-lang</groupId> + <artifactId>scala-actors</artifactId> + <packaging>jar</packaging> + <version>@VERSION@</version> + <name>Scala Actors library</name> + <description>Deprecated Actors Library for Scala</description> + <url>http://www.scala-lang.org/</url> + <inceptionYear>2006</inceptionYear> + <organization> + <name>LAMP/EPFL</name> + <url>http://lamp.epfl.ch/</url> + </organization> + <licenses> + <license> + <name>BSD-like</name> + <url>http://www.scala-lang.org/downloads/license.html + </url> + <distribution>repo</distribution> + </license> + </licenses> + <scm> + <connection>scm:svn:http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk</connection> + <url>https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk</url> + </scm> + <issueManagement> + <system>trac</system> + <url>http://lampsvn.epfl.ch/trac/scala + </url> + </issueManagement> + <dependencies> + <dependency> + <groupId>org.scala-lang</groupId> + <artifactId>scala-library</artifactId> + <version>@VERSION@</version> + </dependency> + </dependencies> + <distributionManagement> + <repository> + <id>scala-tools.org</id> + <url>@RELEASE_REPOSITORY@</url> + </repository> + <snapshotRepository> + <id>scala-tools.org</id> + <url>@SNAPSHOT_REPOSITORY@</url> + <uniqueVersion>false</uniqueVersion> + </snapshotRepository> + </distributionManagement> + <developers> + <developer> + <id>lamp</id> + <name>EPFL LAMP</name> + </developer> + <developer> + <id>Typesafe</id> + <name>Typesafe, Inc.</name> + </developer> + </developers> +</project> diff --git a/src/build/pack.xml b/src/build/pack.xml index e79895e3a8..c12cfa44bf 100644 --- a/src/build/pack.xml +++ b/src/build/pack.xml @@ -114,8 +114,8 @@ MAIN DISTRIBUTION SBAZ version="${version.number}" desc="The Scala library. This is the minimal requirement to run any Scala program." link="${sbaz.universe}/scala-library-${version.number}.sbp"> - <libset dir="${dist.dir}/lib" includes="scala-library.jar,scala-dbc.jar,scala-swing.jar"/> - <srcset dir="${dist.dir}/src" includes="scala-library-src.jar,scala-dbc-src.jar,scala-swing-src.jar"/> + <libset dir="${dist.dir}/lib" includes="scala-library.jar,scala-dbc.jar,scala-swing.jar,scala-actors.jar"/> + <srcset dir="${dist.dir}/src" includes="scala-library-src.jar,scala-dbc-src.jar,scala-swing-src.jar,scala-actors-src.jar"/> <looseset destination="doc"> <fileset dir="${dist.dir}/doc" includes="LICENSE,README"/> </looseset> @@ -228,6 +228,7 @@ MAIN DISTRIBUTION SBAZ <mvn-copy-lib mvn.artifact.name="scala-compiler"/> <mvn-copy-lib mvn.artifact.name="scala-dbc"/> <mvn-copy-lib mvn.artifact.name="scala-swing"/> + <mvn-copy-lib mvn.artifact.name="scala-actors"/> <mvn-copy-lib mvn.artifact.name="scala-partest"/> <mvn-copy-lib mvn.artifact.name="scalap"/> </target> @@ -290,11 +291,13 @@ MAIN DISTRIBUTION SBAZ basedir="${build-docs.dir}/continuations-plugin"> <include name="**/*"/> </jar> - <!-- TODO - Scala swing, dbc should maybe have thier own jar, but creating it is SLOW. --> + <!-- TODO - Scala swing, dbc and actors should maybe have thier own jar, but creating it is SLOW. --> <copy tofile="${dists.dir}/maven/${version.number}/scala-swing/scala-swing-docs.jar" file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar"/> <copy tofile="${dists.dir}/maven/${version.number}/scala-dbc/scala-dbc-docs.jar" file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar"/> + <copy tofile="${dists.dir}/maven/${version.number}/scala-actors/scala-actors-docs.jar" + file="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar"/> </target> <target name="pack-maven.latest.unix" depends="pack-maven.docs" unless="os.win"> diff --git a/src/compiler/scala/reflect/internal/Flags.scala b/src/compiler/scala/reflect/internal/Flags.scala index 8aae80eed4..3110d73461 100644 --- a/src/compiler/scala/reflect/internal/Flags.scala +++ b/src/compiler/scala/reflect/internal/Flags.scala @@ -107,7 +107,7 @@ class ModifierFlags { // pre: PRIVATE or PROTECTED are also set final val JAVA = 0x00100000 // symbol was defined by a Java class final val STATIC = 0x00800000 // static field, method or class - final val CASEACCESSOR = 0x01000000 // symbol is a case parameter (or its accessor) + final val CASEACCESSOR = 0x01000000 // symbol is a case parameter (or its accessor, or a GADT skolem) final val TRAIT = 0x02000000 // symbol is a trait final val DEFAULTPARAM = 0x02000000 // the parameter has a default value final val PARAMACCESSOR = 0x20000000 // for field definitions generated for primary constructor diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala index 1003fa804f..04381937d1 100644 --- a/src/compiler/scala/reflect/internal/Importers.scala +++ b/src/compiler/scala/reflect/internal/Importers.scala @@ -351,6 +351,8 @@ trait Importers { self: SymbolTable => new ApplyToImplicitArgs(importTree(fun), args map importTree) case _: from.ApplyImplicitView => new ApplyImplicitView(importTree(fun), args map importTree) + case _: from.ApplyConstructor => + new ApplyConstructor(importTree(fun), args map importTree) case _ => new Apply(importTree(fun), args map importTree) } diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 9678d2b8cd..907f7d1237 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -269,11 +269,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Create a new existential type skolem with this symbol its owner, * based on the given symbol and origin. */ - def newExistentialSkolem(basis: Symbol, origin: AnyRef, name: TypeName = null, info: Type = null): TypeSkolem = { - val skolem = newTypeSkolemSymbol(if (name eq null) basis.name.toTypeName else name, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) - skolem setInfo (if (info eq null) basis.info cloneInfo skolem else info) + def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = { + val skolem = newTypeSkolemSymbol(basis.name.toTypeName, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) + skolem setInfo (basis.info cloneInfo skolem) } + // flags set up to maintain TypeSkolem's invariant: origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) + // CASEACCESSOR | SYNTHETIC used to single this symbol out in deskolemizeGADT + def newGADTSkolem(name: TypeName, origin: Symbol, info: Type): TypeSkolem = + newTypeSkolemSymbol(name, origin, origin.pos, origin.flags & ~(EXISTENTIAL | PARAM) | CASEACCESSOR | SYNTHETIC) setInfo info + + final def newExistential(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = newAbstractType(name, pos, EXISTENTIAL | newFlags) @@ -495,6 +501,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // List[T] forSome { type T } final def isExistentialSkolem = isExistentiallyBound && isSkolem final def isExistentialQuantified = isExistentiallyBound && !isSkolem + final def isGADTSkolem = isSkolem && hasFlag(CASEACCESSOR | SYNTHETIC) // class C extends D( { class E { ... } ... } ). Here, E is a class local to a constructor final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR) @@ -1985,6 +1992,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isTrait) ("trait", "trait", "TRT") else if (isClass) ("class", "class", "CLS") else if (isType) ("type", "type", "TPE") + else if (isClassConstructor && isPrimaryConstructor) ("primary constructor", "constructor", "PCTOR") else if (isClassConstructor) ("constructor", "constructor", "CTOR") else if (isSourceMethod) ("method", "method", "METH") else if (isTerm) ("value", "value", "VAL") @@ -2129,12 +2137,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE else ExplicitFlags + // make the error message more googlable + def flagsExplanationString = + if (isGADTSkolem) " (this is a GADT skolem)" + else "" + def accessString = hasFlagsToString(PRIVATE | PROTECTED | LOCAL) def defaultFlagString = hasFlagsToString(defaultFlagMask) private def defStringCompose(infoString: String) = compose( defaultFlagString, keyString, - varianceString + nameString + infoString + varianceString + nameString + infoString + flagsExplanationString ) /** String representation of symbol's definition. It uses the * symbol's raw info to avoid forcing types. @@ -2477,7 +2490,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * where the skolem was introduced (this is important for knowing when to pack it * again into ab Existential). origin is `null` only in skolemizeExistentials called * from <:< or isAsSpecific, because here its value does not matter. - * I elieve the following invariant holds: + * I believe the following invariant holds: * * origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) */ diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index 769d7a9ed1..ce3de94335 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -17,7 +17,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass } + import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -312,6 +312,24 @@ abstract class TreeInfo { case _ => false } + /** Is this tree comprised of nothing but identifiers, + * but possibly in bindings or tuples? For instance + * + * foo @ (bar, (baz, quux)) + * + * is a variable pattern; if the structure matches, + * then the remainder is inevitable. + */ + def isVariablePattern(tree: Tree): Boolean = tree match { + case Bind(name, pat) => isVariablePattern(pat) + case Ident(name) => true + case Apply(sel, args) => + ( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName) + && (args forall isVariablePattern) + ) + case _ => false + } + /** Is this argument node of the form <expr> : _* ? */ def isWildcardStarArg(tree: Tree): Boolean = tree match { diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index f823110440..8ed0ee6357 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -433,7 +433,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => /** Hook for extensions */ def xprintTree(treePrinter: TreePrinter, tree: Tree) = - treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) + treePrinter.print(tree.printingPrefix+tree.productIterator.mkString("(", ", ", ")")) def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index 9b1712b790..1a40e0105c 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -350,8 +350,9 @@ trait Trees extends api.Trees { self: SymbolTable => "subst[%s, %s](%s)".format(fromStr, toStr, (from, to).zipped map (_ + " -> " + _) mkString ", ") } - // NOTE: if symbols in `from` occur multiple times in the `tree` passed to `transform`, - // the resulting Tree will be a graph, not a tree... this breaks all sorts of stuff, + // NOTE: calls shallowDuplicate on trees in `to` to avoid problems when symbols in `from` + // occur multiple times in the `tree` passed to `transform`, + // otherwise, the resulting Tree would be a graph, not a tree... this breaks all sorts of stuff, // notably concerning the mutable aspects of Trees (such as setting their .tpe) class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer { override def transform(tree: Tree): Tree = tree match { diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 2382413a9a..2bb19e2b65 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -1041,8 +1041,8 @@ trait Types extends api.Types { self: SymbolTable => baseClasses.head.newOverloaded(this, members.toList) } } - /** The existential skolems and existentially quantified variables which are free in this type */ - def existentialSkolems: List[Symbol] = { + /** The (existential or otherwise) skolems and existentially quantified variables which are free in this type */ + def skolemsExceptMethodTypeParams: List[Symbol] = { var boundSyms: List[Symbol] = List() var skolems: List[Symbol] = List() for (t <- this) { @@ -1050,7 +1050,8 @@ trait Types extends api.Types { self: SymbolTable => case ExistentialType(quantified, qtpe) => boundSyms = boundSyms ::: quantified case TypeRef(_, sym, _) => - if ((sym hasFlag EXISTENTIAL) && !(boundSyms contains sym) && !(skolems contains sym)) + if ((sym.isExistentialSkolem || sym.isGADTSkolem) && // treat GADT skolems like existential skolems + !((boundSyms contains sym) || (skolems contains sym))) skolems = sym :: skolems case _ => } @@ -2616,15 +2617,6 @@ trait Types extends api.Types { self: SymbolTable => } } - // TODO: I don't really know why this happens -- maybe because - // the owner hierarchy changes? the other workaround (besides - // repackExistential) is to explicitly pass expectedTp as the type - // argument for the call to guard, but repacking the existential - // somehow feels more robust - // - // TODO: check if optimization makes a difference, try something else - // if necessary (cache?) - /** Repack existential types, otherwise they sometimes get unpacked in the * wrong location (type inference comes up with an unexpected skolem) */ diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 9466d1c1f2..3d4253f941 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -9,14 +9,13 @@ package ast import compat.Platform.EOL import symtab.Flags._ -/** The object <code>nodePrinter</code> converts the internal tree - * representation to a string formatted as a Scala expression. +/** The object `nodePrinter` converts the internal tree + * representation to a string. * * @author Stephane Micheloud - * @version 1.0 + * @author Paul Phillips */ abstract class NodePrinters { - val global: Global import global._ @@ -25,262 +24,293 @@ abstract class NodePrinters { } var infolevel = InfoLevel.Quiet - object nodeToString extends Function1[Tree, String] { + def nodeToString: Tree => String = + if (sys.props contains "scala.colors") nodeToColorizedString + else nodeToRegularString + + object nodeToRegularString extends DefaultPrintAST with (Tree => String) { + def apply(tree: Tree) = stringify(tree) + } + + object nodeToColorizedString extends ColorPrintAST with (Tree => String) { + def apply(tree: Tree) = stringify(tree) + } + + trait ColorPrintAST extends DefaultPrintAST { + import scala.tools.util.color._ + + def keywordColor = Cyan + def typeColor = Yellow + def termColor = Blue + def flagColor = Red + def literalColor = Green + + override def showFlags(tree: MemberDef) = "" + ( + super.showFlags(tree) in flagColor.bright + ) + override def showDefTreeName(tree: DefTree) = "" + ( + if (tree.name.isTermName) tree.name.decode in termColor.bright + else tree.name.decode in typeColor.bright + ) + override def showName(name: Name): String = "" + ( + if (name == nme.EMPTY || name == tpnme.EMPTY) "<empty>" in keywordColor + else if (name.isTermName) name.decode in termColor + else name.decode in typeColor + ) + override def showLiteral(lit: Literal) = "" + ( + super.showLiteral(lit) in literalColor.bright + ) + } + + trait DefaultPrintAST extends PrintAST { + def showDefTreeName(tree: DefTree) = showName(tree.name) + def showFlags(tree: MemberDef) = flagsToString(tree.symbol.flags | tree.mods.flags) + def showLiteral(lit: Literal) = lit.value.escapedStringValue + def showTypeTree(tt: TypeTree) = "<tpt>" + showAttributes(tt) + def showName(name: Name) = name match { + case nme.EMPTY | tpnme.EMPTY => "<empty>" + case name => "\"" + name + "\"" + } + + def showSymbol(tree: Tree): String = { + val sym = tree.symbol + if (sym == null || sym == NoSymbol) "" + else " sym/owner/tpe=%s %s/%s/%s".format(sym.accurateKindString, sym.name, sym.owner, sym.tpe) + } + def showType(tree: Tree): String = { + val tpe = tree.tpe + if (tpe == null || tpe == NoType) "" + else " tree.tpe=" + tpe + } + + def showAttributes(tree: Tree): String = { + if (infolevel == InfoLevel.Quiet) "" + else { + try { showSymbol(tree) + showType(tree) trim } + catch { case ex: Throwable => "sym= <error> " + ex.getMessage } + } + } + } + + trait PrintAST { private val buf = new StringBuilder + private var level = 0 - def apply(tree: Tree): String = { - def traverse(tree: Tree, level: Int, comma: Boolean) { - def println(s: String) { - for (i <- 0 until level) buf.append(" ") - buf.append(s) - buf.append(EOL) - } - def printcln(s: String) { - for (i <- 0 until level) buf.append(" ") - buf.append(s) - if (comma) buf.append(",") - buf.append(EOL) - } - def annotationInfoToString(annot: AnnotationInfo): String = { - val str = new StringBuilder - str.append(annot.atp.toString()) - if (!annot.args.isEmpty) - str.append(annot.args.mkString("(", ",", ")")) - if (!annot.assocs.isEmpty) - for (((name, value), index) <- annot.assocs.zipWithIndex) { - if (index > 0) - str.append(", ") - str.append(name).append(" = ").append(value) - } - str.toString - } - def symflags(tree: Tree): String = { - val buf = new StringBuffer - val sym = tree.symbol - buf append flagsToString(sym.flags) + def showName(name: Name): String + def showDefTreeName(defTree: DefTree): String + def showFlags(tree: MemberDef): String + def showLiteral(lit: Literal): String + def showTypeTree(tt: TypeTree): String + def showAttributes(tree: Tree): String // symbol and type + + def showRefTreeName(tree: Tree): String = tree match { + case SelectFromTypeTree(qual, name) => showRefTreeName(qual) + "#" + showName(name) + case Select(qual, name) => showRefTreeName(qual) + "." + showName(name) + case Ident(name) => showName(name) + case _ => "" + tree + } + def showRefTree(tree: RefTree): String = { + def prefix0 = showRefTreeName(tree.qualifier) + def prefix = if (prefix0 == "") "" else (tree match { + case SelectFromTypeTree(_, _) => prefix0 + "#" + case Select(_, _) => prefix0 + "." + case _ => "" + }) + def attrs = showAttributes(tree) match { + case "" => "" + case s => " // " + s + } + prefix + showName(tree.name) + attrs + } + + def stringify(tree: Tree): String = { + buf.clear() + level = 0 + traverse(tree) + buf.toString + } + def traverseAny(x: Any) { + x match { + case t: Tree => traverse(t) + case xs: List[_] => printMultiline("List", "")(xs foreach traverseAny) + case _ => println("" + x) + } + } + def println(s: String) = printLine(s, "") + + def printLine(value: String, comment: String) { + buf append " " * level + buf append value + if (comment != "") { + buf append " // " + buf append comment + } + buf append EOL + } - val annots = ", annots=" + ( - if (!sym.annotations.isEmpty) - sym.annotations.map(annotationInfoToString).mkString("[", ",", "]") - else - tree.asInstanceOf[MemberDef].mods.annotations) - (if (buf.length() > 2) buf.substring(3) - else "0") + ", // flags=" + flagsToString(sym.flags) + annots + def annotationInfoToString(annot: AnnotationInfo): String = { + val str = new StringBuilder + str.append(annot.atp.toString()) + if (!annot.args.isEmpty) + str.append(annot.args.mkString("(", ",", ")")) + if (!annot.assocs.isEmpty) + for (((name, value), index) <- annot.assocs.zipWithIndex) { + if (index > 0) + str.append(", ") + str.append(name).append(" = ").append(value) } + str.toString + } + def printModifiers(tree: MemberDef) { + val annots0 = tree.symbol.annotations match { + case Nil => tree.mods.annotations + case xs => xs map annotationInfoToString + } + val annots = annots0 match { + case Nil => "" + case xs => " " + xs.mkString("@{ ", ", ", " }") + } + val flagString = showFlags(tree) match { + case "" => "0" + case s => s + } + println(flagString + annots) + } - def nodeinfo(tree: Tree): String = - if (infolevel == InfoLevel.Quiet) "" - else { - try { - val buf = new StringBuilder(" // sym=" + tree.symbol) - if (tree.hasSymbol) { - if (tree.symbol.isPrimaryConstructor) - buf.append(", isPrimaryConstructor") - else if (tree.symbol.isConstructor) - buf.append(", isConstructor") - if (tree.symbol != NoSymbol) - buf.append(", sym.owner=" + tree.symbol.owner) - buf.append(", sym.tpe=" + tree.symbol.tpe) - } - buf.append(", tpe=" + tree.tpe) - if (tree.tpe != null) { - var sym = tree.tpe.termSymbol - if (sym == NoSymbol) sym = tree.tpe.typeSymbol - buf.append(", tpe.sym=" + sym) - if (sym != NoSymbol) { - buf.append(", tpe.sym.owner=" + sym.owner) - if ((infolevel > InfoLevel.Normal) && - !(sym.owner eq definitions.ScalaPackageClass) && - !sym.isModuleClass && !sym.isPackageClass && - !sym.isJavaDefined) { - val members = for (m <- tree.tpe.decls) - yield m.toString() + ": " + m.tpe + ", " - buf.append(", tpe.decls=" + members) - } + def applyCommon(tree: Tree, fun: Tree, args: List[Tree]) { + printMultiline(tree) { + traverse(fun) + traverseList("Nil", _ + " arguments(s)")(args) + } + } + + def printMultiline(tree: Tree)(body: => Unit) { + printMultiline(tree.printingPrefix, showAttributes(tree))(body) + } + def printMultiline(prefix: String, comment: String)(body: => Unit) { + printLine(prefix + "(", comment) + indent(body) + println(")") + } + @inline private def indent[T](body: => T): T = { + level += 1 + try body + finally level -= 1 + } + + def traverseList(ifEmpty: String, comment: Int => String)(trees: List[Tree]) { + if (trees.isEmpty) + println(ifEmpty) + else + printMultiline("List", comment(trees.length))(trees foreach traverse) + } + + def traverse(tree: Tree) { + tree match { + case AppliedTypeTree(tpt, args) => applyCommon(tree, tpt, args) + case ApplyDynamic(fun, args) => applyCommon(tree, fun, args) + case Apply(fun, args) => applyCommon(tree, fun, args) + + case Block(stats, expr) => + printMultiline(tree) { + traverseList("{}", _ + " statement(s)")(stats) + traverse(expr) + } + case cd @ ClassDef(mods, name, tparams, impl) => + printMultiline(tree) { + printModifiers(cd) + println(showDefTreeName(cd)) + traverseList("[]", _ + " type parameter(s)")(tparams) + traverse(impl) + } + case md @ ModuleDef(mods, name, impl) => + printMultiline(tree) { + printModifiers(md) + println(showDefTreeName(md)) + traverse(impl) + } + case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + printMultiline(tree) { + printModifiers(dd) + println(showDefTreeName(dd)) + traverseList("[]", _ + " type parameter(s)")(tparams) + vparamss match { + case Nil => println("Nil") + case Nil :: Nil => println("List(Nil)") + case xss => + printMultiline("List", xss.length + " parameter list(s)") { + xss foreach (xs => traverseList("()", _ + " parameter(s)")(xs)) } + } + traverse(tpt) + traverse(rhs) + } + case EmptyTree => + println(showName(nme.EMPTY)) + case lit @ Literal(value) => + println(showLiteral(lit)) + case New(tpt) => + printMultiline(tree)(traverse(tpt)) + case Super(This(qual), mix) => + println("Super(This(" + showName(qual) + "), " + showName(mix) + ")") + case Super(qual, mix) => + printMultiline(tree) { + traverse(qual) + showName(mix) + } + case Template(parents, self, body) => + printMultiline(tree) { + val ps0 = parents map { p => + if (p.tpe eq null) p match { + case x: RefTree => showRefTree(x) + case x => "" + x } - buf.toString - } catch { - case ex: Throwable => - return " // sym= <error> " + ex.getMessage + else showName(newTypeName(p.tpe.typeSymbol.fullName)) } + printLine(ps0 mkString ", ", "parents") + traverse(self) + traverseList("{}", _ + " statements in body")(body) } - def nodeinfo2(tree: Tree): String = - (if (comma) "," else "") + nodeinfo(tree) - - def applyCommon(name: String, tree: Tree, fun: Tree, args: List[Tree]) { - println(name + "(" + nodeinfo(tree)) - traverse(fun, level + 1, true) - if (args.isEmpty) - println(" Nil // no argument") - else { - val n = args.length - println(" List( // " + n + " arguments(s)") - for (i <- 0 until n) - traverse(args(i), level + 2, i < n-1) - println(" )") + case This(qual) => + println("This(\"" + showName(qual) + "\")" + showAttributes(tree)) + case TypeApply(fun, args) => + printMultiline(tree) { + traverse(fun) + traverseList("[]", _ + " type argument(s)")(args) } - printcln(")") - } + case tt @ TypeTree() => + println(showTypeTree(tt)) - tree match { - case AppliedTypeTree(tpt, args) => applyCommon("AppliedTypeTree", tree, tpt, args) - case Apply(fun, args) => applyCommon("Apply", tree, fun, args) - case ApplyDynamic(fun, args) => applyCommon("ApplyDynamic", tree, fun, args) + case Typed(expr, tpt) => + printMultiline(tree) { + traverse(expr) + traverse(tpt) + } + case vd @ ValDef(mods, name, tpt, rhs) => + printMultiline(tree) { + printModifiers(vd) + println(showDefTreeName(vd)) + traverse(tpt) + traverse(rhs) + } + case td @ TypeDef(mods, name, tparams, rhs) => + printMultiline(tree) { + printModifiers(td) + println(showDefTreeName(td)) + traverseList("[]", _ + " type parameter(s)")(tparams) + traverse(rhs) + } + + case PackageDef(pid, stats) => + printMultiline("PackageDef", "")(pid :: stats foreach traverse) - case Block(stats, expr) => - println("Block(" + nodeinfo(tree)) - if (stats.isEmpty) - println(" List(), // no statement") - else { - val n = stats.length - println(" List( // " + n + " statement(s)") - for (i <- 0 until n) - traverse(stats(i), level + 2, i < n-1) - println(" ),") - } - traverse(expr, level + 1, false) - printcln(")") - case ClassDef(mods, name, tparams, impl) => - println("ClassDef(" + nodeinfo(tree)) - println(" " + symflags(tree)) - println(" \"" + name + "\",") - if (tparams.isEmpty) - println(" List(), // no type parameter") - else { - val n = tparams.length - println(" List( // " + n + " type parameter(s)") - for (i <- 0 until n) - traverse(tparams(i), level + 2, i < n-1) - println(" ),") - } - traverse(impl, level + 1, false) - printcln(")") - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - println("DefDef(" + nodeinfo(tree)) - println(" " + symflags(tree)) - println(" \"" + name + "\",") - if (tparams.isEmpty) - println(" List(), // no type parameter") - else { - val n = tparams.length - println(" List( // " + n + " type parameter(s)") - for (i <- 0 until n) - traverse(tparams(i), level + 2, i < n-1) - println(" ),") - } - val n = vparamss.length - if (n == 1 && vparamss(0).isEmpty) - println(" List(List()), // no parameter") - else { - println(" List(") - for (i <- 0 until n) { - val m = vparamss(i).length - println(" List( // " + m + " parameter(s)") - for (j <- 0 until m) - traverse(vparamss(i)(j), level + 3, j < m-1) - println(" )") - } - println(" ),") - } - println(" " + tpt + ",") - traverse(rhs, level + 1, false) - printcln(")") - case EmptyTree => - printcln("EmptyTree") - case Ident(name) => - printcln("Ident(\"" + name + "\")" + nodeinfo2(tree)) - case Literal(value) => - printcln("Literal(" + value + ")") - case New(tpt) => - println("New(" + nodeinfo(tree)) - traverse(tpt, level + 1, false) - printcln(")") - case Select(qualifier, selector) => - println("Select(" + nodeinfo(tree)) - traverse(qualifier, level + 1, true) - printcln(" \"" + selector + "\")") - case Super(qual, mix) => - println("Super(\"" + mix + "\")" + nodeinfo(tree)) - traverse(qual, level + 1, true) - case Template(parents, self, body) => - println("Template(" + nodeinfo(tree)) - println(" " + parents.map(p => - if (p.tpe ne null) p.tpe.typeSymbol else "null-" + p - ) + ", // parents") - traverse(self, level + 1, true) - if (body.isEmpty) - println(" List() // no body") - else { - val n = body.length - println(" List( // body") - for (i <- 0 until n) - traverse(body(i), level + 2, i < n-1) - println(" )") - } - printcln(")") - case This(qual) => - println("This(\"" + qual + "\")" + nodeinfo2(tree)) - case TypeApply(fun, args) => - println("TypeApply(" + nodeinfo(tree)) - traverse(fun, level + 1, true) - if (args.isEmpty) - println(" List() // no argument") - else { - val n = args.length - println(" List(") - for (i <- 0 until n) - traverse(args(i), level + 1, i < n-1) - println(" )") - } - printcln(")") - case TypeTree() => - printcln("TypeTree()" + nodeinfo2(tree)) - case Typed(expr, tpt) => - println("Typed(" + nodeinfo(tree)) - traverse(expr, level + 1, true) - traverse(tpt, level + 1, false) - printcln(")") - case ValDef(mods, name, tpt, rhs) => - println("ValDef(" + nodeinfo(tree)) - println(" " + symflags(tree)) - println(" \"" + name + "\",") - traverse(tpt, level + 1, true) - traverse(rhs, level + 1, false) - printcln(")") - case PackageDef(pid, stats) => - println("PackageDef(") - traverse(pid, level + 1, false) - println(",\n") - for (stat <- stats) - traverse(stat, level + 1, false) - printcln(")") - case _ => - tree match { - case p: Product => - if (p.productArity != 0) { - println(p.productPrefix+"(") - for (elem <- (0 until p.productArity) map p.productElement) { - def printElem(elem: Any, level: Int): Unit = elem match { - case t: Tree => - traverse(t, level, false) - case xs: List[_] => - print("List(") - for (x <- xs) printElem(x, level+1) - printcln(")") - case _ => - println(elem.toString) - } - printElem(elem, level+1) - } - printcln(")") - } else printcln(p.productPrefix) - } - } + case _ => + tree match { + case t: RefTree => println(showRefTree(t)) + case t if t.productArity == 0 => println(tree.printingPrefix) + case t => printMultiline(tree)(tree.productIterator foreach traverseAny) + } } - buf setLength 0 - traverse(tree, 0, false) - buf.toString } } diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index c1d6c1a4d4..3302c11127 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -352,144 +352,17 @@ abstract class TreeBrowsers { * Tree. */ object TreeInfo { - /** Return the case class name and the Name, if the node defines one */ - def treeName(t: Tree): (String, Name) = t match { - case ProgramTree(units) => - ("Program", EMPTY) - - case UnitTree(unit) => - ("CompilationUnit", newTermName("" + unit)) - - case DocDef(comment, definition) => - ("DocDef", EMPTY) - - case ClassDef(mods, name, tparams, impl) => - ("ClassDef", name) - - case PackageDef(packaged, impl) => - ("PackageDef", EMPTY) - - case ModuleDef(mods, name, impl) => - ("ModuleDef", name) - - case ValDef(mods, name, tpe, rhs) => - ("ValDef", name) - - case DefDef(mods, name, tparams, vparams, tpe, rhs) => - ("DefDef", name) - - case TypeDef(mods, name, tparams, rhs) => - ("TypeDef", name) - - case Import(expr, selectors) => - ("Import", EMPTY) - - case CaseDef(pat, guard, body) => - ("CaseDef", EMPTY) - - case Template(parents, self, body) => - ("Template", EMPTY) - - case LabelDef(name, params, rhs) => - ("LabelDef", name) - - case Block(stats, expr) => - ("Block", EMPTY) - - case Alternative(trees) => - ("Alternative", EMPTY) - - case Bind(name, rhs) => - ("Bind", name) - - case UnApply(fun, args) => - ("UnApply", EMPTY) - - case Match(selector, cases) => - ("Visitor", EMPTY) - - case Function(vparams, body) => - ("Function", EMPTY) - - case Assign(lhs, rhs) => - ("Assign", EMPTY) - - case If(cond, thenp, elsep) => - ("If", EMPTY) - - case Return(expr) => - ("Return", EMPTY) - - case Throw(expr) => - ("Throw", EMPTY) - - case New(init) => - ("New", EMPTY) - - case Typed(expr, tpe) => - ("Typed", EMPTY) - - case TypeApply(fun, args) => - ("TypeApply", EMPTY) - - case Apply(fun, args) => - ("Apply", EMPTY) - - case ApplyDynamic(qual, args) => - ("Apply", EMPTY) - - case Super(qualif, mix) => - ("Super", newTermName("mix: " + mix)) - - case This(qualifier) => - ("This", qualifier) - - case Select(qualifier, selector) => - ("Select", selector) - - case Ident(name) => - ("Ident", name) - - case Literal(value) => - ("Literal", EMPTY) - - case TypeTree() => - ("TypeTree", EMPTY) - - case Annotated(annot, arg) => - ("Annotated", EMPTY) - - case SingletonTypeTree(ref) => - ("SingletonType", EMPTY) - - case SelectFromTypeTree(qualifier, selector) => - ("SelectFromType", selector) - - case CompoundTypeTree(template) => - ("CompoundType", EMPTY) - - case AppliedTypeTree(tpe, args) => - ("AppliedType", EMPTY) - - case TypeBoundsTree(lo, hi) => - ("TypeBoundsTree", EMPTY) - - case ExistentialTypeTree(tpt, whereClauses) => - ("ExistentialTypeTree", EMPTY) - - case Try(block, catcher, finalizer) => - ("Try", EMPTY) - - case EmptyTree => - ("Empty", EMPTY) - - case ArrayValue(elemtpt, trees) => - ("ArrayValue", EMPTY) - - case Star(t) => - ("Star", EMPTY) - } + def treeName(t: Tree): (String, Name) = ((t.printingPrefix, t match { + case UnitTree(unit) => newTermName("" + unit) + case Super(_, mix) => newTermName("mix: " + mix) + case This(qual) => qual + case Select(_, selector) => selector + case Ident(name) => name + case SelectFromTypeTree(_, selector) => selector + case x: DefTree => x.name + case _ => EMPTY + })) /** Return a list of children for the given tree node */ def children(t: Tree): List[Tree] = t match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 0d2fbc5372..0bc88d1efd 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -262,29 +262,25 @@ abstract class TreeBuilder { else if (stats.length == 1) stats.head else Block(stats.init, stats.last) + def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = { + val cases = List( + CaseDef(condition, EmptyTree, Literal(Constant(true))), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) + ) + val matchTree = makeVisitor(cases, false, scrutineeName) + + atPos(tree.pos)(Apply(Select(tree, nme.withFilter), matchTree :: Nil)) + } + /** Create tree for for-comprehension generator <val pat0 <- rhs0> */ def makeGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = { val pat1 = patvarTransformer.transform(pat) val rhs1 = - if (valeq) rhs - else matchVarPattern(pat1) match { - case Some(_) => - rhs - case None => - atPos(rhs.pos) { - Apply( - Select(rhs, nme.filter), - List( - makeVisitor( - List( - CaseDef(pat1.duplicate, EmptyTree, Literal(Constant(true))), - CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))), - false, - nme.CHECK_IF_REFUTABLE_STRING - ))) - } - } - if (valeq) ValEq(pos, pat1, rhs1) else ValFrom(pos, pat1, rhs1) + if (valeq || treeInfo.isVariablePattern(pat)) rhs + else makeFilter(rhs, pat1.duplicate, nme.CHECK_IF_REFUTABLE_STRING) + + if (valeq) ValEq(pos, pat1, rhs1) + else ValFrom(pos, pat1, rhs1) } def makeParam(pname: TermName, tpe: Tree) = diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala index 4f05678d85..0116e02e0e 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -119,7 +119,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { def writeTemplate(tpl: DocTemplateEntity) { if (!(written contains tpl)) { - writeForThis(new page.Template(tpl)) + writeForThis(new page.Template(universe, tpl)) written += tpl tpl.templates map writeTemplate } diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala index b58c71eaa9..1544dafc69 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala @@ -22,6 +22,16 @@ abstract class HtmlPage extends Page { thisPage => /** The title of this page. */ protected def title: String + /** The page description */ + protected def description: String = + // unless overwritten, will display the title in a spaced format, keeping - and . + title.replaceAll("[^a-zA-Z0-9\\.\\-]+", " ").replaceAll("\\-+", " - ").replaceAll(" +", " ") + + /** The page keywords */ + protected def keywords: String = + // unless overwritten, same as description, minus the " - " + description.replaceAll(" - ", " ") + /** Additional header elements (links, scripts, meta tags, etc.) required for this page. */ protected def headers: NodeSeq @@ -35,6 +45,8 @@ abstract class HtmlPage extends Page { thisPage => <html> <head> <title>{ title }</title> + <meta name="description" content={ description }/> + <meta name="keywords" content={ keywords }/> <meta http-equiv="content-type" content={ "text/html; charset=" + site.encoding }/> { headers } </head> diff --git a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala index f19f449d2c..f67abc58da 100644 --- a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala +++ b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala @@ -219,24 +219,24 @@ private[html] object SyntaxHigh { parse(" ", i+1) case '&' => parse("&", i+1) - case '<' => + case '<' if i+1 < buf.length => val ch = buf(i+1).toChar if (ch == '-' || ch == ':' || ch == '%') parse("<span class=\"kw\"><"+ch+"</span>", i+2) else parse("<", i+1) case '>' => - if (buf(i+1) == ':') + if (i+1 < buf.length && buf(i+1) == ':') parse("<span class=\"kw\">>:</span>", i+2) else parse(">", i+1) case '=' => - if (buf(i+1) == '>') + if (i+1 < buf.length && buf(i+1) == '>') parse("<span class=\"kw\">=></span>", i+2) else parse(buf(i).toChar.toString, i+1) case '/' => - if (buf(i+1) == '/' || buf(i+1) == '*') { + if (i+1 < buf.length && (buf(i+1) == '/' || buf(i+1) == '*')) { val c = comment(i+1) parse("<span class=\"cmt\">"+c+"</span>", i+c.length) } else @@ -257,9 +257,9 @@ private[html] object SyntaxHigh { else parse(buf(i).toChar.toString, i+1) case _ => - if (i == 0 || !Character.isJavaIdentifierPart(buf(i-1).toChar)) { + if (i == 0 || (i >= 1 && !Character.isJavaIdentifierPart(buf(i-1).toChar))) { if (Character.isDigit(buf(i)) || - (buf(i) == '.' && Character.isDigit(buf(i+1)))) { + (buf(i) == '.' && i + 1 < buf.length && Character.isDigit(buf(i+1)))) { val s = numlit(i) parse("<span class=\"num\">"+s+"</span>", i+s.length) } else { diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index e35286b281..bd5f98bab7 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -11,13 +11,19 @@ package page import model._ import scala.xml.{ NodeSeq, Text, UnprefixedAttribute } -class Template(tpl: DocTemplateEntity) extends HtmlPage { +class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { val path = templateToPath(tpl) - val title = - tpl.qualifiedName + def title = { + val s = universe.settings + + tpl.name + + ( if (!s.doctitle.isDefault) " - " + s.doctitle.value else "" ) + + ( if (!s.docversion.isDefault) (" " + s.docversion.value) else "" ) + + " - " + tpl.qualifiedName + } val headers = <xml:group> diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index b088c643cb..bc5cd4a958 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -491,7 +491,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => else jump("}}}") blockEnded("code block") - Code(getRead) + Code(normalizeIndentation(getRead)) } /** {{{ title ::= ('=' inline '=' | "==" inline "==" | ...) '\n' }}} */ @@ -732,6 +732,64 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => nextChar() } + /** + * Eliminates the (common) leading spaces in all lines, based on the first line + * For indented pieces of code, it reduces the indent to the least whitespace prefix: + * {{{ + * indented example + * another indented line + * if (condition) + * then do something; + * ^ this is the least whitespace prefix + * }}} + */ + def normalizeIndentation(_code: String): String = { + + var code = _code.trim + var maxSkip = Integer.MAX_VALUE + var crtSkip = 0 + var wsArea = true + var index = 0 + var firstLine = true + var emptyLine = true + + while (index < code.length) { + code(index) match { + case ' ' => + if (wsArea) + crtSkip += 1 + case c => + wsArea = (c == '\n') + maxSkip = if (firstLine || emptyLine) maxSkip else if (maxSkip <= crtSkip) maxSkip else crtSkip + crtSkip = if (c == '\n') 0 else crtSkip + firstLine = if (c == '\n') false else firstLine + emptyLine = if (c == '\n') true else false + } + index += 1 + } + + if (maxSkip == 0) + code + else { + index = 0 + val builder = new StringBuilder + while (index < code.length) { + builder.append(code(index)) + if (code(index) == '\n') { + // we want to skip as many spaces are available, if there are less spaces (like on empty lines, do not + // over-consume them) + index += 1 + val limit = index + maxSkip + while ((index < code.length) && (code(index) == ' ') && index < limit) + index += 1 + } + else + index += 1 + } + builder.toString + } + } + def checkParaEnded(): Boolean = { (char == endOfText) || ((char == endOfLine) && { diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index d08a363a9d..88e3827403 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -189,7 +189,7 @@ self: scala.tools.nsc.Global => override def validatePositions(tree: Tree) { def reportTree(prefix : String, tree : Tree) { val source = if (tree.pos.isDefined) tree.pos.source else "" - inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.productPrefix+" at "+tree.pos.show+source) + inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.printingPrefix+" at "+tree.pos.show+source) inform("") inform(treeStatus(tree)) inform("") diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala index 8e215cf63b..19266442cb 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Naming.scala @@ -11,16 +11,18 @@ package interpreter */ trait Naming { def unmangle(str: String): String = { + val ESC = '\u001b' val cleaned = removeIWPackages(removeLineWrapper(str)) - var ctrlChars = 0 - cleaned map { ch => - if (ch.isControl && !ch.isWhitespace) { - ctrlChars += 1 - if (ctrlChars > 5) return "[line elided for control chars: possibly a scala signature]" - else '?' - } - else ch - } + // Looking to exclude binary data which hoses the terminal, but + // let through the subset of it we need, like whitespace and also + // <ESC> for ansi codes. + val binaryChars = cleaned count (ch => ch < 32 && !ch.isWhitespace && ch != ESC) + // Lots of binary chars - translate all supposed whitespace into spaces + if (binaryChars > 5) + cleaned map (ch => if (ch.isWhitespace) ' ' else if (ch < 32) '?' else ch) + // Not lots - preserve whitespace and ESC + else + cleaned map (ch => if (ch.isWhitespace || ch == ESC) ch else if (ch < 32) '?' else ch) } // The two name forms this is catching are the two sides of this assignment: diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index fdb5c7e52e..6ebecb02c6 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -36,6 +36,8 @@ abstract class TailCalls extends Transform { } } + private def hasSynthCaseSymbol(t: Tree) = (t.symbol ne null) && (t.symbol hasFlag (Flags.CASE | Flags.SYNTHETIC)) + /** * A Tail Call Transformer * @@ -87,10 +89,22 @@ abstract class TailCalls extends Transform { class TailCallElimination(unit: CompilationUnit) extends Transformer { private val defaultReason = "it contains a recursive call not in tail position" + /** Has the label been accessed? Then its symbol is in this set. */ + private val accessed = new collection.mutable.HashSet[Symbol]() + // `accessed` was stored as boolean in the current context -- this is no longer tenable + // with jumps to labels in tailpositions now considered in tailposition, + // a downstream context may access the label, and the upstream one will be none the wiser + // this is necessary because tail-calls may occur in places where syntactically they seem impossible + // (since we now consider jumps to labels that are in tailposition, such as matchEnd(x) {x}) + + class Context() { /** The current method */ var method: Symbol = NoSymbol + // symbols of label defs in this method that are in tail position + var tailLabels: Set[Symbol] = Set() + /** The current tail-call label */ var label: Symbol = NoSymbol @@ -104,24 +118,20 @@ abstract class TailCalls extends Transform { var failReason = defaultReason var failPos = method.pos - /** Has the label been accessed? */ - var accessed = false - def this(that: Context) = { this() this.method = that.method this.tparams = that.tparams this.tailPos = that.tailPos - this.accessed = that.accessed this.failPos = that.failPos this.label = that.label + this.tailLabels = that.tailLabels } def this(dd: DefDef) { this() this.method = dd.symbol this.tparams = dd.tparams map (_.symbol) this.tailPos = true - this.accessed = false this.failPos = dd.pos /** Create a new method symbol for the current method and store it in @@ -141,14 +151,14 @@ abstract class TailCalls extends Transform { def isEligible = method.isEffectivelyFinal // @tailrec annotation indicates mandatory transformation def isMandatory = method.hasAnnotation(TailrecClass) && !forMSIL - def isTransformed = isEligible && accessed + def isTransformed = isEligible && accessed(label) def tailrecFailure() = unit.error(failPos, "could not optimize @tailrec annotated " + method + ": " + failReason) def newThis(pos: Position) = method.newValue(nme.THIS, pos, SYNTHETIC) setInfo currentClass.typeOfThis override def toString(): String = ( "" + method.name + " tparams: " + tparams + " tailPos: " + tailPos + - " accessed: " + accessed + "\nLabel: " + label + "\nLabel type: " + label.info + " Label: " + label + " Label type: " + label.info ) } @@ -206,7 +216,7 @@ abstract class TailCalls extends Transform { def rewriteTailCall(recv: Tree): Tree = { debuglog("Rewriting tail recursive call: " + fun.pos.lineContent.trim) - ctx.accessed = true + accessed += ctx.label typedPos(fun.pos)(Apply(Ident(ctx.label), recv :: transformArgs)) } @@ -242,10 +252,16 @@ abstract class TailCalls extends Transform { unit.error(tree.pos, "@tailrec annotated method contains no recursive calls") } } - debuglog("Considering " + dd.name + " for tailcalls") + + // labels are local to a method, so only traverse the rhs of a defdef + val collectTailPosLabels = new TailPosLabelsTraverser + collectTailPosLabels traverse rhs0 + newCtx.tailLabels = collectTailPosLabels.tailLabels.toSet + + debuglog("Considering " + dd.name + " for tailcalls, with labels in tailpos: "+ newCtx.tailLabels) val newRHS = transform(rhs0, newCtx) - deriveDefDef(tree)(rhs => + deriveDefDef(tree){rhs => if (newCtx.isTransformed) { /** We have rewritten the tree, but there may be nested recursive calls remaining. * If @tailrec is given we need to fail those now. @@ -270,8 +286,22 @@ abstract class TailCalls extends Transform { newRHS } + } + + // a translated match + case Block(stats, expr) if stats forall hasSynthCaseSymbol => + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + treeCopy.Block(tree, + noTailTransforms(prologue) ++ transformTrees(cases), + transform(expr) ) + // a translated casedef + case LabelDef(_, _, body) if hasSynthCaseSymbol(tree) => + deriveLabelDef(tree)(transform) + case Block(stats, expr) => treeCopy.Block(tree, noTailTransforms(stats), @@ -308,8 +338,18 @@ abstract class TailCalls extends Transform { case Apply(fun, args) => if (fun.symbol == Boolean_or || fun.symbol == Boolean_and) treeCopy.Apply(tree, fun, transformTrees(args)) - else - rewriteApply(fun, fun, Nil, args) + else if (fun.symbol.isLabel && args.nonEmpty && args.tail.isEmpty && ctx.tailLabels(fun.symbol)) { + // this is to detect tailcalls in translated matches + // it's a one-argument call to a label that is in a tailposition and that looks like label(x) {x} + // thus, the argument to the call is in tailposition and we don't need to jump to the label, tail jump instead + val saved = ctx.tailPos + ctx.tailPos = true + debuglog("in tailpos label: "+ args.head) + val res = transform(args.head) + ctx.tailPos = saved + if (res ne args.head) res // we tail-called -- TODO: shield from false-positives where we rewrite but don't tail-call + else rewriteApply(fun, fun, Nil, args) + } else rewriteApply(fun, fun, Nil, args) case Alternative(_) | Star(_) | Bind(_, _) => sys.error("We should've never gotten inside a pattern") @@ -320,4 +360,66 @@ abstract class TailCalls extends Transform { } } } + + // collect the LabelDefs (generated by the pattern matcher) in a DefDef that are in tail position + // the labels all look like: matchEnd(x) {x} + // then, in a forward jump `matchEnd(expr)`, `expr` is considered in tail position (and the matchEnd jump is replaced by the jump generated by expr) + class TailPosLabelsTraverser extends Traverser { + val tailLabels = new collection.mutable.ListBuffer[Symbol]() + + private var maybeTail: Boolean = true // since we start in the rhs of a DefDef + + def traverse(tree: Tree, maybeTailNew: Boolean): Unit = { + val saved = maybeTail + maybeTail = maybeTailNew + try traverse(tree) + finally maybeTail = saved + } + + def traverseNoTail(tree: Tree) = traverse(tree, false) + def traverseTreesNoTail(trees: List[Tree]) = trees foreach traverseNoTail + + override def traverse(tree: Tree) = tree match { + case LabelDef(_, List(arg), body@Ident(_)) if arg.symbol == body.symbol => // we're looking for label(x){x} in tail position, since that means `a` is in tail position in a call `label(a)` + if (maybeTail) tailLabels += tree.symbol + + // a translated casedef + case LabelDef(_, _, body) if hasSynthCaseSymbol(tree) => + traverse(body) + + // a translated match + case Block(stats, expr) if stats forall hasSynthCaseSymbol => + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + traverseTreesNoTail(prologue) // selector (may be absent) + traverseTrees(cases) + traverse(expr) + + case CaseDef(pat, guard, body) => + traverse(body) + + case Match(selector, cases) => + traverseNoTail(selector) + traverseTrees(cases) + + case dd @ DefDef(_, _, _, _, _, _) => // we are run per-method + + case Block(stats, expr) => + traverseTreesNoTail(stats) + traverse(expr) + + case If(cond, thenp, elsep) => + traverse(thenp) + traverse(elsep) + + case Try(block, catches, finalizer) => + traverseNoTail(block) + traverseTreesNoTail(catches) + traverseNoTail(finalizer) + + case EmptyTree | Super(_, _) | This(_) | Select(_, _) | Ident(_) | Literal(_) | Function(_, _) | TypeTree() => + case _ => super.traverse(tree) + } + } } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index ee565530b7..03bef83a90 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -237,8 +237,10 @@ abstract class UnCurry extends InfoTransform def targs = fun.tpe.typeArgs def isPartial = fun.tpe.typeSymbol == PartialFunctionClass + // if the function was eta-expanded, it's not a match without a selector if (fun1 ne fun) fun1 else { + assert(!(opt.virtPatmat && isPartial)) // empty-selector matches have already been translated into instantiations of anonymous (partial) functions val (formals, restpe) = (targs.init, targs.last) val anonClass = owner.newAnonymousFunctionClass(fun.pos, inConstructorFlag) def parents = @@ -286,52 +288,54 @@ abstract class UnCurry extends InfoTransform def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) - val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) +// val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) gen.mkUncheckedMatch( - if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) Literal(Constant(true)) - else substTree(wrap(Match(selector, (casesNoSynthCatchAll map transformCase) :+ defaultCase)).duplicate) + if (cases exists treeInfo.isDefaultCase) Literal(Constant(true)) + else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate) ) } - override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { - object noOne extends Transformer { - override val treeCopy = newStrictTreeCopier // must duplicate everything - val one = _match.tpe member newTermName("one") - override def transform(tree: Tree): Tree = tree match { - case Apply(fun, List(a)) if fun.symbol == one => - // blow one's argument away since all we want to know is whether the match succeeds or not - // (the alternative, making `one` CBN, would entail moving away from Option) - Apply(fun.duplicate, List(gen.mkZeroContravariantAfterTyper(a.tpe))) - case _ => - super.transform(tree) - } - } - substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher)))) - } - - override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = { - object dropMatchResAssign extends Transformer { - // override val treeCopy = newStrictTreeCopier // will duplicate below - override def transform(tree: Tree): Tree = tree match { - // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing - case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol => - Block(List(assignKeepGoing), zero) - case _ => - super.transform(tree) - } - } - val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList - val idaBlock = wrap(Block( - zero :: - x :: - /* drop matchRes def */ - keepGoing :: - statsNoMatchRes, - NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing` - )) - substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed - } + override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = {assert(false); orig} + // { + // object noOne extends Transformer { + // override val treeCopy = newStrictTreeCopier // must duplicate everything + // val one = _match.tpe member newTermName("one") + // override def transform(tree: Tree): Tree = tree match { + // case Apply(fun, List(a)) if fun.symbol == one => + // // blow one's argument away since all we want to know is whether the match succeeds or not + // // (the alternative, making `one` CBN, would entail moving away from Option) + // Apply(fun.duplicate, List(gen.mkZeroContravariantAfterTyper(a.tpe))) + // case _ => + // super.transform(tree) + // } + // } + // substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher)))) + // } + + override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = {assert(false); orig} + // { + // object dropMatchResAssign extends Transformer { + // // override val treeCopy = newStrictTreeCopier // will duplicate below + // override def transform(tree: Tree): Tree = tree match { + // // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing + // case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol => + // Block(List(assignKeepGoing), zero) + // case _ => + // super.transform(tree) + // } + // } + // val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList + // val idaBlock = wrap(Block( + // zero :: + // x :: + // /* drop matchRes def */ + // keepGoing :: + // statsNoMatchRes, + // NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing` + // )) + // substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed + // } } DefDef(m, isDefinedAtTransformer(fun.body)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 29831c8469..eb0d489901 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -256,8 +256,16 @@ abstract class Duplicators extends Analyzer { case ldef @ LabelDef(name, params, rhs) => // log("label def: " + ldef) ldef.tpe = null - val params1 = params map (p => Ident(updateSym(p.symbol))) - super.typed(treeCopy.LabelDef(tree, name, params1, rhs), mode, pt) + // since typer does not create the symbols for a LabelDef's params, + // we do that manually here -- we should really refactor LabelDef to be a subclass of DefDef + def newParam(p: Tree): Ident = { + val newsym = p.symbol.cloneSymbol //(context.owner) // TODO owner? + Ident(newsym.setInfo(fixType(p.symbol.info))) + } + val params1 = params map newParam + val rhs1 = (new TreeSubstituter(params map (_.symbol), params1) transform rhs) // TODO: duplicate? + rhs1.tpe = null + super.typed(treeCopy.LabelDef(tree, name, params1, rhs1), mode, pt) case Bind(name, _) => // log("bind: " + tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 8b3bc253fd..a59622d4df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1099,7 +1099,7 @@ trait Infer { // since instantiateTypeVar wants to modify the skolem that corresponds to the method's type parameter, // and it uses the TypeVar's origin to locate it, deskolemize the existential skolem to the method tparam skolem // (the existential skolem was created by adaptConstrPattern to introduce the type slack necessary to soundly deal with variant type parameters) - case skolem if skolem.isExistentialSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol]) + case skolem if skolem.isGADTSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol]) case p => freshVar(p) } diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index b060fd7121..de7f03dc62 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -43,13 +43,13 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val outer = newTermName("<outer>") val runOrElse = newTermName("runOrElse") val zero = newTermName("zero") - val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching... + val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching... def counted(str: String, i: Int) = newTermName(str+i) } object MatchTranslator { - def apply(typer: Typer): MatchTranslation = { + def apply(typer: Typer): MatchTranslation with CodegenCore = { import typer._ // typing `_match` to decide which MatchTranslator to create adds 4% to quick.comp.timer newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { @@ -116,10 +116,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => import typer.{typed, context, silent, reallyExists} - private def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) - case _ => tp - } /** Implement a pattern match by turning its cases (including the implicit failure case) * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. @@ -131,18 +127,15 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => * thus, you must typecheck the result (and that will in turn translate nested matches) * this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch) */ - def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type): Tree = { + def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type, scrutType: Type, matchFailGenOverride: Option[Tree => Tree] = None): Tree = { // we don't transform after typers // (that would require much more sophistication when generating trees, // and the only place that emits Matches after typers is for exception handling anyway) assert(phase.id <= currentRun.typerPhase.id, phase) - val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen)) - - val scrutSym = freshSym(scrut.pos, pureType(scrutType)) - val okPt = repeatedToSeq(pt) + val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag (Flags.CASE | SYNTHETIC) // the flags allow us to detect generated matches by looking at the scrutinee's symbol (needed to avoid recursing endlessly on generated switches) // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental - combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner) + combineCases(scrut, scrutSym, cases map translateCase(scrutSym, pt), pt, matchOwner, matchFailGenOverride) } // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard) @@ -154,13 +147,12 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => // if they're already simple enough to be handled by the back-end, we're done if (caseDefs forall treeInfo.isCatchCase) caseDefs else { - val okPt = repeatedToSeq(pt) val switch = { val bindersAndCases = caseDefs map { caseDef => // generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there) // if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this) val caseScrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) - (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, okPt)(caseDef), EmptySubstitution)) + (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, pt)(caseDef), EmptySubstitution)) } (emitTypeSwitch(bindersAndCases, pt) map (_.map(fixerUpper(matchOwner, pos).apply(_).asInstanceOf[CaseDef]))) @@ -168,7 +160,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val catches = switch getOrElse { val scrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) - val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, okPt)(caseDef), EmptySubstitution))} + val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, pt)(caseDef), EmptySubstitution))} val exSym = freshSym(pos, pureType(ThrowableClass.tpe), "ex") @@ -177,7 +169,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => CaseDef( Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping? EmptyTree, - combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, scrut => Throw(CODE.REF(exSym))) + combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, Some(scrut => Throw(CODE.REF(exSym)))) ) }) } @@ -706,10 +698,10 @@ class Foo(x: Other) { x._1 } // no error in this order def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = (cases, Nil) - def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = + def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree]): Option[Tree] = None - // for catch + // for catch (no need to customize match failure) def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = None @@ -733,23 +725,25 @@ class Foo(x: Other) { x._1 } // no error in this order private[this] var currSub: Substitution = null // build Tree that chains `next` after the current extractor - def chainBefore(next: Tree, pt: Type): Tree + def chainBefore(next: Tree)(casegen: Casegen): Tree + } + + trait NoNewBinders extends TreeMaker { + protected val localSubstitution: Substitution = EmptySubstitution } - case class TrivialTreeMaker(tree: Tree) extends TreeMaker { - val localSubstitution: Substitution = EmptySubstitution - def chainBefore(next: Tree, pt: Type): Tree = tree + case class TrivialTreeMaker(tree: Tree) extends TreeMaker with NoNewBinders { + def chainBefore(next: Tree)(casegen: Casegen): Tree = tree } - case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker { - val localSubstitution: Substitution = EmptySubstitution - def chainBefore(next: Tree, pt: Type): Tree = // assert(next eq EmptyTree) - atPos(body.pos)(substitution(codegen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here + case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker with NoNewBinders { + def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(next eq EmptyTree) + atPos(body.pos)(casegen.one(substitution(body))) // since SubstOnly treemakers are dropped, need to do it here } case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { val localSubstitution = Substitution(prevBinder, CODE.REF(nextBinder)) - def chainBefore(next: Tree, pt: Type): Tree = substitution(next) + def chainBefore(next: Tree)(casegen: Casegen): Tree = substitution(next) } abstract class FunTreeMaker extends TreeMaker { @@ -766,8 +760,8 @@ class Foo(x: Other) { x._1 } // no error in this order lazy val nextBinder = freshSym(pos, nextBinderTp) lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) - def chainBefore(next: Tree, pt: Type): Tree = - atPos(pos)(codegen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) + def chainBefore(next: Tree)(casegen: Casegen): Tree = + atPos(pos)(casegen.flatMapCond(cond, res, nextBinder, substitution(next))) } /** @@ -778,11 +772,11 @@ class Foo(x: Other) { x._1 } // no error in this order * in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to` */ case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker { - def chainBefore(next: Tree, pt: Type): Tree = { - val condAndNext = extraCond map (codegen.ifThenElseZero(_, next)) getOrElse next + def chainBefore(next: Tree)(casegen: Casegen): Tree = { + val condAndNext = extraCond map (casegen.ifThenElseZero(_, next)) getOrElse next atPos(extractor.pos)( - if (extractorReturnsBoolean) codegen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext)) - else codegen.flatMap(extractor, nextBinder, substitution(condAndNext)) + if (extractorReturnsBoolean) casegen.flatMapCond(extractor, CODE.UNIT, nextBinder, substitution(condAndNext)) + else casegen.flatMap(extractor, nextBinder, substitution(condAndNext)) ) } @@ -791,10 +785,10 @@ class Foo(x: Other) { x._1 } // no error in this order // TODO: allow user-defined unapplyProduct case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree], localSubstitution: Substitution) extends TreeMaker { import CODE._ - def chainBefore(next: Tree, pt: Type): Tree = { + def chainBefore(next: Tree)(casegen: Casegen): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL val cond = extraCond map (nullCheck AND _) getOrElse nullCheck - codegen.ifThenElseZero(cond, substitution(next)) + casegen.ifThenElseZero(cond, substitution(next)) } override def toString = "P"+(prevBinder, extraCond getOrElse "", localSubstitution) @@ -907,61 +901,39 @@ class Foo(x: Other) { x._1 } // no error in this order override def toString = "ET"+(prevBinder, patTree) } - case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker { + case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker with NoNewBinders { // don't substitute prevBinder to nextBinder, a set of alternatives does not need to introduce a new binder, simply reuse the previous one - val localSubstitution: Substitution = EmptySubstitution override private[TreeMakers] def incorporateOuterSubstitution(outerSubst: Substitution): Unit = { super.incorporateOuterSubstitution(outerSubst) altss = altss map (alts => propagateSubstitution(alts, substitution)) } - def chainBefore(next: Tree, pt: Type): Tree = { import CODE._ - // next does not contain deftrees, is pretty short - val canDuplicate = { - var okToInline = true - var sizeBudget = 100 / (altss.length max 1) // yep, totally arbitrary! - object travOkToInline extends Traverser { override def traverse(tree: Tree): Unit = if (sizeBudget >= 0) { sizeBudget -= 1; tree match { - case TypeApply(_, _) | Apply(_, _) | Select(_, _) - | Block(_, _) | Assign(_, _) | If(_, _, _) | Typed(_, _) => super.traverse(tree) // these are allowed if their subtrees are - case EmptyTree | This(_) | New(_) | Literal(_) | Ident(_) => // these are always ok - case _ if tree.isType => // these are always ok - case _ => okToInline = false //; println("not inlining: "+ (tree, tree.getClass)) - }}} - travOkToInline.traverse(next) - // println("(okToInline, sizeBudget): "+ (okToInline, sizeBudget)) - okToInline && sizeBudget > 0 // must be strict comparison - } + def chainBefore(next: Tree)(codegenAlt: Casegen): Tree = { import CODE._ + atPos(pos){ + // one alternative may still generate multiple trees (e.g., an extractor call + equality test) + // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers + val combinedAlts = altss map (altTreeMakers => + ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE)))(casegen)) + ) - atPos(pos)( - if (canDuplicate) { - altss map {altTreeMakers => - combineExtractors(altTreeMakers :+ TrivialTreeMaker(substitution(next).duplicate), pt) - } reduceLeft codegen.typedOrElse(pt) - } else { - val rest = freshSym(pos, functionType(List(), inMatchMonad(pt)), "rest") - // rest.info.member(nme.apply).withAnnotation(AnnotationInfo(ScalaInlineClass.tpe, Nil, Nil)) - - // one alternative may still generate multiple trees (e.g., an extractor call + equality test) - // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers - val combinedAlts = altss map (altTreeMakers => - combineExtractors(altTreeMakers :+ TrivialTreeMaker(REF(rest) APPLY ()), pt) - ) - BLOCK( - VAL(rest) === Function(Nil, substitution(next)), - combinedAlts reduceLeft codegen.typedOrElse(pt) - ) - } - ) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE)) + codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) + } } } - case class GuardTreeMaker(guardTree: Tree) extends TreeMaker { - val localSubstitution: Substitution = EmptySubstitution - def chainBefore(next: Tree, pt: Type): Tree = codegen.flatMapGuard(substitution(guardTree), next) + case class GuardTreeMaker(guardTree: Tree) extends TreeMaker with NoNewBinders { + def chainBefore(next: Tree)(casegen: Casegen): Tree = casegen.flatMapGuard(substitution(guardTree), next) override def toString = "G("+ guardTree +")" } + // combineExtractors changes the current substitution's of the tree makers in `treeMakers` + // requires propagateSubstitution(treeMakers) has been called + def combineExtractors(treeMakers: List[TreeMaker])(casegen: Casegen): Tree = + treeMakers.foldRight(EmptyTree: Tree)((a, b) => a.chainBefore(b)(casegen)) + + def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker]) // a foldLeft to accumulate the localSubstitution left-to-right @@ -976,51 +948,42 @@ class Foo(x: Other) { x._1 } // no error in this order } // calls propagateSubstitution on the treemakers - def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = { - val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them - combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, CODE.MATCHERROR(_)) + def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Tree => Tree]): Tree = { + // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them + val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) + combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, matchFailGenOverride) } - def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Tree => Tree): Tree = fixerUpper(owner, scrut.pos){ - emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{ - val (matcher, hasDefault, toHoist) = - if (casesNoSubstOnly nonEmpty) { - // when specified, need to propagate pt explicitly (type inferencer can't handle it) - val optPt = - if (isFullyDefined(pt)) inMatchMonad(pt) - else NoType + def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Tree => Tree]): Tree = + fixerUpper(owner, scrut.pos){ + val ptDefined = if (isFullyDefined(pt)) pt else NoType + def matchFailGen = (matchFailGenOverride orElse Some(CODE.MATCHERROR(_: Tree))) - // do this check on casesNoSubstOnly, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride).getOrElse{ + if (casesNoSubstOnly nonEmpty) { + // before optimizing, check casesNoSubstOnly for presence of a default case, + // since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one // exhaustivity and reachability must be checked before optimization as well - // TODO: improve, a trivial type test before the body still makes for a default case - // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) - val hasDefault = casesNoSubstOnly.nonEmpty && { - val nonTrivLast = casesNoSubstOnly.last - nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] - } + // TODO: improve notion of trivial/irrefutable -- a trivial type test before the body still makes for a default case + // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) + // irrefutability checking should use the approximation framework also used for CSE, unreachability and exhaustivity checking + val synthCatchAll = + if (casesNoSubstOnly.nonEmpty && { + val nonTrivLast = casesNoSubstOnly.last + nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] + }) None + else matchFailGen val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) - val combinedCases = - cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) + val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, synthCatchAll) - (combinedCases, hasDefault, toHoist) - } else (codegen.zero, false, Nil) - - // catch-all - val catchAll = - if (hasDefault) None // no need for a catch-all when there's already a default - else Some(matchFail) - val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, catchAll) - if (toHoist isEmpty) expr - else Block(toHoist, expr) + if (toHoist isEmpty) matchRes else Block(toHoist, matchRes) + } else { + codegen.matcher(scrut, scrutSym, pt)(Nil, matchFailGen) + } + } } - } - - // combineExtractors changes the current substitution's of the tree makers in `treeMakers` - // requires propagateSubstitution(treeMakers) has been called - def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree = - treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt)) // TODO: do this during tree construction, but that will require tracking the current owner in treemakers // TODO: assign more fine-grained positions @@ -1070,34 +1033,36 @@ class Foo(x: Other) { x._1 } // no error in this order /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// trait CodegenCore extends MatchMonadInterface { private var ctr = 0 - def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = {ctr += 1; - // assert(owner ne null) - // assert(owner ne NoSymbol) - NoSymbol.newTermSymbol(vpmName.counted(prefix, ctr), pos) setInfo repackExistential(tp) - } + def freshName(prefix: String) = {ctr += 1; vpmName.counted(prefix, ctr)} + + // assert(owner ne null); assert(owner ne NoSymbol) + def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = + NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo /*repackExistential*/(tp) // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodegen { - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree - def zero: Tree - def flatMap(prev: Tree, b: Symbol, next: Tree): Tree - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree - - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree - def flatMapGuard(cond: Tree, next: Tree): Tree + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree - def fun(arg: Symbol, body: Tree): Tree - def ifThenElseZero(c: Tree, then: Tree): Tree - def _equals(checker: Tree, binder: Symbol): Tree + // local / context-free def _asInstanceOf(b: Symbol, tp: Type): Tree + def _equals(checker: Tree, binder: Symbol): Tree + def _isInstanceOf(b: Symbol, tp: Type): Tree + def and(a: Tree, b: Tree): Tree + def drop(tgt: Tree)(n: Int): Tree + def index(tgt: Tree)(i: Int): Tree def mkZero(tp: Type): Tree - def tupleSel(binder: Symbol)(i: Int): Tree - def index(tgt: Tree)(i: Int): Tree - def drop(tgt: Tree)(n: Int): Tree - def and(a: Tree, b: Tree): Tree - def _isInstanceOf(b: Symbol, tp: Type): Tree + } + + // structure + trait Casegen extends AbsCodegen { import CODE._ + def one(res: Tree): Tree + + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree + def flatMapGuard(cond: Tree, next: Tree): Tree + def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero + protected def zero: Tree } def codegen: AbsCodegen @@ -1112,21 +1077,20 @@ class Foo(x: Other) { x._1 } // no error in this order def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n)) def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya def and(a: Tree, b: Tree): Tree = a AND b - def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = repackExistential(tp) + def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = /*repackExistential*/(tp) if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX) else gen.mkAsInstanceOf(t, tpX, true, false) } - def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false) - // { val tpX = repackExistential(tp) + def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), /*repackExistential*/(tp), true, false) + // { val tpX = /*repackExistential*/(tp) // if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } // else gen.mkIsInstanceOf(REF(b), tpX, true, false) // } - def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = repackExistential(tp) + def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = /*repackExistential*/(tp) if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX) else gen.mkAsInstanceOf(REF(b), tpX, true, false) } @@ -1166,28 +1130,29 @@ class Foo(x: Other) { x._1 } // no error in this order trait PureCodegen extends CodegenCore with PureMatchMonadInterface { def codegen: AbsCodegen = pureCodegen - object pureCodegen extends CommonCodegen { import CODE._ + object pureCodegen extends CommonCodegen with Casegen { import CODE._ //// methods in MatchingStrategy (the monad companion) -- used directly in translation // __match.runOrElse(`scrut`)(`scrutSym` => `matcher`) // TODO: consider catchAll, or virtualized matching will break in exception handlers - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree - = _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher)) + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = + _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, cases map (f => f(this)) reduceLeft typedOrElse)) + // __match.one(`res`) - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (_match(vpmName.one)) (res) + def one(res: Tree): Tree = (_match(vpmName.one)) (res) // __match.zero - def zero: Tree = _match(vpmName.zero) + protected def zero: Tree = _match(vpmName.zero) // __match.guard(`c`, `then`) - def guard(c: Tree, then: Tree, tp: Type): Tree = _match(vpmName.guard) APPLY (c, then) + def guard(c: Tree, then: Tree): Tree = _match(vpmName.guard) APPLY (c, then) //// methods in the monad instance -- used directly in translation // `prev`.flatMap(`b` => `next`) def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = (prev DOT vpmName.flatMap)(fun(b, next)) // `thisCase`.orElse(`elseCase`) - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (thisCase DOT vpmName.orElse) APPLY (elseCase) + def typedOrElse(thisCase: Tree, elseCase: Tree): Tree = (thisCase DOT vpmName.orElse) APPLY (elseCase) // __match.guard(`cond`, `res`).flatMap(`nextBinder` => `next`) - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), nextBinder, next) + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = flatMap(guard(cond, res), nextBinder, next) // __match.guard(`guardTree`, ()).flatMap((_: P[Unit]) => `next`) - def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, pureType(UnitClass.tpe)), pureType(UnitClass.tpe), next) + def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, pureType(UnitClass.tpe)), next) } } @@ -1456,8 +1421,8 @@ class Foo(x: Other) { x._1 } // no error in this order } // TODO: finer-grained duplication - def chainBefore(next: Tree, pt: Type): Tree = // assert(codegen eq optimizedCodegen) - atPos(pos)(optimizedCodegen.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) + def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(codegen eq optimizedCodegen) + atPos(pos)(casegen.asInstanceOf[optimizedCodegen.OptimizedCasegen].flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) } case class ReusingCondTreeMaker(sharedPrefix: List[Test], toReused: TreeMaker => TreeMaker) extends TreeMaker { import CODE._ @@ -1474,12 +1439,11 @@ class Foo(x: Other) { x._1 } // no error in this order oldSubs.foldLeft(Substitution(from, to))(_ >> _) } - def chainBefore(next: Tree, pt: Type): Tree = { + def chainBefore(next: Tree)(casegen: Casegen): Tree = { val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: ReusedCondTreeMaker)) => ctm}.get.storedCond) - IF (cond) THEN BLOCK( - substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) - ) ELSE codegen.zero + // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) + casegen.ifThenElseZero(cond, substitution(next).duplicate) } } } @@ -1551,7 +1515,7 @@ class Foo(x: Other) { x._1 } // no error in this order } } - class RegularSwitchMaker(scrutSym: Symbol) extends SwitchMaker { + class RegularSwitchMaker(scrutSym: Symbol, matchFailGenOverride: Option[Tree => Tree]) extends SwitchMaker { val switchableTpe = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) val alternativesSupported = true @@ -1574,14 +1538,14 @@ class Foo(x: Other) { x._1 } // no error in this order } def defaultSym: Symbol = scrutSym - def defaultBody: Tree = { import CODE._; MATCHERROR(REF(scrutSym)) } + def defaultBody: Tree = { import CODE._; matchFailGenOverride map (gen => gen(REF(scrutSym))) getOrElse MATCHERROR(REF(scrutSym)) } def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) { DEFAULT ==> body }} } - override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { import CODE._ - val regularSwitchMaker = new RegularSwitchMaker(scrutSym) + override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree]): Option[Tree] = { import CODE._ + val regularSwitchMaker = new RegularSwitchMaker(scrutSym, matchFailGenOverride) // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result if (regularSwitchMaker.switchableTpe(scrutSym.tpe)) { val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) @@ -1589,7 +1553,7 @@ class Foo(x: Other) { x._1 } // no error in this order else { // match on scrutSym -- converted to an int if necessary -- not on scrut directly (to avoid duplicating scrut) val scrutToInt: Tree = - if(scrutSym.tpe =:= IntClass.tpe) REF(scrutSym) + if (scrutSym.tpe =:= IntClass.tpe) REF(scrutSym) else (REF(scrutSym) DOT (nme.toInt)) Some(BLOCK( VAL(scrutSym) === scrut, @@ -1657,8 +1621,7 @@ class Foo(x: Other) { x._1 } // no error in this order // for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard // this is a special instance of the advanced inlining optimization that takes a method call on // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases - object optimizedCodegen extends CommonCodegen /*with AbsOptimizedCodegen*/ { import CODE._ - lazy val zeroSym = freshSym(NoPosition, optionType(NothingClass.tpe), "zero") + object optimizedCodegen extends CommonCodegen { import CODE._ /** Inline runOrElse and get rid of Option allocations * @@ -1666,67 +1629,92 @@ class Foo(x: Other) { x._1 } // no error in this order * the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty, * if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x */ - @inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass) - lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE - lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]) = { - matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL - if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245 - BLOCK( - VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite... - VAL(scrutSym) === scrut, - VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above - VAL(keepGoing) === TRUE, - matcher, - catchAll map { catchAllGen => (IF (REF(keepGoing)) THEN catchAllGen(REF(scrutSym)) ELSE REF(matchRes)) } getOrElse REF(matchRes) - ) - } + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { + val matchEnd = NoSymbol.newLabel(freshName("matchEnd"), NoPosition) setFlag (SYNTHETIC | Flags.CASE) + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe + matchEnd setInfo MethodType(List(matchRes), restpe) + + def newCaseSym = NoSymbol.newLabel(freshName("case"), NoPosition) setInfo MethodType(Nil, restpe) setFlag (SYNTHETIC | Flags.CASE) + var nextCase = newCaseSym + def caseDef(mkCase: Casegen => Tree): Tree = { + val currCase = nextCase + nextCase = newCaseSym + val casegen = new OptimizedCasegen(matchEnd, nextCase) + LabelDef(currCase, Nil, mkCase(casegen)) + } - // only used to wrap the RHS of a body - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = { - BLOCK( - REF(keepGoing) === FALSE, // comes before assignment to matchRes, so the latter is in tail positions (can ignore the trailing zero -- will disappear when we flatten blocks, which is TODO) - if (dontStore(matchPt)) res else (REF(matchRes) === res), // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse... - zero // to have a nice lub for lubs -- otherwise we'll get a boxed unit here -- TODO: get rid of all those dangling else zero's + def catchAll = matchFailGen map { matchFailGen => + val scrutRef = if(scrutSym ne NoSymbol) REF(scrutSym) else EmptyTree // for alternatives + LabelDef(nextCase, Nil, matchEnd APPLY (matchFailGen(scrutRef))) // need to jump to matchEnd with result generated by matchFailGen (could be `FALSE` for isDefinedAt) + } toList + // catchAll.isEmpty iff no synthetic default case needed (the (last) user-defined case is a default) + // if the last user-defined case is a default, it will never jump to the next case; it will go immediately to matchEnd + + // the generated block is taken apart in TailCalls under the following assumptions + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + // val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + + // scrutSym == NoSymbol when generating an alternatives matcher + val scrutDef = if(scrutSym ne NoSymbol) List(VAL(scrutSym) === scrut) else Nil // for alternatives + Block( + scrutDef ++ (cases map caseDef) ++ catchAll, + LabelDef(matchEnd, List(matchRes), REF(matchRes)) ) } - def zero: Tree = REF(zeroSym) - - def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { - val tp = inMatchMonad(b.tpe) - val prevSym = freshSym(prev.pos, tp, "o") - val isEmpty = tp member vpmName.isEmpty - val get = tp member vpmName.get + class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol) extends CommonCodegen with Casegen { + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = + optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, matchFailGen) + + // only used to wrap the RHS of a body + // res: T + // returns MatchMonad[T] + def one(res: Tree): Tree = matchEnd APPLY (res) + protected def zero: Tree = nextCase APPLY () + + // prev: MatchMonad[T] + // b: T + // next: MatchMonad[U] + // returns MatchMonad[U] + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { + val tp = inMatchMonad(b.tpe) + val prevSym = freshSym(prev.pos, tp, "o") + val isEmpty = tp member vpmName.isEmpty + val get = tp member vpmName.get + + BLOCK( + VAL(prevSym) === prev, + // must be isEmpty and get as we don't control the target of the call (prev is an extractor call) + ifThenElseZero(NOT(prevSym DOT isEmpty), Substitution(b, prevSym DOT get)(next)) + ) + } - BLOCK( - VAL(prevSym) === prev, - IF (prevSym DOT isEmpty) THEN zero ELSE Substitution(b, prevSym DOT get)(next) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor) - ) - } + // cond: Boolean + // res: T + // nextBinder: T + // next == MatchMonad[U] + // returns MatchMonad[U] + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = + ifThenElseZero(cond, BLOCK( + VAL(nextBinder) === res, + next + )) - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = { - BLOCK( - thisCase, - IF (REF(keepGoing)) THEN elseCase ELSE zero // leave trailing zero for now, otherwise typer adds () anyway - ) + // guardTree: Boolean + // next: MatchMonad[T] + // returns MatchMonad[T] + def flatMapGuard(guardTree: Tree, next: Tree): Tree = + ifThenElseZero(guardTree, next) + + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = + ifThenElseZero(cond, BLOCK( + condSym === TRUE, + nextBinder === res, + next + )) } - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = - IF (cond) THEN BLOCK( - VAL(nextBinder) === res, - next - ) ELSE zero - - def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = - IF (cond) THEN BLOCK( - condSym === TRUE, - nextBinder === res, - next - ) ELSE zero - - def flatMapGuard(guardTree: Tree, next: Tree): Tree = - IF (guardTree) THEN next ELSE zero } } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index ec42d251ff..73369f09af 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1445,7 +1445,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R private def transformApply(tree: Apply): Tree = tree match { case Apply( - Select(qual, nme.filter), + Select(qual, nme.filter | nme.withFilter), List(Function( List(ValDef(_, pname, tpt, _)), Match(_, CaseDef(pat1, _, _) :: _)))) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 1434002121..e17a271dd0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -157,7 +157,7 @@ trait TypeDiagnostics { } // todo: use also for other error messages - def existentialContext(tp: Type) = tp.existentialSkolems match { + def existentialContext(tp: Type) = tp.skolemsExceptMethodTypeParams match { case Nil => "" case xs => " where " + (disambiguate(xs map (_.existentialToString)) mkString ", ") } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 25f3e7af5c..ad48712a32 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -909,8 +909,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def apply(tp: Type) = mapOver(tp) match { case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) - val skolem = context.owner.newExistentialSkolem(tpSym, tpSym, unit.freshTypeName("?"+tpSym.name), bounds) - // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt) + // origin must be the type param so we can deskolemize + val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) skolems += skolem skolem.tpe case tp1 => tp1 @@ -928,9 +929,19 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { freeVars foreach ctorContext.scope.enter newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) - // tree1's type-slack skolems will be deskolemized (to the method type parameter skolems) - // once the containing CaseDef has been type checked (see typedCase) - tree1 + // simplify types without losing safety, + // so that error messages don't unnecessarily refer to skolems + val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) + val extrapolated = tree1.tpe match { + case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node + ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type + copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) + case tp => tp + } + + // once the containing CaseDef has been type checked (see typedCase), + // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) + tree1 setType extrapolated } else { tree } @@ -1095,7 +1106,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val found = tree.tpe val req = pt if (!found.isErroneous && !req.isErroneous) { - if (!context.reportErrors && isPastTyper && req.existentialSkolems.nonEmpty) { + if (!context.reportErrors && isPastTyper && req.skolemsExceptMethodTypeParams.nonEmpty) { // Ignore type errors raised in later phases that are due to mismatching types with existential skolems // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. // Here's my hypothsis why this happens. The pattern matcher defines a variable of type @@ -1112,7 +1123,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // // val x = expr context.unit.warning(tree.pos, "recovering from existential Skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) - adapt(tree, mode, deriveTypeWithWildcards(pt.existentialSkolems)(pt)) + adapt(tree, mode, deriveTypeWithWildcards(pt.skolemsExceptMethodTypeParams)(pt)) } else { // create an actual error AdaptTypeError(tree, found, req) @@ -2112,26 +2123,161 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // body1 = checkNoEscaping.locals(context.scope, pt, body1) val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe - // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher - // TODO: Paul, can we do the deskolemization lazily in the old pattern matcher - object deskolemizeOnce extends TypeMap { - def apply(tp: Type): Type = mapOver(tp) match { - case TypeRef(pre, sym, args) if sym.isExistentialSkolem && sym.deSkolemize.isSkolem && sym.deSkolemize.owner.isTerm => - typeRef(NoPrefix, sym.deSkolemize, args) - case tp1 => tp1 - } - } - - new TypeMapTreeSubstituter(deskolemizeOnce).traverse(treeWithSkolems) + new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(treeWithSkolems) treeWithSkolems // now without skolems, actually } + // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher + // the flags are used to avoid accidentally deskolemizing unrelated skolems of skolems + object deskolemizeGADTSkolems extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case TypeRef(pre, sym, args) if sym.isGADTSkolem => + typeRef(NoPrefix, sym.deSkolemize, args) + case tp1 => tp1 + } + } + def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => - newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) + val caseTyped = newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) + if (opt.virtPatmat) { + val tpPacked = packedType(caseTyped, context.owner) + caseTyped setType tpPacked + } else caseTyped + } + + def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) + + def translateMatch(selector: Tree, cases: List[CaseDef], mode: Int, resTp: Type, scrutTp: Type = NoType, matchFailGen: Option[Tree => Tree] = None) = { + val selector1 = if(scrutTp eq NoType) checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) else selector + val selectorTp = if(scrutTp eq NoType) packCaptured(selector1.tpe.widen) else scrutTp + val casesTyped = typedCases(cases, selectorTp, resTp) + val (ownType, needAdapt) = if (isFullyDefined(resTp)) (resTp, false) else weakLub(casesTyped map (_.tpe.deconst)) + val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, ownType)) + // val (owntype0, needAdapt) = ptOrLub(casesTyped map (x => repackExistential(x.tpe))) + // val owntype = elimAnonymousClass(owntype0) + + def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { + case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) + case _ => tp + } + + def isSynthSelector(selector: Tree): Boolean = selector match { + case Ident(_) if selector.symbol.hasFlag(SYNTHETIC | CASE) => true + case Select(sel, nme.toInt) => isSynthSelector(sel) // switch may need to convert to int first + case _ => false + } + + if (isSynthSelector(selector1)) { // a switch + (Match(selector1, casesAdapted) setType ownType, ownType) // setType of the Match to avoid recursing endlessly + } else { + val scrutType = repeatedToSeq(elimAnonymousClass(selectorTp)) + (MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownType), scrutType, matchFailGen), ownType) + } + } + + // TODO: use this to synthesize (partial)function implementation for matches from the get-go, + // instead of the dirty post-factum hacks in uncurry -- typedMatchAnonFun is currently not used due to mindboggling failures (see virtpatmat_anonfun_for.scala) + def typedMatchAnonFun(tree: Tree, cases: List[CaseDef], mode: Int, pt0: Type, selOverride: Option[(List[Symbol], Tree)] = None) = { + val pt = deskolemizeGADTSkolems(pt0) + val targs = pt.normalize.typeArgs + val arity = if (isFunctionType(pt)) targs.length - 1 else 1 + val scrutTp0 = if (arity == 1) targs.head else /* arity > 1 */ tupleType(targs.init) + val scrutTp = packCaptured(scrutTp0) + val ptRes = targs.last // may not be fully defined + val isPartial = pt.typeSymbol == PartialFunctionClass + val cname = tpnme.ANON_FUN_NAME + val funThis = This(cname) + // used to create a new context for pattern matching translation so that + // we can easily rejig the owner structure when we have the actual symbols for these methods + // (after type checking them, but type checking requires translation -- this seems like the easiest way to end this vicious cycle) + val applySentinel = NoSymbol.newMethod(nme.apply) + val idaSentinel = NoSymbol.newMethod(nme._isDefinedAt) + + def mkParams = { + val params = + for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { + ValDef(Modifiers(SYNTHETIC | PARAM), unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree) + } + val ids = params map (p => Ident(p.name)) + + val paramsRef = selOverride match { + case None => atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) } + case Some((_, sel)) => sel.duplicate // we'll replace the symbols that refer to the function's original syms by the ones introduced by the DefDef once the method's been type checked (until then, we don't know them) + } + + (params, paramsRef) // paramsRef can't be typed until after match has been translated, thus supply explicit scrutTp to translate below + } + + import CODE._ + + // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up + val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => TRUE).duplicate) else Nil + + val (applyMethod, parents) = { + val (params, paramsRef) = mkParams + val (body, resTp) = newTyper(context.make(context.tree, applySentinel)).translateMatch(paramsRef, cases, mode, ptRes, scrutTp, if (isPartial) Some(scrut => (funThis DOT nme.missingCase) (scrut)) else None) + + def abstractFunctionType = { + val sym = AbstractFunctionClass(arity) + typeRef(sym.typeConstructor.prefix, sym, targs.init :+ resTp) + } + + val parents = + if (isFunctionType(pt)) List(abstractFunctionType, SerializableClass.tpe) + else if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, List(scrutTp, resTp)), SerializableClass.tpe) + else List(ObjectClass.tpe, pt, SerializableClass.tpe) + + (atPos(tree.pos.focus)(DefDef(Modifiers(FINAL), nme.apply, Nil, List(params), TypeTree() setType resTp, body)), parents) + } + + def isDefinedAtMethod = { + val (params, paramsRef) = mkParams + val (body, _) = newTyper(context.make(context.tree, idaSentinel)).translateMatch(paramsRef, casesTrue, mode, BooleanClass.tpe, scrutTp, Some(scrutinee => FALSE)) + atPos(tree.pos.focus)( + DefDef(Modifiers(FINAL), nme._isDefinedAt, Nil, List(params), TypeTree() setType BooleanClass.tpe, body) + ) } + val members = if (!isPartial) List(applyMethod) else List(applyMethod, isDefinedAtMethod) + + val cmods = Modifiers(FINAL | SYNTHETIC /*TODO: when do we need INCONSTRUCTOR ?*/) withAnnotations ( + List(NEW(SerialVersionUIDAttr, LIT(0)))) + val cdef = + ClassDef(cmods, cname, Nil, + Template(parents map (TypeTree() setType _), emptyValDef, Modifiers(0), Nil, List(Nil), members, tree.pos) + ) + val funInst = (Block(List(cdef), Apply(Select(New(Ident(cname)), nme.CONSTRUCTOR), Nil))) + + val res = typed(funInst, mode, pt) + + // now that we have the symbols corresponding to the apply/isDefinedAt methods, + // we can fix up the result of fixerUpper... URGH + // fixerUpper nests the top-level definitions generated in the match under context.owner, but they should be owner by the apply/isDefinedAt method + res foreach { + case d: DefDef if (d.symbol.name == nme.apply) => + d.rhs.changeOwner(applySentinel -> d.symbol) + case d: DefDef if (d.symbol.name == nme._isDefinedAt) => + d.rhs.changeOwner(idaSentinel -> d.symbol) + case _ => + } + + selOverride match { + case None => res + case Some((paramSyms, sel)) => + object substParamSyms extends Transformer { + override def transform(t: Tree): Tree = t match { + case d: DefDef if (d.symbol.name == nme.apply) || (d.symbol.name == nme._isDefinedAt) && (d.symbol.owner == res.tpe.typeSymbol) => + deriveDefDef(d)(rhs => rhs.substTreeSyms(paramSyms, d.vparamss.head.map(_.symbol))) + case _ => + super.transform(t) + } + } + substParamSyms.transform(res) + } + } + /** * @param fun ... * @param mode ... @@ -2144,14 +2290,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { return MaxFunctionArityError(fun) def decompose(pt: Type): (Symbol, List[Type], Type) = - if ((isFunctionType(pt) - || - pt.typeSymbol == PartialFunctionClass && - numVparams == 1 && fun.body.isInstanceOf[Match]) - && // see bug901 for a reason why next conditions are needed - (pt.normalize.typeArgs.length - 1 == numVparams - || - fun.vparams.exists(_.tpt.isEmpty))) + if ((isFunctionType(pt) || (pt.typeSymbol == PartialFunctionClass && numVparams == 1 && fun.body.isInstanceOf[Match])) && // see bug901 for a reason why next conditions are needed + ( pt.normalize.typeArgs.length - 1 == numVparams + || fun.vparams.exists(_.tpt.isEmpty) + )) (pt.typeSymbol, pt.normalize.typeArgs.init, pt.normalize.typeArgs.last) else (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) @@ -2193,13 +2335,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // for (vparam <- vparams) { // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () // } - val body1 = typed(fun.body, respt) - val formals = vparamSyms map (_.tpe) - val restpe = packedType(body1, fun.symbol).deconst.resultType - val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) -// body = checkNoEscaping.locals(context.scope, restpe, body) - treeCopy.Function(fun, vparams, body1).setType(funtpe) - } + + def recompose(from: Type, to: Type) = + if(clazz == PartialFunctionClass) appliedType(PartialFunctionClass.typeConstructor, List(from, to)) + else functionType(List(from), to) + + fun.body match { + case Match(sel, cases) if opt.virtPatmat => + val typedSel = typed(sel, EXPRmode | BYVALmode, WildcardType) + // go to outer context -- must discard the context that was created for the Function since we're discarding the function + // thus, its symbol, which serves as the current context.owner, is not the right owner + // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) + newTyper(context.outer).typedMatchAnonFun(fun, cases, mode, recompose(typedSel.tpe, respt), Some((vparamSyms, typedSel))) + case _ => + val body1 = typed(fun.body, respt) + val formals = vparamSyms map (_.tpe) + val restpe = packedType(body1, fun.symbol).deconst.resultType + val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) + // body = checkNoEscaping.locals(context.scope, restpe, body) + treeCopy.Function(fun, vparams, body1).setType(funtpe) + } + } } def typedRefinement(stats: List[Tree]) { @@ -3410,7 +3566,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } else { var thenp1 = typed(thenp, pt) var elsep1 = typed(elsep, pt) - val (owntype, needAdapt) = ptOrLub(List(thenp1.tpe, elsep1.tpe)) + + lazy val thenTp = packedType(thenp1, context.owner) + lazy val elseTp = packedType(elsep1, context.owner) + val (owntype, needAdapt) = + // virtpatmat needs more aggressive unification of skolemized types, but lub is not robust enough --> middle ground + if (opt.virtPatmat && !isPastTyper && thenTp =:= elseTp) (thenTp, true) // this breaks src/library/scala/collection/immutable/TrieIterator.scala + // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala + else ptOrLub(List(thenp1.tpe, elsep1.tpe)) + if (needAdapt) { //isNumericValueType(owntype)) { thenp1 = adapt(thenp1, mode, owntype) elsep1 = adapt(elsep1, mode, owntype) @@ -3420,7 +3584,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def typedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = { - if (selector == EmptyTree) { + if (opt.virtPatmat && !isPastTyper) { + if (selector ne EmptyTree) typed(translateMatch(selector, cases, mode, pt)._1, mode, pt) + else typedMatchAnonFun(tree, cases, mode, pt) + } else if (selector == EmptyTree) { val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 val params = for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { @@ -3434,32 +3601,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } else { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) - - if (isPastTyper || !opt.virtPatmat) { - val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) - if (needAdapt) { - cases1 = cases1 map (adaptCase(_, owntype)) - } - treeCopy.Match(tree, selector1, cases1) setType owntype - } else { // don't run translator after typers (see comments in PatMatVirtualiser) - val (owntype0, needAdapt) = ptOrLub(cases1 map (x => repackExistential(x.tpe))) - val owntype = elimAnonymousClass(owntype0) - if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype)) - - (MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match { - case Block(vd :: Nil, tree@Match(selector, cases)) => - val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) - val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) - if (needAdapt) - cases1 = cases1 map (adaptCase(_, owntype)) - typed(Block(vd :: Nil, treeCopy.Match(tree, selector1, cases1) setType owntype)) - case translated => - // TODO: get rid of setType owntype -- it should all typecheck - // must call typed, not typed1, or we overflow the stack when emitting switches - typed(translated, mode, WildcardType) setType owntype - } + val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) + if (needAdapt) { + cases1 = cases1 map (adaptCase(_, mode, owntype)) } + treeCopy.Match(tree, selector1, cases1) setType owntype } } @@ -4229,9 +4375,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def adaptCase(cdef: CaseDef, tpe: Type): CaseDef = - deriveCaseDef(cdef)(adapt(_, mode, tpe)) - // begin typed1 val sym: Symbol = tree.symbol if ((sym ne null) && (sym ne NoSymbol)) sym.initialize @@ -4340,7 +4483,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe))) if (needAdapt) { block1 = adapt(block1, mode, owntype) - catches1 = catches1 map (adaptCase(_, owntype)) + catches1 = catches1 map (adaptCase(_, mode, owntype)) } if(!isPastTyper && opt.virtPatmat) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index cc272b7b8d..4f5b6868ae 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -45,6 +45,11 @@ trait Unapplies extends ast.TreeDSL case BooleanClass => Nil case OptionClass | SomeClass => val prod = tp.typeArgs.head +// the spec doesn't allow just any subtype of Product, it *must* be TupleN[...] -- see run/virtpatmat_extends_product.scala +// this breaks plenty of stuff, though... +// val targs = +// if (isTupleType(prod)) getProductArgs(prod) +// else List(prod) val targs = getProductArgs(prod) if (targs.isEmpty || targs.tail.isEmpty) List(prod) // special n == 0 || n == 1 diff --git a/src/library/scala/collection/GenIterableLike.scala b/src/library/scala/collection/GenIterableLike.scala index 7e68733afd..8fa5981969 100644 --- a/src/library/scala/collection/GenIterableLike.scala +++ b/src/library/scala/collection/GenIterableLike.scala @@ -40,17 +40,18 @@ trait GenIterableLike[+A, +Repr] extends Any with GenTraversableLike[A, Repr] { /** Checks if the other iterable collection contains the same elements in the same order as this $coll. * - * $orderDependent - * $willNotTerminateInf - * * @param that the collection to compare with. * @tparam B the type of the elements of collection `that`. * @return `true`, if both collections contain the same elements in the same order, `false` otherwise. * * @usecase def sameElements(that: GenIterable[A]): Boolean + * @inheritdoc * - * @param that the collection to compare with. - * @return `true`, if both collections contain the same elements in the same order, `false` otherwise. + * $orderDependent + * $willNotTerminateInf + * + * @param that the collection to compare with. + * @return `true`, if both collections contain the same elements in the same order, `false` otherwise. */ def sameElements[A1 >: A](that: GenIterable[A1]): Boolean @@ -58,8 +59,6 @@ trait GenIterableLike[+A, +Repr] extends Any with GenTraversableLike[A, Repr] { * by combining corresponding elements in pairs. * If one of the two collections is longer than the other, its remaining elements are ignored. * - * $orderDependent - * * @param that The iterable providing the second half of each result pair * @tparam A1 the type of the first half of the returned pairs (this is always a supertype * of the collection's element type `A`). @@ -71,19 +70,20 @@ trait GenIterableLike[+A, +Repr] extends Any with GenTraversableLike[A, Repr] { * of the returned collection is the minimum of the lengths of this $coll and `that`. * * @usecase def zip[B](that: GenIterable[B]): $Coll[(A, B)] + * @inheritdoc * - * @param that The iterable providing the second half of each result pair - * @tparam B the type of the second half of the returned pairs - * @return a new $coll containing pairs consisting of - * corresponding elements of this $coll and `that`. The length - * of the returned collection is the minimum of the lengths of this $coll and `that`. + * $orderDependent + * + * @param that The iterable providing the second half of each result pair + * @tparam B the type of the second half of the returned pairs + * @return a new $coll containing pairs consisting of + * corresponding elements of this $coll and `that`. The length + * of the returned collection is the minimum of the lengths of this $coll and `that`. */ def zip[A1 >: A, B, That](that: GenIterable[B])(implicit bf: CBF[Repr, (A1, B), That]): That /** Zips this $coll with its indices. * - * $orderDependent - * * @tparam A1 the type of the first half of the returned pairs (this is always a supertype * of the collection's element type `A`). * @tparam That the class of the returned collection. Where possible, `That` is @@ -98,11 +98,14 @@ trait GenIterableLike[+A, +Repr] extends Any with GenTraversableLike[A, Repr] { * $coll paired with their index. Indices start at `0`. * * @usecase def zipWithIndex: $Coll[(A, Int)] + * @inheritdoc * - * @return A new $coll containing pairs consisting of all elements of this - * $coll paired with their index. Indices start at `0`. - * @example - * `List("a", "b", "c").zipWithIndex = List(("a", 0), ("b", 1), ("c", 2))` + * $orderDependent + * + * @return A new $coll containing pairs consisting of all elements of this + * $coll paired with their index. Indices start at `0`. + * @example + * `List("a", "b", "c").zipWithIndex = List(("a", 0), ("b", 1), ("c", 2))` * */ def zipWithIndex[A1 >: A, That](implicit bf: CBF[Repr, (A1, Int), That]): That @@ -112,8 +115,6 @@ trait GenIterableLike[+A, +Repr] extends Any with GenTraversableLike[A, Repr] { * If one of the two collections is shorter than the other, * placeholder elements are used to extend the shorter collection to the length of the longer. * - * $orderDependent - * * @param that the iterable providing the second half of each result pair * @param thisElem the element to be used to fill up the result if this $coll is shorter than `that`. * @param thatElem the element to be used to fill up the result if `that` is shorter than this $coll. @@ -124,16 +125,19 @@ trait GenIterableLike[+A, +Repr] extends Any with GenTraversableLike[A, Repr] { * If `that` is shorter than this $coll, `thatElem` values are used to pad the result. * * @usecase def zipAll[B](that: Iterable[B], thisElem: A, thatElem: B): $Coll[(A, B)] - * - * @param that The iterable providing the second half of each result pair - * @param thisElem the element to be used to fill up the result if this $coll is shorter than `that`. - * @param thatElem the element to be used to fill up the result if `that` is shorter than this $coll. - * @tparam B the type of the second half of the returned pairs - * @return a new $coll containing pairs consisting of - * corresponding elements of this $coll and `that`. The length - * of the returned collection is the maximum of the lengths of this $coll and `that`. - * If this $coll is shorter than `that`, `thisElem` values are used to pad the result. - * If `that` is shorter than this $coll, `thatElem` values are used to pad the result. + * @inheritdoc + * + * $orderDependent + * + * @param that The iterable providing the second half of each result pair + * @param thisElem the element to be used to fill up the result if this $coll is shorter than `that`. + * @param thatElem the element to be used to fill up the result if `that` is shorter than this $coll. + * @tparam B the type of the second half of the returned pairs + * @return a new $coll containing pairs consisting of + * corresponding elements of this $coll and `that`. The length + * of the returned collection is the maximum of the lengths of this $coll and `that`. + * If this $coll is shorter than `that`, `thisElem` values are used to pad the result. + * If `that` is shorter than this $coll, `thatElem` values are used to pad the result. */ def zipAll[B, A1 >: A, That](that: GenIterable[B], thisElem: A1, thatElem: B)(implicit bf: CBF[Repr, (A1, B), That]): That diff --git a/src/library/scala/collection/GenMapLike.scala b/src/library/scala/collection/GenMapLike.scala index 12ecbcf140..114169c849 100644 --- a/src/library/scala/collection/GenMapLike.scala +++ b/src/library/scala/collection/GenMapLike.scala @@ -41,6 +41,8 @@ trait GenMapLike[A, +B, +Repr] extends GenIterableLike[(A, B), Repr] with Equals * @return the value associated with `key` if it exists, * otherwise the result of the `default` computation. * @usecase def getOrElse(key: A, default: => B): B + * @inheritdoc + * @tparam B the result type of the default computation. */ def getOrElse[B1 >: B](key: A, default: => B1): B1 diff --git a/src/library/scala/collection/GenSeqLike.scala b/src/library/scala/collection/GenSeqLike.scala index cb0e96fcbb..755abcd2bf 100644 --- a/src/library/scala/collection/GenSeqLike.scala +++ b/src/library/scala/collection/GenSeqLike.scala @@ -115,21 +115,21 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal /** Finds index of first occurrence of some value in this $coll. * - * $mayNotTerminateInf - * * @param elem the element value to search for. * @tparam B the type of the element `elem`. * @return the index of the first element of this $coll that is equal (wrt `==`) * to `elem`, or `-1`, if none exists. * * @usecase def indexOf(elem: A): Int + * @inheritdoc + * + * $mayNotTerminateInf + * */ def indexOf[B >: A](elem: B): Int = indexOf(elem, 0) /** Finds index of first occurrence of some value in this $coll after or at some start index. * - * $mayNotTerminateInf - * * @param elem the element value to search for. * @tparam B the type of the element `elem`. * @param from the start index @@ -137,19 +137,25 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * to `elem`, or `-1`, if none exists. * * @usecase def indexOf(elem: A, from: Int): Int + * @inheritdoc + * + * $mayNotTerminateInf + * */ def indexOf[B >: A](elem: B, from: Int): Int = indexWhere(elem ==, from) /** Finds index of last occurrence of some value in this $coll. * - * $willNotTerminateInf - * * @param elem the element value to search for. * @tparam B the type of the element `elem`. * @return the index of the last element of this $coll that is equal (wrt `==`) * to `elem`, or `-1`, if none exists. * * @usecase def lastIndexOf(elem: A): Int + * @inheritdoc + * + * $willNotTerminateInf + * */ def lastIndexOf[B >: A](elem: B): Int = lastIndexWhere(elem ==) @@ -162,6 +168,7 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * to `elem`, or `-1`, if none exists. * * @usecase def lastIndexOf(elem: A, end: Int): Int + * @inheritdoc */ def lastIndexOf[B >: A](elem: B, end: Int): Int = lastIndexWhere(elem ==, end) @@ -195,10 +202,6 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * Builds a new collection by applying a function to all elements of this $coll and * collecting the results in reversed order. * - * $willNotTerminateInf - * - * Note: `xs.reverseMap(f)` is the same as `xs.reverse.map(f)` but might be more efficient. - * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. * @tparam That $thatinfo @@ -207,10 +210,14 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * `f` to each element of this $coll and collecting the results in reversed order. * * @usecase def reverseMap[B](f: A => B): $Coll[B] + * @inheritdoc * - * Note: `xs.reverseMap(f)` is the same as `xs.reverse.map(f)` but might be more efficient. - * @return a new $coll resulting from applying the given function - * `f` to each element of this $coll and collecting the results in reversed order. + * $willNotTerminateInf + * + * Note: `xs.reverseMap(f)` is the same as `xs.reverse.map(f)` but might be more efficient. + * + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results in reversed order. */ def reverseMap[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That @@ -254,10 +261,13 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * @return a new $coll consisting of all elements of this $coll * except that `replaced` elements starting from `from` are replaced * by `patch`. + * * @usecase def patch(from: Int, that: GenSeq[A], replaced: Int): $Coll[A] - * @return a new $coll consisting of all elements of this $coll - * except that `replaced` elements starting from `from` are replaced - * by `patch`. + * @inheritdoc + * + * @return a new $coll consisting of all elements of this $coll + * except that `replaced` elements starting from `from` are replaced + * by `patch`. */ def patch[B >: A, That](from: Int, patch: GenSeq[B], replaced: Int)(implicit bf: CanBuildFrom[Repr, B, That]): That @@ -268,20 +278,33 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * @tparam That $thatinfo * @param bf $bfinfo * @return a new $coll` which is a copy of this $coll with the element at position `index` replaced by `elem`. + * * @usecase def updated(index: Int, elem: A): $Coll[A] - * @return a copy of this $coll with the element at position `index` replaced by `elem`. + * @inheritdoc + * + * @return a copy of this $coll with the element at position `index` replaced by `elem`. */ def updated[B >: A, That](index: Int, elem: B)(implicit bf: CanBuildFrom[Repr, B, That]): That /** A copy of the $coll with an element prepended. * - * Note that :-ending operators are right associative (see example). - * A mnemonic for `+:` vs. `:+` is: the COLon goes on the COLlection side. + * @param elem the prepended element + * @tparam B the element type of the returned $coll. + * @tparam That $thatinfo + * @param bf $bfinfo + * @return a new collection of type `That` consisting of `elem` followed + * by all elements of this $coll. + * + * @usecase def +:(elem: A): $Coll[A] + * @inheritdoc + * + * Note that :-ending operators are right associative (see example). + * A mnemonic for `+:` vs. `:+` is: the COLon goes on the COLlection side. * - * Also, the original $coll is not modified, so you will want to capture the result. + * Also, the original $coll is not modified, so you will want to capture the result. * - * Example: - * {{{ + * Example: + * {{{ * scala> val x = LinkedList(1) * x: scala.collection.mutable.LinkedList[Int] = LinkedList(1) * @@ -290,17 +313,10 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * * scala> println(x) * LinkedList(1) - * }}} + * }}} * - * @param elem the prepended element - * @tparam B the element type of the returned $coll. - * @tparam That $thatinfo - * @param bf $bfinfo - * @return a new collection of type `That` consisting of `elem` followed - * by all elements of this $coll. - * @usecase def +:(elem: A): $Coll[A] - * @return a new $coll consisting of `elem` followed - * by all elements of this $coll. + * @return a new $coll consisting of `elem` followed + * by all elements of this $coll. */ def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Repr, B, That]): That @@ -308,18 +324,20 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * * A mnemonic for `+:` vs. `:+` is: the COLon goes on the COLlection side. * - * $willNotTerminateInf * @param elem the appended element * @tparam B the element type of the returned $coll. * @tparam That $thatinfo * @param bf $bfinfo * @return a new collection of type `That` consisting of * all elements of this $coll followed by `elem`. + * * @usecase def :+(elem: A): $Coll[A] - * @return a new $coll consisting of - * all elements of this $coll followed by `elem`. - * @example - * {{{ + * @inheritdoc + * + * $willNotTerminateInf + * + * Example: + * {{{ * scala> import scala.collection.mutable.LinkedList * import scala.collection.mutable.LinkedList * @@ -331,7 +349,10 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * * scala> println(a) * LinkedList(1) - * }}} + * }}} + * + * @return a new $coll consisting of + * all elements of this $coll followed by `elem`. */ def :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Repr, B, That]): That @@ -346,9 +367,11 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * all elements of this $coll followed by the minimal number of occurrences of `elem` so * that the resulting collection has a length of at least `len`. * @usecase def padTo(len: Int, elem: A): $Coll[A] - * @return a new $coll consisting of - * all elements of this $coll followed by the minimal number of occurrences of `elem` so - * that the resulting $coll has a length of at least `len`. + * @inheritdoc + * + * @return a new $coll consisting of + * all elements of this $coll followed by the minimal number of occurrences of `elem` so + * that the resulting $coll has a length of at least `len`. */ def padTo[B >: A, That](len: Int, elem: B)(implicit bf: CanBuildFrom[Repr, B, That]): That @@ -368,13 +391,6 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal /** Produces a new sequence which contains all elements of this $coll and also all elements of * a given sequence. `xs union ys` is equivalent to `xs ++ ys`. - * $willNotTerminateInf - * - * Another way to express this - * is that `xs union ys` computes the order-presevring multi-set union of `xs` and `ys`. - * `union` is hence a counter-part of `diff` and `intersect` which also work on multi-sets. - * - * $willNotTerminateInf * * @param that the sequence to add. * @tparam B the element type of the returned $coll. @@ -382,9 +398,18 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * @param bf $bfinfo * @return a new collection of type `That` which contains all elements of this $coll * followed by all elements of `that`. + * * @usecase def union(that: GenSeq[A]): $Coll[A] - * @return a new $coll which contains all elements of this $coll - * followed by all elements of `that`. + * @inheritdoc + * + * Another way to express this + * is that `xs union ys` computes the order-presevring multi-set union of `xs` and `ys`. + * `union` is hence a counter-part of `diff` and `intersect` which also work on multi-sets. + * + * $willNotTerminateInf + * + * @return a new $coll which contains all elements of this $coll + * followed by all elements of `that`. */ def union[B >: A, That](that: GenSeq[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = this ++ that @@ -393,7 +418,6 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal union(that: GenSeq[B])(bf) /** Computes the multiset difference between this $coll and another sequence. - * $willNotTerminateInf * * @param that the sequence of elements to remove * @tparam B the element type of the returned $coll. @@ -404,17 +428,21 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * If an element value `x` appears * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form * part of the result, but any following occurrences will. + * * @usecase def diff(that: GenSeq[A]): $Coll[A] - * @return a new $coll which contains all elements of this $coll - * except some of occurrences of elements that also appear in `that`. - * If an element value `x` appears - * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form - * part of the result, but any following occurrences will. + * @inheritdoc + * + * $willNotTerminateInf + * + * @return a new $coll which contains all elements of this $coll + * except some of occurrences of elements that also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form + * part of the result, but any following occurrences will. */ def diff[B >: A](that: GenSeq[B]): Repr /** Computes the multiset intersection between this $coll and another sequence. - * $mayNotTerminateInf * * @param that the sequence of elements to intersect with. * @tparam B the element type of the returned $coll. @@ -425,12 +453,17 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * If an element value `x` appears * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained * in the result, but any following occurrences will be omitted. + * * @usecase def intersect(that: GenSeq[A]): $Coll[A] - * @return a new $coll which contains all elements of this $coll - * which also appear in `that`. - * If an element value `x` appears - * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained - * in the result, but any following occurrences will be omitted. + * @inheritdoc + * + * $mayNotTerminateInf + * + * @return a new $coll which contains all elements of this $coll + * which also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained + * in the result, but any following occurrences will be omitted. */ def intersect[B >: A](that: GenSeq[B]): Repr diff --git a/src/library/scala/collection/GenTraversableLike.scala b/src/library/scala/collection/GenTraversableLike.scala index dd5f602c41..fd03e0f446 100644 --- a/src/library/scala/collection/GenTraversableLike.scala +++ b/src/library/scala/collection/GenTraversableLike.scala @@ -136,6 +136,7 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * but this is not necessary. * * @usecase def foreach(f: A => Unit): Unit + * @inheritdoc */ def foreach[U](f: A => U): Unit @@ -149,17 +150,15 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * `f` to each element of this $coll and collecting the results. * * @usecase def map[B](f: A => B): $Coll[B] - * - * @return a new $coll resulting from applying the given function - * `f` to each element of this $coll and collecting the results. + * @inheritdoc + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. */ def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That /** Builds a new collection by applying a partial function to all elements of this $coll * on which the function is defined. * - * $collectExample - * * @param pf the partial function which filters and maps the $coll. * @tparam B the element type of the returned collection. * @tparam That $thatinfo @@ -169,36 +168,18 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * The order of the elements is preserved. * * @usecase def collect[B](pf: PartialFunction[A, B]): $Coll[B] + * @inheritdoc * - * @return a new $coll resulting from applying the given partial function - * `pf` to each element on which it is defined and collecting the results. - * The order of the elements is preserved. + * $collectExample + * + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. */ def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That /** Builds a new collection by applying a function to all elements of this $coll - * and using the elements of the resulting collections. For example: - * - * {{{ - * def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+") - * }}} - * - * The type of the resulting collection is guided by the static type of $coll. This might - * cause unexpected results sometimes. For example: - * - * {{{ - * // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set - * def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet) - * - * // lettersOf will return a Set[Char], not a Seq - * def lettersOf(words: Seq[String]) = words.toSet flatMap (word => word.toSeq) - * - * // xs will be a an Iterable[Int] - * val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2) - * - * // ys will be a Map[Int, Int] - * val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2) - * }}} + * and using the elements of the resulting collections. * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. @@ -208,33 +189,39 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * `f` to each element of this $coll and concatenating the results. * * @usecase def flatMap[B](f: A => GenTraversableOnce[B]): $Coll[B] + * @inheritdoc * - * @return a new $coll resulting from applying the given collection-valued function - * `f` to each element of this $coll and concatenating the results. - */ - def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That - - /** Returns a new $coll containing the elements from the left hand operand followed by the elements from the - * right hand operand. The element type of the $coll is the most specific superclass encompassing - * the element types of the two operands (see example). + * For example: * - * Example: - * {{{ - * scala> val a = LinkedList(1) - * a: scala.collection.mutable.LinkedList[Int] = LinkedList(1) + * {{{ + * def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+") + * }}} * - * scala> val b = LinkedList(2) - * b: scala.collection.mutable.LinkedList[Int] = LinkedList(2) + * The type of the resulting collection is guided by the static type of $coll. This might + * cause unexpected results sometimes. For example: * - * scala> val c = a ++ b - * c: scala.collection.mutable.LinkedList[Int] = LinkedList(1, 2) + * {{{ + * // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set + * def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet) * - * scala> val d = LinkedList('a') - * d: scala.collection.mutable.LinkedList[Char] = LinkedList(a) + * // lettersOf will return a Set[Char], not a Seq + * def lettersOf(words: Seq[String]) = words.toSet flatMap (word => word.toSeq) * - * scala> val e = c ++ d - * e: scala.collection.mutable.LinkedList[AnyVal] = LinkedList(1, 2, a) - * }}} + * // xs will be a an Iterable[Int] + * val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2) + * + * // ys will be a Map[Int, Int] + * val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2) + * }}} + * + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ + def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That + + /** Returns a new $coll containing the elements from the left hand operand followed by the elements from the + * right hand operand. The element type of the $coll is the most specific superclass encompassing + * the element types of the two operands. * * @param that the traversable to append. * @tparam B the element type of the returned collection. @@ -244,9 +231,28 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * of this $coll followed by all elements of `that`. * * @usecase def ++[B](that: GenTraversableOnce[B]): $Coll[B] + * @inheritdoc + * + * Example: + * {{{ + * scala> val a = LinkedList(1) + * a: scala.collection.mutable.LinkedList[Int] = LinkedList(1) + * + * scala> val b = LinkedList(2) + * b: scala.collection.mutable.LinkedList[Int] = LinkedList(2) + * + * scala> val c = a ++ b + * c: scala.collection.mutable.LinkedList[Int] = LinkedList(1, 2) + * + * scala> val d = LinkedList('a') + * d: scala.collection.mutable.LinkedList[Char] = LinkedList(a) + * + * scala> val e = c ++ d + * e: scala.collection.mutable.LinkedList[AnyVal] = LinkedList(1, 2, a) + * }}} * - * @return a new $coll which contains all elements of this $coll - * followed by all elements of `that`. + * @return a new $coll which contains all elements of this $coll + * followed by all elements of `that`. */ def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 67ea4cdb00..f18e2ab6bb 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -316,11 +316,12 @@ trait GenTraversableOnce[+A] extends Any { * @return the sum of all elements of this $coll with respect to the `+` operator in `num`. * * @usecase def sum: A + * @inheritdoc * - * @return the sum of all elements in this $coll of numbers of type `Int`. - * Instead of `Int`, any other type `T` with an implicit `Numeric[T]` implementation - * can be used as element type of the $coll and as result type of `sum`. - * Examples of such types are: `Long`, `Float`, `Double`, `BigInt`. + * @return the sum of all elements in this $coll of numbers of type `Int`. + * Instead of `Int`, any other type `T` with an implicit `Numeric[T]` implementation + * can be used as element type of the $coll and as result type of `sum`. + * Examples of such types are: `Long`, `Float`, `Double`, `BigInt`. * */ def sum[A1 >: A](implicit num: Numeric[A1]): A1 @@ -333,11 +334,12 @@ trait GenTraversableOnce[+A] extends Any { * @return the product of all elements of this $coll with respect to the `*` operator in `num`. * * @usecase def product: A + * @inheritdoc * - * @return the product of all elements in this $coll of numbers of type `Int`. - * Instead of `Int`, any other type `T` with an implicit `Numeric[T]` implementation - * can be used as element type of the $coll and as result type of `product`. - * Examples of such types are: `Long`, `Float`, `Double`, `BigInt`. + * @return the product of all elements in this $coll of numbers of type `Int`. + * Instead of `Int`, any other type `T` with an implicit `Numeric[T]` implementation + * can be used as element type of the $coll and as result type of `product`. + * Examples of such types are: `Long`, `Float`, `Double`, `BigInt`. */ def product[A1 >: A](implicit num: Numeric[A1]): A1 @@ -348,7 +350,9 @@ trait GenTraversableOnce[+A] extends Any { * @return the smallest element of this $coll with respect to the ordering `cmp`. * * @usecase def min: A - * @return the smallest element of this $coll + * @inheritdoc + * + * @return the smallest element of this $coll */ def min[A1 >: A](implicit ord: Ordering[A1]): A @@ -359,7 +363,9 @@ trait GenTraversableOnce[+A] extends Any { * @return the largest element of this $coll with respect to the ordering `cmp`. * * @usecase def max: A - * @return the largest element of this $coll. + * @inheritdoc + * + * @return the largest element of this $coll. */ def max[A1 >: A](implicit ord: Ordering[A1]): A @@ -387,12 +393,13 @@ trait GenTraversableOnce[+A] extends Any { * Copying will stop once either the end of the current $coll is reached, * or the end of the array is reached. * - * $willNotTerminateInf - * * @param xs the array to fill. * @tparam B the type of the elements of the array. * * @usecase def copyToArray(xs: Array[A]): Unit + * @inheritdoc + * + * $willNotTerminateInf */ def copyToArray[B >: A](xs: Array[B]): Unit @@ -401,13 +408,14 @@ trait GenTraversableOnce[+A] extends Any { * Copying will stop once either the end of the current $coll is reached, * or the end of the array is reached. * - * $willNotTerminateInf - * * @param xs the array to fill. * @param start the starting index. * @tparam B the type of the elements of the array. * * @usecase def copyToArray(xs: Array[A], start: Int): Unit + * @inheritdoc + * + * $willNotTerminateInf */ def copyToArray[B >: A](xs: Array[B], start: Int): Unit @@ -450,15 +458,18 @@ trait GenTraversableOnce[+A] extends Any { def mkString: String /** Converts this $coll to an array. - * $willNotTerminateInf * * @tparam B the type of the elements of the array. A `ClassManifest` for * this type must be available. * @return an array containing all elements of this $coll. * * @usecase def toArray: Array[A] - * @return an array containing all elements of this $coll. - * A `ClassManifest` must be available for the element type of this $coll. + * @inheritdoc + * + * $willNotTerminateInf + * + * @return an array containing all elements of this $coll. + * A `ClassManifest` must be available for the element type of this $coll. */ def toArray[A1 >: A: ClassManifest]: Array[A1] @@ -530,11 +541,13 @@ trait GenTraversableOnce[+A] extends Any { * pair in the map. Duplicate keys will be overwritten by later keys: * if this is an unordered collection, which key is in the resulting map * is undefined. - * $willNotTerminateInf * @return a map containing all elements of this $coll. + * * @usecase def toMap[T, U]: Map[T, U] - * @return a map of type `immutable.Map[T, U]` - * containing all key/value pairs of type `(T, U)` of this $coll. + * @inheritdoc + * $willNotTerminateInf + * @return a map of type `immutable.Map[T, U]` + * containing all key/value pairs of type `(T, U)` of this $coll. */ def toMap[K, V](implicit ev: A <:< (K, V)): GenMap[K, V] } diff --git a/src/library/scala/collection/IterableLike.scala b/src/library/scala/collection/IterableLike.scala index 73d4efe125..fb6d154952 100644 --- a/src/library/scala/collection/IterableLike.scala +++ b/src/library/scala/collection/IterableLike.scala @@ -67,6 +67,7 @@ self => * Subclasses should re-implement this method if a more efficient implementation exists. * * @usecase def foreach(f: A => Unit): Unit + * @inheritdoc */ def foreach[U](f: A => U): Unit = iterator.foreach(f) diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index d46d215e0c..7b57a91c41 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -322,7 +322,9 @@ trait Iterator[+A] extends TraversableOnce[A] { * @return a new iterator that first yields the values produced by this * iterator followed by the values produced by iterator `that`. * @note Reuse: $consumesTwoAndProducesOneIterator + * * @usecase def ++(that: => Iterator[A]): Iterator[A] + * @inheritdoc */ def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new AbstractIterator[B] { // optimize a little bit to prevent n log n behavior. @@ -609,7 +611,9 @@ trait Iterator[+A] extends TraversableOnce[A] { * followed by the minimal number of occurrences of `elem` so * that the number of produced values is at least `len`. * @note Reuse: $consumesAndProducesIterator + * * @usecase def padTo(len: Int, elem: A): Iterator[A] + * @inheritdoc */ def padTo[A1 >: A](len: Int, elem: A1): Iterator[A1] = new AbstractIterator[A1] { private var count = 0 @@ -658,7 +662,9 @@ trait Iterator[+A] extends TraversableOnce[A] { * If this iterator is shorter than `that`, `thisElem` values are used to pad the result. * If `that` is shorter than this iterator, `thatElem` values are used to pad the result. * @note Reuse: $consumesTwoAndProducesOneIterator + * * @usecase def zipAll[B](that: Iterator[B], thisElem: A, thatElem: B): Iterator[(A, B)] + * @inheritdoc */ def zipAll[B, A1 >: A, B1 >: B](that: Iterator[B], thisElem: A1, thatElem: B1): Iterator[(A1, B1)] = new AbstractIterator[(A1, B1)] { def hasNext = self.hasNext || that.hasNext @@ -682,7 +688,9 @@ trait Iterator[+A] extends TraversableOnce[A] { * but this is not necessary. * * @note Reuse: $consumesIterator + * * @usecase def foreach(f: A => Unit): Unit + * @inheritdoc */ def foreach[U](f: A => U) { while (hasNext) f(next()) } @@ -1057,15 +1065,17 @@ trait Iterator[+A] extends TraversableOnce[A] { * Copying will stop once either the end of the current iterator is reached, * or the end of the array is reached, or `len` elements have been copied. * - * $willNotTerminateInf - * * @param xs the array to fill. * @param start the starting index. * @param len the maximal number of elements to copy. * @tparam B the type of the elements of the array. * * @note Reuse: $consumesIterator + * * @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit + * @inheritdoc + * + * $willNotTerminateInf */ def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Unit = { var i = start diff --git a/src/library/scala/collection/MapLike.scala b/src/library/scala/collection/MapLike.scala index 07116e99dd..8f88e62791 100644 --- a/src/library/scala/collection/MapLike.scala +++ b/src/library/scala/collection/MapLike.scala @@ -91,14 +91,18 @@ self => * @param kv the key/value pair * @tparam B1 the type of the value in the key/value pair. * @return a new map with the new binding added to this map + * * @usecase def + (kv: (A, B)): Map[A, B] + * @inheritdoc */ def + [B1 >: B] (kv: (A, B1)): Map[A, B1] /** Removes a key from this map, returning a new map. * @param key the key to be removed * @return a new map without a binding for `key` + * * @usecase def - (key: A): Map[A, B] + * @inheritdoc */ def - (key: A): This @@ -115,7 +119,9 @@ self => * @tparam B1 the result type of the default computation. * @return the value associated with `key` if it exists, * otherwise the result of the `default` computation. + * * @usecase def getOrElse(key: A, default: => B): B + * @inheritdoc */ def getOrElse[B1 >: B](key: A, default: => B1): B1 = get(key) match { case Some(v) => v @@ -255,7 +261,9 @@ self => * @param value the value * @tparam B1 the type of the added value * @return A new map with the new key/value mapping added to this map. + * * @usecase def updated(key: A, value: B): Map[A, B] + * @inheritdoc */ def updated [B1 >: B](key: A, value: B1): Map[A, B1] = this + ((key, value)) @@ -269,8 +277,10 @@ self => * @param kvs the remaining key/value pairs * @tparam B1 the type of the added values * @return a new map with the given bindings added to this map + * * @usecase def + (kvs: (A, B)*): Map[A, B] - * @param the key/value pairs + * @inheritdoc + * @param the key/value pairs */ def + [B1 >: B] (kv1: (A, B1), kv2: (A, B1), kvs: (A, B1) *): Map[A, B1] = this + kv1 + kv2 ++ kvs @@ -280,7 +290,9 @@ self => * @param kvs the collection containing the added key/value pairs * @tparam B1 the type of the added values * @return a new map with the given bindings added to this map + * * @usecase def ++ (xs: Traversable[(A, B)]): Map[A, B] + * @inheritdoc */ def ++[B1 >: B](xs: GenTraversableOnce[(A, B1)]): Map[A, B1] = ((repr: Map[A, B1]) /: xs.seq) (_ + _) diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala index 526ea7e240..fd1d42d7e9 100644 --- a/src/library/scala/collection/SeqLike.scala +++ b/src/library/scala/collection/SeqLike.scala @@ -410,13 +410,6 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ /** Produces a new sequence which contains all elements of this $coll and also all elements of * a given sequence. `xs union ys` is equivalent to `xs ++ ys`. - * $willNotTerminateInf - * - * Another way to express this - * is that `xs union ys` computes the order-presevring multi-set union of `xs` and `ys`. - * `union` is hence a counter-part of `diff` and `intersect` which also work on multi-sets. - * - * $willNotTerminateInf * * @param that the sequence to add. * @tparam B the element type of the returned $coll. @@ -425,14 +418,21 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ * @return a new collection of type `That` which contains all elements of this $coll * followed by all elements of `that`. * @usecase def union(that: Seq[A]): $Coll[A] - * @return a new $coll which contains all elements of this $coll - * followed by all elements of `that`. + * @inheritdoc + * + * Another way to express this + * is that `xs union ys` computes the order-presevring multi-set union of `xs` and `ys`. + * `union` is hence a counter-part of `diff` and `intersect` which also work on multi-sets. + * + * $willNotTerminateInf + * + * @return a new $coll which contains all elements of this $coll + * followed by all elements of `that`. */ override def union[B >: A, That](that: GenSeq[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = this ++ that /** Computes the multiset difference between this $coll and another sequence. - * $willNotTerminateInf * * @param that the sequence of elements to remove * @tparam B the element type of the returned $coll. @@ -444,11 +444,15 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form * part of the result, but any following occurrences will. * @usecase def diff(that: Seq[A]): $Coll[A] - * @return a new $coll which contains all elements of this $coll - * except some of occurrences of elements that also appear in `that`. - * If an element value `x` appears - * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form - * part of the result, but any following occurrences will. + * @inheritdoc + * + * $willNotTerminateInf + * + * @return a new $coll which contains all elements of this $coll + * except some of occurrences of elements that also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form + * part of the result, but any following occurrences will. */ def diff[B >: A](that: GenSeq[B]): Repr = { val occ = occCounts(that.seq) @@ -463,7 +467,6 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ def diff[B >: A](that: Seq[B]): Repr = diff(that: GenSeq[B]) /** Computes the multiset intersection between this $coll and another sequence. - * $mayNotTerminateInf * * @param that the sequence of elements to intersect with. * @tparam B the element type of the returned $coll. @@ -475,11 +478,15 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained * in the result, but any following occurrences will be omitted. * @usecase def intersect(that: Seq[A]): $Coll[A] - * @return a new $coll which contains all elements of this $coll - * which also appear in `that`. - * If an element value `x` appears - * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained - * in the result, but any following occurrences will be omitted. + * @inheritdoc + * + * $mayNotTerminateInf + * + * @return a new $coll which contains all elements of this $coll + * which also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained + * in the result, but any following occurrences will be omitted. */ def intersect[B >: A](that: GenSeq[B]): Repr = { val occ = occCounts(that.seq) diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 0da1cfb913..1f5beb5109 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -103,10 +103,6 @@ trait TraversableLike[+A, +Repr] extends Any /** Applies a function `f` to all elements of this $coll. * - * Note: this method underlies the implementation of most other bulk operations. - * It's important to implement this method in an efficient way. - * - * * @param f the function that is applied for its side-effect to every element. * The result of function `f` is discarded. * @@ -115,6 +111,11 @@ trait TraversableLike[+A, +Repr] extends Any * but this is not necessary. * * @usecase def foreach(f: A => Unit): Unit + * @inheritdoc + * + * Note: this method underlies the implementation of most other bulk operations. + * It's important to implement this method in an efficient way. + * */ def foreach[U](f: A => U): Unit @@ -165,17 +166,6 @@ trait TraversableLike[+A, +Repr] extends Any * the resulting collection rather than the left one. * Mnemonic: the COLon is on the side of the new COLlection type. * - * Example: - * {{{ - * scala> val x = List(1) - * x: List[Int] = List(1) - * - * scala> val y = LinkedList(2) - * y: scala.collection.mutable.LinkedList[Int] = LinkedList(2) - * - * scala> val z = x ++: y - * z: scala.collection.mutable.LinkedList[Int] = LinkedList(1, 2) - * }}} * @param that the traversable to append. * @tparam B the element type of the returned collection. * @tparam That $thatinfo @@ -184,9 +174,22 @@ trait TraversableLike[+A, +Repr] extends Any * of this $coll followed by all elements of `that`. * * @usecase def ++:[B](that: TraversableOnce[B]): $Coll[B] - * - * @return a new $coll which contains all elements of this $coll - * followed by all elements of `that`. + * @inheritdoc + * + * Example: + * {{{ + * scala> val x = List(1) + * x: List[Int] = List(1) + * + * scala> val y = LinkedList(2) + * y: scala.collection.mutable.LinkedList[Int] = LinkedList(2) + * + * scala> val z = x ++: y + * z: scala.collection.mutable.LinkedList[Int] = LinkedList(1, 2) + * }}} + * + * @return a new $coll which contains all elements of this $coll + * followed by all elements of `that`. */ def ++:[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) @@ -284,11 +287,12 @@ trait TraversableLike[+A, +Repr] extends Any * The order of the elements is preserved. * * @usecase def filterMap[B](f: A => Option[B]): $Coll[B] + * @inheritdoc * - * @param pf the partial function which filters and maps the $coll. - * @return a new $coll resulting from applying the given option-valued function - * `f` to each element and collecting all defined results. - * The order of the elements is preserved. + * @param pf the partial function which filters and maps the $coll. + * @return a new $coll resulting from applying the given option-valued function + * `f` to each element and collecting all defined results. + * The order of the elements is preserved. def filterMap[B, That](f: A => Option[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) for (x <- this) @@ -588,8 +592,6 @@ trait TraversableLike[+A, +Repr] extends Any * Copying will stop once either the end of the current $coll is reached, * or the end of the array is reached, or `len` elements have been copied. * - * $willNotTerminateInf - * * @param xs the array to fill. * @param start the starting index. * @param len the maximal number of elements to copy. @@ -597,6 +599,9 @@ trait TraversableLike[+A, +Repr] extends Any * * * @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit + * @inheritdoc + * + * $willNotTerminateInf */ def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) { var i = start @@ -694,10 +699,11 @@ trait TraversableLike[+A, +Repr] extends Any * that satisfies predicate `p` and collecting the results. * * @usecase def map[B](f: A => B): $Coll[B] + * @inheritdoc * - * @return a new $coll resulting from applying the given function - * `f` to each element of the outer $coll that satisfies - * predicate `p` and collecting the results. + * @return a new $coll resulting from applying the given function + * `f` to each element of the outer $coll that satisfies + * predicate `p` and collecting the results. */ def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) @@ -710,9 +716,6 @@ trait TraversableLike[+A, +Repr] extends Any * outer $coll containing this `WithFilter` instance that satisfy * predicate `p` and concatenating the results. * - * The type of the resulting collection will be guided by the static type - * of the outer $coll. - * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. * @tparam That $thatinfo @@ -723,11 +726,15 @@ trait TraversableLike[+A, +Repr] extends Any * concatenating the results. * * @usecase def flatMap[B](f: A => TraversableOnce[B]): $Coll[B] + * @inheritdoc + * + * The type of the resulting collection will be guided by the static type + * of the outer $coll. * - * @return a new $coll resulting from applying the given - * collection-valued function `f` to each element of the - * outer $coll that satisfies predicate `p` and concatenating - * the results. + * @return a new $coll resulting from applying the given + * collection-valued function `f` to each element of the + * outer $coll that satisfies predicate `p` and concatenating + * the results. */ def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) @@ -747,6 +754,7 @@ trait TraversableLike[+A, +Repr] extends Any * but this is not necessary. * * @usecase def foreach(f: A => Unit): Unit + * @inheritdoc */ def foreach[U](f: A => U): Unit = for (x <- self) diff --git a/src/library/scala/collection/generic/GenericTraversableTemplate.scala b/src/library/scala/collection/generic/GenericTraversableTemplate.scala index 7333074778..6586434924 100644 --- a/src/library/scala/collection/generic/GenericTraversableTemplate.scala +++ b/src/library/scala/collection/generic/GenericTraversableTemplate.scala @@ -119,22 +119,25 @@ trait GenericTraversableTemplate[+A, +CC[X] <: GenTraversable[X]] extends HasNew * a $coll formed by the elements of these traversable * collections. * - * The resulting collection's type will be guided by the - * static type of $coll. For example: - * - * {{{ - * val xs = List(Set(1, 2, 3), Set(1, 2, 3)) - * // xs == List(1, 2, 3, 1, 2, 3) - * - * val ys = Set(List(1, 2, 3), List(3, 2, 1)) - * // ys == Set(1, 2, 3) - * }}} - * * @tparam B the type of the elements of each traversable collection. * @param asTraversable an implicit conversion which asserts that the element * type of this $coll is a `GenTraversable`. * @return a new $coll resulting from concatenating all element ${coll}s. + * * @usecase def flatten[B]: $Coll[B] + * + * @inheritdoc + * + * The resulting collection's type will be guided by the + * static type of $coll. For example: + * + * {{{ + * val xs = List(Set(1, 2, 3), Set(1, 2, 3)) + * // xs == List(1, 2, 3, 1, 2, 3) + * + * val ys = Set(List(1, 2, 3), List(3, 2, 1)) + * // ys == Set(1, 2, 3) + * }}} */ def flatten[B](implicit asTraversable: A => /*<:<!!!*/ GenTraversableOnce[B]): CC[B] = { val b = genericBuilder[B] diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 381fcf3117..870c179b2d 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -93,8 +93,12 @@ sealed abstract class List[+A] extends AbstractSeq[A] * @param x the element to prepend. * @return a list which contains `x` as first element and * which continues with this list. - * @example `1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)` + * * @usecase def ::(x: A): List[A] + * @inheritdoc + * + * Example: + * {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}} */ def ::[B >: A] (x: B): List[B] = new scala.collection.immutable.::(x, this) @@ -103,8 +107,12 @@ sealed abstract class List[+A] extends AbstractSeq[A] * @param prefix The list elements to prepend. * @return a list resulting from the concatenation of the given * list `prefix` and this list. - * @example `List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)` + * * @usecase def :::(prefix: List[A]): List[A] + * @inheritdoc + * + * Example: + * {{{List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)}}} */ def :::[B >: A](prefix: List[B]): List[B] = if (isEmpty) prefix @@ -117,7 +125,9 @@ sealed abstract class List[+A] extends AbstractSeq[A] * * @param prefix the prefix to reverse and then prepend * @return the concatenation of the reversed prefix and the current list. + * * @usecase def reverse_:::(prefix: List[A]): List[A] + * @inheritdoc */ def reverse_:::[B >: A](prefix: List[B]): List[B] = { var these: List[B] = this @@ -137,7 +147,9 @@ sealed abstract class List[+A] extends AbstractSeq[A] * @tparam B the element type of the returned collection. * @return a list resulting from applying the given function * `f` to each element of this list and collecting the results. + * * @usecase def mapConserve(f: A => A): List[A] + * @inheritdoc */ def mapConserve[B >: A <: AnyRef](f: A => B): List[B] = { @tailrec diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index 7e0fa366ab..5551c04ce2 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -668,8 +668,9 @@ self: ParIterableLike[T, Repr, Sequential] => * @return a collection containing the prefix scan of the elements in the original collection * * @usecase def scan(z: T)(op: (T, T) => T): $Coll[T] + * @inheritdoc * - * @return a new $coll containing the prefix scan of the elements in this $coll + * @return a new $coll containing the prefix scan of the elements in this $coll */ def scan[U >: T, That](z: U)(op: (U, U) => U)(implicit bf: CanBuildFrom[Repr, U, That]): That = if (bf(repr).isCombiner) { if (tasksupport.parallelismLevel > 1) { diff --git a/src/library/scala/collection/parallel/ParSeqLike.scala b/src/library/scala/collection/parallel/ParSeqLike.scala index 9f28a286ca..b3c527da84 100644 --- a/src/library/scala/collection/parallel/ParSeqLike.scala +++ b/src/library/scala/collection/parallel/ParSeqLike.scala @@ -285,7 +285,6 @@ self => } /** Computes the multiset intersection between this $coll and another sequence. - * $mayNotTerminateInf * * @param that the sequence of elements to intersect with. * @tparam B the element type of the returned $coll. @@ -296,12 +295,17 @@ self => * If an element value `x` appears * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained * in the result, but any following occurrences will be omitted. + * * @usecase def intersect(that: Seq[T]): $Coll[T] - * @return a new $coll which contains all elements of this $coll - * which also appear in `that`. - * If an element value `x` appears - * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained - * in the result, but any following occurrences will be omitted. + * @inheritdoc + * + * $mayNotTerminateInf + * + * @return a new $coll which contains all elements of this $coll + * which also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained + * in the result, but any following occurrences will be omitted. */ def intersect[U >: T](that: GenSeq[U]) = sequentially { _ intersect that diff --git a/src/library/scala/reflect/api/TreePrinters.scala b/src/library/scala/reflect/api/TreePrinters.scala index 21b55e9c0e..43865915d3 100644 --- a/src/library/scala/reflect/api/TreePrinters.scala +++ b/src/library/scala/reflect/api/TreePrinters.scala @@ -41,7 +41,7 @@ trait TreePrinters { self: Universe => else if (tree.original != null) print(".setOriginal(", tree.original, ")") case tree: Tree => - print(tree.productPrefix+"(") + print(tree.printingPrefix+"(") val it = tree.productIterator while (it.hasNext) { it.next() match { diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index a355207ff0..a8276dc853 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -76,6 +76,12 @@ trait Trees { self: Universe => private[this] var rawpos: Position = NoPosition + /** Prefix under which to print this tree type. Defaults to product + * prefix (e.g. DefTree) but because that is used in reification + * it cannot be altered without breaking reflection. + */ + def printingPrefix = productPrefix + def pos = rawpos def pos_=(pos: Position) = rawpos = pos def setPos(pos: Position): this.type = { rawpos = pos; this } @@ -249,6 +255,7 @@ trait Trees { self: Universe => * are in DefTrees. */ trait RefTree extends SymTree { + def qualifier: Tree // empty for Idents def name: Name } @@ -489,16 +496,14 @@ trait Trees { self: Universe => /** Factory method for object creation `new tpt(args_1)...(args_n)` * A `New(t, as)` is expanded to: `(new t).<init>(as)` */ - def New(tpt: Tree, argss: List[List[Tree]]): Tree = { - // todo. we need to expose names in scala.reflect.api - val superRef: Tree = Select(New(tpt), nme.CONSTRUCTOR) - if (argss.isEmpty) Apply(superRef, Nil) - else (superRef /: argss) (Apply) + def New(tpt: Tree, argss: List[List[Tree]]): Tree = argss match { + case Nil => new ApplyConstructor(tpt, Nil) + case xs :: rest => rest.foldLeft(new ApplyConstructor(tpt, xs): Tree)(Apply) } /** 0-1 argument list new, based on a type. */ def New(tpe: Type, args: Tree*): Tree = - New(TypeTree(tpe), List(args.toList)) + new ApplyConstructor(TypeTree(tpe), args.toList) /** Type annotation, eliminated by explicit outer */ case class Typed(expr: Tree, tpt: Tree) @@ -537,6 +542,10 @@ trait Trees { self: Universe => class ApplyImplicitView(fun: Tree, args: List[Tree]) extends Apply(fun, args) + class ApplyConstructor(tpt: Tree, args: List[Tree]) extends Apply(Select(New(tpt), nme.CONSTRUCTOR), args) { + override def printingPrefix = "ApplyConstructor" + } + /** Dynamic value application. * In a dynamic application q.f(as) * - q is stored in qual @@ -575,7 +584,9 @@ trait Trees { self: Universe => Select(qualifier, sym.name) setSymbol sym /** Identifier <name> */ - case class Ident(name: Name) extends RefTree + case class Ident(name: Name) extends RefTree { + def qualifier: Tree = EmptyTree + } def Ident(name: String): Ident = Ident(newTermName(name)) diff --git a/src/library/scala/xml/parsing/MarkupParser.scala b/src/library/scala/xml/parsing/MarkupParser.scala index 32feaa2209..af9b5f47cf 100755 --- a/src/library/scala/xml/parsing/MarkupParser.scala +++ b/src/library/scala/xml/parsing/MarkupParser.scala @@ -134,7 +134,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests // /** {{{ - * <? prolog ::= xml S ... ?> + * <? prolog ::= xml S ... ?> * }}} */ def xmlProcInstr(): MetaData = { xToken("xml") @@ -195,7 +195,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * <? prolog ::= xml S? + * <? prolog ::= xml S? * // this is a bit more lenient than necessary... * }}} */ def prolog(): (Option[String], Option[String], Option[Boolean]) = @@ -355,7 +355,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' + * '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' * * see [15] * }}} */ @@ -369,7 +369,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' + * Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' * * see [15] * }}} */ @@ -399,7 +399,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * '<' content1 ::= ... + * '<' content1 ::= ... * }}} */ def content1(pscope: NamespaceBinding, ts: NodeBuffer) { ch match { @@ -420,7 +420,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * content1 ::= '<' content1 | '&' charref ... + * content1 ::= '<' content1 | '&' charref ... * }}} */ def content(pscope: NamespaceBinding): NodeSeq = { var ts = new NodeBuffer @@ -490,7 +490,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests /** parses document type declaration and assigns it to instance variable * dtd. * {{{ - * <! parseDTD ::= DOCTYPE name ... > + * <! parseDTD ::= DOCTYPE name ... > * }}} */ def parseDTD() { // dirty but fast var extID: ExternalID = null @@ -545,8 +545,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag - * | xmlTag1 '/' '>' + * '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag + * | xmlTag1 '/' '>' * }}} */ def element1(pscope: NamespaceBinding): NodeSeq = { val pos = this.pos @@ -778,7 +778,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * <! attlist := ATTLIST + * <! attlist := ATTLIST * }}} */ def attrDecl() = { xToken("TTLIST") @@ -824,7 +824,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * <! element := ELEMENT + * <! element := ELEMENT * }}} */ def entityDecl() = { var isParameterEntity = false diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala index 524dc06327..ad2e155182 100644 --- a/src/partest/scala/tools/partest/PartestTask.scala +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -299,6 +299,16 @@ class PartestTask extends Task with CompilationPathProperty { } } getOrElse sys.error("Provided classpath does not contain a Scala partest.") + val scalaActors = { + (classpath.list map { fs => new File(fs) }) find { f => + f.getName match { + case "scala-actors.jar" => true + case "actors" if (f.getParentFile.getName == "classes") => true + case _ => false + } + } + } getOrElse sys.error("Provided classpath does not contain a Scala actors.") + def scalacArgsFlat: Option[Seq[String]] = scalacArgs map (_ flatMap { a => val parts = a.getParts if(parts eq null) Seq[String]() else parts.toSeq @@ -324,6 +334,7 @@ class PartestTask extends Task with CompilationPathProperty { antFileManager.LATEST_LIB = scalaLibrary.getAbsolutePath antFileManager.LATEST_COMP = scalaCompiler.getAbsolutePath antFileManager.LATEST_PARTEST = scalaPartest.getAbsolutePath + antFileManager.LATEST_ACTORS = scalaActors.getAbsolutePath javacmd foreach (x => antFileManager.JAVACMD = x.getAbsolutePath) javaccmd foreach (x => antFileManager.JAVAC_CMD = x.getAbsolutePath) diff --git a/src/partest/scala/tools/partest/nest/AntRunner.scala b/src/partest/scala/tools/partest/nest/AntRunner.scala index 4795e5551a..e77385d6e9 100644 --- a/src/partest/scala/tools/partest/nest/AntRunner.scala +++ b/src/partest/scala/tools/partest/nest/AntRunner.scala @@ -22,6 +22,7 @@ class AntRunner extends DirectRunner { var LATEST_LIB: String = _ var LATEST_COMP: String = _ var LATEST_PARTEST: String = _ + var LATEST_ACTORS: String = _ val testRootPath: String = "test" val testRootDir: Directory = Directory(testRootPath) } diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala index 3d72227b04..fa533eeb10 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala @@ -83,6 +83,7 @@ class ConsoleFileManager extends FileManager { latestFile = testClassesDir.parent / "bin" latestLibFile = testClassesDir / "library" + latestActorsFile = testClassesDir / "library" / "actors" latestCompFile = testClassesDir / "compiler" latestPartestFile = testClassesDir / "partest" latestFjbgFile = testParent / "lib" / "fjbg.jar" @@ -92,6 +93,7 @@ class ConsoleFileManager extends FileManager { NestUI.verbose("Running on "+dir) latestFile = dir / "bin" latestLibFile = dir / "lib/scala-library.jar" + latestActorsFile = dir / "lib/scala-actors.jar" latestCompFile = dir / "lib/scala-compiler.jar" latestPartestFile = dir / "lib/scala-partest.jar" } @@ -100,6 +102,7 @@ class ConsoleFileManager extends FileManager { NestUI.verbose("Running build/quick") latestFile = prefixFile("build/quick/bin") latestLibFile = prefixFile("build/quick/classes/library") + latestActorsFile = prefixFile("build/quick/classes/library/actors") latestCompFile = prefixFile("build/quick/classes/compiler") latestPartestFile = prefixFile("build/quick/classes/partest") } @@ -109,6 +112,7 @@ class ConsoleFileManager extends FileManager { val p = testParent.getParentFile latestFile = prefixFileWith(p, "bin") latestLibFile = prefixFileWith(p, "lib/scala-library.jar") + latestActorsFile = prefixFileWith(p, "lib/scala-actors.jar") latestCompFile = prefixFileWith(p, "lib/scala-compiler.jar") latestPartestFile = prefixFileWith(p, "lib/scala-partest.jar") } @@ -117,6 +121,7 @@ class ConsoleFileManager extends FileManager { NestUI.verbose("Running dists/latest") latestFile = prefixFile("dists/latest/bin") latestLibFile = prefixFile("dists/latest/lib/scala-library.jar") + latestActorsFile = prefixFile("dists/latest/lib/scala-actors.jar") latestCompFile = prefixFile("dists/latest/lib/scala-compiler.jar") latestPartestFile = prefixFile("dists/latest/lib/scala-partest.jar") } @@ -125,6 +130,7 @@ class ConsoleFileManager extends FileManager { NestUI.verbose("Running build/pack") latestFile = prefixFile("build/pack/bin") latestLibFile = prefixFile("build/pack/lib/scala-library.jar") + latestActorsFile = prefixFile("build/pack/lib/scala-actors.jar") latestCompFile = prefixFile("build/pack/lib/scala-compiler.jar") latestPartestFile = prefixFile("build/pack/lib/scala-partest.jar") } @@ -159,14 +165,17 @@ class ConsoleFileManager extends FileManager { LATEST_LIB = latestLibFile.getAbsolutePath LATEST_COMP = latestCompFile.getAbsolutePath LATEST_PARTEST = latestPartestFile.getAbsolutePath + LATEST_ACTORS = latestActorsFile.getAbsolutePath } var LATEST_LIB: String = "" var LATEST_COMP: String = "" var LATEST_PARTEST: String = "" + var LATEST_ACTORS: String = "" var latestFile: File = _ var latestLibFile: File = _ + var latestActorsFile: File = _ var latestCompFile: File = _ var latestPartestFile: File = _ var latestFjbgFile: File = _ diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala index d3d50ca58c..20f435cfbb 100644 --- a/src/partest/scala/tools/partest/nest/DirectRunner.scala +++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala @@ -57,13 +57,14 @@ trait DirectRunner { // for example, see how it's done in ReflectiveRunner //val consFM = new ConsoleFileManager //import consFM.{ latestCompFile, latestLibFile, latestPartestFile } - val latestCompFile = new File(fileManager.LATEST_COMP); - val latestLibFile = new File(fileManager.LATEST_LIB); - val latestPartestFile = new File(fileManager.LATEST_PARTEST); + val latestCompFile = new File(fileManager.LATEST_COMP) + val latestLibFile = new File(fileManager.LATEST_LIB) + val latestPartestFile = new File(fileManager.LATEST_PARTEST) + val latestActorsFile = new File(fileManager.LATEST_ACTORS) val scalacheckURL = PathSettings.scalaCheck.toURL val scalaCheckParentClassLoader = ScalaClassLoader.fromURLs( - List(scalacheckURL, latestCompFile.toURI.toURL, latestLibFile.toURI.toURL, latestPartestFile.toURI.toURL) + scalacheckURL :: (List(latestCompFile, latestLibFile, latestActorsFile, latestPartestFile).map(_.toURI.toURL)) ) Output.init() diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index a4a94fe93e..6d9e64730f 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -62,6 +62,7 @@ trait FileManager extends FileUtil { var LATEST_LIB: String var LATEST_COMP: String var LATEST_PARTEST: String + var LATEST_ACTORS: String var showDiff = false var updateCheck = false diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala index 5cde63dc81..a0511774a9 100644 --- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -48,9 +48,9 @@ class ReflectiveRunner { new ConsoleFileManager import fileManager. - { latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile } + { latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile, latestActorsFile } val files = - Array(latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile) map (x => io.File(x)) + Array(latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile, latestScalapFile, latestActorsFile) map (x => io.File(x)) val sepUrls = files map (_.toURL) var sepLoader = new URLClassLoader(sepUrls, null) diff --git a/src/partest/scala/tools/partest/nest/SBTRunner.scala b/src/partest/scala/tools/partest/nest/SBTRunner.scala index 4c6f417df5..750e270c18 100644 --- a/src/partest/scala/tools/partest/nest/SBTRunner.scala +++ b/src/partest/scala/tools/partest/nest/SBTRunner.scala @@ -15,6 +15,7 @@ object SBTRunner extends DirectRunner { var LATEST_LIB: String = _ var LATEST_COMP: String = _ var LATEST_PARTEST: String = _ + var LATEST_ACTORS: String = _ val testRootPath: String = "test" val testRootDir: Directory = Directory(testRootPath) } @@ -60,6 +61,9 @@ object SBTRunner extends DirectRunner { fileManager.LATEST_COMP = comp getOrElse sys.error("No scala-compiler found! Classpath = " + fileManager.CLASSPATH) val partest: Option[String] = (fileManager.CLASSPATH split File.pathSeparator filter (_ matches ".*scala-partest.*\\.jar")).headOption fileManager.LATEST_PARTEST = partest getOrElse sys.error("No scala-partest found! Classpath = " + fileManager.CLASSPATH) + val actors: Option[String] = (fileManager.CLASSPATH split File.pathSeparator filter (_ matches ".*scala-actors.*\\.jar")).headOption + fileManager.LATEST_ACTORS = actors getOrElse sys.error("No scala-actors found! Classpath = " + fileManager.CLASSPATH) + // TODO - Do something useful here!!! fileManager.JAVAC_CMD = "javac" fileManager.failed = config.justFailedTests diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 3f2cb16082..cb6f2a0edc 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -55,6 +55,7 @@ class ScalaCheckFileManager(val origmanager: FileManager) extends FileManager { var LATEST_LIB: String = origmanager.LATEST_LIB var LATEST_COMP: String = origmanager.LATEST_COMP var LATEST_PARTEST: String = origmanager.LATEST_PARTEST + var LATEST_ACTORS: String = origmanager.LATEST_ACTORS } object Output { |