From 535c8d73c9a870c3b03bbd25be31cfe7c7ed9fe7 Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Thu, 12 Apr 2012 16:28:35 +0200 Subject: Moving Duration DSL to a separate package. Reverting to Java 6 time units. Review by: @phaller --- src/library/scala/concurrent/util/Duration.scala | 174 ++++++++------------- .../concurrent/util/duration/Classifier.scala | 9 ++ .../util/duration/NumericMultiplication.scala | 18 +++ .../scala/concurrent/util/duration/package.scala | 30 ++++ 4 files changed, 121 insertions(+), 110 deletions(-) create mode 100644 src/library/scala/concurrent/util/duration/Classifier.scala create mode 100644 src/library/scala/concurrent/util/duration/NumericMultiplication.scala create mode 100644 src/library/scala/concurrent/util/duration/package.scala (limited to 'src/library') diff --git a/src/library/scala/concurrent/util/Duration.scala b/src/library/scala/concurrent/util/Duration.scala index 33d034da76..6ee1696d39 100644 --- a/src/library/scala/concurrent/util/Duration.scala +++ b/src/library/scala/concurrent/util/Duration.scala @@ -8,51 +8,6 @@ import java.util.concurrent.TimeUnit import TimeUnit._ import java.lang.{ Double ⇒ JDouble } -object DurationImplicits { - trait Classifier[C] { - type R - def convert(d: FiniteDuration): R - } - - object span - implicit object spanConvert extends Classifier[span.type] { - type R = FiniteDuration - def convert(d: FiniteDuration) = d - } - - object fromNow - implicit object fromNowConvert extends Classifier[fromNow.type] { - type R = Deadline - def convert(d: FiniteDuration) = Deadline.now + d - } - - implicit def intToDurationInt(n: Int) = new DurationInt(n) - implicit def longToDurationLong(n: Long) = new DurationLong(n) - implicit def doubleToDurationDouble(d: Double) = new DurationDouble(d) - - implicit def pairIntToDuration(p: (Int, TimeUnit)) = Duration(p._1, p._2) - implicit def pairLongToDuration(p: (Long, TimeUnit)) = Duration(p._1, p._2) - implicit def durationToPair(d: Duration) = (d.length, d.unit) - - /* - * Avoid reflection based invocation by using non-duck type - */ - class IntMult(i: Int) { - def *(d: Duration) = d * i - } - implicit def intMult(i: Int) = new IntMult(i) - - class LongMult(l: Long) { - def *(d: Duration) = d * l - } - implicit def longMult(l: Long) = new LongMult(l) - - class DoubleMult(f: Double) { - def *(d: Duration) = d * f - } - implicit def doubleMult(f: Double) = new DoubleMult(f) -} - case class Deadline private (time: Duration) { def +(other: Duration): Deadline = copy(time = time + other) def -(other: Duration): Deadline = copy(time = time - other) @@ -71,10 +26,7 @@ object Duration { def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit) def apply(length: Double, unit: TimeUnit): FiniteDuration = fromNanos(unit.toNanos(1) * length) - def apply(length: Long, unit: String): FiniteDuration = { - val (mult, timeUnit) = Duration.timeUnit(unit) - new FiniteDuration(length * mult, timeUnit) - } + def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit)) /** * Construct a Duration by parsing a String. In case of a format error, a @@ -117,11 +69,11 @@ object Duration { def unapply(s: String): Option[Duration] = s match { case RE(length, d, h, m, s, ms, mus, ns) ⇒ if (d ne null) - Some(Duration(JDouble.parseDouble(length) * 86400, SECONDS)) + Some(Duration(JDouble.parseDouble(length), DAYS)) else if (h ne null) - Some(Duration(JDouble.parseDouble(length) * 3600, SECONDS)) + Some(Duration(JDouble.parseDouble(length), HOURS)) else if (m ne null) - Some(Duration(JDouble.parseDouble(length) * 60, SECONDS)) + Some(Duration(JDouble.parseDouble(length), MINUTES)) else if (s ne null) Some(Duration(JDouble.parseDouble(length), SECONDS)) else if (ms ne null) @@ -142,11 +94,11 @@ object Duration { def fromNanos(nanos: Long): FiniteDuration = { if (nanos % 86400000000000L == 0) { - Duration(nanos / 1000000000L, SECONDS) - } else if (nanos % 1000000000L == 0) { - Duration(nanos / 1000000000L, SECONDS) - } else if (nanos % 1000000000L == 0) { - Duration(nanos / 1000000000L, SECONDS) + Duration(nanos / 86400000000000L, DAYS) + } else if (nanos % 3600000000000L == 0) { + Duration(nanos / 3600000000000L, HOURS) + } else if (nanos % 60000000000L == 0) { + Duration(nanos / 60000000000L, MINUTES) } else if (nanos % 1000000000L == 0) { Duration(nanos / 1000000000L, SECONDS) } else if (nanos % 1000000L == 0) { @@ -161,14 +113,14 @@ object Duration { /** * Parse TimeUnit from string representation. */ - protected[util] def timeUnit(unit: String): (Long, TimeUnit) = unit.toLowerCase match { - case "d" | "day" | "days" ⇒ (86400, SECONDS) - case "h" | "hour" | "hours" ⇒ (3600, SECONDS) - case "min" | "minute" | "minutes" ⇒ (60, SECONDS) - case "s" | "sec" | "second" | "seconds" ⇒ (1, SECONDS) - case "ms" | "milli" | "millis" | "millisecond" | "milliseconds" ⇒ (1, MILLISECONDS) - case "µs" | "micro" | "micros" | "microsecond" | "microseconds" ⇒ (1, MICROSECONDS) - case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" ⇒ (1, NANOSECONDS) + protected[util] def timeUnit(unit: String): TimeUnit = unit.toLowerCase match { + case "d" | "day" | "days" ⇒ DAYS + case "h" | "hour" | "hours" ⇒ HOURS + case "min" | "minute" | "minutes" ⇒ MINUTES + case "s" | "sec" | "second" | "seconds" ⇒ SECONDS + case "ms" | "milli" | "millis" | "millisecond" | "milliseconds" ⇒ MILLISECONDS + case "µs" | "micro" | "micros" | "microsecond" | "microseconds" ⇒ MICROSECONDS + case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" ⇒ NANOSECONDS } val Zero: FiniteDuration = new FiniteDuration(0, NANOSECONDS) @@ -328,13 +280,9 @@ object FiniteDuration { def compare(a: FiniteDuration, b: FiniteDuration) = a compare b } - def apply(length: Long, unit: TimeUnit) = - new FiniteDuration(length, unit) + def apply(length: Long, unit: TimeUnit) = new FiniteDuration(length, unit) - def apply(length: Long, unit: String) = { - val (mult, timeUnit) = Duration.timeUnit(unit) - new FiniteDuration(length * mult, timeUnit) - } + def apply(length: Long, unit: String) = new FiniteDuration(length, Duration.timeUnit(unit)) } @@ -351,6 +299,12 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { def toUnit(u: TimeUnit) = long2double(toNanos) / NANOSECONDS.convert(1, u) override def toString = this match { + case Duration(1, DAYS) ⇒ "1 day" + case Duration(x, DAYS) ⇒ x + " days" + case Duration(1, HOURS) ⇒ "1 hour" + case Duration(x, HOURS) ⇒ x + " hours" + case Duration(1, MINUTES) ⇒ "1 minute" + case Duration(x, MINUTES) ⇒ x + " minutes" case Duration(1, SECONDS) ⇒ "1 second" case Duration(x, SECONDS) ⇒ x + " seconds" case Duration(1, MILLISECONDS) ⇒ "1 millisecond" @@ -404,7 +358,7 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { } class DurationInt(n: Int) { - import DurationImplicits.Classifier + import duration.Classifier def nanoseconds = Duration(n, NANOSECONDS) def nanos = Duration(n, NANOSECONDS) @@ -424,14 +378,14 @@ class DurationInt(n: Int) { def seconds = Duration(n, SECONDS) def second = Duration(n, SECONDS) - def minutes = Duration(n * 60, SECONDS) - def minute = Duration(n * 60, SECONDS) + def minutes = Duration(n, MINUTES) + def minute = Duration(n, MINUTES) - def hours = Duration(n * 3600, SECONDS) - def hour = Duration(n * 3600, SECONDS) + def hours = Duration(n, HOURS) + def hour = Duration(n, HOURS) - def days = Duration(n * 86400, SECONDS) - def day = Duration(n * 86400, SECONDS) + def days = Duration(n, DAYS) + def day = Duration(n, DAYS) def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) @@ -451,18 +405,18 @@ class DurationInt(n: Int) { def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) - def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 60, SECONDS)) - def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 60, SECONDS)) + def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) + def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) - def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 3600, SECONDS)) - def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 3600, SECONDS)) + def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) + def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) - def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 86400, SECONDS)) - def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 86400, SECONDS)) + def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) + def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) } class DurationLong(n: Long) { - import DurationImplicits.Classifier + import duration.Classifier def nanoseconds = Duration(n, NANOSECONDS) def nanos = Duration(n, NANOSECONDS) @@ -482,14 +436,14 @@ class DurationLong(n: Long) { def seconds = Duration(n, SECONDS) def second = Duration(n, SECONDS) - def minutes = Duration(n * 60, SECONDS) - def minute = Duration(n * 60, SECONDS) + def minutes = Duration(n, MINUTES) + def minute = Duration(n, MINUTES) - def hours = Duration(n * 3600, SECONDS) - def hour = Duration(n * 3600, SECONDS) + def hours = Duration(n, HOURS) + def hour = Duration(n, HOURS) - def days = Duration(n * 86400, SECONDS) - def day = Duration(n * 86400, SECONDS) + def days = Duration(n, DAYS) + def day = Duration(n, DAYS) def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) @@ -509,18 +463,18 @@ class DurationLong(n: Long) { def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) - def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 60, SECONDS)) - def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 60, SECONDS)) + def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) + def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) - def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 3600, SECONDS)) - def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 3600, SECONDS)) + def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) + def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) - def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 86400, SECONDS)) - def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n * 86400, SECONDS)) + def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) + def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) } class DurationDouble(d: Double) { - import DurationImplicits.Classifier + import duration.Classifier def nanoseconds = Duration(d, NANOSECONDS) def nanos = Duration(d, NANOSECONDS) @@ -540,14 +494,14 @@ class DurationDouble(d: Double) { def seconds = Duration(d, SECONDS) def second = Duration(d, SECONDS) - def minutes = Duration(d * 60, SECONDS) - def minute = Duration(d * 60, SECONDS) + def minutes = Duration(d, MINUTES) + def minute = Duration(d, MINUTES) - def hours = Duration(d * 3600, SECONDS) - def hour = Duration(d * 3600, SECONDS) + def hours = Duration(d, HOURS) + def hour = Duration(d, HOURS) - def days = Duration(d * 86400, SECONDS) - def day = Duration(d * 86400, SECONDS) + def days = Duration(d, DAYS) + def day = Duration(d, DAYS) def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS)) def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS)) @@ -567,12 +521,12 @@ class DurationDouble(d: Double) { def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, SECONDS)) def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, SECONDS)) - def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d * 60, SECONDS)) - def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d * 60, SECONDS)) + def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MINUTES)) + def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MINUTES)) - def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d * 3600, SECONDS)) - def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d * 3600, SECONDS)) + def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, HOURS)) + def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, HOURS)) - def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d * 86400, SECONDS)) - def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d * 86400, SECONDS)) + def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS)) + def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS)) } diff --git a/src/library/scala/concurrent/util/duration/Classifier.scala b/src/library/scala/concurrent/util/duration/Classifier.scala new file mode 100644 index 0000000000..10faf0a5ce --- /dev/null +++ b/src/library/scala/concurrent/util/duration/Classifier.scala @@ -0,0 +1,9 @@ +package scala.concurrent.util.duration + +import scala.concurrent.util.{ FiniteDuration } + +trait Classifier[C] { + type R + def convert(d: FiniteDuration): R +} + diff --git a/src/library/scala/concurrent/util/duration/NumericMultiplication.scala b/src/library/scala/concurrent/util/duration/NumericMultiplication.scala new file mode 100644 index 0000000000..94c58fb8c2 --- /dev/null +++ b/src/library/scala/concurrent/util/duration/NumericMultiplication.scala @@ -0,0 +1,18 @@ +package scala.concurrent.util.duration + +import scala.concurrent.util.{ Duration } + +/* + * Avoid reflection based invocation by using non-duck type + */ +protected[duration] class IntMult(i: Int) { + def *(d: Duration) = d * i +} + +protected[duration] class LongMult(i: Long) { + def *(d: Duration) = d * i +} + +protected[duration] class DoubleMult(f: Double) { + def *(d: Duration) = d * f +} diff --git a/src/library/scala/concurrent/util/duration/package.scala b/src/library/scala/concurrent/util/duration/package.scala new file mode 100644 index 0000000000..25625054ee --- /dev/null +++ b/src/library/scala/concurrent/util/duration/package.scala @@ -0,0 +1,30 @@ +package scala.concurrent.util + +import java.util.concurrent.TimeUnit + +package object duration { + + object span + implicit object spanConvert extends Classifier[span.type] { + type R = FiniteDuration + def convert(d: FiniteDuration) = d + } + + object fromNow + implicit object fromNowConvert extends Classifier[fromNow.type] { + type R = Deadline + def convert(d: FiniteDuration) = Deadline.now + d + } + + implicit def intToDurationInt(n: Int) = new DurationInt(n) + implicit def longToDurationLong(n: Long) = new DurationLong(n) + implicit def doubleToDurationDouble(d: Double) = new DurationDouble(d) + + implicit def pairIntToDuration(p: (Int, TimeUnit)) = Duration(p._1, p._2) + implicit def pairLongToDuration(p: (Long, TimeUnit)) = Duration(p._1, p._2) + implicit def durationToPair(d: Duration) = (d.length, d.unit) + + implicit def intMult(i: Int) = new IntMult(i) + implicit def longMult(l: Long) = new LongMult(l) + implicit def doubleMult(f: Double) = new DoubleMult(f) +} \ No newline at end of file -- cgit v1.2.3 From c7a71c2d5c5afbb3dc047bca20c4b8c72e5c94c9 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Thu, 12 Apr 2012 20:04:57 +0200 Subject: Making changes in the scala.concurrent package. --- .../scala/concurrent/ConcurrentPackageObject.scala | 60 ++++++++++++---------- .../scala/concurrent/ExecutionContext.scala | 21 ++++---- src/library/scala/concurrent/Future.scala | 9 ++++ src/library/scala/concurrent/Promise.scala | 18 +++++-- .../concurrent/impl/ExecutionContextImpl.scala | 20 ++++---- src/library/scala/concurrent/impl/Future.scala | 13 ++++- src/library/scala/concurrent/impl/Promise.scala | 8 +-- 7 files changed, 96 insertions(+), 53 deletions(-) (limited to 'src/library') diff --git a/src/library/scala/concurrent/ConcurrentPackageObject.scala b/src/library/scala/concurrent/ConcurrentPackageObject.scala index d185ade8a4..789738e6ec 100644 --- a/src/library/scala/concurrent/ConcurrentPackageObject.scala +++ b/src/library/scala/concurrent/ConcurrentPackageObject.scala @@ -36,34 +36,42 @@ abstract class ConcurrentPackageObject { case _ => true } - private[concurrent] def resolve[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { - case Left(t: scala.runtime.NonLocalReturnControl[_]) => Right(t.value.asInstanceOf[T]) - case Left(t: scala.util.control.ControlThrowable) => Left(new ExecutionException("Boxed ControlThrowable", t)) - case Left(t: InterruptedException) => Left(new ExecutionException("Boxed InterruptedException", t)) - case Left(e: Error) => Left(new ExecutionException("Boxed Error", e)) - case _ => source + private[concurrent] def resolveEither[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { + case Left(t) => resolver(t) + case _ => source } - private[concurrent] def resolver[T] = - resolverFunction.asInstanceOf[PartialFunction[Throwable, Either[Throwable, T]]] - + private[concurrent] def resolver[T](throwable: Throwable): Either[Throwable, T] = throwable match { + case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value.asInstanceOf[T]) + case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) + case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) + case e: Error => Left(new ExecutionException("Boxed Error", e)) + case t => Left(t) + } + /* concurrency constructs */ + /** Starts an asynchronous computation and returns a `Future` object with the result of that computation. + * + * The result becomes available once the asynchronous computation is completed. + * + * @tparam T the type of the result + * @param body the asychronous computation + * @param execctx the execution context on which the future is run + * @return the `Future` holding the result of the computation + */ def future[T](body: =>T)(implicit execctx: ExecutionContext = defaultExecutionContext): Future[T] = Future[T](body) + /** Creates a promise object which can be completed with a value. + * + * @tparam T the type of the value in the promise + * @param execctx the execution context on which the promise is created on + * @return the newly created `Promise` object + */ def promise[T]()(implicit execctx: ExecutionContext = defaultExecutionContext): Promise[T] = Promise[T]() - /** Wraps a block of code into an awaitable object. */ - def body2awaitable[T](body: =>T) = new Awaitable[T] { - def ready(atMost: Duration)(implicit permit: CanAwait) = { - body - this - } - def result(atMost: Duration)(implicit permit: CanAwait) = body - } - /** Used to block on a piece of code which potentially blocks. * * @param body A piece of code which contains potentially blocking or long running calls. @@ -74,7 +82,7 @@ abstract class ConcurrentPackageObject { * - TimeoutException - in the case that the blockable object timed out */ def blocking[T](body: =>T): T = - blocking(body2awaitable(body), Duration.fromNanos(0)) + blocking(impl.Future.body2awaitable(body), Duration.fromNanos(0)) /** Blocks on an awaitable object. * @@ -100,11 +108,11 @@ private[concurrent] object ConcurrentPackageObject { // compiling a subset of sources; it seems that the wildcard is not // properly handled, and you get messages like "type _$1 defined twice". // This is consistent with other package object breakdowns. - private val resolverFunction: PartialFunction[Throwable, Either[Throwable, _]] = { - case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value) - case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) - case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) - case e: Error => Left(new ExecutionException("Boxed Error", e)) - case t => Left(t) - } + // private val resolverFunction: PartialFunction[Throwable, Either[Throwable, _]] = { + // case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value) + // case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) + // case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) + // case e: Error => Left(new ExecutionException("Boxed Error", e)) + // case t => Left(t) + // } } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index e1d4276396..3f62f58bf8 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -20,19 +20,22 @@ import collection._ trait ExecutionContext { - + + /** Runs a block of code on this execution context. + */ def execute(runnable: Runnable): Unit - - def execute[U](body: () => U): Unit - + + /** Used internally by the framework - blocks execution for at most `atMost` time while waiting + * for an `awaitable` object to become ready. + * + * Clients should use `scala.concurrent.blocking` instead. + */ def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T + /** Reports that an asynchronous computation failed. + */ def reportFailure(t: Throwable): Unit - - /* implementations follow */ - - private implicit val executionContext = this - + } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 5bc9ad783f..1463dbcebf 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -512,6 +512,15 @@ trait Future[+T] extends Awaitable[T] { */ object Future { + /** Starts an asynchronous computation and returns a `Future` object with the result of that computation. + * + * The result becomes available once the asynchronous computation is completed. + * + * @tparam T the type of the result + * @param body the asychronous computation + * @param execctx the execution context on which the future is run + * @return the `Future` holding the result of the computation + */ def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = impl.Future(body) import scala.collection.mutable.Builder diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 8f2bce5d1a..cd22a55ce7 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -107,15 +107,27 @@ trait Promise[T] { object Promise { - /** Creates a new promise. + /** Creates a promise object which can be completed with a value. + * + * @tparam T the type of the value in the promise + * @param execctx the execution context on which the promise is created on + * @return the newly created `Promise` object */ def apply[T]()(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.DefaultPromise[T]() - /** Creates an already completed Promise with the specified exception + /** Creates an already completed Promise with the specified exception. + * + * @tparam T the type of the value in the promise + * @param execctx the execution context on which the promise is created on + * @return the newly created `Promise` object */ def failed[T](exception: Throwable)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Left(exception)) - /** Creates an already completed Promise with the specified result + /** Creates an already completed Promise with the specified result. + * + * @tparam T the type of the value in the promise + * @param execctx the execution context on which the promise is created on + * @return the newly created `Promise` object */ def successful[T](result: T)(implicit executor: ExecutionContext): Promise[T] = new impl.Promise.KeptPromise[T](Right(result)) diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index c5062267dc..d15a9b828b 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -12,7 +12,7 @@ package scala.concurrent.impl import java.util.concurrent.{Callable, Executor, ExecutorService, Executors, ThreadFactory} import scala.concurrent.forkjoin._ -import scala.concurrent.{ExecutionContext, resolver, Awaitable, body2awaitable} +import scala.concurrent.{ExecutionContext, resolver, Awaitable} import scala.concurrent.util.{ Duration } @@ -56,20 +56,20 @@ private[scala] class ExecutionContextImpl(es: AnyRef) extends ExecutionContext w def execute(runnable: Runnable): Unit = executorService match { case fj: ForkJoinPool => - if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) { - val fjtask = ForkJoinTask.adapt(runnable) - fjtask.fork - } else { - fj.execute(runnable) + Thread.currentThread match { + case fjw: ForkJoinWorkerThread if fjw.getPool eq fj => + val fjtask = runnable match { + case fjt: ForkJoinTask[_] => fjt + case _ => ForkJoinTask.adapt(runnable) + } + fjtask.fork + case _ => + fj.execute(runnable) } case executor: Executor => executor execute runnable } - def execute[U](body: () => U): Unit = execute(new Runnable { - def run() = body() - }) - def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = { Future.releaseStack(this) diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 615ab061a5..a3c8ed3095 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -10,9 +10,11 @@ package scala.concurrent.impl -import scala.concurrent.{Awaitable, ExecutionContext} +import scala.concurrent.util.Duration +import scala.concurrent.{Awaitable, ExecutionContext, CanAwait} import scala.collection.mutable.Stack + private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { implicit def executor: ExecutionContext @@ -54,6 +56,15 @@ object Future { classOf[Unit] -> classOf[scala.runtime.BoxedUnit] ) + /** Wraps a block of code into an awaitable object. */ + private[concurrent] def body2awaitable[T](body: =>T) = new Awaitable[T] { + def ready(atMost: Duration)(implicit permit: CanAwait) = { + body + this + } + def result(atMost: Duration)(implicit permit: CanAwait) = body + } + def boxedType(c: Class[_]): Class[_] = { if (c.isPrimitive) toBoxed(c) else c } diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index f7e073cb78..07b6d1f278 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -12,7 +12,7 @@ package scala.concurrent.impl import java.util.concurrent.TimeUnit.{ NANOSECONDS, MILLISECONDS } import java.util.concurrent.atomic.AtomicReferenceFieldUpdater -import scala.concurrent.{Awaitable, ExecutionContext, resolve, resolver, blocking, CanAwait, TimeoutException} +import scala.concurrent.{Awaitable, ExecutionContext, resolveEither, resolver, blocking, CanAwait, TimeoutException} //import scala.util.continuations._ import scala.concurrent.util.Duration import scala.util @@ -126,7 +126,7 @@ object Promise { value.isDefined } - blocking(concurrent.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) + blocking(Future.body2awaitable(awaitUnsafe(dur2long(atMost))), atMost) } def ready(atMost: Duration)(implicit permit: CanAwait): this.type = @@ -166,7 +166,7 @@ object Promise { case _ => null } } - tryComplete(resolve(value)) + tryComplete(resolveEither(value)) } finally { synchronized { notifyAll() } // notify any blockers from `tryAwait` } @@ -220,7 +220,7 @@ object Promise { */ final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val executor: ExecutionContext) extends Promise[T] { - val value = Some(resolve(suppliedValue)) + val value = Some(resolveEither(suppliedValue)) def tryComplete(value: Either[Throwable, T]): Boolean = false -- cgit v1.2.3 From 225d205f83ceb7fc6f0af005f0085bf7ab493b38 Mon Sep 17 00:00:00 2001 From: aleksandar Date: Fri, 13 Apr 2012 01:22:57 +0200 Subject: Add managed blockers to execution contexts. --- .../scala/concurrent/impl/ExecutionContextImpl.scala | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src/library') diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index d15a9b828b..c308a59297 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -73,7 +73,23 @@ private[scala] class ExecutionContextImpl(es: AnyRef) extends ExecutionContext w def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = { Future.releaseStack(this) - awaitable.result(atMost)(scala.concurrent.Await.canAwaitEvidence) + executorService match { + case fj: ForkJoinPool => + var result: T = null.asInstanceOf[T] + val managedBlocker = new ForkJoinPool.ManagedBlocker { + @volatile var isdone = false + def block() = { + result = awaitable.result(atMost)(scala.concurrent.Await.canAwaitEvidence) + isdone = true + true + } + def isReleasable = isdone + } + ForkJoinPool.managedBlock(managedBlocker) + result + case _ => + awaitable.result(atMost)(scala.concurrent.Await.canAwaitEvidence) + } } def reportFailure(t: Throwable) = t match { -- cgit v1.2.3 From 355264f9d53c09182fe6f480319543dc914860d1 Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Fri, 13 Apr 2012 11:55:35 +0200 Subject: Scaladoc feature that shows implicit conversions See https://github.com/VladUreche/scala/tree/feature/doc-implicits for the history. See https://scala-webapps.epfl.ch/jenkins/view/scaladoc/job/scaladoc-implicits-nightly/ for nightlies. Many thanks fly out to Adriaan for his help with implicit search! --- build.xml | 140 +++--- src/compiler/scala/reflect/internal/Types.scala | 39 +- src/compiler/scala/tools/ant/Scaladoc.scala | 70 ++- src/compiler/scala/tools/nsc/ast/DocComments.scala | 8 +- src/compiler/scala/tools/nsc/doc/DocFactory.scala | 6 +- src/compiler/scala/tools/nsc/doc/Settings.scala | 127 +++++- .../scala/tools/nsc/doc/Uncompilable.scala | 4 +- .../scala/tools/nsc/doc/html/HtmlFactory.scala | 3 + .../scala/tools/nsc/doc/html/HtmlPage.scala | 12 +- .../scala/tools/nsc/doc/html/page/Template.scala | 156 +++++-- .../nsc/doc/html/resource/lib/conversionbg.gif | Bin 0 -> 167 bytes .../doc/html/resource/lib/selected-implicits.png | Bin 0 -> 1150 bytes .../html/resource/lib/selected-right-implicits.png | Bin 0 -> 646 bytes .../tools/nsc/doc/html/resource/lib/template.css | 101 ++++- .../tools/nsc/doc/html/resource/lib/template.js | 104 +++-- .../scala/tools/nsc/doc/model/Entity.scala | 107 +++++ .../scala/tools/nsc/doc/model/ModelFactory.scala | 99 ++-- .../doc/model/ModelFactoryImplicitSupport.scala | 501 +++++++++++++++++++++ .../scala/tools/nsc/doc/model/TreeFactory.scala | 2 +- .../scala/tools/nsc/typechecker/Implicits.scala | 47 +- .../scala/tools/nsc/typechecker/Infer.scala | 2 +- src/library/scala/Array.scala | 13 + src/library/scala/Option.scala | 11 + src/library/scala/Tuple2.scala | 12 +- src/library/scala/Tuple3.scala | 13 +- .../scala/tools/partest/ScaladocModelTest.scala | 78 ++-- test/scaladoc/resources/implicits-base-res.scala | 143 ++++++ .../resources/implicits-chaining-res.scala | 48 ++ .../resources/implicits-elimination-res.scala | 9 + test/scaladoc/resources/implicits-scopes-res.scala | 51 +++ test/scaladoc/run/SI-5373.check | 2 +- test/scaladoc/run/SI-5373.scala | 6 +- test/scaladoc/run/implicits-base.check | 1 + test/scaladoc/run/implicits-base.scala | 179 ++++++++ test/scaladoc/run/implicits-chaining.check | 1 + test/scaladoc/run/implicits-chaining.scala | 64 +++ test/scaladoc/run/implicits-elimination.check | 1 + test/scaladoc/run/implicits-elimination.scala | 22 + test/scaladoc/run/implicits-scopes.check | 1 + test/scaladoc/run/implicits-scopes.scala | 76 ++++ test/scaladoc/scalacheck/CommentFactoryTest.scala | 5 +- 41 files changed, 2001 insertions(+), 263 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif create mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png create mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png create mode 100644 src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala create mode 100644 test/scaladoc/resources/implicits-base-res.scala create mode 100644 test/scaladoc/resources/implicits-chaining-res.scala create mode 100644 test/scaladoc/resources/implicits-elimination-res.scala create mode 100644 test/scaladoc/resources/implicits-scopes-res.scala create mode 100644 test/scaladoc/run/implicits-base.check create mode 100644 test/scaladoc/run/implicits-base.scala create mode 100644 test/scaladoc/run/implicits-chaining.check create mode 100644 test/scaladoc/run/implicits-chaining.scala create mode 100644 test/scaladoc/run/implicits-elimination.check create mode 100644 test/scaladoc/run/implicits-elimination.scala create mode 100644 test/scaladoc/run/implicits-scopes.check create mode 100644 test/scaladoc/run/implicits-scopes.scala (limited to 'src/library') diff --git a/build.xml b/build.xml index 51fffd79d0..1a0e85a6f0 100644 --- a/build.xml +++ b/build.xml @@ -8,10 +8,10 @@ SuperSabbus for Scala core, builds the scala library and compiler. It can also p - + - + @@ -34,20 +34,20 @@ END-USER TARGETS - + - + - + - + @@ -67,7 +67,7 @@ END-USER TARGETS - + @@ -81,7 +81,7 @@ END-USER TARGETS - + @@ -103,7 +103,7 @@ END-USER TARGETS - + @@ -144,7 +144,7 @@ END-USER TARGETS description="Requires forkjoin library to be rebuilt. Add this target before any other if class file format is incompatible."> - + @@ -159,7 +159,7 @@ PROPERTIES - + @@ -281,7 +281,7 @@ INITIALISATION - + @@ -337,7 +337,7 @@ INITIALISATION - + @@ -387,7 +387,7 @@ INITIALISATION - + - + @@ -660,7 +660,7 @@ QUICK BUILD (QUICK) classpath="${build-quick.dir}/classes/library" includes="**/*.java" target="1.5" source="1.5"> - + - + - + @@ -838,7 +838,7 @@ QUICK BUILD (QUICK) - + @@ -863,7 +863,7 @@ QUICK BUILD (QUICK) - + @@ -898,7 +898,7 @@ QUICK BUILD (QUICK) - + @@ -993,7 +993,7 @@ QUICK BUILD (QUICK) - + @@ -1009,9 +1009,9 @@ QUICK BUILD (QUICK) - + - + - + - + - + - + - + - + - + - + @@ -1194,9 +1194,9 @@ PACKED QUICK BUILD (PACK) - + - + @@ -1281,7 +1281,7 @@ BOOTSTRAPPING BUILD (STRAP) - + @@ -1375,7 +1375,7 @@ BOOTSTRAPPING BUILD (STRAP) - + - + - + @@ -1477,9 +1477,9 @@ BOOTSTRAPPING BUILD (STRAP) - + - + @@ -1495,7 +1495,7 @@ LIBRARIES (MSIL, FJBG maybe later) - + - + - + - + @@ -1530,7 +1530,7 @@ LIBRARIES (MSIL, FJBG maybe later) - + - + - + - + @@ -1575,7 +1575,7 @@ LIBRARIES (MSIL, FJBG maybe later) - + - + - + @@ -1602,7 +1602,7 @@ LIBRARIES (MSIL, FJBG maybe later) - + @@ -1610,7 +1610,7 @@ LIBRARIES (MSIL, FJBG maybe later) - + @@ -1640,7 +1640,7 @@ DOCUMENTATION - + @@ -1659,13 +1659,14 @@ DOCUMENTATION destdir="${build-docs.dir}/library" doctitle="Scala Standard Library API (Scaladoc)" docversion="${version.number}" - docfooter="epfl" + docfooter="epfl" docsourceurl="${scaladoc.url}€{FILE_PATH}.scala#L1" docUncompilable="${src.dir}/library-aux" sourcepath="${src.dir}" classpathref="pack.classpath" addparams="${scalac.args.all}" - docRootContent="${src.dir}/library/rootdoc.txt"> + docRootContent="${src.dir}/library/rootdoc.txt" + implicits="on" diagrams="on"> @@ -1746,7 +1747,8 @@ DOCUMENTATION classpathref="pack.classpath" srcdir="${src.dir}/compiler" docRootContent="${src.dir}/compiler/rootdoc.txt" - addparams="${scalac.args.all}"> + addparams="${scalac.args.all}" + implicits="on" diagrams="on"> @@ -1767,7 +1769,8 @@ DOCUMENTATION sourcepath="${src.dir}" classpathref="pack.classpath" srcdir="${src.dir}/jline/src/main/java" - addparams="${scalac.args.all}"> + addparams="${scalac.args.all}" + implicits="on" diagrams="on"> @@ -1790,7 +1793,8 @@ DOCUMENTATION sourcepath="${src.dir}" classpathref="pack.classpath" srcdir="${src.dir}/scalap" - addparams="${scalac.args.all}"> + addparams="${scalac.args.all}" + implicits="on" diagrams="on"> @@ -1811,7 +1815,8 @@ DOCUMENTATION sourcepath="${src.dir}" classpathref="pack.classpath" srcdir="${src.dir}/partest" - addparams="${scalac.args.all}"> + addparams="${scalac.args.all}" + implicits="on" diagrams="on"> @@ -1832,7 +1837,8 @@ DOCUMENTATION sourcepath="${src.dir}" classpathref="pack.classpath" srcdir="${src.dir}/continuations/plugin" - addparams="${scalac.args.all}"> + addparams="${scalac.args.all}" + implicits="on" diagrams="on"> @@ -1864,7 +1870,7 @@ BOOTRAPING TEST AND TEST SUITE - + @@ -2155,7 +2161,7 @@ STABLE REFERENCE (STARR) - + @@ -2170,7 +2176,7 @@ FORWARDED TARGETS FOR PACKAGING - + diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 73a8f5c55c..8bb1d5e2fa 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -917,8 +917,8 @@ trait Types extends api.Types { self: SymbolTable => /** A test whether a type contains any unification type variables. */ def isGround: Boolean = this match { - case TypeVar(_, constr) => - constr.instValid && constr.inst.isGround + case tv@TypeVar(_, _) => + tv.untouchable || (tv.instValid && tv.constr.inst.isGround) case TypeRef(pre, sym, args) => sym.isPackageClass || pre.isGround && (args forall (_.isGround)) case SingleType(pre, sym) => @@ -2677,14 +2677,15 @@ trait Types extends api.Types { self: SymbolTable => def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr)) def apply(origin: Type, constr: TypeConstraint): TypeVar = apply(origin, constr, Nil, Nil) def apply(tparam: Symbol): TypeVar = apply(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams) + def apply(tparam: Symbol, untouchable: Boolean): TypeVar = apply(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable) /** This is the only place TypeVars should be instantiated. */ - def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]): TypeVar = { + def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol], untouchable: Boolean = false): TypeVar = { val tv = ( - if (args.isEmpty && params.isEmpty) new TypeVar(origin, constr) - else if (args.size == params.size) new AppliedTypeVar(origin, constr, params zip args) - else if (args.isEmpty) new HKTypeVar(origin, constr, params) + if (args.isEmpty && params.isEmpty) new TypeVar(origin, constr, untouchable) + else if (args.size == params.size) new AppliedTypeVar(origin, constr, untouchable, params zip args) + else if (args.isEmpty) new HKTypeVar(origin, constr, untouchable, params) else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) ) @@ -2712,8 +2713,9 @@ trait Types extends api.Types { self: SymbolTable => class HKTypeVar( _origin: Type, _constr: TypeConstraint, + _untouchable: Boolean, override val params: List[Symbol] - ) extends TypeVar(_origin, _constr) { + ) extends TypeVar(_origin, _constr, _untouchable) { require(params.nonEmpty, this) override def isHigherKinded = true @@ -2725,8 +2727,9 @@ trait Types extends api.Types { self: SymbolTable => class AppliedTypeVar( _origin: Type, _constr: TypeConstraint, + _untouchable: Boolean, zippedArgs: List[(Symbol, Type)] - ) extends TypeVar(_origin, _constr) { + ) extends TypeVar(_origin, _constr, _untouchable) { require(zippedArgs.nonEmpty, this) @@ -2749,7 +2752,8 @@ trait Types extends api.Types { self: SymbolTable => */ class TypeVar( val origin: Type, - val constr0: TypeConstraint + val constr0: TypeConstraint, + val untouchable: Boolean = false // by other typevars ) extends Type { override def params: List[Symbol] = Nil override def typeArgs: List[Type] = Nil @@ -2931,14 +2935,15 @@ trait Types extends api.Types { self: SymbolTable => // would be pointless. In this case, each check we perform causes us to lose specificity: in // the end the best we'll do is the least specific type we tested against, since the typevar // does not see these checks as "probes" but as requirements to fulfill. - // TODO: the `suspended` flag can be used to poke around with leaving a trace + // TODO: can the `suspended` flag be used to poke around without leaving a trace? // // So the strategy used here is to test first the type, then the direct parents, and finally // to fall back on the individual base types. This warrants eventual re-examination. // AM: I think we could use the `suspended` flag to avoid side-effecting during unification - if (suspended) // constraint accumulation is disabled + if (tp.isInstanceOf[TypeVar] && untouchable && !tp.asInstanceOf[TypeVar].untouchable) tp.asInstanceOf[TypeVar].registerBound(this, !isLowerBound, isNumericBound) + else if (suspended) // constraint accumulation is disabled checkSubtype(tp, origin) else if (constr.instValid) // type var is already set checkSubtype(tp, constr.inst) @@ -2962,7 +2967,8 @@ trait Types extends api.Types { self: SymbolTable => if(typeVarLHS) constr.inst =:= tp else tp =:= constr.inst - if (suspended) tp =:= origin + if (tp.isInstanceOf[TypeVar] && untouchable && !tp.asInstanceOf[TypeVar].untouchable) tp.asInstanceOf[TypeVar].registerTypeEquality(this, !typeVarLHS) + else if (suspended) tp =:= origin else if (constr.instValid) checkIsSameType(tp) else isRelatable(tp) && { val newInst = wildcardToTypeVarMap(tp) @@ -3036,7 +3042,7 @@ trait Types extends api.Types { self: SymbolTable => override def safeToString = ( if ((constr eq null) || (constr.inst eq null)) "TVar<" + originName + "=null>" else if (constr.inst ne NoType) "" + constr.inst - else "?" + levelString + originName + else (if(untouchable) "!?" else "?") + levelString + originName ) override def kind = "TypeVar" @@ -4733,7 +4739,7 @@ trait Types extends api.Types { self: SymbolTable => val sym1 = adaptToNewRun(sym.owner.thisType, sym) if (sym1 == sym) tp else ThisType(sym1) } catch { - case ex: MissingTypeControl => + case ex: MissingTypeControl => tp } case SingleType(pre, sym) => @@ -6044,8 +6050,9 @@ trait Types extends api.Types { self: SymbolTable => def stripType(tp: Type) = tp match { case ExistentialType(_, res) => res - case TypeVar(_, constr) => - if (constr.instValid) constr.inst + case tv@TypeVar(_, constr) => + if (tv.instValid) constr.inst + else if (tv.untouchable) tv else abort("trying to do lub/glb of typevar "+tp) case t => t } diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala index c92474b33e..daa08ef8a7 100644 --- a/src/compiler/scala/tools/ant/Scaladoc.scala +++ b/src/compiler/scala/tools/ant/Scaladoc.scala @@ -75,6 +75,11 @@ class Scaladoc extends ScalaMatchingTask { */ object Flag extends PermissibleValue { val values = List("yes", "no", "on", "off") + def getBooleanValue(value: String, flagName: String): Boolean = + if (Flag.isPermissible(value)) + return ("yes".equals(value) || "on".equals(value)) + else + buildError("Unknown " + flagName + " flag '" + value + "'") } /** The directories that contain source files to compile. */ @@ -127,6 +132,25 @@ class Scaladoc extends ScalaMatchingTask { /** Instruct the ant task not to fail in the event of errors */ private var nofail: Boolean = false + /** Instruct the scaladoc tool to document implicit conversions */ + private var docImplicits: Boolean = false + + /** Instruct the scaladoc tool to document all (including impossible) implicit conversions */ + private var docImplicitsShowAll: Boolean = false + + /** Instruct the scaladoc tool to output implicits debugging information */ + private var docImplicitsDebug: Boolean = false + + /** Instruct the scaladoc tool to create diagrams */ + private var docDiagrams: Boolean = false + + /** Instruct the scaladoc tool to output diagram creation debugging information */ + private var docDiagramsDebug: Boolean = false + + /** Instruct the scaladoc tool to use the binary given to create diagrams */ + private var docDiagramsDotPath: Option[String] = None + + /*============================================================================*\ ** Properties setters ** \*============================================================================*/ @@ -361,12 +385,39 @@ class Scaladoc extends ScalaMatchingTask { * * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ - def setNoFail(input: String) { - if (Flag.isPermissible(input)) - nofail = "yes".equals(input) || "on".equals(input) - else - buildError("Unknown nofail flag '" + input + "'") - } + def setNoFail(input: String) = + nofail = Flag.getBooleanValue(input, "nofail") + + /** Set the `implicits` info attribute. + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setImplicits(input: String) = + docImplicits = Flag.getBooleanValue(input, "implicits") + + /** Set the `implicitsShowAll` info attribute to enable scaladoc to show all implicits, including those impossible to + * convert to from the default scope + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setImplicitsShowAll(input: String) = + docImplicitsShowAll = Flag.getBooleanValue(input, "implicitsShowAll") + + /** Set the `implicitsDebug` info attribute so scaladoc outputs implicit conversion debug information + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setImplicitsDebug(input: String) = + docImplicitsDebug = Flag.getBooleanValue(input, "implicitsDebug") + + /** Set the `diagrams` bit so Scaladoc adds diagrams to the documentation + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setDiagrams(input: String) = + docDiagrams = Flag.getBooleanValue(input, "diagrams") + + /** Set the `diagramsDebug` bit so Scaladoc outputs diagram building debug information + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setDiagramsDebug(input: String) = + docDiagramsDebug = Flag.getBooleanValue(input, "diagramsDebug") + + /** Set the `diagramsDotPath` attribute to the path where graphviz dot can be found (including the binary file name, + * eg: /usr/bin/dot) */ + def setDiagramsDotPath(input: String) = + docDiagramsDotPath = Some(input) /*============================================================================*\ ** Properties getters ** @@ -560,6 +611,13 @@ class Scaladoc extends ScalaMatchingTask { docSettings.deprecation.value = deprecation docSettings.unchecked.value = unchecked + docSettings.docImplicits.value = docImplicits + docSettings.docImplicitsDebug.value = docImplicitsDebug + docSettings.docImplicitsShowAll.value = docImplicitsShowAll + docSettings.docDiagrams.value = docDiagrams + docSettings.docDiagramsDebug.value = docDiagramsDebug + if(!docDiagramsDotPath.isEmpty) docSettings.docDiagramsDotPath.value = docDiagramsDotPath.get + if (!docgenerator.isEmpty) docSettings.docgenerator.value = docgenerator.get if (!docrootcontent.isEmpty) docSettings.docRootContent.value = docrootcontent.get.getAbsolutePath() log("Scaladoc params = '" + addParams + "'", Project.MSG_DEBUG) diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index ff4e2f3fb5..8e7eeed3cc 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -252,7 +252,7 @@ trait DocComments { self: Global => def replaceInheritdoc(childSection: String, parentSection: => String) = if (childSection.indexOf("@inheritdoc") == -1) childSection - else + else childSection.replaceAllLiterally("@inheritdoc", parentSection) def getParentSection(section: (Int, Int)): String = { @@ -275,9 +275,9 @@ trait DocComments { self: Global => } child.substring(section._1, section._1 + 7) match { - case param@("@param "|"@tparam"|"@throws") => + case param@("@param "|"@tparam"|"@throws") => sectionString(extractSectionParam(child, section), parentNamedParams(param.trim)) - case _ => + case _ => sectionString(extractSectionTag(child, section), parentTagMap) } } @@ -367,7 +367,7 @@ trait DocComments { self: Global => case vname => lookupVariable(vname, site) match { case Some(replacement) => replaceWith(replacement) - case None => reporter.warning(sym.pos, "Variable " + vname + " undefined in comment for " + sym) + case None => reporter.warning(sym.pos, "Variable " + vname + " undefined in comment for " + sym + " in " + site) } } } diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index f32564f097..76a8b87ba7 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -58,7 +58,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor case Right(sourceCode) => new compiler.Run() compileSources List(new BatchSourceFile("newSource", sourceCode)) } - + if (reporter.hasErrors) return None @@ -80,6 +80,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor val modelFactory = ( new { override val global: compiler.type = compiler } with model.ModelFactory(compiler, settings) + with model.ModelFactoryImplicitSupport with model.comment.CommentFactory with model.TreeFactory { override def templateShouldDocument(sym: compiler.Symbol) = @@ -89,7 +90,8 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor modelFactory.makeModel match { case Some(madeModel) => - println("model contains " + modelFactory.templatesCount + " documentable templates") + if (settings.reportModel) + println("model contains " + modelFactory.templatesCount + " documentable templates") Some(madeModel) case None => println("no documentable class found in compilation units") diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 45a2ad78b4..3da87bf763 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -87,6 +87,38 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) { "" ) + val docImplicits = BooleanSetting ( + "-implicits", + "Document members inherited by implicit conversions." + ) + + val docImplicitsDebug = BooleanSetting ( + "-implicits-debug", + "Show debugging information for members inherited by implicit conversions." + ) + + val docImplicitsShowAll = BooleanSetting ( + "-implicits-show-all", + "Show members inherited by implicit conversions that are impossible in the default scope. " + + "(for example conversions that require Numeric[String] to be in scope)" + ) + + val docDiagrams = BooleanSetting ( + "-diagrams", + "Create inheritance diagrams for classes, traits and packages." + ) + + val docDiagramsDebug = BooleanSetting ( + "-diagrams-debug", + "Show debugging information for the diagram creation process." + ) + + val docDiagramsDotPath = PathSetting ( + "-diagrams-dot-path", + "The path to the dot executable used to generate the inheritance diagrams. Ex: /usr/bin/dot", + "dot" // by default, just pick up the system-wide dot + ) + // Somewhere slightly before r18708 scaladoc stopped building unless the // self-type check was suppressed. I hijacked the slotted-for-removal-anyway // suppress-vt-warnings option and renamed it for this purpose. @@ -94,9 +126,102 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) { // For improved help output. def scaladocSpecific = Set[Settings#Setting]( - docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator + docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator, docRootContent, useStupidTypes, + docDiagrams, docDiagramsDebug, docDiagramsDotPath, + docImplicits, docImplicitsDebug, docImplicitsShowAll ) val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name) override def isScaladoc = true + + // unset by the testsuite, we don't need to count the entities in the model + var reportModel = true + + /** + * This is the hardcoded area of Scaladoc. This is where "undesirable" stuff gets eliminated. I know it's not pretty, + * but ultimately scaladoc has to be useful. :) + */ + object hardcoded { + + /** The common context bounds and some humanly explanations. Feel free to add more explanations + * `.scala.package.Numeric` is the type class + * `tparam` is the name of the type parameter it gets (this only describes type classes with 1 type param) + * the function result should be a humanly-understandable description of the type class + */ + val knownTypeClasses: Map[String, String => String] = Map() + + (".scala.package.Numeric" -> ((tparam: String) => tparam + " is a numeric class, such as Int, Long, Float or Double")) + + (".scala.package.Integral" -> ((tparam: String) => tparam + " is an integral numeric class, such as Int or Long")) + + (".scala.package.Fractional" -> ((tparam: String) => tparam + " is a fractional numeric class, such as Float or Double")) + + (".scala.reflect.Manifest" -> ((tparam: String) => tparam + " is accompanied by a Manifest, which is a runtime representation of its type that survives erasure")) + + (".scala.reflect.ClassManifest" -> ((tparam: String) => tparam + " is accompanied by a ClassManifest, which is a runtime representation of its type that survives erasure")) + + (".scala.reflect.OptManifest" -> ((tparam: String) => tparam + " is accompanied by an OptManifest, which can be either a runtime representation of its type or the NoManifest, which means the runtime type is not available")) + + /** + * Set of classes to exclude from index and diagrams + * TODO: Should be configurable + */ + def isExcluded(qname: String) = { + ( ( qname.startsWith("scala.Tuple") || qname.startsWith("scala.Product") || + qname.startsWith("scala.Function") || qname.startsWith("scala.runtime.AbstractFunction") + ) && !( + qname == "scala.Tuple1" || qname == "scala.Tuple2" || + qname == "scala.Product" || qname == "scala.Product1" || qname == "scala.Product2" || + qname == "scala.Function" || qname == "scala.Function1" || qname == "scala.Function2" || + qname == "scala.runtime.AbstractFunction0" || qname == "scala.runtime.AbstractFunction1" || + qname == "scala.runtime.AbstractFunction2" + ) + ) + } + + /** Common conversion targets that affect any class in Scala */ + val commonConversionTargets = List( + "scala.Predef.any2stringfmt", + "scala.Predef.any2stringadd", + "scala.Predef.any2ArrowAssoc", + "scala.Predef.any2Ensuring") + + /** There's a reason all these are specialized by hand but documenting each of them is beyond the point */ + val arraySkipConversions = List( + "scala.Predef.refArrayOps", + "scala.Predef.intArrayOps", + "scala.Predef.doubleArrayOps", + "scala.Predef.longArrayOps", + "scala.Predef.floatArrayOps", + "scala.Predef.charArrayOps", + "scala.Predef.byteArrayOps", + "scala.Predef.shortArrayOps", + "scala.Predef.booleanArrayOps", + "scala.Predef.unitArrayOps", + "scala.LowPriorityImplicits.wrapRefArray", + "scala.LowPriorityImplicits.wrapIntArray", + "scala.LowPriorityImplicits.wrapDoubleArray", + "scala.LowPriorityImplicits.wrapLongArray", + "scala.LowPriorityImplicits.wrapFloatArray", + "scala.LowPriorityImplicits.wrapCharArray", + "scala.LowPriorityImplicits.wrapByteArray", + "scala.LowPriorityImplicits.wrapShortArray", + "scala.LowPriorityImplicits.wrapBooleanArray", + "scala.LowPriorityImplicits.wrapUnitArray", + "scala.LowPriorityImplicits.genericWrapArray") + + // included as names as here we don't have access to a Global with Definitions :( + def valueClassList = List("unit", "boolean", "byte", "short", "char", "int", "long", "float", "double") + def valueClassFilterPrefixes = List("scala.LowPriorityImplicits", "scala.Predef") + + /** Dirty, dirty, dirty hack: the value params conversions can all kick in -- and they are disambiguated by priority + * but showing priority in scaladoc would make no sense -- so we have to manually remove the conversions that we + * know will never get a chance to kick in. Anyway, DIRTY DIRTY DIRTY! */ + def valueClassFilter(value: String, conversionName: String): Boolean = { + val valueName = value.toLowerCase + val otherValues = valueClassList.filterNot(_ == valueName) + + for (prefix <- valueClassFilterPrefixes) + if (conversionName.startsWith(prefix)) + for (otherValue <- otherValues) + if (conversionName.startsWith(prefix + "." + otherValue)) + return false + + true + } + } } diff --git a/src/compiler/scala/tools/nsc/doc/Uncompilable.scala b/src/compiler/scala/tools/nsc/doc/Uncompilable.scala index 9b29ebd745..dbeca46c25 100644 --- a/src/compiler/scala/tools/nsc/doc/Uncompilable.scala +++ b/src/compiler/scala/tools/nsc/doc/Uncompilable.scala @@ -14,7 +14,7 @@ trait Uncompilable { val settings: Settings import global.{ reporter, inform, warning, newTypeName, newTermName, Symbol, Name, DocComment, NoSymbol } - import global.definitions.RootClass + import global.definitions.{ RootClass, AnyRefClass } private implicit def translateName(name: Global#Name) = if (name.isTypeName) newTypeName("" + name) else newTermName("" + name) @@ -32,7 +32,7 @@ trait Uncompilable { } def files = settings.uncompilableFiles def symbols = pairs map (_._1) - def templates = symbols filter (x => x.isClass || x.isTrait) toSet + def templates = symbols filter (x => x.isClass || x.isTrait || x == AnyRefClass/* which is now a type alias */) toSet def comments = { if (settings.debug.value || settings.verbose.value) inform("Found %d uncompilable files: %s".format(files.size, files mkString ", ")) diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala index 0116e02e0e..914824d523 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -71,6 +71,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "signaturebg.gif", "signaturebg2.gif", "typebg.gif", + "conversionbg.gif", "valuemembersbg.gif", "navigation-li-a.png", @@ -80,6 +81,8 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "selected.png", "selected2-right.png", "selected2.png", + "selected-right-implicits.png", + "selected-implicits.png", "unselected.png" ) diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala index 1544dafc69..e3da8bddea 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala @@ -23,7 +23,7 @@ abstract class HtmlPage extends Page { thisPage => protected def title: String /** The page description */ - protected def description: String = + protected def description: String = // unless overwritten, will display the title in a spaced format, keeping - and . title.replaceAll("[^a-zA-Z0-9\\.\\-]+", " ").replaceAll("\\-+", " - ").replaceAll(" +", " ") @@ -164,15 +164,15 @@ abstract class HtmlPage extends Page { thisPage => } /** Returns the HTML code that represents the template in `tpl` as a hyperlinked name. */ - def templateToHtml(tpl: TemplateEntity) = tpl match { + def templateToHtml(tpl: TemplateEntity, name: String = null) = tpl match { case dTpl: DocTemplateEntity => if (hasPage(dTpl)) { - { dTpl.name } + { if (name eq null) dTpl.name else name } } else { - xml.Text(dTpl.name) + xml.Text(if (name eq null) dTpl.name else name) } case ndTpl: NoDocTemplate => - xml.Text(ndTpl.name) + xml.Text(if (name eq null) ndTpl.name else name) } /** Returns the HTML code that represents the templates in `tpls` as a list of hyperlinked names. */ @@ -192,6 +192,6 @@ abstract class HtmlPage extends Page { thisPage => else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isTrait) "object_to_trait_big.png" else if (ety.isObject) "object_big.png" else if (ety.isPackage) "package_big.png" - else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not + else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not } 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 f059b5c0cb..680957c64c 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -88,21 +88,42 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage
- { if (tpl.linearizationTemplates.isEmpty) NodeSeq.Empty else + { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else
Ordering
  1. Alphabetic
  2. By inheritance
} - { if (tpl.linearizationTemplates.isEmpty) NodeSeq.Empty else -
- Inherited -
  1. Hide All
  2. -
  3. Show all
-
    { - (tpl :: tpl.linearizationTemplates) map { wte =>
  1. { wte.name }
  2. } - }
-
+ { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else + { + if (!tpl.linearization.isEmpty) +
+ Inherited
+
+
    + { (tpl :: tpl.linearizationTemplates).map(wte =>
  1. { wte.name }
  2. ) } +
+
+ else NodeSeq.Empty + } ++ { + if (!tpl.conversions.isEmpty) +
+ Implicitly
+
+
    + { tpl.conversions.map(conv =>
  1. { "by " + conv.conversionShortName }
  2. ) } +
+
+ else NodeSeq.Empty + } ++ +
+ +
    +
  1. Hide All
  2. +
  3. Show all
  4. +
+ Learn more about member selection +
} {
@@ -152,23 +173,25 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage
{ + // linearization NodeSeq fromSeq (for ((superTpl, superType) <- (tpl.linearizationTemplates zip tpl.linearizationTypes)) yield

Inherited from { - if (tpl.universe.settings.useStupidTypes.value) - superTpl match { - case dtpl: DocTemplateEntity => - val sig = signature(dtpl, false, true) \ "_" - sig - case tpl: TemplateEntity => - tpl.name - } - else - typeToHtml(superType, true) + typeToHtmlWithStupidTypes(tpl, superTpl, superType) }

) } + { + // implicitly inherited + NodeSeq fromSeq (for (conversion <- (tpl.conversions)) yield +
+

Inherited by implicit conversion { conversion.conversionShortName } from + { typeToHtml(tpl.resultType, true) } to { typeToHtml(conversion.targetType, true) } +

+
+ ) + }
@@ -219,11 +242,12 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage case d:MemberEntity with Def => defParamsToString(d) case _ => "" } + val memberComment = memberToCommentHtml(mbr, false)
  • + data-isabs={ mbr.isAbstract.toString } fullComment={ if(memberComment.isEmpty) "no" else "yes" }> { signature(mbr, false) } - { memberToCommentHtml(mbr, false) } + { memberComment }
  • } @@ -275,6 +299,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage

    { inlineToHtml(mbr.comment.get.short) }

    def memberToCommentBodyHtml(mbr: MemberEntity, isSelf: Boolean, isReduced: Boolean = false): NodeSeq = { + val memberComment = if (mbr.comment.isEmpty) NodeSeq.Empty else
    { commentToHtml(mbr.comment) }
    @@ -326,6 +351,45 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } } + val implicitInformation = mbr.byConversion match { + case Some(conv) => +
    Implicit information
    ++ + { + val targetType = typeToHtml(conv.targetType, true) + val conversionMethod = conv.convertorMethod match { + case Left(member) => Text(member.name) + case Right(name) => Text(name) + } + + // strip off the package object endings, they make things harder to follow + val conversionOwnerQualifiedNane = conv.convertorOwner.qualifiedName.stripSuffix(".package") + val conversionOwner = templateToHtml(conv.convertorOwner, conversionOwnerQualifiedNane) + + val constraintText = conv.constraints match { + case Nil => + NodeSeq.Empty + case List(constraint) => + xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint) ++ xml.Text(".") + case List(constraint1, constraint2) => + xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint1) ++ + xml.Text(" and at the same time ") ++ constraintToHtml(constraint2) ++ xml.Text(".") + case constraints => +
    ++ "This conversion will take place only if all of the following constraints are met:" ++
    ++ { + var index = 0 + constraints map { constraint => xml.Text({ index += 1; index } + ". ") ++ constraintToHtml(constraint) ++
    } + } + } + +
    + This member is added by an implicit conversion from { typeToHtml(mbr.inTemplate.resultType, true) } to + { targetType } performed by method { conversionMethod } in { conversionOwner }. + { constraintText } +
    + } + case _ => + NodeSeq.Empty + } + // --- start attributes block vals val attributes: Seq[scala.xml.Node] = { val fvs: List[comment.Paragraph] = visibility(mbr).toList @@ -354,7 +418,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage
    case _ => NodeSeq.Empty } - } + } val selfType: Seq[scala.xml.Node] = mbr match { case dtpl: DocTemplateEntity if (isSelf && !dtpl.selfType.isEmpty && !isReduced) => @@ -477,7 +541,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } // end attributes block vals --- - val attributesInfo = attributes ++ definitionClasses ++ fullSignature ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment + val attributesInfo = implicitInformation ++ attributes ++ definitionClasses ++ fullSignature ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment val attributesBlock = if (attributesInfo.isEmpty) NodeSeq.Empty @@ -561,12 +625,13 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { + val nameClass = if (mbr.byConversion.isDefined) "implicit" else "name" val nameHtml = { val value = if (mbr.isConstructor) tpl.name else mbr.name val span = if (mbr.deprecation.isDefined) - { value } + { value } else - { value } + { value } val encoded = scala.reflect.NameTransformer.encode(value) if (encoded != value) { span % new UnprefixedAttribute("title", @@ -765,4 +830,43 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage case _ => inl.toString } + private def typeToHtmlWithStupidTypes(tpl: TemplateEntity, superTpl: TemplateEntity, superType: TypeEntity): NodeSeq = + if (tpl.universe.settings.useStupidTypes.value) + superTpl match { + case dtpl: DocTemplateEntity => + val sig = signature(dtpl, false, true) \ "_" + sig + case tpl: TemplateEntity => + Text(tpl.name) + } + else + typeToHtml(superType, true) + + private def constraintToHtml(constraint: Constraint): NodeSeq = constraint match { + case ktcc: KnownTypeClassConstraint => + xml.Text(ktcc.typeExplanation(ktcc.typeParamName) + " (" + ktcc.typeParamName + ": ") ++ + templateToHtml(ktcc.typeClassEntity) ++ xml.Text(")") + case tcc: TypeClassConstraint => + xml.Text(tcc.typeParamName + " is ") ++ + + context-bounded ++ xml.Text(" by " + tcc.typeClassEntity.qualifiedName + " (" + tcc.typeParamName + ": ") ++ + templateToHtml(tcc.typeClassEntity) ++ xml.Text(")") + case impl: ImplicitInScopeConstraint => + xml.Text("an implicit value of type ") ++ typeToHtml(impl.implicitType, true) ++ xml.Text(" is in scope") + case eq: EqualTypeParamConstraint => + xml.Text(eq.typeParamName + " is " + eq.rhs.name + " (" + eq.typeParamName + " =:= ") ++ + typeToHtml(eq.rhs, true) ++ xml.Text(")") + case bt: BoundedTypeParamConstraint => + xml.Text(bt.typeParamName + " is a superclass of " + bt.lowerBound.name + " and a subclass of " + + bt.upperBound.name + " (" + bt.typeParamName + " >: ") ++ + typeToHtml(bt.lowerBound, true) ++ xml.Text(" <: ") ++ + typeToHtml(bt.upperBound, true) ++ xml.Text(")") + case lb: LowerBoundedTypeParamConstraint => + xml.Text(lb.typeParamName + " is a superclass of " + lb.lowerBound.name + " (" + lb.typeParamName + " >: ") ++ + typeToHtml(lb.lowerBound, true) ++ xml.Text(")") + case ub: UpperBoundedTypeParamConstraint => + xml.Text(ub.typeParamName + " is a subclass of " + ub.upperBound.name + " (" + ub.typeParamName + " <: ") ++ + typeToHtml(ub.upperBound, true) ++ xml.Text(")") + } + } diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif b/src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif new file mode 100644 index 0000000000..4be145d0af Binary files /dev/null and b/src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif differ diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png new file mode 100644 index 0000000000..bc29efb3e6 Binary files /dev/null and b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png differ diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png new file mode 100644 index 0000000000..8313f4975b Binary files /dev/null and b/src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png differ diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css index 6fb83c133e..5a1779bba5 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css @@ -106,7 +106,7 @@ a[href]:hover { font-size: 24pt; text-shadow: black 0px 2px 0px; /* text-shadow: black 0px 0px 0px;*/ -text-decoration: none; +text-decoration: none; } #definition #owner { @@ -162,7 +162,7 @@ text-decoration: none; padding-left: 15px; background: url("arrow-right.png") no-repeat 0 3px transparent; } - + .toggleContainer.open .toggle { background: url("arrow-down.png") no-repeat 0 3px transparent; } @@ -205,6 +205,11 @@ dl.attributes > dt { font-style: italic; } +dl.attributes > dt.implicit { + font-weight: bold; + color: darkgreen; +} + dl.attributes > dd { display: block; padding-left: 10em; @@ -241,6 +246,17 @@ dl.attributes > dd { color: white; } +#inheritedMembers > div.conversion > h3 { + background: #dadada url("conversionbg.gif") repeat-x bottom left; /* gray */ + height: 17px; + font-style: italic; + font-size: 12pt; +} + +#inheritedMembers > div.conversion > h3 * { + color: white; +} + /* Member cells */ div.members > ol { @@ -310,10 +326,21 @@ div.members > ol > li:last-child { font-weight: bold; } -.signature .symbol .params .implicit { +.signature .symbol > .implicit { + display: inline-block; + font-weight: bold; + text-decoration: underline; + color: darkgreen; +} + +.signature .symbol .params > .implicit { font-style: italic; } +.signature .symbol .implicit.deprecated { + text-decoration: line-through; +} + .signature .symbol .name.deprecated { text-decoration: line-through; } @@ -369,15 +396,15 @@ div.members > ol > li:last-child { .cmt {} .cmt p { - margin: 0.7em 0; + margin: 0.7em 0; } .cmt p:first-child { - margin-top: 0; + margin-top: 0; } .cmt p:last-child { - margin-bottom: 0; + margin-bottom: 0; } .cmt h3, @@ -539,7 +566,7 @@ div.fullcommenttop .block { margin-bottom: 5px } -div.fullcomment div.block ol li p, +div.fullcomment div.block ol li p, div.fullcomment div.block ol li { display:inline } @@ -583,10 +610,10 @@ div.fullcomment dl.paramcmts > dd { /* Members filter tool */ #textfilter { - position: relative; - display: block; + position: relative; + display: block; height: 20px; - margin-bottom: 5px; + margin-bottom: 5px; } #textfilter > .pre { @@ -600,7 +627,7 @@ div.fullcomment dl.paramcmts > dd { } #textfilter > .input { - display: block; + display: block; position: absolute; top: 0; right: 20px; @@ -608,10 +635,10 @@ div.fullcomment dl.paramcmts > dd { } #textfilter > .input > input { - height: 20px; - padding: 1px; - font-weight: bold; - color: #000000; + height: 20px; + padding: 1px; + font-weight: bold; + color: #000000; background: #ffffff url("filterboxbarbg.png") repeat-x top left; width: 100%; } @@ -660,6 +687,13 @@ div.fullcomment dl.paramcmts > dd { display: inline-block; } +#mbrsel > div > a { + position:relative; + top: -8px; + font-size: 11px; + text-shadow: #ffffff 0 1px 0; +} + #mbrsel > div > ol#linearization { display: table; margin-left: 70px; @@ -683,9 +717,32 @@ div.fullcomment dl.paramcmts > dd { text-shadow: #ffffff 0 1px 0; } +#mbrsel > div > ol#implicits { + display: table; + margin-left: 70px; +} + +#mbrsel > div > ol#implicits > li.in { + text-decoration: none; + float: left; + padding-right: 10px; + margin-right: 5px; + background: url(selected-right-implicits.png) no-repeat; + background-position: right 0px; +} + +#mbrsel > div > ol#implicits > li.in > span{ + color: #404040; + float: left; + padding: 1px 0 1px 10px; + background: url(selected-implicits.png) no-repeat; + background-position: 0px 0px; + text-shadow: #ffffff 0 1px 0; +} + #mbrsel > div > ol > li { /* padding: 3px 10px;*/ - line-height: 16pt; + line-height: 16pt; display: inline-block; cursor: pointer; } @@ -709,10 +766,10 @@ div.fullcomment dl.paramcmts > dd { } #mbrsel > div > ol > li.out { - text-decoration: none; - float: left; - padding-right: 10px; - margin-right: 5px; + text-decoration: none; + float: left; + padding-right: 10px; + margin-right: 5px; } #mbrsel > div > ol > li.out > span{ @@ -739,10 +796,10 @@ div.fullcomment dl.paramcmts > dd { #mbrsel .showall { color: #4C4C4C; line-height: 16px; - font-weight: bold; + font-weight: bold; } #mbrsel .showall span { color: #4C4C4C; - font-weight: bold; + font-weight: bold; }*/ \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js index 3cdd9a7f27..fd5a981cb0 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js @@ -2,21 +2,23 @@ // code by Gilles Dubochet with contributions by Pedro Furlanetto $(document).ready(function(){ - var isHiddenClass; - if (document.title == 'scala.AnyRef') { - isHiddenClass = function (name) { - return name == 'scala.Any'; - }; - } else { - isHiddenClass = function (name) { - return name == 'scala.Any' || name == 'scala.AnyRef'; - }; - } + var isHiddenClass = function (name) { + return name == 'scala.Any' || + name == 'scala.AnyRef' || + name == 'scala.Predef.any2stringfmt' || + name == 'scala.Predef.any2stringadd' || + name == 'scala.Predef.any2ArrowAssoc' || + name == 'scala.Predef.any2Ensuring' + }; + + $("#linearization li:gt(0)").filter(function(){ + return isHiddenClass($(this).attr("name")); + }).removeClass("in").addClass("out"); - $("#linearization li").filter(function(){ + $("#implicits li").filter(function(){ return isHiddenClass($(this).attr("name")); }).removeClass("in").addClass("out"); - + // Pre-filter members filter(); @@ -54,17 +56,38 @@ $(document).ready(function(){ }; filter(); }); - $("#ancestors > ol > li.hideall").click(function() { + + $("#implicits li").click(function(){ + if ($(this).hasClass("in")) { + $(this).removeClass("in"); + $(this).addClass("out"); + } + else if ($(this).hasClass("out")) { + $(this).removeClass("out"); + $(this).addClass("in"); + }; + filter(); + }); + + $("#mbrsel > div[id=ancestors] > ol > li.hideall").click(function() { $("#linearization li.in").removeClass("in").addClass("out"); $("#linearization li:first").removeClass("out").addClass("in"); + $("#implicits li.in").removeClass("in").addClass("out"); filter(); }) - $("#ancestors > ol > li.showall").click(function() { - var filtered = + $("#mbrsel > div[id=ancestors] > ol > li.showall").click(function() { + var filteredLinearization = $("#linearization li.out").filter(function() { return ! isHiddenClass($(this).attr("name")); }); - filtered.removeClass("out").addClass("in"); + filteredLinearization.removeClass("out").addClass("in"); + + var filteredImplicits = + $("#implicits li.out").filter(function() { + return ! isHiddenClass($(this).attr("name")); + }); + filteredImplicits.removeClass("out").addClass("in"); + filter(); }); $("#visbl > ol > li.public").click(function() { @@ -108,8 +131,10 @@ $(document).ready(function(){ }); /* Add toggle arrows */ - var docAllSigs = $("#template li").has(".fullcomment").find(".signature"); - + //var docAllSigs = $("#template li").has(".fullcomment").find(".signature"); + // trying to speed things up a little bit + var docAllSigs = $("#template li[fullComment=yes] .signature"); + function commentToggleFct(signature){ var parent = signature.parent(); var shortComment = $(".shortcomment", parent); @@ -129,7 +154,7 @@ $(document).ready(function(){ docAllSigs.click(function() { commentToggleFct($(this)); }); - + /* Linear super types and known subclasses */ function toggleShowContentFct(outerElement){ var content = $(".hiddenContent", outerElement); @@ -148,20 +173,22 @@ $(document).ready(function(){ $(".toggleContainer").click(function() { toggleShowContentFct($(this)); }); - + // Set parent window title windowTitle(); }); function orderAlpha() { $("#template > div.parent").hide(); - $("#ancestors").show(); + $("#template > div.conversion").hide(); + $("#mbrsel > div[id=ancestors]").show(); filter(); }; function orderInherit() { $("#template > div.parent").show(); - $("#ancestors").hide(); + $("#template > div.conversion").show(); + $("#mbrsel > div[id=ancestors]").hide(); filter(); }; @@ -177,6 +204,9 @@ function initInherit() { $("#inheritedMembers > div.parent").each(function(){ parents[$(this).attr("name")] = $(this); }); + $("#inheritedMembers > div.conversion").each(function(){ + parents[$(this).attr("name")] = $(this); + }); $("#types > ol > li").each(function(){ var mbr = $(this); this.mbrText = mbr.find("> .fullcomment .cmt").text(); @@ -216,6 +246,9 @@ function initInherit() { $("#inheritedMembers > div.parent").each(function() { if ($("> div.members", this).length == 0) { $(this).remove(); }; }); + $("#inheritedMembers > div.conversion").each(function() { + if ($("> div.members", this).length == 0) { $(this).remove(); }; + }); }; function filter(scrollToMember) { @@ -224,13 +257,17 @@ function filter(scrollToMember) { var queryRegExp = new RegExp(query, "i"); var privateMembersHidden = $("#visbl > ol > li.public").hasClass("in"); var orderingAlphabetic = $("#order > ol > li.alpha").hasClass("in"); - var hiddenSuperclassElements = orderingAlphabetic ? $("#linearization > li.out") : $("#linearization > li:gt(0)"); - var hiddenSuperclasses = hiddenSuperclassElements.map(function() { + var hiddenSuperclassElementsLinearization = orderingAlphabetic ? $("#linearization > li.out") : $("#linearization > li:gt(0)"); + var hiddenSuperclassesLinearization = hiddenSuperclassElementsLinearization.map(function() { + return $(this).attr("name"); + }).get(); + var hiddenSuperclassElementsImplicits = orderingAlphabetic ? $("#implicits > li.out") : $("#implicits > li"); + var hiddenSuperclassesImplicits = hiddenSuperclassElementsImplicits.map(function() { return $(this).attr("name"); }).get(); var hideInheritedMembers; - + if(orderingAlphabetic) { $("#inheritedMembers").hide(); hideInheritedMembers = true; @@ -242,9 +279,10 @@ function filter(scrollToMember) { $("#allMembers > .members").each(filterFunc); hideInheritedMembers = false; $("#inheritedMembers > .parent > .members").each(filterFunc); + $("#inheritedMembers > .conversion > .members").each(filterFunc); } - + function filterFunc() { var membersVisible = false; var members = $(this); @@ -262,12 +300,18 @@ function filter(scrollToMember) { ownerIndex = name.lastIndexOf("."); } var owner = name.slice(0, ownerIndex); - for (var i = 0; i < hiddenSuperclasses.length; i++) { - if (hiddenSuperclasses[i] == owner) { + for (var i = 0; i < hiddenSuperclassesLinearization.length; i++) { + if (hiddenSuperclassesLinearization[i] == owner) { mbr.hide(); return; } - } + }; + for (var i = 0; i < hiddenSuperclassesImplicits.length; i++) { + if (hiddenSuperclassesImplicits[i] == owner) { + mbr.hide(); + return; + } + }; } if (query && !(queryRegExp.test(name) || queryRegExp.test(this.mbrText))) { mbr.hide(); @@ -276,7 +320,7 @@ function filter(scrollToMember) { mbr.show(); membersVisible = true; }); - + if (membersVisible) members.show(); else diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index 6eb14a4907..6488847049 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -167,6 +167,8 @@ trait MemberEntity extends Entity { /** Whether this member is abstract. */ def isAbstract: Boolean + /** If this member originates from an implicit conversion, we set the implicit information to the correct origin */ + def byConversion: Option[ImplicitConversion] } object MemberEntity { // Oh contravariance, contravariance, wherefore art thou contravariance? @@ -246,6 +248,8 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { * other entity of the pair is the companion. */ def companion: Option[DocTemplateEntity] + /** The implicit conversions this template (class or trait, objects and packages are not affected) */ + def conversions: List[ImplicitConversion] } @@ -413,3 +417,106 @@ trait Annotation extends Entity { def arguments: List[ValueArgument] } + +/** A trait that signals the member results from an implicit conversion */ +trait ImplicitConversion { + + /** The source of the implicit conversion*/ + def source: DocTemplateEntity + + /** The result type after the conversion */ + def targetType: TypeEntity + + /** The entity for the method that performed the conversion, if it's documented (or just its name, otherwise) */ + def convertorMethod: Either[MemberEntity, String] + + /** A short name of the convertion */ + def conversionShortName: String + + /** A qualified name uniquely identifying the convertion (currently: the conversion method's qualified name) */ + def conversionQualifiedName: String + + /** The entity that performed the conversion */ + def convertorOwner: TemplateEntity + + /** The constraints that the transformations puts on the type parameters */ + def constraints: List[Constraint] + + /** The members inherited by this implicit conversion */ + def members: List[MemberEntity] +} + +/** A trait that encapsulates a constraint necessary for implicit conversion */ +trait Constraint { + // /** The implicit conversion during which this constraint appears */ + // def conversion: ImplicitConversion +} + +/** A constraint involving a type parameter which must be in scope */ +trait ImplicitInScopeConstraint extends Constraint { + /** The type of the implicit value required */ + def implicitType: TypeEntity + + /** toString for debugging */ + override def toString = "an implicit _: " + implicitType.name + " must be in scope" +} + +trait TypeClassConstraint extends ImplicitInScopeConstraint with TypeParamConstraint { + /** Type class name */ + def typeClassEntity: TemplateEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a class of type " + typeClassEntity.qualifiedName + " (" + + typeParamName + ": " + typeClassEntity.name + ")" +} + +trait KnownTypeClassConstraint extends TypeClassConstraint { + /** Type explanation, takes the type parameter name and generates the explanation */ + def typeExplanation: (String) => String + + /** toString for debugging */ + override def toString = typeExplanation(typeParamName) + " (" + typeParamName + ": " + typeClassEntity.name + ")" +} + +/** A constraint involving a type parameter */ +trait TypeParamConstraint extends Constraint { + /** The type parameter involved */ + def typeParamName: String +} + +trait EqualTypeParamConstraint extends TypeParamConstraint { + /** The rhs */ + def rhs: TypeEntity + /** toString for debugging */ + override def toString = typeParamName + " is " + rhs.name + " (" + typeParamName + " =:= " + rhs.name + ")" +} + +trait BoundedTypeParamConstraint extends TypeParamConstraint { + /** The lower bound */ + def lowerBound: TypeEntity + + /** The upper bound */ + def upperBound: TypeEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a superclass of " + lowerBound.name + " and a subclass of " + + upperBound.name + " (" + typeParamName + " >: " + lowerBound.name + " <: " + upperBound.name + ")" +} + +trait LowerBoundedTypeParamConstraint extends TypeParamConstraint { + /** The lower bound */ + def lowerBound: TypeEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a superclass of " + lowerBound.name + " (" + typeParamName + " >: " + + lowerBound.name + ")" +} + +trait UpperBoundedTypeParamConstraint extends TypeParamConstraint { + /** The lower bound */ + def upperBound: TypeEntity + + /** toString for debugging */ + override def toString = typeParamName + " is a subclass of " + upperBound.name + " (" + typeParamName + " <: " + + upperBound.name + ")" +} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 670c9bbb3b..f295e4d211 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -17,7 +17,7 @@ import model.{ RootPackage => RootPackageEntity } /** This trait extracts all required information for documentation from compilation units */ class ModelFactory(val global: Global, val settings: doc.Settings) { - thisFactory: ModelFactory with CommentFactory with TreeFactory => + thisFactory: ModelFactory with ModelFactoryImplicitSupport with CommentFactory with TreeFactory => import global._ import definitions.{ ObjectClass, RootPackage, EmptyPackage, NothingClass, AnyClass, AnyValClass, AnyRefClass } @@ -95,7 +95,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isDocTemplate = false } - abstract class MemberImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { + abstract class MemberImpl(sym: Symbol, implConv: ImplicitConversionImpl = null, inTpl: => DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { lazy val comment = if (inTpl == null) None else thisFactory.comment(sym, inTpl) override def inTemplate = inTpl @@ -128,7 +128,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { if (sym.isImplicit) fgs += Paragraph(Text("implicit")) if (sym.isSealed) fgs += Paragraph(Text("sealed")) if (!sym.isTrait && (sym hasFlag Flags.ABSTRACT)) fgs += Paragraph(Text("abstract")) - if (!sym.isTrait && (sym hasFlag Flags.DEFERRED)) fgs += Paragraph(Text("abstract")) + /* Resetting the DEFERRED flag is a little trick here for refined types: (example from scala.collections) + * {{{ + * implicit def traversable2ops[T](t: collection.GenTraversableOnce[T]) = new TraversableOps[T] { + * def isParallel = ... + * }}} + * the type the method returns is TraversableOps, which has all-abstract symbols. But in reality, it couldn't have + * any abstract terms, otherwise it would fail compilation. So we reset the DEFERRED flag. */ + if (!sym.isTrait && (sym hasFlag Flags.DEFERRED) && (implConv eq null)) fgs += Paragraph(Text("abstract")) if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final")) fgs.toList } @@ -162,7 +169,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case NullaryMethodType(res) => resultTpe(res) case _ => tpe } - makeTypeInTemplateContext(resultTpe(sym.tpe), inTemplate, sym) + val tpe = if (implConv eq null) sym.tpe else implConv.toType memberInfo sym + makeTypeInTemplateContext(resultTpe(tpe), inTemplate, sym) } def isDef = false def isVal = false @@ -173,15 +181,17 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isAliasType = false def isAbstractType = false def isAbstract = - ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED))) || + // for the explanation of implConv == null see comment on flags + ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (implConv == null)) || sym.isAbstractClass || sym.isAbstractType) && !sym.isSynthetic def isTemplate = false + def byConversion = if (implConv ne null) Some(implConv) else None } /** The instantiation of `TemplateImpl` triggers the creation of the following entities: * All ancestors of the template and all non-package members. */ - abstract class DocTemplateImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends MemberImpl(sym, inTpl) with TemplateImpl with HigherKindedImpl with DocTemplateEntity { + abstract class DocTemplateImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends MemberImpl(sym, null, inTpl) with TemplateImpl with HigherKindedImpl with DocTemplateEntity { //if (inTpl != null) println("mbr " + sym + " in " + (inTpl.toRoot map (_.sym)).mkString(" > ")) if (settings.verbose.value) inform("Creating doc template for " + sym) @@ -245,16 +255,20 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def subClasses = if (subClassesCache == null) Nil else subClassesCache.toList - protected lazy val memberSyms = + val conversions = if (settings.docImplicits.value) makeImplicitConversions(sym, this) else Nil + + lazy val memberSyms = // Only this class's constructors are part of its members, inherited constructors are not. sym.info.members.filter(s => localShouldDocument(s) && (!s.isConstructor || s.owner == sym) && !isPureBridge(sym) ) - val members = memberSyms flatMap (makeMember(_, this)) - val templates = members collect { case c: DocTemplateEntity => c } - val methods = members collect { case d: Def => d } - val values = members collect { case v: Val => v } - val abstractTypes = members collect { case t: AbstractType => t } - val aliasTypes = members collect { case t: AliasType => t } + val members = (memberSyms.flatMap(makeMember(_, null, this))) ::: + (conversions.flatMap((_.members))) // also take in the members from implicit conversions + + val templates = members collect { case c: DocTemplateEntity => c } + val methods = members collect { case d: Def => d } + val values = members collect { case v: Val => v } + val abstractTypes = members collect { case t: AbstractType => t } + val aliasTypes = members collect { case t: AliasType => t } override def isTemplate = true def isDocTemplate = true def companion = sym.companionSymbol match { @@ -273,18 +287,22 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { abstract class RootPackageImpl(sym: Symbol) extends PackageImpl(sym, null) with RootPackageEntity - abstract class NonTemplateMemberImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends MemberImpl(sym, inTpl) with NonTemplateMemberEntity { + abstract class NonTemplateMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl) extends MemberImpl(sym, implConv, inTpl) with NonTemplateMemberEntity { override def qualifiedName = optimize(inTemplate.qualifiedName + "#" + name) - lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "#" + name) + lazy val definitionName = + if (implConv == null) optimize(inDefinitionTemplates.head.qualifiedName + "#" + name) + else optimize(implConv.conversionQualifiedName + "#" + name) def isUseCase = sym.isSynthetic def isBridge = sym.isBridge } - abstract class NonTemplateParamMemberImpl(sym: Symbol, inTpl: => DocTemplateImpl) extends NonTemplateMemberImpl(sym, inTpl) { - def valueParams = - sym.paramss map { ps => (ps.zipWithIndex) map { case (p, i) => + abstract class NonTemplateParamMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl) extends NonTemplateMemberImpl(sym, implConv, inTpl) { + def valueParams = { + val info = if (implConv eq null) sym.info else implConv.toType memberInfo sym + info.paramss map { ps => (ps.zipWithIndex) map { case (p, i) => if (p.nameString contains "$") makeValueParam(p, inTpl, optimize("arg" + i)) else makeValueParam(p, inTpl) }} + } } abstract class ParameterImpl(sym: Symbol, inTpl: => TemplateImpl) extends EntityImpl(sym, inTpl) with ParameterEntity { @@ -356,7 +374,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { override def qualifiedName = "_root_" override def inheritedFrom = Nil override def isRootPackage = true - override protected lazy val memberSyms = + override lazy val memberSyms = (bSym.info.members ++ EmptyPackage.info.members) filter { s => s != EmptyPackage && s != RootPackage } @@ -454,18 +472,19 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } /** */ - def makeMember(aSym: Symbol, inTpl: => DocTemplateImpl): List[MemberImpl] = { + // TODO: Should be able to override the type + def makeMember(aSym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl): List[MemberImpl] = { def makeMember0(bSym: Symbol, _useCaseOf: Option[MemberImpl]): Option[MemberImpl] = { if (bSym.isGetter && bSym.isLazy) - Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { override lazy val comment = // The analyser does not duplicate the lazy val's DocDef when it introduces its accessor. thisFactory.comment(bSym.accessed, inTpl) // This hack should be removed after analyser is fixed. override def isLazyVal = true override def useCaseOf = _useCaseOf }) else if (bSym.isGetter && bSym.accessed.isMutable) - Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { override def isVar = true override def useCaseOf = _useCaseOf }) @@ -481,36 +500,36 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } else bSym } - Some(new NonTemplateParamMemberImpl(cSym, inTpl) with HigherKindedImpl with Def { + Some(new NonTemplateParamMemberImpl(cSym, implConv, inTpl) with HigherKindedImpl with Def { override def isDef = true override def useCaseOf = _useCaseOf }) } - else if (bSym.isConstructor) - Some(new NonTemplateParamMemberImpl(bSym, inTpl) with Constructor { + else if (bSym.isConstructor && (implConv == null)) + Some(new NonTemplateParamMemberImpl(bSym, implConv, inTpl) with Constructor { override def isConstructor = true def isPrimary = sym.isPrimaryConstructor override def useCaseOf = _useCaseOf }) else if (bSym.isGetter) // Scala field accessor or Java field - Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { override def isVal = true override def useCaseOf = _useCaseOf }) else if (bSym.isAbstractType) - Some(new NonTemplateMemberImpl(bSym, inTpl) with TypeBoundsImpl with HigherKindedImpl with AbstractType { + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with TypeBoundsImpl with HigherKindedImpl with AbstractType { override def isAbstractType = true override def useCaseOf = _useCaseOf }) - else if (bSym.isAliasType) - Some(new NonTemplateMemberImpl(bSym, inTpl) with HigherKindedImpl with AliasType { + else if (bSym.isAliasType && bSym != AnyRefClass) + Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with HigherKindedImpl with AliasType { override def isAliasType = true def alias = makeTypeInTemplateContext(sym.tpe.dealias, inTpl, sym) override def useCaseOf = _useCaseOf }) else if (bSym.isPackage) inTpl match { case inPkg: PackageImpl => makePackage(bSym, inPkg) } - else if ((bSym.isClass || bSym.isModule) && templateShouldDocument(bSym)) + else if ((bSym.isClass || bSym.isModule || bSym == AnyRefClass) && templateShouldDocument(bSym)) Some(makeDocTemplate(bSym, inTpl)) else None @@ -520,16 +539,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { Nil else { val allSyms = useCases(aSym, inTpl.sym) map { case (bSym, bComment, bPos) => - docComments.put(bSym, DocComment(bComment, bPos)) // put the comment in the list, don't parse it yet, closes SI-4898 + docComments.put(bSym, DocComment(bComment, bPos)) // put the comment in the list, don't parse it yet, closes SI-4898 bSym } val member = makeMember0(aSym, None) - if (allSyms.isEmpty) - member.toList - else - // Use cases replace the original definitions - SI-5054 - allSyms flatMap { makeMember0(_, member) } + if (allSyms.isEmpty) + member.toList + else + // Use cases replace the original definitions - SI-5054 + allSyms flatMap { makeMember0(_, member) } } } @@ -639,9 +658,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // nameBuffer append stripPrefixes.foldLeft(pre.prefixString)(_ stripPrefix _) // } val bSym = normalizeTemplate(aSym) - if (bSym.isNonClassType) + if (bSym.isNonClassType) { nameBuffer append bSym.decodedName - else { + } else { val tpl = makeTemplate(bSym) val pos0 = nameBuffer.length refBuffer += pos0 -> (tpl, tpl.name.length) @@ -692,8 +711,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def templateShouldDocument(aSym: Symbol): Boolean = { - // TODO: document sourceless entities (e.g., Any, etc), based on a new Setting to be added - (aSym.isPackageClass || (aSym.sourceFile != null)) && localShouldDocument(aSym) && + // TODO: document sourceless entities (e.g., Any, etc), based on a new Setting to be added + (aSym.isPackageClass || (aSym.sourceFile != null)) && localShouldDocument(aSym) && ( aSym.owner == NoSymbol || templateShouldDocument(aSym.owner) ) && !isEmptyJavaObject(aSym) } diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala new file mode 100644 index 0000000000..23bef02bed --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -0,0 +1,501 @@ +/* NSC -- new Scala compiler -- Copyright 2007-2012 LAMP/EPFL + * + * This trait finds implicit conversions for a class in the default scope and creates scaladoc entries for each of them. + * + * @author Vlad Ureche + * @author Adriaan Moors + */ + +package scala.tools.nsc +package doc +package model + +import comment._ + +import scala.collection._ +import scala.util.matching.Regex + +import symtab.Flags +import io._ + +import model.{ RootPackage => RootPackageEntity } + +/** + * This trait finds implicit conversions for a class in the default scope and creates scaladoc entries for each of them. + * + * Let's take this as an example: + * {{{ + * object Test { + * class A + * + * class B { + * def foo = 1 + * } + * + * class C extends B { + * def bar = 2 + * class implicit + * } + * + * D def conv(a: A) = new C + * } + * }}} + * + * Overview: + * - scaladoc-ing the above classes, `A` will get two more methods: foo and bar, over its default methods + * - the nested classes (specifically `D` above), abstract types, type aliases and constructor members are not added to + * `A` (see makeMember0 in ModelFactory, last 3 cases) + * - the members added by implicit conversion are always listed under the implicit conversion, not under the class they + * actually come from (`foo` will be listed as coming from the implicit conversion to `C` instead of `B`) - see + * `definitionName` in MemberImpl + * + * Internals: + * TODO: Give an overview here + */ +trait ModelFactoryImplicitSupport { + thisFactory: ModelFactory with CommentFactory with TreeFactory => + + import global._ + import global.analyzer._ + import global.definitions._ + import settings.hardcoded + + // debugging: + val DEBUG: Boolean = settings.docImplicitsDebug.value + val ERROR: Boolean = true // currently we show all errors + @inline final def debug(msg: => String) = if (DEBUG) println(msg) + @inline final def error(msg: => String) = if (ERROR) println(msg) + + /** This is a flag that indicates whether to eliminate implicits that cannot be satisfied within the current scope. + * For example, if an implicit conversion requires that there is a Numeric[T] in scope: + * {{{ + * class A[T] + * class B extends A[Int] + * class C extends A[String] + * implicit def pimpA[T: Numeric](a: A[T]): D + * }}} + * For B, no constraints are generated as Numeric[Int] is already in the default scope. On the other hand, for the + * conversion from C to D, depending on -implicits-show-all, the conversion can: + * - not be generated at all, since there's no Numeric[String] in scope (if ran without -implicits-show-all) + * - generated with a *weird* constraint, Numeric[String] as the user might add it by hand (if flag is enabled) + */ + val implicitsShowAll: Boolean = settings.docImplicitsShowAll.value + class ImplicitNotFound(tpe: Type) extends Exception("No implicit of type " + tpe + " found in scope.") + + /* ============== IMPLEMENTATION PROVIDING ENTITY TYPES ============== */ + + class ImplicitConversionImpl( + val sym: Symbol, + val convSym: Symbol, + val toType: Type, + val constrs: List[Constraint], + inTpl: => DocTemplateImpl) + extends ImplicitConversion { + + def source: DocTemplateEntity = inTpl + + def targetType: TypeEntity = makeType(toType, inTpl) + + def convertorOwner: TemplateEntity = + if (convSym != NoSymbol) + makeTemplate(convSym.owner) + else { + error("Scaladoc implicits: Implicit conversion from " + sym.tpe + " to " + toType + " done by " + convSym + " = NoSymbol!") + makeRootPackage.get // surely the root package was created :) + } + + def convertorMethod: Either[MemberEntity, String] = { + var convertor: MemberEntity = null + + convertorOwner match { + case doc: DocTemplateImpl => + val convertors = members.collect { case m: MemberImpl if m.sym == convSym => m } + if (convertors.length == 1) + convertor = convertors.head + case _ => + } + if (convertor ne null) + Left(convertor) + else + Right(convSym.nameString) + } + + def conversionShortName = convSym.nameString + + def conversionQualifiedName = convertorOwner.qualifiedName + "." + convSym.nameString + + lazy val constraints: List[Constraint] = constrs + + val members: List[MemberEntity] = { + // Obtain the members inherited by the implicit conversion + var memberSyms = toType.members.filter(implicitShouldDocument(_)) + val existingMembers = sym.info.members + + // Debugging part :) + debug(sym.nameString + "\n" + "=" * sym.nameString.length()) + debug(" * conversion " + convSym + " from " + sym.tpe + " to " + toType) + + // Members inherited by implicit conversions cannot override actual members + memberSyms = memberSyms.filterNot((sym1: Symbol) => + existingMembers.exists(sym2 => sym1.name == sym2.name && + isSameType(toType.memberInfo(sym1), sym.info.memberInfo(sym2)))) + + debug(" -> full type: " + toType) + if (constraints.length != 0) { + debug(" -> constraints: ") + constraints foreach { constr => debug(" - " + constr) } + } + debug(" -> members:") + memberSyms foreach (sym => debug(" - "+ sym.decodedName +" : " + sym.info)) + debug("") + + memberSyms.flatMap((makeMember(_, this, inTpl))) + } + } + + /* ============== MAKER METHODS ============== */ + + /** + * Make the implicit conversion objects + * + * A word about the scope of the implicit conversions: currently we look at a very basic context composed of the + * default Scala imports (Predef._ for example) and the companion object of the current class, if one exists. In the + * future we might want to extend this to more complex scopes. + */ + def makeImplicitConversions(sym: Symbol, inTpl: => DocTemplateImpl): List[ImplicitConversion] = + // Nothing and Null are somewhat special -- they can be transformed by any implicit conversion available in scope. + // But we don't want that, so we'll simply refuse to find implicit conversions on for Nothing and Null + if (!(sym.isClass || sym.isTrait || sym == AnyRefClass) || sym == NothingClass || sym == NullClass) Nil + else { + var context: global.analyzer.Context = global.analyzer.rootContext(NoCompilationUnit) + + val results = global.analyzer.allViewsFrom(sym.tpe, context, sym.typeParams) + var conversions = results.flatMap(result => makeImplicitConversion(sym, result._1, result._2, context, inTpl)) + conversions = conversions.filterNot(_.members.isEmpty) + + // Filter out specialized conversions from array + if (sym == ArrayClass) + conversions = conversions.filterNot((conv: ImplicitConversion) => + hardcoded.arraySkipConversions.contains(conv.conversionQualifiedName)) + + // Filter out non-sensical conversions from value types + if (isScalaValueType(sym.tpe)) + conversions = conversions.filter((ic: ImplicitConversion) => + hardcoded.valueClassFilter(sym.nameString, ic.conversionQualifiedName)) + + // Put the class-specific conversions in front + val (ownConversions, commonConversions) = + conversions.partition(conv => !hardcoded.commonConversionTargets.contains(conv.conversionQualifiedName)) + + ownConversions ::: commonConversions + } + + /** makeImplicitConversion performs the heavier lifting to get the implicit listing: + * - for each possible conversion function (also called view) + * * figures out the final result of the view (to what is our class transformed?) + * * figures out the necessary constraints on the type parameters (such as T <: Int) and the context (such as Numeric[T]) + * * lists all inherited members + * + * What? in details: + * - say we start from a class A[T1, T2, T3, T4] + * - we have an implicit function (view) in scope: + * def pimpA[T3 <: Long, T4](a: A[Int, Foo[Bar[X]], T3, T4])(implicit ev1: Manifest[T4], ev2: Numeric[T4]): PimpedA + * - A is converted to PimpedA ONLY if a couple of constraints are satisfied: + * * T1 must be equal to Int + * * T2 must be equal to Foo[Bar[X]] + * * T3 must be upper bounded by Long + * * there must be evidence of Numeric[T4] and a Mainfest[T4] within scope + * - the final type is PimpedA and A therefore inherits a couple of members from pimpedA + * + * How? + * some notes: + * - Scala's type inference will want to solve all type parameters down to actual types, but we only want constraints + * to maintain generality + * - therefore, allViewsFrom wraps type parameters into "untouchable" type variables that only gather constraints, + * but are never solved down to a type + * - these must be reverted back to the type parameters and the constraints must be extracted and simplified (this is + * done by the uniteConstraints and boundedTParamsConstraints. Be sure to check them out + * - we also need to transform implicit parameters in the view's signature into constraints, such that Numeric[T4] + * appears as a constraint + */ + def makeImplicitConversion(sym: Symbol, result: SearchResult, constrs: List[TypeConstraint], context: Context, inTpl: => DocTemplateImpl): List[ImplicitConversion] = + if (result.tree == EmptyTree) Nil + else { + // `result` will contain the type of the view (= implicit conversion method) + // the search introduces untouchable type variables, but we want to get back to type parameters + val viewFullType = result.tree.tpe + // set the previously implicit parameters to being explicit + + val (viewSimplifiedType, viewImplicitTypes) = removeImplicitParameters(viewFullType) + + // TODO: Isolate this corner case :) - Predef.<%< and put it in the testsuite + if (viewSimplifiedType.params.length != 1) { + // This is known to be caused by the `<%<` object in Predef: + // {{{ + // sealed abstract class <%<[-From, +To] extends (From => To) with Serializable + // object <%< { + // implicit def conformsOrViewsAs[A <% B, B]: A <%< B = new (A <%< B) {def apply(x: A) = x} + // } + // }}} + // so we just won't generate an implicit conversion for implicit methods that only take implicit parameters + return Nil + } + + // type the view application so we get the exact type of the result (not the formal type) + val viewTree = result.tree.setType(viewSimplifiedType) + val appliedTree = new ApplyImplicitView(viewTree, List(Ident("") setType viewTree.tpe.paramTypes.head)) + val appliedTreeTyped: Tree = { + val newContext = context.makeImplicit(context.ambiguousErrors) + val newTyper = global.analyzer.newTyper(newContext) + newTyper.silent(_.typed(appliedTree, global.analyzer.EXPRmode, WildcardType), false) match { + + case global.analyzer.SilentResultValue(t: Tree) => t + case global.analyzer.SilentTypeError(err) => + global.reporter.warning(sym.pos, err.toString) + return Nil + } + } + + // now we have the final type: + val toType = wildcardToNothing(typeVarToOriginOrWildcard(appliedTreeTyped.tpe.finalResultType)) + + try { + // Transform bound constraints into scaladoc constraints + val implParamConstraints = makeImplicitConstraints(viewImplicitTypes, sym, context, inTpl) + val boundsConstraints = makeBoundedConstraints(sym.typeParams, constrs, inTpl) + // TODO: no substitution constraints appear in the library and compiler scaladoc. Maybe they can be removed? + val substConstraints = makeSubstitutionConstraints(result.subst, inTpl) + val constraints = implParamConstraints ::: boundsConstraints ::: substConstraints + + List(new ImplicitConversionImpl(sym, result.tree.symbol, toType, constraints, inTpl)) + } catch { + case i: ImplicitNotFound => + //println(" Eliminating: " + toType) + Nil + } + } + + def makeImplicitConstraints(types: List[Type], sym: Symbol, context: Context, inTpl: => DocTemplateImpl): List[Constraint] = + types.flatMap((tpe:Type) => { + // TODO: Before creating constraints, map typeVarToOriginOrWildcard on the implicitTypes + val implType = typeVarToOriginOrWildcard(tpe) + val qualifiedName = implType.typeSymbol.ownerChain.reverse.map(_.nameString).mkString(".") + + var available: Option[Boolean] = None + + // see: https://groups.google.com/forum/?hl=en&fromgroups#!topic/scala-internals/gm_fr0RKzC4 + // + // println(implType + " => " + implType.isTrivial) + // var tpes: List[Type] = List(implType) + // while (!tpes.isEmpty) { + // val tpe = tpes.head + // tpes = tpes.tail + // tpe match { + // case TypeRef(pre, sym, args) => + // tpes = pre :: args ::: tpes + // println(tpe + " => " + tpe.isTrivial) + // case _ => + // println(tpe + " (of type" + tpe.getClass + ") => " + tpe.isTrivial) + // } + // } + // println("\n") + + // look for type variables in the type. If there are none, we can decide if the implicit is there or not + if (implType.isTrivial) { + try { + context.flushBuffer() /* any errors here should not prevent future findings */ + // TODO: Not sure this is the right thing to do -- seems similar to what scalac should be doing + val context2 = context.make(context.unit, context.tree, sym.owner, context.scope, context.imports) + val search = inferImplicit(EmptyTree, tpe, false, false, context2, false) + context.flushBuffer() /* any errors here should not prevent future findings */ + + available = Some(search.tree != EmptyTree) + } catch { + case _ => + } + } + + available match { + case Some(true) => + Nil + case Some(false) if (!implicitsShowAll) => + // if -implicits-show-all is not set, we get rid of impossible conversions (such as Numeric[String]) + throw new ImplicitNotFound(implType) + case _ => + val typeParamNames = sym.typeParams.map(_.name) + + // TODO: This is maybe the worst hack I ever did - it's as dirty as hell, but it seems to work, so until I + // learn more about symbols, it'll have to do. + implType match { + case TypeRef(pre, sym, List(TypeRef(NoPrefix, targ, Nil))) if (typeParamNames contains targ.name) => + hardcoded.knownTypeClasses.get(qualifiedName) match { + case Some(explanation) => + List(new KnownTypeClassConstraint { + val typeParamName = targ.nameString + val typeExplanation = explanation + val typeClassEntity = makeTemplate(sym) + val implicitType: TypeEntity = makeType(implType, inTpl) + }) + case None => + List(new TypeClassConstraint { + val typeParamName = targ.nameString + val typeClassEntity = makeTemplate(sym) + val implicitType: TypeEntity = makeType(implType, inTpl) + }) + } + case _ => + List(new ImplicitInScopeConstraint{ + val implicitType: TypeEntity = makeType(implType, inTpl) + }) + } + } + }) + + def makeSubstitutionConstraints(subst: TreeTypeSubstituter, inTpl: => DocTemplateImpl): List[Constraint] = + (subst.from zip subst.to) map { + case (from, to) => + new EqualTypeParamConstraint { + error("Scaladoc implicits: Unexpected type substitution constraint from: " + from + " to: " + to) + val typeParamName = from.toString + val rhs = makeType(to, inTpl) + } + } + + def makeBoundedConstraints(tparams: List[Symbol], constrs: List[TypeConstraint], inTpl: => DocTemplateImpl): List[Constraint] = + (tparams zip constrs) flatMap { + case (tparam, constr) => { + uniteConstraints(constr) match { + case (loBounds, upBounds) => (loBounds filter (_ != NothingClass.tpe), upBounds filter (_ != AnyClass.tpe)) match { + case (Nil, Nil) => + Nil + case (List(lo), List(up)) if (lo == up) => + List(new EqualTypeParamConstraint { + val typeParamName = tparam.nameString + val rhs = makeType(lo, inTpl) + }) + case (List(lo), List(up)) => + List(new BoundedTypeParamConstraint { + val typeParamName = tparam.nameString + val lowerBound = makeType(lo, inTpl) + val upperBound = makeType(up, inTpl) + }) + case (List(lo), Nil) => + List(new LowerBoundedTypeParamConstraint { + val typeParamName = tparam.nameString + val lowerBound = makeType(lo, inTpl) + }) + case (Nil, List(up)) => + List(new UpperBoundedTypeParamConstraint { + val typeParamName = tparam.nameString + val upperBound = makeType(up, inTpl) + }) + case other => + // this is likely an error on the lub/glb side + error("Scaladoc implicits: Error computing lub/glb for: " + (tparam, constr) + ":\n" + other) + Nil + } + } + } + } + + /** + * uniteConstraints takes a TypeConstraint instance and simplifies the constraints inside + * + * Normally TypeConstraint contains multiple lower and upper bounds, and we want to reduce this to a lower and an + * upper bound. Here are a couple of catches we need to be aware of: + * - before finding a view (implicit method in scope that maps class A[T1,T2,.. Tn] to something else) the type + * parameters are transformed into "untouchable" type variables so that type inference does not attempt to + * fully solve them down to a type but rather constrains them on both sides just enough for the view to be + * applicable -- now, we want to transform those type variables back to the original type parameters + * - some of the bounds fail type inference and therefore refer to Nothing => when performing unification (lub, glb) + * they start looking ugly => we (unsoundly) transform Nothing to WildcardType so we fool the unification algorithms + * into thinking there's nothing there + * - we don't want the wildcard types surviving the unification so we replace them back to Nothings + */ + def uniteConstraints(constr: TypeConstraint): (List[Type], List[Type]) = + try { + (List(wildcardToNothing(lub(constr.loBounds map typeVarToOriginOrWildcard))), + List(wildcardToNothing(glb(constr.hiBounds map typeVarToOriginOrWildcard)))) + } catch { + // does this actually ever happen? (probably when type vars occur in the bounds) + case x: Throwable => (constr.loBounds.distinct, constr.hiBounds.distinct) + } + + /** + * Make implicits explicit - Not used curently + */ + object implicitToExplicit extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case MethodType(params, resultType) => + MethodType(params.map(param => if (param.isImplicit) param.cloneSymbol.resetFlag(Flags.IMPLICIT) else param), resultType) + case other => + other + } + } + + /** + * removeImplicitParameters transforms implicit parameters from the view result type into constraints and + * returns the simplified type of the view + * + * for the example view: + * implicit def pimpMyClass[T](a: MyClass[T])(implicit ev: Numeric[T]): PimpedMyClass[T] + * the implicit view result type is: + * (a: MyClass[T])(implicit ev: Numeric[T]): PimpedMyClass[T] + * and the simplified type will be: + * MyClass[T] => PimpedMyClass[T] + */ + def removeImplicitParameters(viewType: Type): (Type, List[Type]) = { + + val params = viewType.paramss.flatten + val (normalParams, implParams) = params.partition(!_.isImplicit) + val simplifiedType = MethodType(normalParams, viewType.finalResultType) + val implicitTypes = implParams.map(_.tpe) + + (simplifiedType, implicitTypes) + } + + /** + * typeVarsToOriginOrWildcard transforms the "untouchable" type variables into either their origins (the original + * type parameters) or into wildcard types if nothing matches + */ + object typeVarToOriginOrWildcard extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case tv: TypeVar => + if (tv.constr.inst.typeSymbol == NothingClass) + WildcardType + else + tv.origin //appliedType(tv.origin.typeConstructor, tv.typeArgs map this) + case other => + if (other.typeSymbol == NothingClass) + WildcardType + else + other + } + } + + /** + * wildcardToNothing transforms wildcard types back to Nothing + */ + object wildcardToNothing extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case WildcardType => + NothingClass.tpe + case other => + other + } + } + + /** implicitShouldDocument decides whether a member inherited by implicit conversion should be documented */ + def implicitShouldDocument(aSym: Symbol): Boolean = { + // We shouldn't document: + // - constructors + // - common methods (in Any, AnyRef, Object) as they are automatically removed + // - private and protected members (not accessible following an implicit conversion) + // - members starting with _ (usually reserved for internal stuff) + localShouldDocument(aSym) && (!aSym.isConstructor) && (aSym.owner != ObjectClass) && + (aSym.owner != AnyClass) && (aSym.owner != AnyRefClass) && + (!aSym.isProtected) && (!aSym.isPrivate) && (!aSym.name.startsWith("_")) && + (aSym.isMethod || aSym.isGetter || aSym.isSetter) && + (aSym.nameString != "getClass") + } +} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala index 988f2e0ba9..f948d53c8b 100755 --- a/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala @@ -52,7 +52,7 @@ trait TreeFactory { thisTreeFactory: ModelFactory with TreeFactory => if (asym.isSetter) asym = asym.getter(asym.owner) makeTemplate(asym.owner) match { case docTmpl: DocTemplateImpl => - val mbrs: List[MemberImpl] = makeMember(asym,docTmpl) + val mbrs: List[MemberImpl] = makeMember(asym, null, docTmpl) mbrs foreach { mbr => refs += ((start, (mbr,end))) } case _ => } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 3a789b83b6..2de86c67bf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -94,6 +94,27 @@ trait Implicits { result } + /** Find all views from type `tp` (in which `tpars` are free) + * + * Note that the trees in the search results in the returned list share the same type variables. + * Ignore their constr field! The list of type constraints returned along with each tree specifies the constraints that + * must be met by the corresponding type parameter in `tpars` (for the returned implicit view to be valid). + * + * @arg tp from-type for the implicit conversion + * @arg context search implicits here + * @arg tpars symbols that should be considered free type variables + * (implicit search should not try to solve them, just track their constraints) + */ + def allViewsFrom(tp: Type, context: Context, tpars: List[Symbol]): List[(SearchResult, List[TypeConstraint])] = { + // my untouchable typevars are better than yours (they can't be constrained by them) + val tvars = tpars map (TypeVar.apply(_, untouchable = true)) + val tpSubsted = tp.subst(tpars, tvars) + + val search = new ImplicitSearch(EmptyTree, functionType(List(tpSubsted), AnyClass.tpe), true, context.makeImplicit(false)) + + search.allImplicitsPoly(tvars) + } + private final val sizeLimit = 50000 private type Infos = List[ImplicitInfo] private type Infoss = List[List[ImplicitInfo]] @@ -369,7 +390,7 @@ trait Implicits { private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult = { (context.openImplicits find { case (tp, tree1) => tree1.symbol == tree.symbol && dominates(pt, tp)}) match { case Some(pending) => - // println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG + //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG throw DivergentImplicit case None => try { @@ -378,7 +399,7 @@ trait Implicits { typedImplicit0(info, ptChecked) } catch { case ex: DivergentImplicit => - // println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG + //println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG if (context.openImplicits.tail.isEmpty) { if (!(pt.isErroneous)) DivergingImplicitExpansionError(tree, pt, info.sym)(context) @@ -510,7 +531,7 @@ trait Implicits { private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean): SearchResult = { incCounter(plausiblyCompatibleImplicits) - printTyping( + printTyping ( ptBlock("typedImplicit0", "info.name" -> info.name, "ptChecked" -> ptChecked, @@ -1202,6 +1223,26 @@ trait Implicits { def search(iss: Infoss, isLocal: Boolean) = applicableInfos(iss, isLocal).values (search(context.implicitss, true) ++ search(implicitsOfExpectedType, false)).toList.filter(_.tree ne EmptyTree) } + + // find all implicits for some type that contains type variables + // collect the constraints that result from typing each implicit + def allImplicitsPoly(tvars: List[TypeVar]): List[(SearchResult, List[TypeConstraint])] = { + def resetTVars() = tvars foreach { _.constr = new TypeConstraint } + + def eligibleInfos(iss: Infoss, isLocal: Boolean) = new ImplicitComputation(iss, if (isLocal) util.HashSet[Name](512) else null).eligible + val allEligibleInfos = (eligibleInfos(context.implicitss, true) ++ eligibleInfos(implicitsOfExpectedType, false)).toList + + allEligibleInfos flatMap { ii => + // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) + // thus, start each type var off with a fresh for every typedImplicit + resetTVars() + // any previous errors should not affect us now + context.flushBuffer() + val res = typedImplicit(ii, false) + if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) + else Nil + } + } } object ImplicitNotFoundMsg { diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 98b8d7673e..d2fe106b14 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -83,7 +83,7 @@ trait Infer { def apply(tp: Type): Type = tp match { case WildcardType | BoundedWildcardType(_) | NoType => throw new NoInstance("undetermined type") - case tv @ TypeVar(origin, constr) => + case tv @ TypeVar(origin, constr) if !tv.untouchable => if (constr.inst == NoType) { throw new DeferredNoInstance(() => "no unique instantiation of type variable " + origin + " could be found") diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala index 99c54ce58c..5b8ebde308 100644 --- a/src/library/scala/Array.scala +++ b/src/library/scala/Array.scala @@ -467,6 +467,19 @@ object Array extends FallbackArrayBuilding { * @version 1.0 * @see [[http://www.scala-lang.org/docu/files/collections-api/collections_38.html#anchor "The Scala 2.8 Collections' API"]] * section on `Array` by Martin Odersky for more information. + * @define coll array + * @define Coll Array + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define collectExample + * @define undefinedorder + * @define thatinfo the class of the returned collection. In the standard library configuration, + * `That` is either `Array[B]` if a ClassManifest is available for B or `ArraySeq[B]` otherwise. + * @define zipthatinfo $thatinfo + * @define bfinfo an implicit value of class `CanBuildFrom` which determines the result class `That` from the current + * representation type `Repr` and the new element type `B`. */ final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable { diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index 2d87ccb261..68ea67ca00 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -79,6 +79,17 @@ object Option { * @define option [[scala.Option]] * @define p `p` * @define f `f` + * @define coll option + * @define Coll Option + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define collectExample + * @define undefinedorder + * @define thatinfo the class of the returned collection. In the standard library configuration, `That` is `Iterable[B]` + * @define bfinfo an implicit value of class `CanBuildFrom` which determines the result class `That` from the current + * representation type `Repr` and the new element type `B`. */ sealed abstract class Option[+A] extends Product with Serializable { self => diff --git a/src/library/scala/Tuple2.scala b/src/library/scala/Tuple2.scala index b1befca4fa..37ab564c3c 100644 --- a/src/library/scala/Tuple2.scala +++ b/src/library/scala/Tuple2.scala @@ -23,7 +23,7 @@ case class Tuple2[@specialized(Int, Long, Double, Char, Boolean, AnyRef) +T1, @s extends Product2[T1, T2] { override def toString() = "(" + _1 + "," + _2 + ")" - + /** Swaps the elements of this `Tuple`. * @return a new Tuple where the first element is the second element of this Tuple and the * second element is the first element of this Tuple. @@ -54,6 +54,16 @@ case class Tuple2[@specialized(Int, Long, Double, Char, Boolean, AnyRef) +T1, @s def zipped[Repr1, El1, Repr2, El2](implicit w1: T1 => TLike[El1, Repr1], w2: T2 => ILike[El2, Repr2]): Zipped[Repr1, El1, Repr2, El2] = new Zipped[Repr1, El1, Repr2, El2](_1, _2) + /** + * @define coll zipped + * @define Coll Zipped + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define collectExample + * @define undefinedorder + */ class Zipped[+Repr1, +El1, +Repr2, +El2](coll1: TLike[El1, Repr1], coll2: ILike[El2, Repr2]) { // coll2: ILike for filter def map[B, To](f: (El1, El2) => B)(implicit cbf: CBF[Repr1, B, To]): To = { val b = cbf(coll1.repr) diff --git a/src/library/scala/Tuple3.scala b/src/library/scala/Tuple3.scala index 0d5399308b..cd5ee23757 100644 --- a/src/library/scala/Tuple3.scala +++ b/src/library/scala/Tuple3.scala @@ -24,7 +24,7 @@ case class Tuple3[+T1, +T2, +T3](_1: T1, _2: T2, _3: T3) extends Product3[T1, T2, T3] { override def toString() = "(" + _1 + "," + _2 + "," + _3 + ")" - + @deprecated("Use `zipped` instead.", "2.9.0") def zip[Repr1, El1, El2, El3, To](implicit w1: T1 => TLike[El1, Repr1], @@ -53,6 +53,17 @@ case class Tuple3[+T1, +T2, +T3](_1: T1, _2: T2, _3: T3) w3: T3 => ILike[El3, Repr3]): Zipped[Repr1, El1, Repr2, El2, Repr3, El3] = new Zipped[Repr1, El1, Repr2, El2, Repr3, El3](_1, _2, _3) + /** + * @define coll zipped + * @define Coll Zipped + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define collectExample + * @define undefinedorder + * @define thatInfo The class of the returned collection. + */ class Zipped[+Repr1, +El1, +Repr2, +El2, +Repr3, +El3](coll1: TLike[El1, Repr1], coll2: ILike[El2, Repr2], coll3: ILike[El3, Repr3]) { diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala index 2eb026ceee..142f2baea5 100644 --- a/src/partest/scala/tools/partest/ScaladocModelTest.scala +++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala @@ -21,10 +21,10 @@ import scala.tools.nsc.reporters.ConsoleReporter import scala.tools.nsc.doc.model._ import scala.tools.partest.ScaladocModelTest - object Test extends ScaladocModelTest { + object Test extends ScaladocModelTest { - def code = """ ... """ - def scaladocSettings = "" + override def code = """ ... """ // or override def resourceFile = ".scala" (from test/scaladoc/resources) + def scaladocSettings = " ... " def testModel(rootPackage: Package) = { // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) import access._ @@ -39,10 +39,22 @@ abstract class ScaladocModelTest extends DirectTest { /** Override this to give scaladoc command line parameters */ def scaladocSettings: String - + /** Override this to test the model */ def testModel(root: Package): Unit + /** Override to feed a file in resources to scaladoc*/ + def resourceFile: String = null + + /** Override to feed code into scaladoc */ + override def code = + if (resourceFile ne null) + io.File(resourcePath + "/" + resourceFile).slurp() + else + sys.error("Scaladoc Model Test: You need to give a file or some code to feed to scaladoc!") + + def resourcePath = io.Directory(sys.props("partest.cwd") + "/../resources") + // Implementation follows: override def extraSettings: String = "-usejavacp" @@ -50,15 +62,15 @@ abstract class ScaladocModelTest extends DirectTest { // redirect err to out, for logging val prevErr = System.err System.setErr(System.out) - + try { // 1 - compile with scaladoc and get the model out - val args = scaladocSettings.split(" ") - val universe = model(args:_*).getOrElse({sys.error("Scaladoc Model Test ERROR: No universe generated!")}) + val universe = model.getOrElse({sys.error("Scaladoc Model Test ERROR: No universe generated!")}) // 2 - check the model generated testModel(universe.rootPackage) + println("Done.") } catch { - case e => + case e => println(e) e.printStackTrace } @@ -66,51 +78,46 @@ abstract class ScaladocModelTest extends DirectTest { System.setErr(prevErr) } + private[this] var settings: Settings = null + // create a new scaladoc compiler - def newDocFactory(args: String*): DocFactory = { - val settings = new Settings(_ => ()) - val command = new ScalaDoc.Command((CommandLineParser tokenize extraSettings) ++ args.toList, settings) + def newDocFactory: DocFactory = { + settings = new Settings(_ => ()) + settings.reportModel = false // yaay, no more "model contains X documentable templates"! + val args = extraSettings + " " + scaladocSettings + val command = new ScalaDoc.Command((CommandLineParser tokenize (args)), settings) val docFact = new DocFactory(new ConsoleReporter(settings), settings) docFact } // compile with scaladoc and output the result - def model(args: String*): Option[Universe] = newDocFactory(args: _*).makeUniverse(Right(code)) + def model: Option[Universe] = newDocFactory.makeUniverse(Right(code)) // so we don't get the newSettings warning - override def isDebug = false + override def isDebug = false // finally, enable easy navigation inside the entities object access { - // Make it easy to access things class TemplateAccess(tpl: DocTemplateEntity) { - def _class(name: String): DocTemplateEntity = getTheFirst(_classes(name), tpl.qualifiedName + ".class(" + name + ")") - def _classes(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).flatMap({ case c: Class => List(c)}) + def _classes(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: Class => c}) def _trait(name: String): DocTemplateEntity = getTheFirst(_traits(name), tpl.qualifiedName + ".trait(" + name + ")") - def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).flatMap({ case t: Trait => List(t)}) + def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: Trait => t}) def _object(name: String): DocTemplateEntity = getTheFirst(_objects(name), tpl.qualifiedName + ".object(" + name + ")") - def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).flatMap({ case o: Object => List(o)}) + def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: Object => o}) def _method(name: String): Def = getTheFirst(_methods(name), tpl.qualifiedName + ".method(" + name + ")") def _methods(name: String): List[Def] = tpl.methods.filter(_.name == name) - + def _value(name: String): Val = getTheFirst(_values(name), tpl.qualifiedName + ".value(" + name + ")") def _values(name: String): List[Val] = tpl.values.filter(_.name == name) - def getTheFirst[T](list: List[T], expl: String): T = { - if (list.length == 1) - list.head - else if (list.length == 0) - sys.error("Error getting " + expl + ": No such element. All elements in list: [" + list.mkString(", ") + "]") - else - sys.error("Error getting " + expl + ": " + list.length + " elements with this name. " + - "All elements in list: [" + list.mkString(", ") + "]") - } + def _conversion(name: String): ImplicitConversion = getTheFirst(_conversions(name), tpl.qualifiedName + ".conversion(" + name + ")") + def _conversions(name: String): List[ImplicitConversion] = tpl.conversions.filter(_.conversionQualifiedName == name) } class PackageAccess(pack: Package) extends TemplateAccess(pack) { @@ -118,7 +125,22 @@ abstract class ScaladocModelTest extends DirectTest { def _packages(name: String): List[Package] = pack.packages.filter(_.name == name) } + class MemberAccess(mbrs: WithMembers) { + def _member(name: String): MemberEntity = getTheFirst(_members(name), mbrs.toString + ".member(" + name + ")") + def _members(name: String): List[MemberEntity] = mbrs.members.filter(_.name == name) + } + + type WithMembers = { def members: List[MemberEntity]; def toString: String } /* DocTemplates and ImplicitConversions */ + implicit def templateAccess(tpl: DocTemplateEntity) = new TemplateAccess(tpl) implicit def packageAccess(pack: Package) = new PackageAccess(pack) + implicit def membersAccess(mbrs: WithMembers) = new MemberAccess(mbrs) + + def getTheFirst[T](list: List[T], expl: String): T = list.length match { + case 1 => list.head + case 0 => sys.error("Error getting " + expl + ": No such element.") + case _ => sys.error("Error getting " + expl + ": " + list.length + " elements with this name. " + + "All elements in list: [" + list.mkString(", ") + "]") + } } } diff --git a/test/scaladoc/resources/implicits-base-res.scala b/test/scaladoc/resources/implicits-base-res.scala new file mode 100644 index 0000000000..db7ca4fa51 --- /dev/null +++ b/test/scaladoc/resources/implicits-base-res.scala @@ -0,0 +1,143 @@ +/** + * Test scaladoc implicits - the bread and butter of the testsuite :) + */ +package scala.test.scaladoc.implicits.base + +class Foo[T] +class Bar[T] +trait MyNumeric[R] + +/** Class A + * - tests the complete type inference + * - the following inherited methods should appear: + * {{{ + * def convToGtColonDoubleA: Double // pimpA3: with a constraint that T <: Double + * def convToIntA: Int // pimpA2: with a constraint that T = Int + * def convToManifestA: T // pimpA7: with 2 constraints: T: Manifest and T <: Double + * def convToMyNumericA: T // pimpA6: with a constraint that there is x: MyNumeric[T] implicit in scope + * def convToNumericA: T // pimpA1: with a constraint that there is x: Numeric[T] implicit in scope + * def convToPimpedA: Bar[Foo[T]] // pimpA5: no constraints + * def convToPimpedA: S // pimpA4: with 3 constraints: T = Foo[Bar[S]], S: Foo and S: Bar + * def convToTraversableOps: T // pimpA7: with 2 constraints: T: Manifest and T <: Double + * // should not be abstract! + * }}} + */ +class A[T] { + /** This should prevent the implicitly inherited `def convToPimpedA: T` from `pimpA0` from showing up */ + def convToPimpedA: T = sys.error("Let's check it out!") +} +/** Companion object with implicit transformations */ +object A { + implicit def pimpA0[V](a: A[V]) = new PimpedA(a) + implicit def pimpA1[ZBUR: Numeric](a: A[ZBUR]) = new NumericA[ZBUR](a) + implicit def pimpA2(a: A[Int]) = new IntA(a) + implicit def pimpA3(a: A[T] forSome { type T <: Double }) = new GtColonDoubleA(a) + implicit def pimpA4[S](a: A[Foo[Bar[S]]])(implicit foo: Foo[S], bar: Bar[S]): PimpedA[S] = sys.error("not implemented") + implicit def pimpA5[Z](a: A[Z]): PimpedA[Bar[Foo[Z]]] = sys.error("not implemented") + implicit def pimpA6[Z: MyNumeric](a: A[Z]) = new MyNumericA[Z](a) + // TODO: Add H <: Double and see why it crashes for C and D -- context bounds, need to check! + implicit def pimpA7[H <: Double : Manifest](a: A[H]) = new ManifestA[H](a) with MyTraversableOps[H] { def convToTraversableOps: H = sys.error("no") } +} + + +/** Class B + * - tests the existential type solving + * - the following inherited methods should appear: + * {{{ + * def convToGtColonDoubleA: Double // pimpA3: no constraints + * def convToManifestA: Double // pimpA7: no constraints + * def convToMyNumericA: Double // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Double] implicit in scope + * def convToNumericA: Double // pimpA1: no constraintsd + * def convToPimpedA: Bar[Foo[Double]] // pimpA5: no constraints + * def convToTraversableOps: Double // pimpA7: no constraints + * // should not be abstract! + * }}} + */ +class B extends A[Double] +object B extends A + + +/** Class C + * - tests asSeenFrom + * - the following inherited methods should appear: + * {{{ + * def convToIntA: Int // pimpA2: no constraints + * def convToMyNumericA: Int // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Int] implicit in scope + * def convToNumericA: Int // pimpA1: no constraints + * def convToPimpedA: Bar[Foo[Int]] // pimpA5: no constraints + * }}} + */ +class C extends A[Int] +object C extends A + + +/** Class D + * - tests implicit elimination + * - the following inherited methods should appear: + * {{{ + * def convToMyNumericA: String // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[String] implicit in scope + * def convToNumericA: String // pimpA1: (if showAll is set) with a constraint that there is x: Numeric[String] implicit in scope + * def convToPimpedA: Bar[Foo[String]] // pimpA5: no constraints + * }}} + */ +class D extends A[String] +/** Companion object with implicit transformations */ +object D extends A + + +/** PimpedA class
    + * - tests simple inheritance and asSeenFrom + * - A, B and C should be implicitly converted to this */ +class PimpedA[V](a: A[V]) { + /** The convToPimpedA: V documentation... */ + def convToPimpedA: V = sys.error("Not implemented") +} + +/** NumericA class
    + * - tests the implicit conversion between parametric and fixed types + * - A, B and C should be implicitly converted to this */ +class NumericA[U: Numeric](a: A[U]) { + /** The convToNumericA: U documentation... */ + def convToNumericA: U = implicitly[Numeric[U]].zero +} + +/** IntA class
    + * - tests the interaction between implicit conversion and specific types + * - A and C should be implicitly converted to this */ +class IntA(a: A[Int]) { + /** The convToIntA: Int documentation... */ + def convToIntA: Int = 0 +} + +/** GtColonDoubleA class
    + * - tests the interaction between implicit conversion and existential types + * - A and B should be implicitly converted to this */ +class GtColonDoubleA(a: A[T] forSome { type T <: Double }) { + /** The convToGtColonDoubleA: Double documentation... */ + def convToGtColonDoubleA: Double = 0 +} + +/** MyNumericA class
    + * - tests the implicit conversion between parametric and fixed types + * - A should be implicitly converted to this */ +class MyNumericA[U: MyNumeric](a: A[U]) { + /** The convToMyNumericA: U documentation... */ + def convToMyNumericA: U = sys.error("dunno") +} + +/** ManifestA class
    + * - tests the manifest recognition + * - A, B, C, D should be implicitly converted to this */ +class ManifestA[W: Manifest](a: A[W]) { + /** The convToManifestA: W documentation... */ + def convToManifestA: W = sys.error("dunno") +} + +/** MyTraversableOps class
    + * - checks if any abstract members are added - should not happen! + */ +trait MyTraversableOps[S] { + /** The convToTraversableOps: S documentation... */ + def convToTraversableOps: S +} + diff --git a/test/scaladoc/resources/implicits-chaining-res.scala b/test/scaladoc/resources/implicits-chaining-res.scala new file mode 100644 index 0000000000..b20c8f846c --- /dev/null +++ b/test/scaladoc/resources/implicits-chaining-res.scala @@ -0,0 +1,48 @@ +/** + * Testing scaladoc implicits chaining + */ +package scala.test.scaladoc.implicits { + + // the classes involved + case class Z[U](a: U) + case class Intermediate[T, U](t: T, u: U) + class Implicit1[T](b: Implicit2[T]) + class Implicit2[T](c: Implicit3[T]) + class Implicit3[T](/* and so on */) + + object chaining { + + // the base conversion + implicit def convertToZ[T](a: A[T])(implicit b: Implicit1[T]): Z[A[T]] = Z(a) + + // and the implicit chaining, don't you just love it? :D + // implicit1, with one alternative + implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b) + // implicit2, with two alternatives + implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + // implicit3, with two alternatives + implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]() + implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]() + + // and our targets + /** conversion here, with constraints */ + class A[T]() + /** conversion here, no constraints */ + class B extends A[Intermediate[String, Int]] + /** no conversion */ + class C extends A[Intermediate[String, String]] + /** conversion here, no constraints */ + class D extends A[Intermediate[Double, Int]] + /** conversion here, no constraints */ + class E extends A[Intermediate[Double, String]] + /** no conversion */ + class F extends A[Intermediate[String, Double]] + + object scalacTest { + (new B).a + (new D).a + (new E).a + } + } +} diff --git a/test/scaladoc/resources/implicits-elimination-res.scala b/test/scaladoc/resources/implicits-elimination-res.scala new file mode 100644 index 0000000000..68743aee06 --- /dev/null +++ b/test/scaladoc/resources/implicits-elimination-res.scala @@ -0,0 +1,9 @@ +/** + * Testing scaladoc implicits elimination + */ +package scala.test.scaladoc.implicits.elimination { + /** No conversion, as B doesn't bring any member */ + class A + class B { class C; trait V; type T; } + object A { implicit def toB(a: A): B = null } +} diff --git a/test/scaladoc/resources/implicits-scopes-res.scala b/test/scaladoc/resources/implicits-scopes-res.scala new file mode 100644 index 0000000000..4e55c3e388 --- /dev/null +++ b/test/scaladoc/resources/implicits-scopes-res.scala @@ -0,0 +1,51 @@ +/** + * Testing scaladoc implicit scopes - looking for implicits in the right places + */ +package scala.test.scaladoc.implicits.scopes + +// TEST1 - In package object +package object test1 { + implicit def toB(a: A): B = null +} +package test1 { + class A + class B { def b = "" } +} + +// TEST2 - In enclosing package - doesn't seem to work even in scalac +package object test2 { + import classes._ + implicit def toB(a: A): B = null +} +package test2 { + package classes { + class A + class B { def b = "" } + object test { /* (new A).b won't compile */ } + } +} + +// TEST3 - In companion object +package test3 { + class A + object A { implicit def toB(a: A): B = null } + class B { def b = "" } +} + +// TEST4 - Nested type's companion object +package test4 { + class U[V] + class S + object S { implicit def toB(a: A): B = null } + class A extends U[S] + class B { def b = "" } +} + +// TEST5 - In scope +package test5 { + object scope { + class A + class B { def b = "" } + implicit def toB(a: A): B = null + } +} diff --git a/test/scaladoc/run/SI-5373.check b/test/scaladoc/run/SI-5373.check index c55eb001cf..619c56180b 100644 --- a/test/scaladoc/run/SI-5373.check +++ b/test/scaladoc/run/SI-5373.check @@ -1 +1 @@ -model contains 6 documentable templates +Done. diff --git a/test/scaladoc/run/SI-5373.scala b/test/scaladoc/run/SI-5373.scala index af433a1844..0062abbb2a 100644 --- a/test/scaladoc/run/SI-5373.scala +++ b/test/scaladoc/run/SI-5373.scala @@ -1,9 +1,9 @@ import scala.tools.nsc.doc.model._ import scala.tools.partest.ScaladocModelTest -object Test extends ScaladocModelTest { +object Test extends ScaladocModelTest { - def code = """ + override def code = """ import scala.annotation.bridge package scala.test { @@ -23,7 +23,7 @@ object Test extends ScaladocModelTest { // no need for special settings def scaladocSettings = "" - + def testModel(rootPackage: Package) = { // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) import access._ diff --git a/test/scaladoc/run/implicits-base.check b/test/scaladoc/run/implicits-base.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/implicits-base.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/implicits-base.scala b/test/scaladoc/run/implicits-base.scala new file mode 100644 index 0000000000..a0dd2071d7 --- /dev/null +++ b/test/scaladoc/run/implicits-base.scala @@ -0,0 +1,179 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // test a file instead of a piece of code + override def resourceFile = "implicits-base-res.scala" + + // start implicits + def scaladocSettings = "-implicits -implicits-show-all" + + def testModel(root: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // SEE THE test/resources/implicits-base-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("base") + var conv: ImplicitConversion = null + +//// class A /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val A = base._class("A") + + // the method pimped on by pimpA0 should be shadowed by the method in class A + assert(A._conversions(A.qualifiedName + ".pimpA0").isEmpty) + + // def convToNumericA: T // pimpA1: with a constraint that there is x: Numeric[T] implicit in scope + conv = A._conversion(A.qualifiedName + ".pimpA1") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToNumericA").resultType.name == "T") + + // def convToIntA: Int // pimpA2: with a constraint that T = Int + conv = A._conversion(A.qualifiedName + ".pimpA2") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToIntA").resultType.name == "Int") + + // def convToGtColonDoubleA: Double // pimpA3: with a constraint that T <: Double + conv = A._conversion(A.qualifiedName + ".pimpA3") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToGtColonDoubleA").resultType.name == "Double") + + // def convToPimpedA: S // pimpA4: with 3 constraints: T = Foo[Bar[S]], S: Foo and S: Bar + conv = A._conversion(A.qualifiedName + ".pimpA4") + assert(conv.members.length == 1) + assert(conv.constraints.length == 3) + assert(conv._member("convToPimpedA").resultType.name == "S") + + // def convToPimpedA: Bar[Foo[T]] // pimpA5: no constraints + conv = A._conversion(A.qualifiedName + ".pimpA5") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[T]]") + + // def convToMyNumericA: T // pimpA6: with a constraint that there is x: MyNumeric[T] implicit in scope + conv = A._conversion(A.qualifiedName + ".pimpA6") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToMyNumericA").resultType.name == "T") + + // def convToManifestA: T // pimpA7: with 2 constraints: T: Manifest and T <: Double + // def convToTraversableOps: T // pimpA7: with 2 constraints: T: Manifest and T <: Double + // should not be abstract! + conv = A._conversion(A.qualifiedName + ".pimpA7") + assert(conv.members.length == 2) + assert(conv.constraints.length == 2) + assert(conv._member("convToManifestA").resultType.name == "T") + assert(conv._member("convToTraversableOps").resultType.name == "T") + assert(conv._member("convToTraversableOps").flags.toString.indexOf("abstract") == -1) + +//// class B /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val B = base._class("B") + + // these conversions should not affect B + assert(B._conversions(A.qualifiedName + ".pimpA0").isEmpty) + assert(B._conversions(A.qualifiedName + ".pimpA2").isEmpty) + assert(B._conversions(A.qualifiedName + ".pimpA4").isEmpty) + + // def convToNumericA: Double // pimpA1: no constraintsd + conv = B._conversion(A.qualifiedName + ".pimpA1") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToNumericA").resultType.name == "Double") + + // def convToGtColonDoubleA: Double // pimpA3: no constraints + conv = B._conversion(A.qualifiedName + ".pimpA3") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToGtColonDoubleA").resultType.name == "Double") + + // def convToPimpedA: Bar[Foo[Double]] // pimpA5: no constraints + conv = B._conversion(A.qualifiedName + ".pimpA5") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[Double]]") + + // def convToMyNumericA: Double // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Double] implicit in scope + conv = B._conversion(A.qualifiedName + ".pimpA6") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToMyNumericA").resultType.name == "Double") + + // def convToManifestA: Double // pimpA7: no constraints + // def convToTraversableOps: Double // pimpA7: no constraints + // // should not be abstract! + conv = B._conversion(A.qualifiedName + ".pimpA7") + assert(conv.members.length == 2) + assert(conv.constraints.length == 0) + assert(conv._member("convToManifestA").resultType.name == "Double") + assert(conv._member("convToTraversableOps").resultType.name == "Double") + assert(conv._member("convToTraversableOps").flags.toString.indexOf("abstract") == -1) + +//// class C /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val C = base._class("C") + + // these conversions should not affect C + assert(C._conversions(A.qualifiedName + ".pimpA0").isEmpty) + assert(C._conversions(A.qualifiedName + ".pimpA3").isEmpty) + assert(C._conversions(A.qualifiedName + ".pimpA4").isEmpty) + assert(C._conversions(A.qualifiedName + ".pimpA7").isEmpty) + + // def convToNumericA: Int // pimpA1: no constraints + conv = C._conversion(A.qualifiedName + ".pimpA1") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToNumericA").resultType.name == "Int") + + // def convToIntA: Int // pimpA2: no constraints + conv = C._conversion(A.qualifiedName + ".pimpA2") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToIntA").resultType.name == "Int") + + // def convToPimpedA: Bar[Foo[Int]] // pimpA5: no constraints + conv = C._conversion(A.qualifiedName + ".pimpA5") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[Int]]") + + // def convToMyNumericA: Int // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Int] implicit in scope + conv = C._conversion(A.qualifiedName + ".pimpA6") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToMyNumericA").resultType.name == "Int") + +//// class D /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val D = base._class("D") + + // these conversions should not affect D + assert(D._conversions(A.qualifiedName + ".pimpA0").isEmpty) + assert(D._conversions(A.qualifiedName + ".pimpA2").isEmpty) + assert(D._conversions(A.qualifiedName + ".pimpA3").isEmpty) + assert(D._conversions(A.qualifiedName + ".pimpA4").isEmpty) + assert(D._conversions(A.qualifiedName + ".pimpA7").isEmpty) + + // def convToNumericA: String // pimpA1: (if showAll is set) with a constraint that there is x: Numeric[String] implicit in scope + conv = D._conversion(A.qualifiedName + ".pimpA1") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToNumericA").resultType.name == "String") + + // def convToPimpedA: Bar[Foo[String]] // pimpA5: no constraints + conv = D._conversion(A.qualifiedName + ".pimpA5") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[String]]") + + // def convToMyNumericA: String // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[String] implicit in scope + conv = D._conversion(A.qualifiedName + ".pimpA6") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + assert(conv._member("convToMyNumericA").resultType.name == "String") + } +} \ No newline at end of file diff --git a/test/scaladoc/run/implicits-chaining.check b/test/scaladoc/run/implicits-chaining.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/implicits-chaining.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/implicits-chaining.scala b/test/scaladoc/run/implicits-chaining.scala new file mode 100644 index 0000000000..96e288b204 --- /dev/null +++ b/test/scaladoc/run/implicits-chaining.scala @@ -0,0 +1,64 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // test a file instead of a piece of code + override def resourceFile = "implicits-chaining-res.scala" + + // start implicits + def scaladocSettings = "-implicits" + + def testModel(root: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._object("chaining") + var conv: ImplicitConversion = null + +//// class A /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val A = base._class("A") + + conv = A._conversion(base.qualifiedName + ".convertToZ") + assert(conv.members.length == 1) + assert(conv.constraints.length == 1) + +//// class B /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val B = base._class("B") + + conv = B._conversion(base.qualifiedName + ".convertToZ") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + +//// class C /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val C = base._class("C") + + assert(C._conversions(base.qualifiedName + ".convertToZ").isEmpty) + +//// class D /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val D = base._class("D") + + conv = D._conversion(base.qualifiedName + ".convertToZ") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + +//// class E /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val E = base._class("E") + + conv = E._conversion(base.qualifiedName + ".convertToZ") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + +//// class F /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val F = base._class("F") + + assert(F._conversions(base.qualifiedName + ".convertToZ").isEmpty) + } +} \ No newline at end of file diff --git a/test/scaladoc/run/implicits-elimination.check b/test/scaladoc/run/implicits-elimination.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/implicits-elimination.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/implicits-elimination.scala b/test/scaladoc/run/implicits-elimination.scala new file mode 100644 index 0000000000..71319f9f47 --- /dev/null +++ b/test/scaladoc/run/implicits-elimination.scala @@ -0,0 +1,22 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // test a file instead of a piece of code + override def resourceFile = "implicits-elimination-res.scala" + + // start implicits + def scaladocSettings = "-implicits" + + def testModel(root: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // SEE THE test/resources/implicits-elimination-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("elimination") + val A = base._class("A") + + assert(A._conversions(A.qualifiedName + ".toB").isEmpty) + } +} \ No newline at end of file diff --git a/test/scaladoc/run/implicits-scopes.check b/test/scaladoc/run/implicits-scopes.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/implicits-scopes.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/implicits-scopes.scala b/test/scaladoc/run/implicits-scopes.scala new file mode 100644 index 0000000000..7fb41e1ae8 --- /dev/null +++ b/test/scaladoc/run/implicits-scopes.scala @@ -0,0 +1,76 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // test a file instead of a piece of code + override def resourceFile = "implicits-scopes-res.scala" + + // start implicits + def scaladocSettings = "-implicits" + + def testModel(root: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + var conv: ImplicitConversion = null + + // SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("scopes") + +//// test1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val doTest1 = { + val test1 = base._package("test1") + val A = test1._class("A") + + conv = A._conversion(test1.qualifiedName + ".package.toB") // the .package means it's the package object + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + } + +//// test2 ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val doTest2 = { + val test2 = base._package("test2") + val classes = test2._package("classes") + val A = classes._class("A") + + assert(A._conversions(test2.qualifiedName + ".toB").isEmpty) + } + +//// test3 ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val doTest3 = { + val test3 = base._package("test3") + val A = test3._class("A") + + conv = A._conversion(A.qualifiedName + ".toB") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + } + +//// test4 ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val doTest4 = { + val test4 = base._package("test4") + val A = test4._class("A") + val S = test4._object("S") + + conv = A._conversion(S.qualifiedName + ".toB") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + } + +//// test5 ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + + val doTest5 = { + val test5 = base._package("test5") + val scope = test5._object("scope") + val A = scope._class("A") + + conv = A._conversion(scope.qualifiedName + ".toB") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) + } + } +} \ No newline at end of file diff --git a/test/scaladoc/scalacheck/CommentFactoryTest.scala b/test/scaladoc/scalacheck/CommentFactoryTest.scala index 69c314a64c..68ca68efdd 100644 --- a/test/scaladoc/scalacheck/CommentFactoryTest.scala +++ b/test/scaladoc/scalacheck/CommentFactoryTest.scala @@ -3,11 +3,12 @@ import org.scalacheck.Prop._ import scala.tools.nsc.Global import scala.tools.nsc.doc +import scala.tools.nsc.doc.model._ import scala.tools.nsc.doc.model.comment._ class Factory(val g: Global, val s: doc.Settings) extends doc.model.ModelFactory(g, s) { - thisFactory: Factory with CommentFactory with doc.model.TreeFactory => + thisFactory: Factory with ModelFactoryImplicitSupport with CommentFactory with doc.model.TreeFactory => def strip(c: Comment): Option[Inline] = { c.body match { @@ -28,7 +29,7 @@ object Test extends Properties("CommentFactory") { val settings = new doc.Settings((str: String) => {}) val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) val g = new Global(settings, reporter) - (new Factory(g, settings) with CommentFactory with doc.model.TreeFactory) + (new Factory(g, settings) with ModelFactoryImplicitSupport with CommentFactory with doc.model.TreeFactory) } def parse(src: String, dst: Inline) = { -- cgit v1.2.3 From 4b95f21060078af1ae7037ce2e6cfc4c1a5a155d Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 13 Apr 2012 10:57:41 +0100 Subject: Introducing some whitespace before colons. Maybe helping -Xcheckinit along too. --- src/compiler/scala/reflect/internal/StdNames.scala | 7 ------- src/library/scala/reflect/api/StandardNames.scala | 15 +++++---------- 2 files changed, 5 insertions(+), 17 deletions(-) (limited to 'src/library') diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index 1666887133..04f67a3196 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -268,8 +268,6 @@ trait StdNames extends NameManglers { self: SymbolTable => case _ => newTermName("x$" + i) } - // [Eugene to Paul] see comments in StandardNames.scala to find out why's this here - val QQQ = ??? val ??? = encode("???") val wrapRefArray: NameType = "wrapRefArray" @@ -641,14 +639,9 @@ trait StdNames extends NameManglers { self: SymbolTable => val ZOR = encode("||") // unary operators - // [Eugene to Paul] see comments in StandardNames.scala to find out why's this here - val UNARY_TILDE = UNARY_~ val UNARY_~ = encode("unary_~") - val UNARY_PLUS = UNARY_+ val UNARY_+ = encode("unary_+") - val UNARY_MINUS = UNARY_- val UNARY_- = encode("unary_-") - val UNARY_NOT = UNARY_! val UNARY_! = encode("unary_!") // Grouped here so Cleanup knows what tests to perform. diff --git a/src/library/scala/reflect/api/StandardNames.scala b/src/library/scala/reflect/api/StandardNames.scala index bfc165f613..d2110ede75 100644 --- a/src/library/scala/reflect/api/StandardNames.scala +++ b/src/library/scala/reflect/api/StandardNames.scala @@ -93,18 +93,13 @@ trait StandardNames { self: Universe => val ZOR: TermName // [Eugene] this doesn't compile. why?! -// val UNARY_~: TermName -// val UNARY_+: TermName -// val UNARY_-: TermName -// val UNARY_!: TermName - val UNARY_TILDE: TermName - val UNARY_PLUS: TermName - val UNARY_MINUS: TermName - val UNARY_NOT: TermName + val UNARY_~ : TermName + val UNARY_+ : TermName + val UNARY_- : TermName + val UNARY_! : TermName // [Eugene] this doesn't compile. why?! -// val ???: TermName - val QQQ: TermName + val ??? : TermName val MODULE_SUFFIX_NAME: TermName val NAME_JOIN_NAME: TermName -- cgit v1.2.3 From 1d0610840bb7409f0da084d3cc94e4110dd2e2c4 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 13 Apr 2012 23:15:25 +0100 Subject: Renamed seven files. Hey everyone, if classnames don't match filenames, ant will recompile said file on every run until the end of time. Still here, ant is. --- .../makro/runtime/AbortMacroException.scala | 6 + .../scala/reflect/makro/runtime/Errors.scala | 6 - src/compiler/scala/reflect/reify/Reifier.scala | 154 +++++++++++++++++++ src/compiler/scala/reflect/reify/Reifiers.scala | 154 ------------------- .../scala/concurrent/util/duration/IntMult.scala | 18 +++ .../util/duration/NumericMultiplication.scala | 18 --- src/library/scala/reflect/ArrayTag.scala | 19 +++ src/library/scala/reflect/ArrayTags.scala | 19 --- src/library/scala/reflect/ClassTag.scala | 167 +++++++++++++++++++++ src/library/scala/reflect/ClassTags.scala | 167 --------------------- src/library/scala/reflect/api/Attachment.scala | 16 ++ src/library/scala/reflect/api/Attachments.scala | 16 -- .../scala/reflect/makro/internal/Utils.scala | 133 ++++++++++++++++ .../scala/reflect/makro/internal/typeTagImpl.scala | 133 ---------------- 14 files changed, 513 insertions(+), 513 deletions(-) create mode 100644 src/compiler/scala/reflect/makro/runtime/AbortMacroException.scala delete mode 100644 src/compiler/scala/reflect/makro/runtime/Errors.scala create mode 100644 src/compiler/scala/reflect/reify/Reifier.scala delete mode 100644 src/compiler/scala/reflect/reify/Reifiers.scala create mode 100644 src/library/scala/concurrent/util/duration/IntMult.scala delete mode 100644 src/library/scala/concurrent/util/duration/NumericMultiplication.scala create mode 100644 src/library/scala/reflect/ArrayTag.scala delete mode 100644 src/library/scala/reflect/ArrayTags.scala create mode 100644 src/library/scala/reflect/ClassTag.scala delete mode 100644 src/library/scala/reflect/ClassTags.scala create mode 100644 src/library/scala/reflect/api/Attachment.scala delete mode 100644 src/library/scala/reflect/api/Attachments.scala create mode 100644 src/library/scala/reflect/makro/internal/Utils.scala delete mode 100644 src/library/scala/reflect/makro/internal/typeTagImpl.scala (limited to 'src/library') diff --git a/src/compiler/scala/reflect/makro/runtime/AbortMacroException.scala b/src/compiler/scala/reflect/makro/runtime/AbortMacroException.scala new file mode 100644 index 0000000000..d78eae9237 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/AbortMacroException.scala @@ -0,0 +1,6 @@ +package scala.reflect.makro +package runtime + +import scala.reflect.api.Position + +class AbortMacroException(val pos: Position, val msg: String) extends Throwable(msg) diff --git a/src/compiler/scala/reflect/makro/runtime/Errors.scala b/src/compiler/scala/reflect/makro/runtime/Errors.scala deleted file mode 100644 index d78eae9237..0000000000 --- a/src/compiler/scala/reflect/makro/runtime/Errors.scala +++ /dev/null @@ -1,6 +0,0 @@ -package scala.reflect.makro -package runtime - -import scala.reflect.api.Position - -class AbortMacroException(val pos: Position, val msg: String) extends Throwable(msg) diff --git a/src/compiler/scala/reflect/reify/Reifier.scala b/src/compiler/scala/reflect/reify/Reifier.scala new file mode 100644 index 0000000000..16c26734b2 --- /dev/null +++ b/src/compiler/scala/reflect/reify/Reifier.scala @@ -0,0 +1,154 @@ +package scala.reflect +package reify + +import scala.tools.nsc.Global + +/** Given a tree or a type, generate a tree that when executed at runtime produces the original tree or type. + * See more info in the comments to ``reify'' in scala.reflect.api.Universe. + * + * @author Martin Odersky + * @version 2.10 + */ +abstract class Reifier extends Phases + with Errors { + + val mirror: Global + import mirror._ + import definitions._ + import treeInfo._ + + val typer: mirror.analyzer.Typer + val prefix: Tree + val reifee: Any + val dontSpliceAtTopLevel: Boolean + val requireConcreteTypeTag: Boolean + + /** + * For ``reifee'' and other reification parameters, generate a tree of the form + * + * { + * val $mr = <[ prefix ]> + * $mr.Expr[T](rtree) // if data is a Tree + * $mr.TypeTag[T](rtree) // if data is a Type + * } + * + * where + * + * - `prefix` is the tree that represents the universe + * the result will be bound to + * - `rtree` is code that generates `reifee` at runtime. + * - `T` is the type that corresponds to `data`. + * + * This is not a method, but a value to indicate the fact that Reifier instances are a one-off. + */ + lazy val reified: Tree = { + try { + // [Eugene] conventional way of doing this? + if (prefix exists (_.isErroneous)) CannotReifyErroneousPrefix(prefix) + if (prefix.tpe == null) CannotReifyUntypedPrefix(prefix) + + val rtree = reifee match { + case tree: Tree => + reifyTrace("reifying = ")(if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) + reifyTrace("reifee is located at: ")(tree.pos) + reifyTrace("prefix = ")(prefix) + // [Eugene] conventional way of doing this? + if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(prefix) + if (tree.tpe == null) CannotReifyUntypedReifee(tree) + val pipeline = mkReificationPipeline + val rtree = pipeline(tree) + + // consider the following code snippet + // + // val x = reify { class C; new C } + // + // inferred type for x will be C + // but C ceases to exist after reification so this type is clearly incorrect + // however, reify is "just" a library function, so it cannot affect type inference + // + // hence we crash here even though the reification itself goes well + // fortunately, all that it takes to fix the error is to cast "new C" to Object + // so I'm not very much worried about introducing this restriction + if (tree.tpe exists (sub => sub.typeSymbol.isLocalToReifee)) + CannotReifyReifeeThatHasTypeLocalToReifee(tree) + + val manifestedType = typer.packedType(tree, NoSymbol) + val manifestedRtype = reifyType(manifestedType) + val tagModule = if (definitelyConcrete) ConcreteTypeTagModule else TypeTagModule + var typeTagCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType))) + var exprCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), ExprModule.name), List(TypeTree(manifestedType))) + Apply(Apply(exprCtor, List(rtree)), List(Apply(typeTagCtor, List(manifestedRtype)))) + + case tpe: Type => + reifyTrace("reifying = ")(tpe.toString) + reifyTrace("prefix = ")(prefix) + val rtree = reify(tpe) + + val manifestedType = tpe + var tagModule = if (definitelyConcrete) ConcreteTypeTagModule else TypeTagModule + var ctor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType))) + Apply(ctor, List(rtree)) + + case _ => + throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString)) + } + + val mirrorAlias = ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(prefix), prefix) + val wrapped = Block(mirrorAlias :: symbolTable, rtree) + + // todo. why do we resetAllAttrs? + // + // typically we do some preprocessing before reification and + // the code emitted/moved around during preprocessing is very hard to typecheck, so we leave it as it is + // however this "as it is" sometimes doesn't make any sense + // + // ===example 1=== + // we move a freevar from a nested symbol table to a top-level symbol table, + // and then the reference to mr$ becomes screwed up, because nested symbol tables are already typechecked, + // so we have an mr$ symbol that points to the nested mr$ rather than to the top-level one. + // + // ===example 2=== + // we inline a freevar by replacing a reference to it, e.g. $mr.Apply($mr.Select($mr.Ident($mr.newTermName("$mr")), $mr.newTermName("Ident")), List($mr.Ident($mr.newTermName("free$x")))) + // with its original binding (e.g. $mr.Ident("x")) + // we'd love to typecheck the result, but we cannot do this easily, because $mr is external to this tree + // what's even worse, sometimes $mr can point to the top-level symbol table's $mr, which doesn't have any symbol/type yet - + // it's just a ValDef that will be emitted only after the reification is completed + // + // hence, the simplest solution is to erase all attrs so that invalid (as well as non-existent) bindings get rebound correctly + // this is ugly, but it's the best we can do + // + // todo. this is a common problem with non-trivial macros in our current macro system + // needs to be solved some day + // + // list of non-hygienic transformations: + // 1) local freetype inlining in Nested + // 2) external freevar moving in Nested + // 3) local freeterm inlining in Metalevels + // 4) trivial tree splice inlining in Reify (Trees.scala) + // 5) trivial type splice inlining in Reify (Types.scala) + val freevarBindings = symbolTable collect { case freedef @ FreeDef(_, _, binding, _) => binding.symbol } toSet + val untyped = resetAllAttrs(wrapped, leaveAlone = { + case ValDef(_, mr, _, _) if mr == nme.MIRROR_SHORT => true + case tree if freevarBindings contains tree.symbol => true + case _ => false + }) + + if (reifyCopypaste) { + if (reifyDebug) println("=============================") + println(reifiedNodeToString(prefix, untyped)) + if (reifyDebug) println("=============================") + } else { + reifyTrace("reified = ")(untyped) + } + + untyped + } catch { + case ex: ReificationError => + throw ex + case ex: UnexpectedReificationError => + throw ex + case ex: Throwable => + throw new UnexpectedReificationError(defaultErrorPosition, "reification crashed", ex) + } + } +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Reifiers.scala b/src/compiler/scala/reflect/reify/Reifiers.scala deleted file mode 100644 index 16c26734b2..0000000000 --- a/src/compiler/scala/reflect/reify/Reifiers.scala +++ /dev/null @@ -1,154 +0,0 @@ -package scala.reflect -package reify - -import scala.tools.nsc.Global - -/** Given a tree or a type, generate a tree that when executed at runtime produces the original tree or type. - * See more info in the comments to ``reify'' in scala.reflect.api.Universe. - * - * @author Martin Odersky - * @version 2.10 - */ -abstract class Reifier extends Phases - with Errors { - - val mirror: Global - import mirror._ - import definitions._ - import treeInfo._ - - val typer: mirror.analyzer.Typer - val prefix: Tree - val reifee: Any - val dontSpliceAtTopLevel: Boolean - val requireConcreteTypeTag: Boolean - - /** - * For ``reifee'' and other reification parameters, generate a tree of the form - * - * { - * val $mr = <[ prefix ]> - * $mr.Expr[T](rtree) // if data is a Tree - * $mr.TypeTag[T](rtree) // if data is a Type - * } - * - * where - * - * - `prefix` is the tree that represents the universe - * the result will be bound to - * - `rtree` is code that generates `reifee` at runtime. - * - `T` is the type that corresponds to `data`. - * - * This is not a method, but a value to indicate the fact that Reifier instances are a one-off. - */ - lazy val reified: Tree = { - try { - // [Eugene] conventional way of doing this? - if (prefix exists (_.isErroneous)) CannotReifyErroneousPrefix(prefix) - if (prefix.tpe == null) CannotReifyUntypedPrefix(prefix) - - val rtree = reifee match { - case tree: Tree => - reifyTrace("reifying = ")(if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) - reifyTrace("reifee is located at: ")(tree.pos) - reifyTrace("prefix = ")(prefix) - // [Eugene] conventional way of doing this? - if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(prefix) - if (tree.tpe == null) CannotReifyUntypedReifee(tree) - val pipeline = mkReificationPipeline - val rtree = pipeline(tree) - - // consider the following code snippet - // - // val x = reify { class C; new C } - // - // inferred type for x will be C - // but C ceases to exist after reification so this type is clearly incorrect - // however, reify is "just" a library function, so it cannot affect type inference - // - // hence we crash here even though the reification itself goes well - // fortunately, all that it takes to fix the error is to cast "new C" to Object - // so I'm not very much worried about introducing this restriction - if (tree.tpe exists (sub => sub.typeSymbol.isLocalToReifee)) - CannotReifyReifeeThatHasTypeLocalToReifee(tree) - - val manifestedType = typer.packedType(tree, NoSymbol) - val manifestedRtype = reifyType(manifestedType) - val tagModule = if (definitelyConcrete) ConcreteTypeTagModule else TypeTagModule - var typeTagCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType))) - var exprCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), ExprModule.name), List(TypeTree(manifestedType))) - Apply(Apply(exprCtor, List(rtree)), List(Apply(typeTagCtor, List(manifestedRtype)))) - - case tpe: Type => - reifyTrace("reifying = ")(tpe.toString) - reifyTrace("prefix = ")(prefix) - val rtree = reify(tpe) - - val manifestedType = tpe - var tagModule = if (definitelyConcrete) ConcreteTypeTagModule else TypeTagModule - var ctor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType))) - Apply(ctor, List(rtree)) - - case _ => - throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString)) - } - - val mirrorAlias = ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(prefix), prefix) - val wrapped = Block(mirrorAlias :: symbolTable, rtree) - - // todo. why do we resetAllAttrs? - // - // typically we do some preprocessing before reification and - // the code emitted/moved around during preprocessing is very hard to typecheck, so we leave it as it is - // however this "as it is" sometimes doesn't make any sense - // - // ===example 1=== - // we move a freevar from a nested symbol table to a top-level symbol table, - // and then the reference to mr$ becomes screwed up, because nested symbol tables are already typechecked, - // so we have an mr$ symbol that points to the nested mr$ rather than to the top-level one. - // - // ===example 2=== - // we inline a freevar by replacing a reference to it, e.g. $mr.Apply($mr.Select($mr.Ident($mr.newTermName("$mr")), $mr.newTermName("Ident")), List($mr.Ident($mr.newTermName("free$x")))) - // with its original binding (e.g. $mr.Ident("x")) - // we'd love to typecheck the result, but we cannot do this easily, because $mr is external to this tree - // what's even worse, sometimes $mr can point to the top-level symbol table's $mr, which doesn't have any symbol/type yet - - // it's just a ValDef that will be emitted only after the reification is completed - // - // hence, the simplest solution is to erase all attrs so that invalid (as well as non-existent) bindings get rebound correctly - // this is ugly, but it's the best we can do - // - // todo. this is a common problem with non-trivial macros in our current macro system - // needs to be solved some day - // - // list of non-hygienic transformations: - // 1) local freetype inlining in Nested - // 2) external freevar moving in Nested - // 3) local freeterm inlining in Metalevels - // 4) trivial tree splice inlining in Reify (Trees.scala) - // 5) trivial type splice inlining in Reify (Types.scala) - val freevarBindings = symbolTable collect { case freedef @ FreeDef(_, _, binding, _) => binding.symbol } toSet - val untyped = resetAllAttrs(wrapped, leaveAlone = { - case ValDef(_, mr, _, _) if mr == nme.MIRROR_SHORT => true - case tree if freevarBindings contains tree.symbol => true - case _ => false - }) - - if (reifyCopypaste) { - if (reifyDebug) println("=============================") - println(reifiedNodeToString(prefix, untyped)) - if (reifyDebug) println("=============================") - } else { - reifyTrace("reified = ")(untyped) - } - - untyped - } catch { - case ex: ReificationError => - throw ex - case ex: UnexpectedReificationError => - throw ex - case ex: Throwable => - throw new UnexpectedReificationError(defaultErrorPosition, "reification crashed", ex) - } - } -} \ No newline at end of file diff --git a/src/library/scala/concurrent/util/duration/IntMult.scala b/src/library/scala/concurrent/util/duration/IntMult.scala new file mode 100644 index 0000000000..94c58fb8c2 --- /dev/null +++ b/src/library/scala/concurrent/util/duration/IntMult.scala @@ -0,0 +1,18 @@ +package scala.concurrent.util.duration + +import scala.concurrent.util.{ Duration } + +/* + * Avoid reflection based invocation by using non-duck type + */ +protected[duration] class IntMult(i: Int) { + def *(d: Duration) = d * i +} + +protected[duration] class LongMult(i: Long) { + def *(d: Duration) = d * i +} + +protected[duration] class DoubleMult(f: Double) { + def *(d: Duration) = d * f +} diff --git a/src/library/scala/concurrent/util/duration/NumericMultiplication.scala b/src/library/scala/concurrent/util/duration/NumericMultiplication.scala deleted file mode 100644 index 94c58fb8c2..0000000000 --- a/src/library/scala/concurrent/util/duration/NumericMultiplication.scala +++ /dev/null @@ -1,18 +0,0 @@ -package scala.concurrent.util.duration - -import scala.concurrent.util.{ Duration } - -/* - * Avoid reflection based invocation by using non-duck type - */ -protected[duration] class IntMult(i: Int) { - def *(d: Duration) = d * i -} - -protected[duration] class LongMult(i: Long) { - def *(d: Duration) = d * i -} - -protected[duration] class DoubleMult(f: Double) { - def *(d: Duration) = d * f -} diff --git a/src/library/scala/reflect/ArrayTag.scala b/src/library/scala/reflect/ArrayTag.scala new file mode 100644 index 0000000000..8df7fe5f4e --- /dev/null +++ b/src/library/scala/reflect/ArrayTag.scala @@ -0,0 +1,19 @@ +package scala.reflect + +/** An `ArrayTag[T]` is a descriptor that is requested by the compiler every time + * when an array is instantiated, but the element type is unknown at compile time. + * + * Scala library provides a standard implementation of this trait, + * `ClassTag[T]` that explicitly carries the `java.lang.Class` erasure of type T. + * + * However other platforms (e.g. a Scala -> JS crosscompiler) may reimplement this trait as they see fit + * and then expose the implementation via an implicit macro. + */ +@annotation.implicitNotFound(msg = "No ArrayTag available for ${T}") +trait ArrayTag[T] { + /** Produces an `ArrayTag` that knows how to build `Array[Array[T]]` */ + def wrap: ArrayTag[Array[T]] + + /** Produces a new array with element type `T` and length `len` */ + def newArray(len: Int): Array[T] +} \ No newline at end of file diff --git a/src/library/scala/reflect/ArrayTags.scala b/src/library/scala/reflect/ArrayTags.scala deleted file mode 100644 index 8df7fe5f4e..0000000000 --- a/src/library/scala/reflect/ArrayTags.scala +++ /dev/null @@ -1,19 +0,0 @@ -package scala.reflect - -/** An `ArrayTag[T]` is a descriptor that is requested by the compiler every time - * when an array is instantiated, but the element type is unknown at compile time. - * - * Scala library provides a standard implementation of this trait, - * `ClassTag[T]` that explicitly carries the `java.lang.Class` erasure of type T. - * - * However other platforms (e.g. a Scala -> JS crosscompiler) may reimplement this trait as they see fit - * and then expose the implementation via an implicit macro. - */ -@annotation.implicitNotFound(msg = "No ArrayTag available for ${T}") -trait ArrayTag[T] { - /** Produces an `ArrayTag` that knows how to build `Array[Array[T]]` */ - def wrap: ArrayTag[Array[T]] - - /** Produces a new array with element type `T` and length `len` */ - def newArray(len: Int): Array[T] -} \ No newline at end of file diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala new file mode 100644 index 0000000000..cde6da5539 --- /dev/null +++ b/src/library/scala/reflect/ClassTag.scala @@ -0,0 +1,167 @@ +package scala.reflect + +import java.lang.{ Class => jClass } +import scala.reflect.{ mirror => rm } + +/** A `ClassTag[T]` wraps a Java class, which can be accessed via the `erasure` method. + * + * This is useful in itself, but also enables very important use case. + * Having this knowledge ClassTag can instantiate `Arrays` + * in those cases where the element type is unknown at compile time. + * Hence, ClassTag[T] conforms to the ArrayTag[T] trait. + * + * If an implicit value of type u.ClassTag[T] is required, the compiler will make one up on demand. + * The implicitly created value contains in its erasure field the Java class that is the result of erasing type T. + * In that value, any occurrences of type parameters or abstract types U which come themselves with a ClassTag + * or a reflect.mirror.ConcreteTypeTag are represented by the type referenced by that tag. + * If the type T contains unresolved references to type parameters or abstract types, a static error results. + * + * A ConcreteTypeTag member of the reflect.mirror object is convertible to a ClassTag via an implicit conversion + * (this is not possible to do in all reflection universes because an operation that converts a type to a Java class might not be available). */ +// please, don't add any APIs here, like it was with `newWrappedArray` and `newArrayBuilder` +// class tags, and all tags in general, should be as minimalistic as possible +@annotation.implicitNotFound(msg = "No ClassTag available for ${T}") +abstract case class ClassTag[T](erasure: jClass[_]) extends ArrayTag[T] { + // quick and dirty fix to a deadlock in Predef: + // http://groups.google.com/group/scala-internals/browse_thread/thread/977de028a4e75d6f + // todo. fix that in a sane way + // assert(erasure != null) + + /** A Scala reflection type representing T. + * For ClassTags this representation is lossy (in their case tpe is retrospectively constructed from erasure). + * For TypeTags and ConcreteTypeTags the representation is almost precise, because they use reification + * (information is lost only when T refers to non-locatable symbols, which are then reified as free variables). */ + def tpe: rm.Type = rm.classToType(erasure) + + /** A Scala reflection symbol representing T. */ + def symbol: rm.Symbol = rm.classToSymbol(erasure) + + /** Produces a `ClassTag` that knows how to build `Array[Array[T]]` */ + def wrap: ClassTag[Array[T]] = { + val arrayClazz = java.lang.reflect.Array.newInstance(erasure, 0).getClass.asInstanceOf[jClass[Array[T]]] + ClassTag[Array[T]](arrayClazz) + } + + /** Produces a new array with element type `T` and length `len` */ + def newArray(len: Int): Array[T] = + erasure match { + case java.lang.Byte.TYPE => new Array[Byte](len).asInstanceOf[Array[T]] + case java.lang.Short.TYPE => new Array[Short](len).asInstanceOf[Array[T]] + case java.lang.Character.TYPE => new Array[Char](len).asInstanceOf[Array[T]] + case java.lang.Integer.TYPE => new Array[Int](len).asInstanceOf[Array[T]] + case java.lang.Long.TYPE => new Array[Long](len).asInstanceOf[Array[T]] + case java.lang.Float.TYPE => new Array[Float](len).asInstanceOf[Array[T]] + case java.lang.Double.TYPE => new Array[Double](len).asInstanceOf[Array[T]] + case java.lang.Boolean.TYPE => new Array[Boolean](len).asInstanceOf[Array[T]] + case java.lang.Void.TYPE => new Array[Unit](len).asInstanceOf[Array[T]] + case _ => java.lang.reflect.Array.newInstance(erasure, len).asInstanceOf[Array[T]] + } +} + +object ClassTag { + private val ObjectTYPE = classOf[java.lang.Object] + private val StringTYPE = classOf[java.lang.String] + + val Byte : ClassTag[scala.Byte] = new ClassTag[scala.Byte](java.lang.Byte.TYPE) { private def readResolve() = ClassTag.Byte } + val Short : ClassTag[scala.Short] = new ClassTag[scala.Short](java.lang.Short.TYPE) { private def readResolve() = ClassTag.Short } + val Char : ClassTag[scala.Char] = new ClassTag[scala.Char](java.lang.Character.TYPE) { private def readResolve() = ClassTag.Char } + val Int : ClassTag[scala.Int] = new ClassTag[scala.Int](java.lang.Integer.TYPE) { private def readResolve() = ClassTag.Int } + val Long : ClassTag[scala.Long] = new ClassTag[scala.Long](java.lang.Long.TYPE) { private def readResolve() = ClassTag.Long } + val Float : ClassTag[scala.Float] = new ClassTag[scala.Float](java.lang.Float.TYPE) { private def readResolve() = ClassTag.Float } + val Double : ClassTag[scala.Double] = new ClassTag[scala.Double](java.lang.Double.TYPE) { private def readResolve() = ClassTag.Double } + val Boolean : ClassTag[scala.Boolean] = new ClassTag[scala.Boolean](java.lang.Boolean.TYPE) { private def readResolve() = ClassTag.Boolean } + val Unit : ClassTag[scala.Unit] = new ClassTag[scala.Unit](java.lang.Void.TYPE) { private def readResolve() = ClassTag.Unit } + val Any : ClassTag[scala.Any] = new ClassTag[scala.Any](ObjectTYPE) { private def readResolve() = ClassTag.Any } + val Object : ClassTag[java.lang.Object] = new ClassTag[java.lang.Object](ObjectTYPE) { private def readResolve() = ClassTag.Object } + val AnyVal : ClassTag[scala.AnyVal] = new ClassTag[scala.AnyVal](ObjectTYPE) { private def readResolve() = ClassTag.AnyVal } + val AnyRef : ClassTag[scala.AnyRef] = new ClassTag[scala.AnyRef](ObjectTYPE) { private def readResolve() = ClassTag.AnyRef } + val Nothing : ClassTag[scala.Nothing] = new ClassTag[scala.Nothing](ObjectTYPE) { private def readResolve() = ClassTag.Nothing } + val Null : ClassTag[scala.Null] = new ClassTag[scala.Null](ObjectTYPE) { private def readResolve() = ClassTag.Null } + val String : ClassTag[java.lang.String] = new ClassTag[java.lang.String](StringTYPE) { private def readResolve() = ClassTag.String } + + def apply[T](clazz: jClass[_]): ClassTag[T] = + clazz match { + case java.lang.Byte.TYPE => ClassTag.Byte.asInstanceOf[ClassTag[T]] + case java.lang.Short.TYPE => ClassTag.Short.asInstanceOf[ClassTag[T]] + case java.lang.Character.TYPE => ClassTag.Char.asInstanceOf[ClassTag[T]] + case java.lang.Integer.TYPE => ClassTag.Int.asInstanceOf[ClassTag[T]] + case java.lang.Long.TYPE => ClassTag.Long.asInstanceOf[ClassTag[T]] + case java.lang.Float.TYPE => ClassTag.Float.asInstanceOf[ClassTag[T]] + case java.lang.Double.TYPE => ClassTag.Double.asInstanceOf[ClassTag[T]] + case java.lang.Boolean.TYPE => ClassTag.Boolean.asInstanceOf[ClassTag[T]] + case java.lang.Void.TYPE => ClassTag.Unit.asInstanceOf[ClassTag[T]] + case ObjectTYPE => ClassTag.Object.asInstanceOf[ClassTag[T]] + case StringTYPE => ClassTag.String.asInstanceOf[ClassTag[T]] + case _ => new ClassTag[T](clazz) {} + } + + def apply[T](tpe: rm.Type): ClassTag[T] = + tpe match { + case rm.ByteTpe => ClassTag.Byte.asInstanceOf[ClassTag[T]] + case rm.ShortTpe => ClassTag.Short.asInstanceOf[ClassTag[T]] + case rm.CharTpe => ClassTag.Char.asInstanceOf[ClassTag[T]] + case rm.IntTpe => ClassTag.Int.asInstanceOf[ClassTag[T]] + case rm.LongTpe => ClassTag.Long.asInstanceOf[ClassTag[T]] + case rm.FloatTpe => ClassTag.Float.asInstanceOf[ClassTag[T]] + case rm.DoubleTpe => ClassTag.Double.asInstanceOf[ClassTag[T]] + case rm.BooleanTpe => ClassTag.Boolean.asInstanceOf[ClassTag[T]] + case rm.UnitTpe => ClassTag.Unit.asInstanceOf[ClassTag[T]] + case rm.AnyTpe => ClassTag.Any.asInstanceOf[ClassTag[T]] + case rm.ObjectTpe => ClassTag.Object.asInstanceOf[ClassTag[T]] + case rm.AnyValTpe => ClassTag.AnyVal.asInstanceOf[ClassTag[T]] + case rm.AnyRefTpe => ClassTag.AnyRef.asInstanceOf[ClassTag[T]] + case rm.NothingTpe => ClassTag.Nothing.asInstanceOf[ClassTag[T]] + case rm.NullTpe => ClassTag.Null.asInstanceOf[ClassTag[T]] + case rm.StringTpe => ClassTag.String.asInstanceOf[ClassTag[T]] + case _ => apply[T](rm.typeToClass(tpe.erasure)) + } + + implicit def toDeprecatedClassManifestApis[T](ctag: ClassTag[T]): DeprecatedClassManifestApis[T] = new DeprecatedClassManifestApis[T](ctag) +} + +// this class should not be used directly in client code +class DeprecatedClassManifestApis[T](ctag: ClassTag[T]) { + import scala.collection.mutable.{ WrappedArray, ArrayBuilder } + + @deprecated("Use `tpe` to analyze the underlying type", "2.10.0") + def <:<(that: ClassManifest[_]): Boolean = ctag.tpe <:< that.tpe + + @deprecated("Use `tpe` to analyze the underlying type", "2.10.0") + def >:>(that: ClassManifest[_]): Boolean = that <:< ctag + + @deprecated("Use `wrap` instead", "2.10.0") + def arrayManifest: ClassManifest[Array[T]] = ctag.wrap + + @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") + def newArray2(len: Int): Array[Array[T]] = ctag.wrap.newArray(len) + + @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") + def newArray3(len: Int): Array[Array[Array[T]]] = ctag.wrap.wrap.newArray(len) + + @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") + def newArray4(len: Int): Array[Array[Array[Array[T]]]] = ctag.wrap.wrap.wrap.newArray(len) + + @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") + def newArray5(len: Int): Array[Array[Array[Array[Array[T]]]]] = ctag.wrap.wrap.wrap.wrap.newArray(len) + + @deprecated("Use `@scala.collection.mutable.WrappedArray` object instead", "2.10.0") + def newWrappedArray(len: Int): WrappedArray[T] = + ctag.erasure match { + case java.lang.Byte.TYPE => new WrappedArray.ofByte(new Array[Byte](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Short.TYPE => new WrappedArray.ofShort(new Array[Short](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Character.TYPE => new WrappedArray.ofChar(new Array[Char](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Integer.TYPE => new WrappedArray.ofInt(new Array[Int](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Long.TYPE => new WrappedArray.ofLong(new Array[Long](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Float.TYPE => new WrappedArray.ofFloat(new Array[Float](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Double.TYPE => new WrappedArray.ofDouble(new Array[Double](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Boolean.TYPE => new WrappedArray.ofBoolean(new Array[Boolean](len)).asInstanceOf[WrappedArray[T]] + case java.lang.Void.TYPE => new WrappedArray.ofUnit(new Array[Unit](len)).asInstanceOf[WrappedArray[T]] + case _ => new WrappedArray.ofRef[T with AnyRef](ctag.newArray(len).asInstanceOf[Array[T with AnyRef]]).asInstanceOf[WrappedArray[T]] + } + + @deprecated("Use `@scala.collection.mutable.ArrayBuilder` object instead", "2.10.0") + def newArrayBuilder(): ArrayBuilder[T] = ArrayBuilder.make[T]()(ctag) + + @deprecated("`typeArguments` is no longer supported, and will always return an empty list. Use `@scala.reflect.TypeTag` or `@scala.reflect.ConcreteTypeTag` to capture and analyze type arguments", "2.10.0") + def typeArguments: List[OptManifest[_]] = List() +} \ No newline at end of file diff --git a/src/library/scala/reflect/ClassTags.scala b/src/library/scala/reflect/ClassTags.scala deleted file mode 100644 index cde6da5539..0000000000 --- a/src/library/scala/reflect/ClassTags.scala +++ /dev/null @@ -1,167 +0,0 @@ -package scala.reflect - -import java.lang.{ Class => jClass } -import scala.reflect.{ mirror => rm } - -/** A `ClassTag[T]` wraps a Java class, which can be accessed via the `erasure` method. - * - * This is useful in itself, but also enables very important use case. - * Having this knowledge ClassTag can instantiate `Arrays` - * in those cases where the element type is unknown at compile time. - * Hence, ClassTag[T] conforms to the ArrayTag[T] trait. - * - * If an implicit value of type u.ClassTag[T] is required, the compiler will make one up on demand. - * The implicitly created value contains in its erasure field the Java class that is the result of erasing type T. - * In that value, any occurrences of type parameters or abstract types U which come themselves with a ClassTag - * or a reflect.mirror.ConcreteTypeTag are represented by the type referenced by that tag. - * If the type T contains unresolved references to type parameters or abstract types, a static error results. - * - * A ConcreteTypeTag member of the reflect.mirror object is convertible to a ClassTag via an implicit conversion - * (this is not possible to do in all reflection universes because an operation that converts a type to a Java class might not be available). */ -// please, don't add any APIs here, like it was with `newWrappedArray` and `newArrayBuilder` -// class tags, and all tags in general, should be as minimalistic as possible -@annotation.implicitNotFound(msg = "No ClassTag available for ${T}") -abstract case class ClassTag[T](erasure: jClass[_]) extends ArrayTag[T] { - // quick and dirty fix to a deadlock in Predef: - // http://groups.google.com/group/scala-internals/browse_thread/thread/977de028a4e75d6f - // todo. fix that in a sane way - // assert(erasure != null) - - /** A Scala reflection type representing T. - * For ClassTags this representation is lossy (in their case tpe is retrospectively constructed from erasure). - * For TypeTags and ConcreteTypeTags the representation is almost precise, because they use reification - * (information is lost only when T refers to non-locatable symbols, which are then reified as free variables). */ - def tpe: rm.Type = rm.classToType(erasure) - - /** A Scala reflection symbol representing T. */ - def symbol: rm.Symbol = rm.classToSymbol(erasure) - - /** Produces a `ClassTag` that knows how to build `Array[Array[T]]` */ - def wrap: ClassTag[Array[T]] = { - val arrayClazz = java.lang.reflect.Array.newInstance(erasure, 0).getClass.asInstanceOf[jClass[Array[T]]] - ClassTag[Array[T]](arrayClazz) - } - - /** Produces a new array with element type `T` and length `len` */ - def newArray(len: Int): Array[T] = - erasure match { - case java.lang.Byte.TYPE => new Array[Byte](len).asInstanceOf[Array[T]] - case java.lang.Short.TYPE => new Array[Short](len).asInstanceOf[Array[T]] - case java.lang.Character.TYPE => new Array[Char](len).asInstanceOf[Array[T]] - case java.lang.Integer.TYPE => new Array[Int](len).asInstanceOf[Array[T]] - case java.lang.Long.TYPE => new Array[Long](len).asInstanceOf[Array[T]] - case java.lang.Float.TYPE => new Array[Float](len).asInstanceOf[Array[T]] - case java.lang.Double.TYPE => new Array[Double](len).asInstanceOf[Array[T]] - case java.lang.Boolean.TYPE => new Array[Boolean](len).asInstanceOf[Array[T]] - case java.lang.Void.TYPE => new Array[Unit](len).asInstanceOf[Array[T]] - case _ => java.lang.reflect.Array.newInstance(erasure, len).asInstanceOf[Array[T]] - } -} - -object ClassTag { - private val ObjectTYPE = classOf[java.lang.Object] - private val StringTYPE = classOf[java.lang.String] - - val Byte : ClassTag[scala.Byte] = new ClassTag[scala.Byte](java.lang.Byte.TYPE) { private def readResolve() = ClassTag.Byte } - val Short : ClassTag[scala.Short] = new ClassTag[scala.Short](java.lang.Short.TYPE) { private def readResolve() = ClassTag.Short } - val Char : ClassTag[scala.Char] = new ClassTag[scala.Char](java.lang.Character.TYPE) { private def readResolve() = ClassTag.Char } - val Int : ClassTag[scala.Int] = new ClassTag[scala.Int](java.lang.Integer.TYPE) { private def readResolve() = ClassTag.Int } - val Long : ClassTag[scala.Long] = new ClassTag[scala.Long](java.lang.Long.TYPE) { private def readResolve() = ClassTag.Long } - val Float : ClassTag[scala.Float] = new ClassTag[scala.Float](java.lang.Float.TYPE) { private def readResolve() = ClassTag.Float } - val Double : ClassTag[scala.Double] = new ClassTag[scala.Double](java.lang.Double.TYPE) { private def readResolve() = ClassTag.Double } - val Boolean : ClassTag[scala.Boolean] = new ClassTag[scala.Boolean](java.lang.Boolean.TYPE) { private def readResolve() = ClassTag.Boolean } - val Unit : ClassTag[scala.Unit] = new ClassTag[scala.Unit](java.lang.Void.TYPE) { private def readResolve() = ClassTag.Unit } - val Any : ClassTag[scala.Any] = new ClassTag[scala.Any](ObjectTYPE) { private def readResolve() = ClassTag.Any } - val Object : ClassTag[java.lang.Object] = new ClassTag[java.lang.Object](ObjectTYPE) { private def readResolve() = ClassTag.Object } - val AnyVal : ClassTag[scala.AnyVal] = new ClassTag[scala.AnyVal](ObjectTYPE) { private def readResolve() = ClassTag.AnyVal } - val AnyRef : ClassTag[scala.AnyRef] = new ClassTag[scala.AnyRef](ObjectTYPE) { private def readResolve() = ClassTag.AnyRef } - val Nothing : ClassTag[scala.Nothing] = new ClassTag[scala.Nothing](ObjectTYPE) { private def readResolve() = ClassTag.Nothing } - val Null : ClassTag[scala.Null] = new ClassTag[scala.Null](ObjectTYPE) { private def readResolve() = ClassTag.Null } - val String : ClassTag[java.lang.String] = new ClassTag[java.lang.String](StringTYPE) { private def readResolve() = ClassTag.String } - - def apply[T](clazz: jClass[_]): ClassTag[T] = - clazz match { - case java.lang.Byte.TYPE => ClassTag.Byte.asInstanceOf[ClassTag[T]] - case java.lang.Short.TYPE => ClassTag.Short.asInstanceOf[ClassTag[T]] - case java.lang.Character.TYPE => ClassTag.Char.asInstanceOf[ClassTag[T]] - case java.lang.Integer.TYPE => ClassTag.Int.asInstanceOf[ClassTag[T]] - case java.lang.Long.TYPE => ClassTag.Long.asInstanceOf[ClassTag[T]] - case java.lang.Float.TYPE => ClassTag.Float.asInstanceOf[ClassTag[T]] - case java.lang.Double.TYPE => ClassTag.Double.asInstanceOf[ClassTag[T]] - case java.lang.Boolean.TYPE => ClassTag.Boolean.asInstanceOf[ClassTag[T]] - case java.lang.Void.TYPE => ClassTag.Unit.asInstanceOf[ClassTag[T]] - case ObjectTYPE => ClassTag.Object.asInstanceOf[ClassTag[T]] - case StringTYPE => ClassTag.String.asInstanceOf[ClassTag[T]] - case _ => new ClassTag[T](clazz) {} - } - - def apply[T](tpe: rm.Type): ClassTag[T] = - tpe match { - case rm.ByteTpe => ClassTag.Byte.asInstanceOf[ClassTag[T]] - case rm.ShortTpe => ClassTag.Short.asInstanceOf[ClassTag[T]] - case rm.CharTpe => ClassTag.Char.asInstanceOf[ClassTag[T]] - case rm.IntTpe => ClassTag.Int.asInstanceOf[ClassTag[T]] - case rm.LongTpe => ClassTag.Long.asInstanceOf[ClassTag[T]] - case rm.FloatTpe => ClassTag.Float.asInstanceOf[ClassTag[T]] - case rm.DoubleTpe => ClassTag.Double.asInstanceOf[ClassTag[T]] - case rm.BooleanTpe => ClassTag.Boolean.asInstanceOf[ClassTag[T]] - case rm.UnitTpe => ClassTag.Unit.asInstanceOf[ClassTag[T]] - case rm.AnyTpe => ClassTag.Any.asInstanceOf[ClassTag[T]] - case rm.ObjectTpe => ClassTag.Object.asInstanceOf[ClassTag[T]] - case rm.AnyValTpe => ClassTag.AnyVal.asInstanceOf[ClassTag[T]] - case rm.AnyRefTpe => ClassTag.AnyRef.asInstanceOf[ClassTag[T]] - case rm.NothingTpe => ClassTag.Nothing.asInstanceOf[ClassTag[T]] - case rm.NullTpe => ClassTag.Null.asInstanceOf[ClassTag[T]] - case rm.StringTpe => ClassTag.String.asInstanceOf[ClassTag[T]] - case _ => apply[T](rm.typeToClass(tpe.erasure)) - } - - implicit def toDeprecatedClassManifestApis[T](ctag: ClassTag[T]): DeprecatedClassManifestApis[T] = new DeprecatedClassManifestApis[T](ctag) -} - -// this class should not be used directly in client code -class DeprecatedClassManifestApis[T](ctag: ClassTag[T]) { - import scala.collection.mutable.{ WrappedArray, ArrayBuilder } - - @deprecated("Use `tpe` to analyze the underlying type", "2.10.0") - def <:<(that: ClassManifest[_]): Boolean = ctag.tpe <:< that.tpe - - @deprecated("Use `tpe` to analyze the underlying type", "2.10.0") - def >:>(that: ClassManifest[_]): Boolean = that <:< ctag - - @deprecated("Use `wrap` instead", "2.10.0") - def arrayManifest: ClassManifest[Array[T]] = ctag.wrap - - @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") - def newArray2(len: Int): Array[Array[T]] = ctag.wrap.newArray(len) - - @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") - def newArray3(len: Int): Array[Array[Array[T]]] = ctag.wrap.wrap.newArray(len) - - @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") - def newArray4(len: Int): Array[Array[Array[Array[T]]]] = ctag.wrap.wrap.wrap.newArray(len) - - @deprecated("Use a combination of `wrap` and `newArray` instead", "2.10.0") - def newArray5(len: Int): Array[Array[Array[Array[Array[T]]]]] = ctag.wrap.wrap.wrap.wrap.newArray(len) - - @deprecated("Use `@scala.collection.mutable.WrappedArray` object instead", "2.10.0") - def newWrappedArray(len: Int): WrappedArray[T] = - ctag.erasure match { - case java.lang.Byte.TYPE => new WrappedArray.ofByte(new Array[Byte](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Short.TYPE => new WrappedArray.ofShort(new Array[Short](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Character.TYPE => new WrappedArray.ofChar(new Array[Char](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Integer.TYPE => new WrappedArray.ofInt(new Array[Int](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Long.TYPE => new WrappedArray.ofLong(new Array[Long](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Float.TYPE => new WrappedArray.ofFloat(new Array[Float](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Double.TYPE => new WrappedArray.ofDouble(new Array[Double](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Boolean.TYPE => new WrappedArray.ofBoolean(new Array[Boolean](len)).asInstanceOf[WrappedArray[T]] - case java.lang.Void.TYPE => new WrappedArray.ofUnit(new Array[Unit](len)).asInstanceOf[WrappedArray[T]] - case _ => new WrappedArray.ofRef[T with AnyRef](ctag.newArray(len).asInstanceOf[Array[T with AnyRef]]).asInstanceOf[WrappedArray[T]] - } - - @deprecated("Use `@scala.collection.mutable.ArrayBuilder` object instead", "2.10.0") - def newArrayBuilder(): ArrayBuilder[T] = ArrayBuilder.make[T]()(ctag) - - @deprecated("`typeArguments` is no longer supported, and will always return an empty list. Use `@scala.reflect.TypeTag` or `@scala.reflect.ConcreteTypeTag` to capture and analyze type arguments", "2.10.0") - def typeArguments: List[OptManifest[_]] = List() -} \ No newline at end of file diff --git a/src/library/scala/reflect/api/Attachment.scala b/src/library/scala/reflect/api/Attachment.scala new file mode 100644 index 0000000000..dfd362ebe0 --- /dev/null +++ b/src/library/scala/reflect/api/Attachment.scala @@ -0,0 +1,16 @@ +package scala.reflect +package api + +/** Attachment is a generalisation of Position. + * Typically it stores a Position of a tree, but this can be extended to encompass arbitrary payloads. + * + * Attachments have to carry positions, because we don't want to introduce even a single additional field in Tree + * imposing an unnecessary memory tax because of something that will not be used in most cases. + */ +trait Attachment { + /** Gets the underlying position */ + def pos: Position + + /** Creates a copy of this attachment with its position updated */ + def withPos(pos: Position): Attachment +} diff --git a/src/library/scala/reflect/api/Attachments.scala b/src/library/scala/reflect/api/Attachments.scala deleted file mode 100644 index dfd362ebe0..0000000000 --- a/src/library/scala/reflect/api/Attachments.scala +++ /dev/null @@ -1,16 +0,0 @@ -package scala.reflect -package api - -/** Attachment is a generalisation of Position. - * Typically it stores a Position of a tree, but this can be extended to encompass arbitrary payloads. - * - * Attachments have to carry positions, because we don't want to introduce even a single additional field in Tree - * imposing an unnecessary memory tax because of something that will not be used in most cases. - */ -trait Attachment { - /** Gets the underlying position */ - def pos: Position - - /** Creates a copy of this attachment with its position updated */ - def withPos(pos: Position): Attachment -} diff --git a/src/library/scala/reflect/makro/internal/Utils.scala b/src/library/scala/reflect/makro/internal/Utils.scala new file mode 100644 index 0000000000..de404ff39f --- /dev/null +++ b/src/library/scala/reflect/makro/internal/Utils.scala @@ -0,0 +1,133 @@ +package scala.reflect.makro + +import scala.reflect.api.Universe + +/** This package is required by the compiler and should not be used in client code. */ +package object internal { + /** This method is required by the compiler and should not be used in client code. */ + def materializeClassTag[T](u: Universe): ClassTag[T] = macro materializeClassTag_impl[T] + + /** This method is required by the compiler and should not be used in client code. */ + def materializeClassTag_impl[T: c.TypeTag](c: Context)(u: c.Expr[Universe]): c.Expr[ClassTag[T]] = + c.Expr[Nothing](c.materializeClassTag(u.tree, implicitly[c.TypeTag[T]].tpe))(c.TypeTag.Nothing) + + /** This method is required by the compiler and should not be used in client code. */ + def materializeTypeTag[T](u: Universe): u.TypeTag[T] = macro materializeTypeTag_impl[T] + + /** This method is required by the compiler and should not be used in client code. */ + def materializeTypeTag_impl[T: c.TypeTag](c: Context)(u: c.Expr[Universe]): c.Expr[u.value.TypeTag[T]] = + c.Expr[Nothing](c.materializeTypeTag(u.tree, implicitly[c.TypeTag[T]].tpe, requireConcreteTypeTag = false))(c.TypeTag.Nothing) + + /** This method is required by the compiler and should not be used in client code. */ + def materializeConcreteTypeTag[T](u: Universe): u.ConcreteTypeTag[T] = macro materializeConcreteTypeTag_impl[T] + + /** This method is required by the compiler and should not be used in client code. */ + def materializeConcreteTypeTag_impl[T: c.TypeTag](c: Context)(u: c.Expr[Universe]): c.Expr[u.value.ConcreteTypeTag[T]] = + c.Expr[Nothing](c.materializeTypeTag(u.tree, implicitly[c.TypeTag[T]].tpe, requireConcreteTypeTag = true))(c.TypeTag.Nothing) + + /** This method is required by the compiler and should not be used in client code. */ + private[scala] implicit def context2utils(c0: Context) : Utils { val c: c0.type } = new { val c: c0.type = c0 } with Utils +} + +package internal { + private[scala] abstract class Utils { + val c: Context + + import c.mirror._ + import definitions._ + + val coreTags = Map( + ByteClass.asType -> newTermName("Byte"), + ShortClass.asType -> newTermName("Short"), + CharClass.asType -> newTermName("Char"), + IntClass.asType -> newTermName("Int"), + LongClass.asType -> newTermName("Long"), + FloatClass.asType -> newTermName("Float"), + DoubleClass.asType -> newTermName("Double"), + BooleanClass.asType -> newTermName("Boolean"), + UnitClass.asType -> newTermName("Unit"), + AnyClass.asType -> newTermName("Any"), + ObjectClass.asType -> newTermName("Object"), + AnyValClass.asType -> newTermName("AnyVal"), + AnyRefClass.asType -> newTermName("AnyRef"), + NothingClass.asType -> newTermName("Nothing"), + NullClass.asType -> newTermName("Null")) + + def materializeClassTag(prefix: Tree, tpe: Type): Tree = { + val typetagInScope = c.inferImplicitValue(appliedType(typeRef(prefix.tpe, ConcreteTypeTagClass, Nil), List(tpe))) + def typetagIsSynthetic(tree: Tree) = tree.isInstanceOf[Block] || (tree exists (sub => sub.symbol == TypeTagModule || sub.symbol == ConcreteTypeTagModule)) + typetagInScope match { + case success if !success.isEmpty && !typetagIsSynthetic(success) => + val factory = TypeApply(Select(Ident(ClassTagModule), newTermName("apply")), List(TypeTree(tpe))) + Apply(factory, List(Select(typetagInScope, newTermName("tpe")))) + case _ => + val result = + tpe match { + case coreTpe if coreTags contains coreTpe => + Select(Ident(ClassTagModule), coreTags(coreTpe)) + case _ => + if (tpe.typeSymbol == ArrayClass) { + val componentTpe = tpe.typeArguments(0) + val classtagInScope = c.inferImplicitValue(appliedType(typeRef(NoPrefix, ClassTagClass, Nil), List(componentTpe))) + val componentTag = classtagInScope orElse materializeClassTag(prefix, componentTpe) + Select(componentTag, newTermName("wrap")) + } else { + // [Eugene] what's the intended behavior? there's no spec on ClassManifests + // for example, should we ban Array[T] or should we tag them with Array[AnyRef]? + // if its the latter, what should be the result of tagging Array[T] where T <: Int? + if (tpe.typeSymbol.isAbstractType) fail("tpe is an abstract type") + val erasure = + if (tpe.typeSymbol.isDerivedValueClass) tpe // [Eugene to Martin] is this correct? + else tpe.erasure.normalize // necessary to deal with erasures of HK types + val factory = TypeApply(Select(Ident(ClassTagModule), newTermName("apply")), List(TypeTree(tpe))) + Apply(factory, List(TypeApply(Ident(newTermName("classOf")), List(TypeTree(erasure))))) + } + } + try c.typeCheck(result) + catch { case terr @ c.TypeError(pos, msg) => fail(terr) } + } + } + + def materializeTypeTag(prefix: Tree, tpe: Type, requireConcreteTypeTag: Boolean): Tree = { + val tagModule = if (requireConcreteTypeTag) ConcreteTypeTagModule else TypeTagModule + val result = + tpe match { + case coreTpe if coreTags contains coreTpe => + Select(Select(prefix, tagModule.name), coreTags(coreTpe)) + case _ => + try c.reifyType(prefix, tpe, dontSpliceAtTopLevel = true, requireConcreteTypeTag = requireConcreteTypeTag) + catch { + case ex: Throwable => + // [Eugene] cannot pattern match on an abstract type, so had to do this + val ex1 = ex + if (ex.getClass.toString.endsWith("$ReificationError")) { + ex match { + case c.ReificationError(pos, msg) => + c.error(pos, msg) + EmptyTree + } + } else if (ex.getClass.toString.endsWith("$UnexpectedReificationError")) { + ex match { + case c.UnexpectedReificationError(pos, err, cause) => + if (cause != null) throw cause else throw ex + } + } else { + throw ex + } + } + } + try c.typeCheck(result) + catch { case terr @ c.TypeError(pos, msg) => fail(terr) } + } + + private def fail(reason: Any): Nothing = { + val Apply(TypeApply(fun, List(tpeTree)), _) = c.macroApplication + val tpe = tpeTree.tpe + val PolyType(_, MethodType(_, tagTpe)) = fun.tpe + val tagModule = tagTpe.typeSymbol.companionSymbol + if (c.compilerSettings.contains("-Xlog-implicits")) + c.echo(c.enclosingPosition, "cannot materialize " + tagModule.name + "[" + tpe + "] because:\n" + reason) + c.abort(c.enclosingPosition, "No %s available for %s".format(tagModule.name, tpe)) + } + } +} diff --git a/src/library/scala/reflect/makro/internal/typeTagImpl.scala b/src/library/scala/reflect/makro/internal/typeTagImpl.scala deleted file mode 100644 index de404ff39f..0000000000 --- a/src/library/scala/reflect/makro/internal/typeTagImpl.scala +++ /dev/null @@ -1,133 +0,0 @@ -package scala.reflect.makro - -import scala.reflect.api.Universe - -/** This package is required by the compiler and should not be used in client code. */ -package object internal { - /** This method is required by the compiler and should not be used in client code. */ - def materializeClassTag[T](u: Universe): ClassTag[T] = macro materializeClassTag_impl[T] - - /** This method is required by the compiler and should not be used in client code. */ - def materializeClassTag_impl[T: c.TypeTag](c: Context)(u: c.Expr[Universe]): c.Expr[ClassTag[T]] = - c.Expr[Nothing](c.materializeClassTag(u.tree, implicitly[c.TypeTag[T]].tpe))(c.TypeTag.Nothing) - - /** This method is required by the compiler and should not be used in client code. */ - def materializeTypeTag[T](u: Universe): u.TypeTag[T] = macro materializeTypeTag_impl[T] - - /** This method is required by the compiler and should not be used in client code. */ - def materializeTypeTag_impl[T: c.TypeTag](c: Context)(u: c.Expr[Universe]): c.Expr[u.value.TypeTag[T]] = - c.Expr[Nothing](c.materializeTypeTag(u.tree, implicitly[c.TypeTag[T]].tpe, requireConcreteTypeTag = false))(c.TypeTag.Nothing) - - /** This method is required by the compiler and should not be used in client code. */ - def materializeConcreteTypeTag[T](u: Universe): u.ConcreteTypeTag[T] = macro materializeConcreteTypeTag_impl[T] - - /** This method is required by the compiler and should not be used in client code. */ - def materializeConcreteTypeTag_impl[T: c.TypeTag](c: Context)(u: c.Expr[Universe]): c.Expr[u.value.ConcreteTypeTag[T]] = - c.Expr[Nothing](c.materializeTypeTag(u.tree, implicitly[c.TypeTag[T]].tpe, requireConcreteTypeTag = true))(c.TypeTag.Nothing) - - /** This method is required by the compiler and should not be used in client code. */ - private[scala] implicit def context2utils(c0: Context) : Utils { val c: c0.type } = new { val c: c0.type = c0 } with Utils -} - -package internal { - private[scala] abstract class Utils { - val c: Context - - import c.mirror._ - import definitions._ - - val coreTags = Map( - ByteClass.asType -> newTermName("Byte"), - ShortClass.asType -> newTermName("Short"), - CharClass.asType -> newTermName("Char"), - IntClass.asType -> newTermName("Int"), - LongClass.asType -> newTermName("Long"), - FloatClass.asType -> newTermName("Float"), - DoubleClass.asType -> newTermName("Double"), - BooleanClass.asType -> newTermName("Boolean"), - UnitClass.asType -> newTermName("Unit"), - AnyClass.asType -> newTermName("Any"), - ObjectClass.asType -> newTermName("Object"), - AnyValClass.asType -> newTermName("AnyVal"), - AnyRefClass.asType -> newTermName("AnyRef"), - NothingClass.asType -> newTermName("Nothing"), - NullClass.asType -> newTermName("Null")) - - def materializeClassTag(prefix: Tree, tpe: Type): Tree = { - val typetagInScope = c.inferImplicitValue(appliedType(typeRef(prefix.tpe, ConcreteTypeTagClass, Nil), List(tpe))) - def typetagIsSynthetic(tree: Tree) = tree.isInstanceOf[Block] || (tree exists (sub => sub.symbol == TypeTagModule || sub.symbol == ConcreteTypeTagModule)) - typetagInScope match { - case success if !success.isEmpty && !typetagIsSynthetic(success) => - val factory = TypeApply(Select(Ident(ClassTagModule), newTermName("apply")), List(TypeTree(tpe))) - Apply(factory, List(Select(typetagInScope, newTermName("tpe")))) - case _ => - val result = - tpe match { - case coreTpe if coreTags contains coreTpe => - Select(Ident(ClassTagModule), coreTags(coreTpe)) - case _ => - if (tpe.typeSymbol == ArrayClass) { - val componentTpe = tpe.typeArguments(0) - val classtagInScope = c.inferImplicitValue(appliedType(typeRef(NoPrefix, ClassTagClass, Nil), List(componentTpe))) - val componentTag = classtagInScope orElse materializeClassTag(prefix, componentTpe) - Select(componentTag, newTermName("wrap")) - } else { - // [Eugene] what's the intended behavior? there's no spec on ClassManifests - // for example, should we ban Array[T] or should we tag them with Array[AnyRef]? - // if its the latter, what should be the result of tagging Array[T] where T <: Int? - if (tpe.typeSymbol.isAbstractType) fail("tpe is an abstract type") - val erasure = - if (tpe.typeSymbol.isDerivedValueClass) tpe // [Eugene to Martin] is this correct? - else tpe.erasure.normalize // necessary to deal with erasures of HK types - val factory = TypeApply(Select(Ident(ClassTagModule), newTermName("apply")), List(TypeTree(tpe))) - Apply(factory, List(TypeApply(Ident(newTermName("classOf")), List(TypeTree(erasure))))) - } - } - try c.typeCheck(result) - catch { case terr @ c.TypeError(pos, msg) => fail(terr) } - } - } - - def materializeTypeTag(prefix: Tree, tpe: Type, requireConcreteTypeTag: Boolean): Tree = { - val tagModule = if (requireConcreteTypeTag) ConcreteTypeTagModule else TypeTagModule - val result = - tpe match { - case coreTpe if coreTags contains coreTpe => - Select(Select(prefix, tagModule.name), coreTags(coreTpe)) - case _ => - try c.reifyType(prefix, tpe, dontSpliceAtTopLevel = true, requireConcreteTypeTag = requireConcreteTypeTag) - catch { - case ex: Throwable => - // [Eugene] cannot pattern match on an abstract type, so had to do this - val ex1 = ex - if (ex.getClass.toString.endsWith("$ReificationError")) { - ex match { - case c.ReificationError(pos, msg) => - c.error(pos, msg) - EmptyTree - } - } else if (ex.getClass.toString.endsWith("$UnexpectedReificationError")) { - ex match { - case c.UnexpectedReificationError(pos, err, cause) => - if (cause != null) throw cause else throw ex - } - } else { - throw ex - } - } - } - try c.typeCheck(result) - catch { case terr @ c.TypeError(pos, msg) => fail(terr) } - } - - private def fail(reason: Any): Nothing = { - val Apply(TypeApply(fun, List(tpeTree)), _) = c.macroApplication - val tpe = tpeTree.tpe - val PolyType(_, MethodType(_, tagTpe)) = fun.tpe - val tagModule = tagTpe.typeSymbol.companionSymbol - if (c.compilerSettings.contains("-Xlog-implicits")) - c.echo(c.enclosingPosition, "cannot materialize " + tagModule.name + "[" + tpe + "] because:\n" + reason) - c.abort(c.enclosingPosition, "No %s available for %s".format(tagModule.name, tpe)) - } - } -} -- cgit v1.2.3