From da7235afe89dc0b94898b76fb347fed61811bf1f Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Mon, 18 Jun 2012 16:15:20 +0200 Subject: Fix SI-4954. Override inner classes in `LinkedHashMap` that correspond to `filterKeys`, `mapValues` and `keys` to retain a proper ordering of elements when they are transformed. --- test/files/run/t4954.scala | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/files/run/t4954.scala (limited to 'test/files/run') diff --git a/test/files/run/t4954.scala b/test/files/run/t4954.scala new file mode 100644 index 0000000000..b4916e651d --- /dev/null +++ b/test/files/run/t4954.scala @@ -0,0 +1,45 @@ + + +import collection._ + + +object Test { + + def main(args: Array[String]) { + val m = scala.collection.mutable.LinkedHashMap("one" -> 1, "two" -> 2, "three" -> 3, "four" -> 4, "five" -> 5) + val expected = List("one", "two", "three", "four", "five") + assert(m.keys.iterator.toList == expected) + assert(m.keys.drop(0).iterator.toList == expected) + assert(m.keys.drop(1).iterator.toList == expected.drop(1)) + assert(m.keys.drop(2).iterator.toList == expected.drop(2)) + assert(m.keys.drop(3).iterator.toList == expected.drop(3)) + assert(m.keys.drop(4).iterator.toList == expected.drop(4)) + assert(m.keys.drop(5).iterator.toList == expected.drop(5)) + + val expvals = List(1, 2, 3, 4, 5) + assert(m.values.iterator.toList == expvals) + assert(m.values.drop(0).iterator.toList == expvals) + assert(m.values.drop(1).iterator.toList == expvals.drop(1)) + assert(m.values.drop(2).iterator.toList == expvals.drop(2)) + assert(m.values.drop(3).iterator.toList == expvals.drop(3)) + assert(m.values.drop(4).iterator.toList == expvals.drop(4)) + assert(m.values.drop(5).iterator.toList == expvals.drop(5)) + + val pred = (x: String) => x.length < 6 + val filtered = m.filterKeys(pred) + assert(filtered.drop(0).keys.toList == expected.filter(pred)) + assert(filtered.drop(1).keys.toList == expected.filter(pred).drop(1)) + assert(filtered.drop(2).keys.toList == expected.filter(pred).drop(2)) + assert(filtered.drop(3).keys.toList == expected.filter(pred).drop(3)) + assert(filtered.drop(4).keys.toList == expected.filter(pred).drop(4)) + + val mapped = m.mapValues(-_) + assert(mapped.drop(0).keys.toList == expected) + assert(mapped.drop(1).keys.toList == expected.drop(1)) + assert(mapped.drop(2).keys.toList == expected.drop(2)) + assert(mapped.drop(3).keys.toList == expected.drop(3)) + assert(mapped.drop(4).keys.toList == expected.drop(4)) + assert(mapped.drop(5).keys.toList == expected.drop(5)) + } + +} -- cgit v1.2.3 From f559505c1f254c98ae34bb4cdf1e2b624d25a84c Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 11:43:18 -0400 Subject: Adding copyInto and toVector methods to collections. * Added generic copyInto method for collections. For any collection with a CanBuildFrom, can convert a generic collection into it using the builder. * Added specifici toVector method for collections. This is more efficient than copyInto if the collection is a Vector. --- .../scala/collection/GenTraversableOnce.scala | 19 ++++ src/library/scala/collection/Iterator.scala | 10 ++ src/library/scala/collection/TraversableLike.scala | 7 ++ .../scala/collection/immutable/Vector.scala | 2 + .../collection/parallel/ParIterableLike.scala | 5 + .../collection/parallel/immutable/ParVector.scala | 2 + test/files/run/collection-conversions.check | 104 +++++++++++++++++++++ test/files/run/collection-conversions.scala | 60 ++++++++++++ 8 files changed, 209 insertions(+) create mode 100644 test/files/run/collection-conversions.check create mode 100644 test/files/run/collection-conversions.scala (limited to 'test/files/run') diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index eadacd9209..d22fb0f3ad 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -9,6 +9,8 @@ package scala.collection import scala.reflect.ClassTag +import scala.collection.generic.CanBuildFrom +import scala.annotation.unchecked.{ uncheckedVariance => uV } /** A template trait for all traversable-once objects which may be * traversed in parallel. @@ -552,4 +554,21 @@ trait GenTraversableOnce[+A] extends Any { * containing all key/value pairs of type `(T, U)` of this $coll. */ def toMap[K, V](implicit ev: A <:< (K, V)): GenMap[K, V] + + /** Converts this $coll to a Vector. + * $willNotTerminateInf + * @return a vector containing all elements of this $coll. + */ + def toVector: Vector[A] + + /** Converts this $coll into another by copying all elemnents. + * $willNotTerminateInf + * @return a new collection containing all elements of this $coll. + * + * @usecase def copyInto[Col[_]]: Col[A] + * @inheritdoc + * $willNotTerminateInf + * @return a new collection containing all elemnts of this $coll. + */ + def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index b2bbc8d888..62449c7712 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -11,6 +11,8 @@ package scala.collection import mutable.ArrayBuffer import annotation.migration import immutable.Stream +import scala.collection.generic.CanBuildFrom +import scala.annotation.unchecked.{ uncheckedVariance => uV } /** The `Iterator` object provides various functions for creating specialized iterators. * @@ -1139,6 +1141,14 @@ trait Iterator[+A] extends TraversableOnce[A] { if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] + def toVector: Vector[A] = copyInto[Vector] + def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + val b = cbf() + while(hasNext) b += next + b.result + } + + /** Converts this iterator to a string. * * @return `"empty iterator"` or `"non-empty iterator"`, depending on diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 3716a318d9..898ea4d654 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,6 +616,13 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream + def toVector: Vector[A] = copyInto[Vector] + def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + val b = cbf() + b.sizeHint(this) + b ++= thisCollection + b.result + } /** Converts this $coll to a string. * diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 1395a8f52d..d100bf93df 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -77,6 +77,8 @@ override def companion: GenericCompanion[Vector] = Vector override def par = new ParVector(this) + override def toVector: Vector[A] = this + override def lengthCompare(len: Int): Int = length - len private[collection] final def initIterator[B >: A](s: VectorIterator[B]) { diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index a447f1b5e4..ed667d85a0 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -851,6 +851,11 @@ self: ParIterableLike[T, Repr, Sequential] => override def toMap[K, V](implicit ev: T <:< (K, V)): immutable.ParMap[K, V] = toParMap[K, V, immutable.ParMap[K, V]](() => immutable.ParMap.newCombiner[K, V]) + override def toVector: Vector[T] = seq.toVector + + override def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyInto[Col] + + /* tasks */ protected trait StrictSplitterCheckTask[R, Tp] extends Task[R, Tp] { diff --git a/src/library/scala/collection/parallel/immutable/ParVector.scala b/src/library/scala/collection/parallel/immutable/ParVector.scala index 1ece663a1d..e4099f1809 100644 --- a/src/library/scala/collection/parallel/immutable/ParVector.scala +++ b/src/library/scala/collection/parallel/immutable/ParVector.scala @@ -62,6 +62,8 @@ extends ParSeq[T] override def seq: Vector[T] = vector + override def toVector: Vector[T] = vector + class ParVectorIterator(_start: Int, _end: Int) extends VectorIterator[T](_start, _end) with SeqSplitter[T] { def remaining: Int = remainingElementCount def dup: SeqSplitter[T] = (new ParVector(remainingVector)).splitter diff --git a/test/files/run/collection-conversions.check b/test/files/run/collection-conversions.check new file mode 100644 index 0000000000..08d0fa32c5 --- /dev/null +++ b/test/files/run/collection-conversions.check @@ -0,0 +1,104 @@ +-- Testing iterator --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing Vector --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing List --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing Buffer --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing ParVector --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing Set --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing SetView --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing BufferView --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala new file mode 100644 index 0000000000..df12134c04 --- /dev/null +++ b/test/files/run/collection-conversions.scala @@ -0,0 +1,60 @@ +import collection._ +import mutable.Buffer +import parallel.immutable.ParVector +import reflect.ClassTag + +object Test { + + def printResult[A,B](msg: String, obj: A, expected: B)(implicit tag: ClassTag[A], tag2: ClassTag[B]) = { + print(" :" + msg +": ") + val isArray = obj match { + case x: Array[Int] => true + case _ => false + } + val expectedEquals = + if(isArray) obj.asInstanceOf[Array[Int]].toSeq == expected.asInstanceOf[Array[Int]].toSeq + else obj == expected + val tagEquals = tag == tag2 + if(expectedEquals && tagEquals) print("OK") + else print("FAILED") + if(!expectedEquals) print(", " + obj + " != " + expected) + if(!tagEquals) print(", " + tag + " != " + tag2) + println("") + } + + val testVector = Vector(1,2,3) + val testBuffer = Buffer(1,2,3) + val testGenSeq = GenSeq(1,2,3) + val testSeq = Seq(1,2,3) + val testStream = Stream(1,2,3) + val testArray = Array(1,2,3) + val testParVector = ParVector(1,2,3) + + def testConversion[A: ClassTag](name: String, col: => GenTraversableOnce[A]): Unit = { + val tmp = col + println("-- Testing " + name + " ---") + printResult("[Direct] Vector ", col.toVector, testVector) + printResult("[Copy] Vector ", col.copyInto[Vector], testVector) + printResult("[Direct] Buffer ", col.toBuffer, testBuffer) + printResult("[Copy] Buffer ", col.copyInto[Buffer], testBuffer) + printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) + printResult("[Copy] GenSeq ", col.copyInto[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.copyInto[Seq], testSeq) + printResult("[Direct] Stream ", col.toStream, testStream) + printResult("[Copy] Stream ", col.copyInto[Stream], testStream) + printResult("[Direct] Array ", col.toArray, testArray) + printResult("[Copy] Array ", col.copyInto[Array], testArray) + printResult("[Copy] ParVector", col.copyInto[ParVector], testParVector) + } + + def main(args: Array[String]): Unit = { + testConversion("iterator", (1 to 3).iterator) + testConversion("Vector", Vector(1,2,3)) + testConversion("List", List(1,2,3)) + testConversion("Buffer", Buffer(1,2,3)) + testConversion("ParVector", ParVector(1,2,3)) + testConversion("Set", Set(1,2,3)) + testConversion("SetView", Set(1,2,3).view) + testConversion("BufferView", Buffer(1,2,3).view) + } +} -- cgit v1.2.3 From de6519952582a74a3f9769b1dbde26c617a819f1 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 12:14:44 -0400 Subject: Fixes from review. * Fixed typo * Renamed copyInto to copyTo * Added tparam doc. --- src/library/scala/collection/GenTraversableOnce.scala | 8 ++++---- src/library/scala/collection/Iterator.scala | 4 ++-- src/library/scala/collection/TraversableLike.scala | 4 ++-- .../scala/collection/parallel/ParIterableLike.scala | 2 +- test/files/run/collection-conversions.scala | 14 +++++++------- 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'test/files/run') diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index d22fb0f3ad..2996071d71 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -562,13 +562,13 @@ trait GenTraversableOnce[+A] extends Any { def toVector: Vector[A] /** Converts this $coll into another by copying all elemnents. - * $willNotTerminateInf + * @tparam Col The collection type to build. * @return a new collection containing all elements of this $coll. * - * @usecase def copyInto[Col[_]]: Col[A] + * @usecase def copyTo[Col[_]]: Col[A] * @inheritdoc * $willNotTerminateInf - * @return a new collection containing all elemnts of this $coll. + * @return a new collection containing all elements of this $coll. */ - def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] + def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 62449c7712..4283120519 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -1141,8 +1141,8 @@ trait Iterator[+A] extends TraversableOnce[A] { if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] - def toVector: Vector[A] = copyInto[Vector] - def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = copyTo[Vector] + def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() while(hasNext) b += next b.result diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 898ea4d654..68abc36bf7 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,8 +616,8 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream - def toVector: Vector[A] = copyInto[Vector] - def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = copyTo[Vector] + def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() b.sizeHint(this) b ++= thisCollection diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index ed667d85a0..12b777832e 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -853,7 +853,7 @@ self: ParIterableLike[T, Repr, Sequential] => override def toVector: Vector[T] = seq.toVector - override def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyInto[Col] + override def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyTo[Col] /* tasks */ diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index df12134c04..390ba06dac 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -34,17 +34,17 @@ object Test { val tmp = col println("-- Testing " + name + " ---") printResult("[Direct] Vector ", col.toVector, testVector) - printResult("[Copy] Vector ", col.copyInto[Vector], testVector) + printResult("[Copy] Vector ", col.copyTo[Vector], testVector) printResult("[Direct] Buffer ", col.toBuffer, testBuffer) - printResult("[Copy] Buffer ", col.copyInto[Buffer], testBuffer) + printResult("[Copy] Buffer ", col.copyTo[Buffer], testBuffer) printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) - printResult("[Copy] GenSeq ", col.copyInto[GenSeq], testGenSeq) - printResult("[Copy] Seq ", col.copyInto[Seq], testSeq) + printResult("[Copy] GenSeq ", col.copyTo[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.copyTo[Seq], testSeq) printResult("[Direct] Stream ", col.toStream, testStream) - printResult("[Copy] Stream ", col.copyInto[Stream], testStream) + printResult("[Copy] Stream ", col.copyTo[Stream], testStream) printResult("[Direct] Array ", col.toArray, testArray) - printResult("[Copy] Array ", col.copyInto[Array], testArray) - printResult("[Copy] ParVector", col.copyInto[ParVector], testParVector) + printResult("[Copy] Array ", col.copyTo[Array], testArray) + printResult("[Copy] ParVector", col.copyTo[ParVector], testParVector) } def main(args: Array[String]): Unit = { -- cgit v1.2.3 From bc3b1e2c9453ef90f6fb7aaa3dea6e24ba19d017 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 12:59:33 -0400 Subject: Rename copyTo to build based on consensus of 3 --- src/library/scala/collection/GenTraversableOnce.scala | 4 ++-- src/library/scala/collection/Iterator.scala | 4 ++-- src/library/scala/collection/TraversableLike.scala | 4 ++-- .../scala/collection/parallel/ParIterableLike.scala | 2 +- test/files/run/collection-conversions.scala | 14 +++++++------- 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'test/files/run') diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 2996071d71..46be27f1cd 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -565,10 +565,10 @@ trait GenTraversableOnce[+A] extends Any { * @tparam Col The collection type to build. * @return a new collection containing all elements of this $coll. * - * @usecase def copyTo[Col[_]]: Col[A] + * @usecase def build[Col[_]]: Col[A] * @inheritdoc * $willNotTerminateInf * @return a new collection containing all elements of this $coll. */ - def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] + def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 4283120519..6b96c8dba5 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -1141,8 +1141,8 @@ trait Iterator[+A] extends TraversableOnce[A] { if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] - def toVector: Vector[A] = copyTo[Vector] - def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = build[Vector] + def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() while(hasNext) b += next b.result diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 68abc36bf7..e2e9195f4d 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,8 +616,8 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream - def toVector: Vector[A] = copyTo[Vector] - def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = build[Vector] + def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() b.sizeHint(this) b ++= thisCollection diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index 12b777832e..20d0a65a4c 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -853,7 +853,7 @@ self: ParIterableLike[T, Repr, Sequential] => override def toVector: Vector[T] = seq.toVector - override def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyTo[Col] + override def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.build[Col] /* tasks */ diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index 390ba06dac..d7fcbb352a 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -34,17 +34,17 @@ object Test { val tmp = col println("-- Testing " + name + " ---") printResult("[Direct] Vector ", col.toVector, testVector) - printResult("[Copy] Vector ", col.copyTo[Vector], testVector) + printResult("[Copy] Vector ", col.build[Vector], testVector) printResult("[Direct] Buffer ", col.toBuffer, testBuffer) - printResult("[Copy] Buffer ", col.copyTo[Buffer], testBuffer) + printResult("[Copy] Buffer ", col.build[Buffer], testBuffer) printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) - printResult("[Copy] GenSeq ", col.copyTo[GenSeq], testGenSeq) - printResult("[Copy] Seq ", col.copyTo[Seq], testSeq) + printResult("[Copy] GenSeq ", col.build[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.build[Seq], testSeq) printResult("[Direct] Stream ", col.toStream, testStream) - printResult("[Copy] Stream ", col.copyTo[Stream], testStream) + printResult("[Copy] Stream ", col.build[Stream], testStream) printResult("[Direct] Array ", col.toArray, testArray) - printResult("[Copy] Array ", col.copyTo[Array], testArray) - printResult("[Copy] ParVector", col.copyTo[ParVector], testParVector) + printResult("[Copy] Array ", col.build[Array], testArray) + printResult("[Copy] ParVector", col.build[ParVector], testParVector) } def main(args: Array[String]): Unit = { -- cgit v1.2.3 From fae3e8925a2125f723517d5f1eaa0a2087507df1 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 15:26:07 -0400 Subject: Migrate build to @odersky's suggestion of convertTo. * Move method into TraversableOnce from Iterator and Traversable to make the build pass. * Udpate IDE tests with new collection methods. * Rewire default toXYZ methods to use convertTo. --- src/library/scala/collection/GenTraversableOnce.scala | 4 ++-- src/library/scala/collection/Iterator.scala | 7 ------- src/library/scala/collection/TraversableLike.scala | 4 ++-- src/library/scala/collection/TraversableOnce.scala | 17 +++++++++++++---- .../scala/collection/parallel/ParIterableLike.scala | 3 ++- test/files/presentation/ide-bug-1000531.check | 4 +++- test/files/run/collection-conversions.scala | 14 +++++++------- 7 files changed, 29 insertions(+), 24 deletions(-) (limited to 'test/files/run') diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 46be27f1cd..37f726c8fb 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -565,10 +565,10 @@ trait GenTraversableOnce[+A] extends Any { * @tparam Col The collection type to build. * @return a new collection containing all elements of this $coll. * - * @usecase def build[Col[_]]: Col[A] + * @usecase def convertTo[Col[_]]: Col[A] * @inheritdoc * $willNotTerminateInf * @return a new collection containing all elements of this $coll. */ - def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] + def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 6b96c8dba5..5f369de3b7 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -1140,13 +1140,6 @@ trait Iterator[+A] extends TraversableOnce[A] { def toStream: Stream[A] = if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] - - def toVector: Vector[A] = build[Vector] - def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { - val b = cbf() - while(hasNext) b += next - b.result - } /** Converts this iterator to a string. diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index e2e9195f4d..e5861f5760 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,8 +616,8 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream - def toVector: Vector[A] = build[Vector] - def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + // Override to provide size hint. + override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() b.sizeHint(this) b ++= thisCollection diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala index 386ce2d95a..8dc6184d88 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -9,6 +9,7 @@ package scala.collection import mutable.{ Buffer, Builder, ListBuffer, ArrayBuffer } +import generic.CanBuildFrom import annotation.unchecked.{ uncheckedVariance => uV } import language.{implicitConversions, higherKinds} import reflect.ClassTag @@ -239,17 +240,25 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] { def toTraversable: Traversable[A] - def toList: List[A] = (new ListBuffer[A] ++= seq).toList + def toList: List[A] = convertTo[List] def toIterable: Iterable[A] = toStream def toSeq: Seq[A] = toStream - def toIndexedSeq: immutable.IndexedSeq[A] = immutable.IndexedSeq() ++ seq + def toIndexedSeq: immutable.IndexedSeq[A] = convertTo[immutable.IndexedSeq] - def toBuffer[B >: A]: mutable.Buffer[B] = new ArrayBuffer[B] ++= seq + def toBuffer[B >: A]: mutable.Buffer[B] = convertTo[ArrayBuffer].asInstanceOf[mutable.Buffer[B]] - def toSet[B >: A]: immutable.Set[B] = immutable.Set() ++ seq + def toSet[B >: A]: immutable.Set[B] = convertTo[immutable.Set].asInstanceOf[immutable.Set[B]] + + def toVector: Vector[A] = convertTo[Vector] + + def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + val b = cbf() + b ++= seq + b.result + } def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = { val b = immutable.Map.newBuilder[T, U] diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index 20d0a65a4c..a7ec833193 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -851,9 +851,10 @@ self: ParIterableLike[T, Repr, Sequential] => override def toMap[K, V](implicit ev: T <:< (K, V)): immutable.ParMap[K, V] = toParMap[K, V, immutable.ParMap[K, V]](() => immutable.ParMap.newCombiner[K, V]) + // TODO(@alex22): make these better override def toVector: Vector[T] = seq.toVector - override def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.build[Col] + override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.convertTo[Col] /* tasks */ diff --git a/test/files/presentation/ide-bug-1000531.check b/test/files/presentation/ide-bug-1000531.check index e813ce119b..9d4674d7c7 100644 --- a/test/files/presentation/ide-bug-1000531.check +++ b/test/files/presentation/ide-bug-1000531.check @@ -3,7 +3,7 @@ reload: CrashOnLoad.scala askTypeCompletion at CrashOnLoad.scala(6,12) ================================================================================ [response] aksTypeCompletion at (6,12) -retrieved 124 members +retrieved 126 members [accessible: true] `class GroupedIteratorIterator[B]#GroupedIterator` [accessible: true] `method !=(x$1: Any)Boolean` [accessible: true] `method !=(x$1: AnyRef)Boolean` @@ -25,6 +25,7 @@ retrieved 124 members [accessible: true] `method collectFirst[B](pf: PartialFunction[B,B])Option[B]` [accessible: true] `method collect[B](pf: PartialFunction[B,B])Iterator[B]` [accessible: true] `method contains(elem: Any)Boolean` +[accessible: true] `method convertTo[Col[_]](implicit cbf: scala.collection.generic.CanBuildFrom[Nothing,B,Col[B]])Col[B]` [accessible: true] `method copyToArray[B >: B](xs: Array[B])Unit` [accessible: true] `method copyToArray[B >: B](xs: Array[B], start: Int)Unit` [accessible: true] `method copyToArray[B >: B](xs: Array[B], start: Int, len: Int)Unit` @@ -109,6 +110,7 @@ retrieved 124 members [accessible: true] `method toStream=> scala.collection.immutable.Stream[B]` [accessible: true] `method toString()String` [accessible: true] `method toTraversable=> Traversable[B]` +[accessible: true] `method toVector=> Vector[B]` [accessible: true] `method wait()Unit` [accessible: true] `method wait(x$1: Long)Unit` [accessible: true] `method wait(x$1: Long, x$2: Int)Unit` diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index d7fcbb352a..b5c4d8e261 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -34,17 +34,17 @@ object Test { val tmp = col println("-- Testing " + name + " ---") printResult("[Direct] Vector ", col.toVector, testVector) - printResult("[Copy] Vector ", col.build[Vector], testVector) + printResult("[Copy] Vector ", col.convertTo[Vector], testVector) printResult("[Direct] Buffer ", col.toBuffer, testBuffer) - printResult("[Copy] Buffer ", col.build[Buffer], testBuffer) + printResult("[Copy] Buffer ", col.convertTo[Buffer], testBuffer) printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) - printResult("[Copy] GenSeq ", col.build[GenSeq], testGenSeq) - printResult("[Copy] Seq ", col.build[Seq], testSeq) + printResult("[Copy] GenSeq ", col.convertTo[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.convertTo[Seq], testSeq) printResult("[Direct] Stream ", col.toStream, testStream) - printResult("[Copy] Stream ", col.build[Stream], testStream) + printResult("[Copy] Stream ", col.convertTo[Stream], testStream) printResult("[Direct] Array ", col.toArray, testArray) - printResult("[Copy] Array ", col.build[Array], testArray) - printResult("[Copy] ParVector", col.build[ParVector], testParVector) + printResult("[Copy] Array ", col.convertTo[Array], testArray) + printResult("[Copy] ParVector", col.convertTo[ParVector], testParVector) } def main(args: Array[String]): Unit = { -- cgit v1.2.3 From 3be520bcfc84f207d172934f9b147e31355cd877 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 15 Jun 2012 13:35:54 +0200 Subject: improves showRaw addresses concerns raised in http://groups.google.com/group/scala-user/browse_thread/thread/de5a5be2e083cf8e --- .../scala/reflect/reify/utils/NodePrinters.scala | 39 +- src/compiler/scala/tools/nsc/Global.scala | 2 +- .../scala/tools/nsc/ast/NodePrinters.scala | 2 +- src/compiler/scala/tools/nsc/ast/Printers.scala | 295 ++++++++++ .../scala/tools/nsc/ast/TreePrinters.scala | 295 ---------- .../scala/tools/nsc/matching/MatchSupport.scala | 2 +- src/library/scala/reflect/base/Base.scala | 2 +- src/library/scala/reflect/base/Trees.scala | 4 +- src/reflect/scala/reflect/api/Printers.scala | 94 +++ src/reflect/scala/reflect/api/Symbols.scala | 19 + src/reflect/scala/reflect/api/TreePrinters.scala | 87 --- src/reflect/scala/reflect/api/Universe.scala | 2 +- src/reflect/scala/reflect/internal/Printers.scala | 642 +++++++++++++++++++++ .../scala/reflect/internal/SymbolTable.scala | 2 +- .../scala/reflect/internal/TreePrinters.scala | 478 --------------- src/reflect/scala/reflect/internal/Trees.scala | 2 +- src/reflect/scala/reflect/makro/Universe.scala | 19 - test/files/run/showraw_mods.check | 1 + test/files/run/showraw_mods.scala | 6 + test/files/run/showraw_tree.check | 2 + test/files/run/showraw_tree.scala | 8 + test/files/run/showraw_tree_ids.check | 2 + test/files/run/showraw_tree_ids.scala | 8 + test/files/run/showraw_tree_kinds.check | 2 + test/files/run/showraw_tree_kinds.scala | 8 + test/files/run/showraw_tree_types_ids.check | 10 + test/files/run/showraw_tree_types_ids.scala | 10 + test/files/run/showraw_tree_types_typed.check | 10 + test/files/run/showraw_tree_types_typed.scala | 10 + test/files/run/showraw_tree_types_untyped.check | 2 + test/files/run/showraw_tree_types_untyped.scala | 8 + test/files/run/showraw_tree_ultimate.check | 10 + test/files/run/showraw_tree_ultimate.scala | 10 + 33 files changed, 1168 insertions(+), 925 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/ast/Printers.scala delete mode 100644 src/compiler/scala/tools/nsc/ast/TreePrinters.scala create mode 100644 src/reflect/scala/reflect/api/Printers.scala delete mode 100644 src/reflect/scala/reflect/api/TreePrinters.scala create mode 100644 src/reflect/scala/reflect/internal/Printers.scala delete mode 100644 src/reflect/scala/reflect/internal/TreePrinters.scala create mode 100644 test/files/run/showraw_mods.check create mode 100644 test/files/run/showraw_mods.scala create mode 100644 test/files/run/showraw_tree.check create mode 100644 test/files/run/showraw_tree.scala create mode 100644 test/files/run/showraw_tree_ids.check create mode 100644 test/files/run/showraw_tree_ids.scala create mode 100644 test/files/run/showraw_tree_kinds.check create mode 100644 test/files/run/showraw_tree_kinds.scala create mode 100644 test/files/run/showraw_tree_types_ids.check create mode 100644 test/files/run/showraw_tree_types_ids.scala create mode 100644 test/files/run/showraw_tree_types_typed.check create mode 100644 test/files/run/showraw_tree_types_typed.scala create mode 100644 test/files/run/showraw_tree_types_untyped.check create mode 100644 test/files/run/showraw_tree_types_untyped.scala create mode 100644 test/files/run/showraw_tree_ultimate.check create mode 100644 test/files/run/showraw_tree_ultimate.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index ce0ab2196a..7214da597e 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -15,41 +15,6 @@ trait NodePrinters { import Flag._ object reifiedNodeToString extends (Tree => String) { - // [Eugene++ to Martin] can we do better? - // didn't want to invent anything myself in order not to interfere with your line of thought - def bitsToFlags(bits: String): String = { - val flags = bits.toLong - if (flags == NoFlags) nme.NoFlags.toString - else { - val s_flags = new collection.mutable.ListBuffer[String] - if (flags containsAll TRAIT) s_flags += "TRAIT" - if (flags containsAll MODULE) s_flags += "MODULE" - if (flags containsAll MUTABLE) s_flags += "MUTABLE" - if (flags containsAll PACKAGE) s_flags += "PACKAGE" - if (flags containsAll METHOD) s_flags += "METHOD" - if (flags containsAll DEFERRED) s_flags += "DEFERRED" - if (flags containsAll ABSTRACT) s_flags += "ABSTRACT" - if (flags containsAll FINAL) s_flags += "FINAL" - if (flags containsAll SEALED) s_flags += "SEALED" - if (flags containsAll IMPLICIT) s_flags += "IMPLICIT" - if (flags containsAll LAZY) s_flags += "LAZY" - if (flags containsAll OVERRIDE) s_flags += "OVERRIDE" - if (flags containsAll PRIVATE) s_flags += "PRIVATE" - if (flags containsAll PROTECTED) s_flags += "PROTECTED" - if (flags containsAll CASE) s_flags += "CASE" - if (flags containsAll ABSOVERRIDE) s_flags += "ABSOVERRIDE" - if (flags containsAll BYNAMEPARAM) s_flags += "BYNAMEPARAM" - if (flags containsAll PARAM) s_flags += "PARAM" - if (flags containsAll PARAMACCESSOR) s_flags += "PARAMACCESSOR" - if (flags containsAll CASEACCESSOR) s_flags += "CASEACCESSOR" - if (flags containsAll COVARIANT) s_flags += "COVARIANT" - if (flags containsAll CONTRAVARIANT) s_flags += "CONTRAVARIANT" - if (flags containsAll DEFAULTPARAM) s_flags += "DEFAULTPARAM" - if (flags containsAll INTERFACE) s_flags += "INTERFACE" - s_flags mkString " | " - } - } - def apply(tree: Tree): String = { var mirrorIsUsed = false var flagsAreUsed = false @@ -70,7 +35,7 @@ trait NodePrinters { s = s.replace("immutable.this.Nil", "List()") s = """build\.flagsFromBits\((\d+)[lL]\)""".r.replaceAllIn(s, m => { flagsAreUsed = true - bitsToFlags(m.group(1)) + show(m.group(1).toLong) }) s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()") s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => { @@ -87,7 +52,7 @@ trait NodePrinters { val bits = m.group(1) if (buf.nonEmpty || bits != "0L") { flagsAreUsed = true - buf.append(bitsToFlags(bits)) + buf.append(show(bits.toLong)) } val replacement = "Modifiers(" + buf.reverse.mkString(", ") + ")" diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 787c9c7f57..38a08bbd60 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -39,7 +39,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) with Plugins with PhaseAssembly with Trees - with TreePrinters + with Printers with DocComments with Positions { self => diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index ba1f3b2e3c..4afd3545b9 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -146,7 +146,7 @@ abstract class NodePrinters { } def printModifiers(tree: MemberDef) { // [Eugene++] there's most likely a bug here (?) - // see `TreePrinters.printAnnotations` for more information + // see `Printers.printAnnotations` for more information val annots0 = tree.symbol.annotations match { case Nil => tree.mods.annotations case xs => xs map annotationInfoToString diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala new file mode 100644 index 0000000000..94d0c4f45e --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -0,0 +1,295 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package ast + +import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } +import symtab.Flags._ +import symtab.SymbolTable + +trait Printers extends reflect.internal.Printers { this: Global => + + import treeInfo.{ IsTrue, IsFalse } + + class TreePrinter(out: PrintWriter) extends super.TreePrinter(out) { + + override def print(args: Any*): Unit = args foreach { + case tree: Tree => + printPosition(tree) + printTree( + if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) { + tree match { + case ClassDef(_, _, _, impl @ Template(ps, emptyValDef, body)) + if (tree.symbol.thisSym != tree.symbol) => + ClassDef(tree.symbol, Template(ps, ValDef(tree.symbol.thisSym), body)) + case ClassDef(_, _, _, impl) => ClassDef(tree.symbol, impl) + case ModuleDef(_, _, impl) => ModuleDef(tree.symbol, impl) + case ValDef(_, _, _, rhs) => ValDef(tree.symbol, rhs) + case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs) + case TypeDef(_, _, _, rhs) => TypeDef(tree.symbol, rhs) + case _ => tree + } + } else tree) + case unit: CompilationUnit => + print("// Scala source: " + unit.source + "\n") + if (unit.body == null) print("") + else { print(unit.body); println() } + println() + out.flush() + case arg => + super.print(arg) + } + } + + // overflow cases missing from TreePrinter in reflect.api + override def xprintTree(treePrinter: super.TreePrinter, tree: Tree) = tree match { + case DocDef(comment, definition) => + treePrinter.print(comment.raw) + treePrinter.println() + treePrinter.print(definition) + + case TypeTreeWithDeferredRefCheck() => + treePrinter.print("") + + case SelectFromArray(qualifier, name, _) => + treePrinter.print(qualifier, ".", symName(tree, name)) + + case _ => + super.xprintTree(treePrinter, tree) + } + + /** A tree printer which is stingier about vertical whitespace and unnecessary + * punctuation than the standard one. + */ + class CompactTreePrinter(out: PrintWriter) extends TreePrinter(out) { + override def printRow(ts: List[Tree], start: String, sep: String, end: String) { + print(start) + printSeq(ts)(print(_))(print(sep)) + print(end) + } + + // drill down through Blocks and pull out the real statements. + def allStatements(t: Tree): List[Tree] = t match { + case Block(stmts, expr) => (stmts flatMap allStatements) ::: List(expr) + case _ => List(t) + } + + def printLogicalOr(t1: (Tree, Boolean), t2: (Tree, Boolean)) = + printLogicalOp(t1, t2, "||") + + def printLogicalAnd(t1: (Tree, Boolean), t2: (Tree, Boolean)) = + printLogicalOp(t1, t2, "&&") + + def printLogicalOp(t1: (Tree, Boolean), t2: (Tree, Boolean), op: String) = { + def maybenot(tvalue: Boolean) = if (tvalue) "" else "!" + + print("%s(" format maybenot(t1._2)) + printTree(t1._1) + print(") %s %s(".format(op, maybenot(t2._2))) + printTree(t2._1) + print(")") + } + + override def printTree(tree: Tree): Unit = { + // routing supercalls through this for debugging ease + def s() = super.printTree(tree) + + tree match { + // labels used for jumps - does not map to valid scala code + case LabelDef(name, params, rhs) => + print("labeldef %s(%s) = ".format(name, params mkString ",")) + printTree(rhs) + + case Ident(name) => + print(decodedSymName(tree, name)) + + // target.method(arg) ==> target method arg + case Apply(Select(target, method), List(arg)) => + if (method.decode.toString == "||") + printLogicalOr(target -> true, arg -> true) + else if (method.decode.toString == "&&") + printLogicalAnd(target -> true, arg -> true) + else (target, arg) match { + case (_: Ident, _: Literal | _: Ident) => + printTree(target) + print(" ") + printTree(Ident(method)) + print(" ") + printTree(arg) + case _ => s() + } + + // target.unary_! ==> !target + case Select(qualifier, name) if (name.decode startsWith "unary_") => + print(name.decode drop 6) + printTree(qualifier) + + case Select(qualifier, name) => + printTree(qualifier) + print(".") + print(quotedName(name, true)) + + // target.toString() ==> target.toString + case Apply(fn, Nil) => printTree(fn) + + // if a Block only continues one actual statement, just print it. + case Block(stats, expr) => + allStatements(tree) match { + case List(x) => printTree(x) + case xs => s() + } + + // We get a lot of this stuff + case If( IsTrue(), x, _) => printTree(x) + case If(IsFalse(), _, x) => printTree(x) + + case If(cond, IsTrue(), elsep) => printLogicalOr(cond -> true, elsep -> true) + case If(cond, IsFalse(), elsep) => printLogicalAnd(cond -> false, elsep -> true) + case If(cond, thenp, IsTrue()) => printLogicalOr(cond -> false, thenp -> true) + case If(cond, thenp, IsFalse()) => printLogicalAnd(cond -> true, thenp -> true) + + // If thenp or elsep has only one statement, it doesn't need more than one line. + case If(cond, thenp, elsep) => + def ifIndented(x: Tree) = { + indent ; println() ; printTree(x) ; undent + } + + val List(thenStmts, elseStmts) = List(thenp, elsep) map allStatements + print("if ("); print(cond); print(") ") + + thenStmts match { + case List(x: If) => ifIndented(x) + case List(x) => printTree(x) + case _ => printTree(thenp) + } + + if (elseStmts.nonEmpty) { + print(" else") + indent ; println() + elseStmts match { + case List(x) => printTree(x) + case _ => printTree(elsep) + } + undent ; println() + } + case _ => s() + } + } + } + + /** This must guarantee not to force any evaluation, so we can learn + * a little bit about trees in the midst of compilation without altering + * the natural course of events. + */ + class SafeTreePrinter(out: PrintWriter) extends TreePrinter(out) { + + private def default(t: Tree) = t.getClass.getName.reverse.takeWhile(_ != '.').reverse + private def params(trees: List[Tree]): String = trees map safe mkString ", " + + private def safe(name: Name): String = name.decode + private def safe(tree: Tree): String = tree match { + case Apply(fn, args) => "%s(%s)".format(safe(fn), params(args)) + case Select(qual, name) => safe(qual) + "." + safe(name) + case This(qual) => safe(qual) + ".this" + case Ident(name) => safe(name) + case Literal(value) => value.stringValue + case _ => "(?: %s)".format(default(tree)) + } + + override def printTree(tree: Tree) { print(safe(tree)) } + } + + class TreeMatchTemplate { + // non-trees defined in Trees + // + // case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) + // case class Modifiers(flags: Long, privateWithin: Name, annotations: List[Tree], positions: Map[Long, Position]) + // + def apply(t: Tree): Unit = t match { + // eliminated by typer + case Annotated(annot, arg) => + case AssignOrNamedArg(lhs, rhs) => + case DocDef(comment, definition) => + case Import(expr, selectors) => + + // eliminated by refchecks + case ModuleDef(mods, name, impl) => + case TypeTreeWithDeferredRefCheck() => + + // eliminated by erasure + case TypeDef(mods, name, tparams, rhs) => + case Typed(expr, tpt) => + + // eliminated by cleanup + case ApplyDynamic(qual, args) => + + // eliminated by explicitouter + case Alternative(trees) => + case Bind(name, body) => + case CaseDef(pat, guard, body) => + case Star(elem) => + case UnApply(fun, args) => + + // eliminated by lambdalift + case Function(vparams, body) => + + // eliminated by uncurry + case AppliedTypeTree(tpt, args) => + case CompoundTypeTree(templ) => + case ExistentialTypeTree(tpt, whereClauses) => + case SelectFromTypeTree(qual, selector) => + case SingletonTypeTree(ref) => + case TypeBoundsTree(lo, hi) => + + // survivors + case Apply(fun, args) => + case ArrayValue(elemtpt, trees) => + case Assign(lhs, rhs) => + case Block(stats, expr) => + case ClassDef(mods, name, tparams, impl) => + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case EmptyTree => + case Ident(name) => + case If(cond, thenp, elsep) => + case LabelDef(name, params, rhs) => + case Literal(value) => + case Match(selector, cases) => + case New(tpt) => + case PackageDef(pid, stats) => + case Return(expr) => + case Select(qualifier, selector) => + case Super(qual, mix) => + case Template(parents, self, body) => + case This(qual) => + case Throw(expr) => + case Try(block, catches, finalizer) => + case TypeApply(fun, args) => + case TypeTree() => + case ValDef(mods, name, tpt, rhs) => + + // missing from the Trees comment + case Parens(args) => // only used during parsing + case SelectFromArray(qual, name, erasure) => // only used during erasure + } + } + + def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) + def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) + + def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) + def newStandardTreePrinter(stream: OutputStream): TreePrinter = newStandardTreePrinter(new PrintWriter(stream)) + def newStandardTreePrinter(): TreePrinter = newStandardTreePrinter(new PrintWriter(ConsoleWriter)) + + def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) + def newCompactTreePrinter(stream: OutputStream): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(stream)) + def newCompactTreePrinter(): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(ConsoleWriter)) + + override def newTreePrinter(writer: PrintWriter): TreePrinter = + if (settings.Ycompacttrees.value) newCompactTreePrinter(writer) + else newStandardTreePrinter(writer) + override def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) + override def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) +} diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala deleted file mode 100644 index 3371353f25..0000000000 --- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala +++ /dev/null @@ -1,295 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package ast - -import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } -import symtab.Flags._ -import symtab.SymbolTable - -trait TreePrinters extends reflect.internal.TreePrinters { this: Global => - - import treeInfo.{ IsTrue, IsFalse } - - class TreePrinter(out: PrintWriter) extends super.TreePrinter(out) { - - override def print(args: Any*): Unit = args foreach { - case tree: Tree => - printPosition(tree) - printTree( - if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) { - tree match { - case ClassDef(_, _, _, impl @ Template(ps, emptyValDef, body)) - if (tree.symbol.thisSym != tree.symbol) => - ClassDef(tree.symbol, Template(ps, ValDef(tree.symbol.thisSym), body)) - case ClassDef(_, _, _, impl) => ClassDef(tree.symbol, impl) - case ModuleDef(_, _, impl) => ModuleDef(tree.symbol, impl) - case ValDef(_, _, _, rhs) => ValDef(tree.symbol, rhs) - case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs) - case TypeDef(_, _, _, rhs) => TypeDef(tree.symbol, rhs) - case _ => tree - } - } else tree) - case unit: CompilationUnit => - print("// Scala source: " + unit.source + "\n") - if (unit.body == null) print("") - else { print(unit.body); println() } - println() - out.flush() - case arg => - super.print(arg) - } - } - - // overflow cases missing from TreePrinter in reflect.api - override def xprintTree(treePrinter: super.TreePrinter, tree: Tree) = tree match { - case DocDef(comment, definition) => - treePrinter.print(comment.raw) - treePrinter.println() - treePrinter.print(definition) - - case TypeTreeWithDeferredRefCheck() => - treePrinter.print("") - - case SelectFromArray(qualifier, name, _) => - treePrinter.print(qualifier, ".", symName(tree, name)) - - case _ => - super.xprintTree(treePrinter, tree) - } - - /** A tree printer which is stingier about vertical whitespace and unnecessary - * punctuation than the standard one. - */ - class CompactTreePrinter(out: PrintWriter) extends TreePrinter(out) { - override def printRow(ts: List[Tree], start: String, sep: String, end: String) { - print(start) - printSeq(ts)(print(_))(print(sep)) - print(end) - } - - // drill down through Blocks and pull out the real statements. - def allStatements(t: Tree): List[Tree] = t match { - case Block(stmts, expr) => (stmts flatMap allStatements) ::: List(expr) - case _ => List(t) - } - - def printLogicalOr(t1: (Tree, Boolean), t2: (Tree, Boolean)) = - printLogicalOp(t1, t2, "||") - - def printLogicalAnd(t1: (Tree, Boolean), t2: (Tree, Boolean)) = - printLogicalOp(t1, t2, "&&") - - def printLogicalOp(t1: (Tree, Boolean), t2: (Tree, Boolean), op: String) = { - def maybenot(tvalue: Boolean) = if (tvalue) "" else "!" - - print("%s(" format maybenot(t1._2)) - printTree(t1._1) - print(") %s %s(".format(op, maybenot(t2._2))) - printTree(t2._1) - print(")") - } - - override def printTree(tree: Tree): Unit = { - // routing supercalls through this for debugging ease - def s() = super.printTree(tree) - - tree match { - // labels used for jumps - does not map to valid scala code - case LabelDef(name, params, rhs) => - print("labeldef %s(%s) = ".format(name, params mkString ",")) - printTree(rhs) - - case Ident(name) => - print(decodedSymName(tree, name)) - - // target.method(arg) ==> target method arg - case Apply(Select(target, method), List(arg)) => - if (method.decode.toString == "||") - printLogicalOr(target -> true, arg -> true) - else if (method.decode.toString == "&&") - printLogicalAnd(target -> true, arg -> true) - else (target, arg) match { - case (_: Ident, _: Literal | _: Ident) => - printTree(target) - print(" ") - printTree(Ident(method)) - print(" ") - printTree(arg) - case _ => s() - } - - // target.unary_! ==> !target - case Select(qualifier, name) if (name.decode startsWith "unary_") => - print(name.decode drop 6) - printTree(qualifier) - - case Select(qualifier, name) => - printTree(qualifier) - print(".") - print(quotedName(name, true)) - - // target.toString() ==> target.toString - case Apply(fn, Nil) => printTree(fn) - - // if a Block only continues one actual statement, just print it. - case Block(stats, expr) => - allStatements(tree) match { - case List(x) => printTree(x) - case xs => s() - } - - // We get a lot of this stuff - case If( IsTrue(), x, _) => printTree(x) - case If(IsFalse(), _, x) => printTree(x) - - case If(cond, IsTrue(), elsep) => printLogicalOr(cond -> true, elsep -> true) - case If(cond, IsFalse(), elsep) => printLogicalAnd(cond -> false, elsep -> true) - case If(cond, thenp, IsTrue()) => printLogicalOr(cond -> false, thenp -> true) - case If(cond, thenp, IsFalse()) => printLogicalAnd(cond -> true, thenp -> true) - - // If thenp or elsep has only one statement, it doesn't need more than one line. - case If(cond, thenp, elsep) => - def ifIndented(x: Tree) = { - indent ; println() ; printTree(x) ; undent - } - - val List(thenStmts, elseStmts) = List(thenp, elsep) map allStatements - print("if ("); print(cond); print(") ") - - thenStmts match { - case List(x: If) => ifIndented(x) - case List(x) => printTree(x) - case _ => printTree(thenp) - } - - if (elseStmts.nonEmpty) { - print(" else") - indent ; println() - elseStmts match { - case List(x) => printTree(x) - case _ => printTree(elsep) - } - undent ; println() - } - case _ => s() - } - } - } - - /** This must guarantee not to force any evaluation, so we can learn - * a little bit about trees in the midst of compilation without altering - * the natural course of events. - */ - class SafeTreePrinter(out: PrintWriter) extends TreePrinter(out) { - - private def default(t: Tree) = t.getClass.getName.reverse.takeWhile(_ != '.').reverse - private def params(trees: List[Tree]): String = trees map safe mkString ", " - - private def safe(name: Name): String = name.decode - private def safe(tree: Tree): String = tree match { - case Apply(fn, args) => "%s(%s)".format(safe(fn), params(args)) - case Select(qual, name) => safe(qual) + "." + safe(name) - case This(qual) => safe(qual) + ".this" - case Ident(name) => safe(name) - case Literal(value) => value.stringValue - case _ => "(?: %s)".format(default(tree)) - } - - override def printTree(tree: Tree) { print(safe(tree)) } - } - - class TreeMatchTemplate { - // non-trees defined in Trees - // - // case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) - // case class Modifiers(flags: Long, privateWithin: Name, annotations: List[Tree], positions: Map[Long, Position]) - // - def apply(t: Tree): Unit = t match { - // eliminated by typer - case Annotated(annot, arg) => - case AssignOrNamedArg(lhs, rhs) => - case DocDef(comment, definition) => - case Import(expr, selectors) => - - // eliminated by refchecks - case ModuleDef(mods, name, impl) => - case TypeTreeWithDeferredRefCheck() => - - // eliminated by erasure - case TypeDef(mods, name, tparams, rhs) => - case Typed(expr, tpt) => - - // eliminated by cleanup - case ApplyDynamic(qual, args) => - - // eliminated by explicitouter - case Alternative(trees) => - case Bind(name, body) => - case CaseDef(pat, guard, body) => - case Star(elem) => - case UnApply(fun, args) => - - // eliminated by lambdalift - case Function(vparams, body) => - - // eliminated by uncurry - case AppliedTypeTree(tpt, args) => - case CompoundTypeTree(templ) => - case ExistentialTypeTree(tpt, whereClauses) => - case SelectFromTypeTree(qual, selector) => - case SingletonTypeTree(ref) => - case TypeBoundsTree(lo, hi) => - - // survivors - case Apply(fun, args) => - case ArrayValue(elemtpt, trees) => - case Assign(lhs, rhs) => - case Block(stats, expr) => - case ClassDef(mods, name, tparams, impl) => - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - case EmptyTree => - case Ident(name) => - case If(cond, thenp, elsep) => - case LabelDef(name, params, rhs) => - case Literal(value) => - case Match(selector, cases) => - case New(tpt) => - case PackageDef(pid, stats) => - case Return(expr) => - case Select(qualifier, selector) => - case Super(qual, mix) => - case Template(parents, self, body) => - case This(qual) => - case Throw(expr) => - case Try(block, catches, finalizer) => - case TypeApply(fun, args) => - case TypeTree() => - case ValDef(mods, name, tpt, rhs) => - - // missing from the Trees comment - case Parens(args) => // only used during parsing - case SelectFromArray(qual, name, erasure) => // only used during erasure - } - } - - def asString(t: Tree): String = show(t, newStandardTreePrinter) - def asCompactString(t: Tree): String = show(t, newCompactTreePrinter) - - def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) - def newStandardTreePrinter(stream: OutputStream): TreePrinter = newStandardTreePrinter(new PrintWriter(stream)) - def newStandardTreePrinter(): TreePrinter = newStandardTreePrinter(new PrintWriter(ConsoleWriter)) - - def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) - def newCompactTreePrinter(stream: OutputStream): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(stream)) - def newCompactTreePrinter(): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(ConsoleWriter)) - - override def newTreePrinter(writer: PrintWriter): TreePrinter = - if (settings.Ycompacttrees.value) newCompactTreePrinter(writer) - else newStandardTreePrinter(writer) - override def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) - override def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) -} diff --git a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala index 72e6f32af1..16761144d7 100644 --- a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala +++ b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package matching import transform.ExplicitOuter -import ast.{ TreePrinters, Trees } +import ast.{ Printers, Trees } import java.io.{ StringWriter, PrintWriter } import annotation.elidable import language.postfixOps diff --git a/src/library/scala/reflect/base/Base.scala b/src/library/scala/reflect/base/Base.scala index 461eaa2e9e..490a9e8c03 100644 --- a/src/library/scala/reflect/base/Base.scala +++ b/src/library/scala/reflect/base/Base.scala @@ -451,7 +451,7 @@ class Base extends Universe { self => } } - def show(tree: Tree) = s"" + def treeToString(tree: Tree) = s"" trait TermTree extends Tree diff --git a/src/library/scala/reflect/base/Trees.scala b/src/library/scala/reflect/base/Trees.scala index 298d229570..2814450ae3 100644 --- a/src/library/scala/reflect/base/Trees.scala +++ b/src/library/scala/reflect/base/Trees.scala @@ -28,11 +28,11 @@ trait Trees { self: Universe => def isType: Boolean /** Obtains string representation of a tree */ - override def toString: String = show(this) + override def toString: String = treeToString(this) } /** Obtains string representation of a tree */ - def show(tree: Tree): String + protected def treeToString(tree: Tree): String /** Tree is the basis for scala's abstract syntax. The nodes are * implemented as case classes, and the parameters which initialize diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala new file mode 100644 index 0000000000..7f4ff8a7fb --- /dev/null +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -0,0 +1,94 @@ +package scala.reflect +package api + +import java.io.{ PrintWriter, StringWriter } + +trait Printers { self: Universe => + + trait TreePrinter { + def print(args: Any*) + protected var printTypes = false + protected var printIds = false + protected var printKinds = false + def withTypes: this.type = { printTypes = true; this } + def withoutTypes: this.type = { printTypes = false; this } + def withIds: this.type = { printIds = true; this } + def withoutIds: this.type = { printIds = false; this } + def withKinds: this.type = { printKinds = true; this } + def withoutKinds: this.type = { printKinds = false; this } + } + + case class BooleanFlag(val value: Option[Boolean]) + object BooleanFlag { + import language.implicitConversions + implicit def booleanToBooleanFlag(value: Boolean): BooleanFlag = BooleanFlag(Some(value)) + implicit def optionToBooleanFlag(value: Option[Boolean]): BooleanFlag = BooleanFlag(value) + } + + protected def render(what: Any, mkPrinter: PrintWriter => TreePrinter, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None): String = { + val buffer = new StringWriter() + val writer = new PrintWriter(buffer) + var printer = mkPrinter(writer) + printTypes.value.map(printTypes => if (printTypes) printer.withTypes else printer.withoutTypes) + printIds.value.map(printIds => if (printIds) printer.withIds else printer.withoutIds) + printKinds.value.map(printKinds => if (printKinds) printer.withKinds else printer.withoutKinds) + printer.print(what) + writer.flush() + buffer.toString + } + + /** By default trees are printed with `show` */ + override protected def treeToString(tree: Tree) = show(tree) + + /** Renders a prettified representation of a tree. + * Typically it looks very close to the Scala code it represents. + * This function is used in Tree.toString. + */ + def show(tree: Tree, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None): String = + render(tree, newTreePrinter(_), printTypes, printIds, printKinds) + + /** Hook to define what `show(tree)` means. + */ + def newTreePrinter(out: PrintWriter): TreePrinter + + /** Renders internal structure of a tree. + */ + def showRaw(tree: Tree, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None): String = + render(tree, newRawTreePrinter(_), printTypes, printIds, printKinds) + + /** Hook to define what `showRaw(tree)` means. + */ + def newRawTreePrinter(out: PrintWriter): TreePrinter + + /** Renders a prettified representation of a symbol. + */ + def show(sym: Symbol): String = sym.toString + + /** Renders internal structure of a symbol. + */ + def showRaw(sym: Symbol): String = render(sym, newRawTreePrinter(_)) + + /** Renders a prettified representation of a type. + */ + def show(tpe: Type): String = tpe.toString + + /** Renders internal structure of a type. + */ + def showRaw(tpe: Type): String = render(tpe, newRawTreePrinter(_)) + + /** Renders a prettified representation of a name. + */ + def show(name: Name): String + + /** Renders internal structure of a name. + */ + def showRaw(name: Name): String = name.toString + + /** Renders a prettified representation of a flag set. + */ + def show(flags: FlagSet): String + + /** Renders internal structure of a flag set. + */ + def showRaw(flags: FlagSet): String = flags.toString +} diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 1d266dc778..130f9e612a 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -177,6 +177,25 @@ trait Symbols extends base.Symbols { self: Universe => */ def isErroneous : Boolean + /** Can this symbol be loaded by a reflective mirror? + * + * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. + * Such annotations (also called "pickles") are applied on top-level classes and include information + * about all symbols reachable from the annotee. However, local symbols (e.g. classes or definitions local to a block) + * are typically unreachable and information about them gets lost. + * + * This method is useful for macro writers who wish to save certain ASTs to be used at runtime. + * With `isLocatable' it's possible to check whether a tree can be retained as is, or it needs special treatment. + */ + def isLocatable: Boolean + + /** Is this symbol static (i.e. with no outer instance)? + * Q: When exactly is a sym marked as STATIC? + * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. + * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 + */ + def isStatic: Boolean + /** The type signature of this symbol seen as a member of given type `site`. */ def typeSignatureIn(site: Type): Type diff --git a/src/reflect/scala/reflect/api/TreePrinters.scala b/src/reflect/scala/reflect/api/TreePrinters.scala deleted file mode 100644 index 08a08e7b90..0000000000 --- a/src/reflect/scala/reflect/api/TreePrinters.scala +++ /dev/null @@ -1,87 +0,0 @@ -package scala.reflect -package api - -import java.io.{ PrintWriter, StringWriter } - -trait TreePrinters { self: Universe => - - trait TreePrinter { - def print(args: Any*) - protected var typesPrinted = false - protected var uniqueIds = false - def withTypesPrinted: this.type = { typesPrinted = true; this } - def withUniqueIds: this.type = { uniqueIds = true; this } - } - - def show(tree: Tree): String = show(tree, newTreePrinter) - - def show(tree: Tree, mkPrinter: PrintWriter => TreePrinter): String = { - val buffer = new StringWriter() - val writer = new PrintWriter(buffer) - val printer = mkPrinter(writer) - printer.print(tree) - writer.flush() - buffer.toString - } - - def showRaw(tree: Tree): String = show(tree, new RawTreePrinter(_)) - - /** Hook to define what `show(tree)` means. - */ - def newTreePrinter(out: PrintWriter): TreePrinter - - // emits more or less verbatim representation of the provided tree - // [Eugene] todo. needs to be refined - // http://groups.google.com/group/scala-user/browse_thread/thread/de5a5be2e083cf8e - class RawTreePrinter(out: PrintWriter) extends TreePrinter { - def print(args: Any*): Unit = args foreach { - case EmptyTree => - print("EmptyTree") - case tree @ TypeTree() => - print("TypeTree()") - if (tree.tpe != null) - print(".setType(", tree.tpe, ")") - else if (tree.original != null) - print(".setOriginal(", tree.original, ")") - case Literal(Constant(s: String)) => - print("Literal(Constant(\"" + s + "\"))") - case tree: Tree => - print(tree.productPrefix+"(") - val it = tree.productIterator - while (it.hasNext) { - it.next() match { - case name: Name if uniqueIds && tree.hasSymbol && tree.symbol != NoSymbol => - print(tree.symbol.name, "#", tree.symbol.id) - case arg => - print(arg) - } - print(if (it.hasNext) ", " else "") - } - print(")") - if (typesPrinted) - print(".setType(", tree.tpe, ")") - case list: List[_] => - print("List(") - val it = list.iterator - while (it.hasNext) { - print(it.next()) - print(if (it.hasNext) ", " else "") - } - print(")") - case mods: Modifiers => - val parts = collection.mutable.ListBuffer[String]() - parts += mods.flagString - if (mods.privateWithin.toString.nonEmpty) - parts += "newTypeName(\"" + mods.privateWithin.toString + "\")" - if (mods.annotations.nonEmpty) - parts += mods.annotations map showRaw mkString ("List(", ", ", ")") - print(parts mkString ("Modifiers(", ", ", ")")) - case name: Name => - if (name.isTermName) print("newTermName(\"") else print("newTypeName(\"") - print(name.toString) - print("\")") - case arg => - out.print(arg) - } - } -} diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala index 002cd2e673..85d8adc44f 100644 --- a/src/reflect/scala/reflect/api/Universe.scala +++ b/src/reflect/scala/reflect/api/Universe.scala @@ -9,7 +9,7 @@ abstract class Universe extends base.Universe with FlagSets with Names with Trees - with TreePrinters + with Printers with Constants with Positions with Mirrors diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala new file mode 100644 index 0000000000..82a8c42f7c --- /dev/null +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -0,0 +1,642 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +// [Eugene++ to Martin] we need to unify this prettyprinter with NodePrinters + +package scala.reflect +package internal + +import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } +import Flags._ + +trait Printers extends api.Printers { self: SymbolTable => + + //nsc import treeInfo.{ IsTrue, IsFalse } + + final val showOuterTests = false + + /** Adds backticks if the name is a scala keyword. */ + def quotedName(name: Name, decode: Boolean): String = { + val s = if (decode) name.decode else name.toString + val term = name.toTermName + if (nme.keywords(term) && term != nme.USCOREkw) "`%s`" format s + else s + } + def quotedName(name: Name): String = quotedName(name, false) + def quotedName(name: String): String = quotedName(newTermName(name), false) + + private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { + val sym = tree.symbol + if (sym.name.toString == nme.ERROR.toString) { + "<" + quotedName(name, decoded) + ": error>" + } else if (sym != null && sym != NoSymbol) { + val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" + var suffix = "" + if (settings.uniqid.value) suffix += ("#" + sym.id) + if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) + prefix + quotedName(tree.symbol.decodedName) + suffix + } else { + quotedName(name, decoded) + } + } + + def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) + def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) + + /** Turns a path into a String, introducing backquotes + * as necessary. + */ + def backquotedPath(t: Tree): String = { + t match { + case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) + case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) + case Ident(name) => symName(t, name) + case _ => t.toString + } + } + + class TreePrinter(out: PrintWriter) extends super.TreePrinter { + protected var indentMargin = 0 + protected val indentStep = 2 + protected var indentString = " " // 40 + + printTypes = settings.printtypes.value + printIds = settings.uniqid.value + printKinds = settings.Yshowsymkinds.value + protected def doPrintPositions = settings.Xprintpos.value + + def indent() = indentMargin += indentStep + def undent() = indentMargin -= indentStep + + def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) + + def println() { + out.println() + while (indentMargin > indentString.length()) + indentString += indentString + if (indentMargin > 0) + out.write(indentString, 0, indentMargin) + } + + def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit) { + ls match { + case List() => + case List(x) => printelem(x) + case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) + } + } + + def printColumn(ts: List[Tree], start: String, sep: String, end: String) { + print(start); indent; println() + printSeq(ts){print(_)}{print(sep); println()}; undent; println(); print(end) + } + + def printRow(ts: List[Tree], start: String, sep: String, end: String) { + print(start); printSeq(ts){print(_)}{print(sep)}; print(end) + } + + def printRow(ts: List[Tree], sep: String) { printRow(ts, "", sep, "") } + + def printTypeParams(ts: List[TypeDef]) { + if (!ts.isEmpty) { + print("["); printSeq(ts){ t => + printAnnotations(t) + printParam(t) + }{print(", ")}; print("]") + } + } + + def printLabelParams(ps: List[Ident]) { + print("(") + printSeq(ps){printLabelParam}{print(", ")} + print(")") + } + + def printLabelParam(p: Ident) { + print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe) + } + + def printValueParams(ts: List[ValDef]) { + print("(") + if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "") + printSeq(ts){printParam}{print(", ")} + print(")") + } + + def printParam(tree: Tree) { + tree match { + case ValDef(mods, name, tp, rhs) => + printPosition(tree) + printAnnotations(tree) + print(symName(tree, name)); printOpt(": ", tp); printOpt(" = ", rhs) + case TypeDef(mods, name, tparams, rhs) => + printPosition(tree) + print(symName(tree, name)) + printTypeParams(tparams); print(rhs) + } + } + + def printBlock(tree: Tree) { + tree match { + case Block(_, _) => + print(tree) + case _ => + printColumn(List(tree), "{", ";", "}") + } + } + + private def symFn[T](tree: Tree, f: Symbol => T, orElse: => T): T = tree.symbol match { + case null | NoSymbol => orElse + case sym => f(sym) + } + private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) + + def printOpt(prefix: String, tree: Tree) { + if (!tree.isEmpty) { print(prefix, tree) } + } + + def printModifiers(tree: Tree, mods: Modifiers): Unit = printFlags( + if (tree.symbol == NoSymbol) mods.flags else tree.symbol.flags, "" + ( + if (tree.symbol == NoSymbol) mods.privateWithin + else if (tree.symbol.hasAccessBoundary) tree.symbol.privateWithin.name + else "" + ) + ) + + def printFlags(flags: Long, privateWithin: String) { + var mask: Long = if (settings.debug.value) -1L else PrintableFlags + val s = flagsToString(flags & mask, privateWithin) + if (s != "") print(s + " ") + } + + def printAnnotations(tree: Tree) { + if (!isCompilerUniverse && tree.symbol != null && tree.symbol != NoSymbol) + // [Eugene++] todo. this is not 100% correct, but is necessary for sane printing + // the problem is that getting annotations doesn't automatically initialize the symbol + // so we might easily print something as if it doesn't have annotations, whereas it does + tree.symbol.initialize + + val annots = tree.symbol.annotations match { + case Nil => tree.asInstanceOf[MemberDef].mods.annotations + case anns => anns + } + annots foreach (annot => print("@"+annot+" ")) + } + + private var currentOwner: Symbol = NoSymbol + private var selectorType: Type = NoType + + def printTree(tree: Tree) { + tree match { + case EmptyTree => + print("") + + case ClassDef(mods, name, tparams, impl) => + printAnnotations(tree) + printModifiers(tree, mods) + val word = + if (mods.isTrait) "trait" + else if (ifSym(tree, _.isModuleClass)) "object" + else "class" + + print(word, " ", symName(tree, name)) + printTypeParams(tparams) + print(if (mods.isDeferred) " <: " else " extends ", impl) + + case PackageDef(packaged, stats) => + printAnnotations(tree) + print("package ", packaged); printColumn(stats, " {", ";", "}") + + case ModuleDef(mods, name, impl) => + printAnnotations(tree) + printModifiers(tree, mods); + print("object " + symName(tree, name), " extends ", impl) + + case ValDef(mods, name, tp, rhs) => + printAnnotations(tree) + printModifiers(tree, mods) + print(if (mods.isMutable) "var " else "val ", symName(tree, name)) + printOpt(": ", tp) + if (!mods.isDeferred) + print(" = ", if (rhs.isEmpty) "_" else rhs) + + case DefDef(mods, name, tparams, vparamss, tp, rhs) => + printAnnotations(tree) + printModifiers(tree, mods) + print("def " + symName(tree, name)) + printTypeParams(tparams); vparamss foreach printValueParams + printOpt(": ", tp); printOpt(" = ", rhs) + + case TypeDef(mods, name, tparams, rhs) => + if (mods hasFlag (PARAM | DEFERRED)) { + printAnnotations(tree) + printModifiers(tree, mods); print("type "); printParam(tree) + } else { + printAnnotations(tree) + printModifiers(tree, mods); print("type " + symName(tree, name)) + printTypeParams(tparams); printOpt(" = ", rhs) + } + + case LabelDef(name, params, rhs) => + print(symName(tree, name)); printLabelParams(params); printBlock(rhs) + + case Import(expr, selectors) => + // Is this selector remapping a name (i.e, {name1 => name2}) + def isNotRemap(s: ImportSelector) : Boolean = (s.name == nme.WILDCARD || s.name == s.rename) + def selectorToString(s: ImportSelector): String = { + val from = quotedName(s.name) + if (isNotRemap(s)) from + else from + "=>" + quotedName(s.rename) + } + print("import ", backquotedPath(expr), ".") + selectors match { + case List(s) => + // If there is just one selector and it is not remapping a name, no braces are needed + if (isNotRemap(s)) print(selectorToString(s)) + else print("{", selectorToString(s), "}") + // If there is more than one selector braces are always needed + case many => + print(many.map(selectorToString).mkString("{", ", ", "}")) + } + + case Template(parents, self, body) => + val currentOwner1 = currentOwner + if (tree.symbol != NoSymbol) currentOwner = tree.symbol.owner +// if (parents exists isReferenceToAnyVal) { +// print("AnyVal") +// } +// else { + printRow(parents, " with ") + if (!body.isEmpty) { + if (self.name != nme.WILDCARD) { + print(" { ", self.name); printOpt(": ", self.tpt); print(" => ") + } else if (!self.tpt.isEmpty) { + print(" { _ : ", self.tpt, " => ") + } else { + print(" {") + } + printColumn(body, "", ";", "}") + } +// } + currentOwner = currentOwner1 + + case Block(stats, expr) => + printColumn(stats ::: List(expr), "{", ";", "}") + + case Match(selector, cases) => + val selectorType1 = selectorType + selectorType = selector.tpe + print(selector); printColumn(cases, " match {", "", "}") + selectorType = selectorType1 + + case CaseDef(pat, guard, body) => + print("case ") + def patConstr(pat: Tree): Tree = pat match { + case Apply(fn, args) => patConstr(fn) + case _ => pat + } + if (showOuterTests && + needsOuterTest( + patConstr(pat).tpe.finalResultType, selectorType, currentOwner)) + print("???") + print(pat); printOpt(" if ", guard) + print(" => ", body) + + case Alternative(trees) => + printRow(trees, "(", "| ", ")") + + case Star(elem) => + print("(", elem, ")*") + + case Bind(name, t) => + print("(", symName(tree, name), " @ ", t, ")") + + case UnApply(fun, args) => + print(fun, " "); printRow(args, "(", ", ", ")") + + case ArrayValue(elemtpt, trees) => + print("Array[", elemtpt); printRow(trees, "]{", ", ", "}") + + case Function(vparams, body) => + print("("); printValueParams(vparams); print(" => ", body, ")") + if (printIds && tree.symbol != null) print("#"+tree.symbol.id) + + case Assign(lhs, rhs) => + print(lhs, " = ", rhs) + + case AssignOrNamedArg(lhs, rhs) => + print(lhs, " = ", rhs) + + case If(cond, thenp, elsep) => + print("if (", cond, ")"); indent; println() + print(thenp); undent + if (!elsep.isEmpty) { + println(); print("else"); indent; println(); print(elsep); undent + } + + case Return(expr) => + print("return ", expr) + + case Try(block, catches, finalizer) => + print("try "); printBlock(block) + if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") + printOpt(" finally ", finalizer) + + case Throw(expr) => + print("throw ", expr) + + case New(tpe) => + print("new ", tpe) + + case Typed(expr, tp) => + print("(", expr, ": ", tp, ")") + + case TypeApply(fun, targs) => + print(fun); printRow(targs, "[", ", ", "]") + + case Apply(fun, vargs) => + print(fun); printRow(vargs, "(", ", ", ")") + + case ApplyDynamic(qual, vargs) => + print("(", qual, "#", tree.symbol.nameString) + printRow(vargs, ", (", ", ", "))") + + case Super(This(qual), mix) => + if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + ".") + print("super") + if (!mix.isEmpty) + print("[" + mix + "]") + + case Super(qual, mix) => + print(qual, ".super") + if (!mix.isEmpty) + print("[" + mix + "]") + + case This(qual) => + if (!qual.isEmpty) print(symName(tree, qual) + ".") + print("this") + + case Select(qual @ New(tpe), name) if (!settings.debug.value) => + print(qual) + + case Select(qualifier, name) => + print(backquotedPath(qualifier), ".", symName(tree, name)) + + case id @ Ident(name) => + val str = symName(tree, name) + print( if (id.isBackquoted) "`" + str + "`" else str ) + + case Literal(x) => + print(x.escapedStringValue) + + case tt: TypeTree => + if ((tree.tpe eq null) || (doPrintPositions && tt.original != null)) { + if (tt.original != null) print("") + else print("") + } else if ((tree.tpe.typeSymbol ne null) && tree.tpe.typeSymbol.isAnonymousClass) { + print(tree.tpe.typeSymbol.toString) + } else { + print(tree.tpe.toString) + } + + case Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => + def printAnnot() { + print("@", tpt) + if (!args.isEmpty) + printRow(args, "(", ",", ")") + } + print(tree, if (tree.isType) " " else ": ") + printAnnot() + + case SingletonTypeTree(ref) => + print(ref, ".type") + + case SelectFromTypeTree(qualifier, selector) => + print(qualifier, "#", symName(tree, selector)) + + case CompoundTypeTree(templ) => + print(templ) + + case AppliedTypeTree(tp, args) => + print(tp); printRow(args, "[", ", ", "]") + + case TypeBoundsTree(lo, hi) => + printOpt(" >: ", lo); printOpt(" <: ", hi) + + case ExistentialTypeTree(tpt, whereClauses) => + print(tpt); + printColumn(whereClauses, " forSome { ", ";", "}") + +// SelectFromArray is no longer visible in reflect.internal. +// eliminated until we figure out what we will do with both Printers and +// SelectFromArray. +// case SelectFromArray(qualifier, name, _) => +// print(qualifier); print("."); print(symName(tree, name)) + + case tree => + xprintTree(this, tree) + } + if (printTypes && tree.isTerm && !tree.isEmpty) { + print("{", if (tree.tpe eq null) "" else tree.tpe.toString, "}") + } + } + + def print(args: Any*): Unit = args foreach { + case tree: Tree => + printPosition(tree) + printTree(tree) + case name: Name => + print(quotedName(name)) + case arg => + out.print(if (arg == null) "null" else arg.toString) + } + } + + /** Hook for extensions */ + def xprintTree(treePrinter: TreePrinter, tree: Tree) = + treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) + + def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) + def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) + def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) + + /** A writer that writes to the current Console and + * is sensitive to replacement of the Console's + * output stream. + */ + object ConsoleWriter extends Writer { + override def write(str: String) { Console.print(str) } + + def write(cbuf: Array[Char], off: Int, len: Int) { + write(new String(cbuf, off, len)) + } + + def close = { /* do nothing */ } + def flush = { /* do nothing */ } + } + + // provides footnotes for types + private var typeCounter = 0 + private val typeMap = collection.mutable.WeakHashMap[Type, Int]() + + def newRawTreePrinter(writer: PrintWriter): RawTreePrinter = new RawTreePrinter(writer) + def newRawTreePrinter(stream: OutputStream): RawTreePrinter = newRawTreePrinter(new PrintWriter(stream)) + def newRawTreePrinter(): RawTreePrinter = newRawTreePrinter(new PrintWriter(ConsoleWriter)) + + // emits more or less verbatim representation of the provided tree + class RawTreePrinter(out: PrintWriter) extends super.TreePrinter { + private var depth = 0 + private var footnotes = collection.mutable.Map[Int, Type]() + private var printingFootnotes = false + private var printTypesInFootnotes = true + + def print(args: Any*): Unit = { + if (depth == 0 && args.length == 1 && args(0) != null && args(0).isInstanceOf[Type]) + printTypesInFootnotes = false + + depth += 1 + args foreach { + case EmptyTree => + print("EmptyTree") + case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => + print("emptyValDef") + case Literal(Constant(value)) => + def print(s: String) = this.print("Literal(Constant(" + s + "))") + value match { + case s: String => print("\"" + s + "\"") + case null => print(null) + case _ => print(value.toString) + } + case tree: Tree => + val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol + val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString + printProduct( + tree, + preamble = _ => { + print(tree.productPrefix) + if (printTypes && tree.tpe != null) print(tree.tpe) + }, + body = { + case name: Name => + if (isError) { + if (isError) print("<") + print(name) + if (isError) print(": error>") + } else if (hasSymbol) { + tree match { + case _: Ident | _: Select | _: SelectFromTypeTree => print(tree.symbol) + case _ => print(tree.symbol.name) + } + } else { + print(name) + } + case arg => + print(arg) + }, + postamble = { + case tree @ TypeTree() if tree.original != null => print(".setOriginal(", tree.original, ")") + case _ => // do nothing + }) + case sym: Symbol => + if (sym.isStatic && (sym.isClass || sym.isModule)) print(sym.fullName) + else print(sym.name) + if (printIds) print("#", sym.id) + if (printKinds) print("#", sym.abbreviatedKindString) + case NoType => + print("NoType") + case NoPrefix => + print("NoPrefix") + case tpe: Type if printTypesInFootnotes && !printingFootnotes => + val index = typeMap.getOrElseUpdate(tpe, { typeCounter += 1; typeCounter }) + footnotes(index) = tpe + print("[", index, "]") + case mods: Modifiers => + print("Modifiers(") + if (mods.flags != NoFlags || mods.privateWithin != tpnme.EMPTY || mods.annotations.nonEmpty) print(show(mods.flags)) + if (mods.privateWithin != tpnme.EMPTY || mods.annotations.nonEmpty) { print(", "); print(mods.privateWithin) } + if (mods.annotations.nonEmpty) { print(", "); print(mods.annotations); } + print(")") + case name: Name => + print(show(name)) + case list: List[_] => + print("List") + printIterable(list) + case product: Product => + printProduct(product) + case arg => + out.print(arg) + } + depth -= 1 + if (depth == 0 && footnotes.nonEmpty && !printingFootnotes) { + printingFootnotes = true + out.println() + val typeIndices = footnotes.keys.toList.sorted + typeIndices.zipWithIndex foreach { + case (typeIndex, i) => + print("[" + typeIndex + "] ") + print(footnotes(typeIndex)) + if (i < typeIndices.length - 1) out.println() + } + } + } + + def printProduct( + p: Product, + preamble: Product => Unit = p => print(p.productPrefix), + body: Any => Unit = print(_), + postamble: Product => Unit = p => print("")): Unit = + { + preamble(p) + printIterable(p.productIterator.toList, body = body) + postamble(p) + } + + def printIterable( + iterable: List[_], + preamble: => Unit = print(""), + body: Any => Unit = print(_), + postamble: => Unit = print("")): Unit = + { + preamble + print("(") + val it = iterable.iterator + while (it.hasNext) { + body(it.next) + print(if (it.hasNext) ", " else "") + } + print(")") + postamble + } + } + + def show(name: Name): String = name match { + // base.StandardNames + case tpnme.EMPTY => "tpnme.EMPTY" + case tpnme.ROOT => "tpnme.ROOT" + case tpnme.EMPTY_PACKAGE_NAME => "tpnme.EMPTY_PACKAGE_NAME" + case tpnme.WILDCARD => "tpnme.WILDCARD" + case nme.CONSTRUCTOR => "nme.CONSTRUCTOR" + case nme.NO_NAME => "nme.NO_NAME" + // api.StandardNames + case tpnme.ERROR => "tpnme.ERROR" + case nme.ERROR => "nme.ERROR" + case nme.EMPTY => "nme.EMPTY" + case tpnme.PACKAGE => "tpnme.PACKAGE" + case nme.PACKAGE => "nme.PACKAGE" + case _ => + val prefix = if (name.isTermName) "newTermName(\"" else "newTypeName(\"" + prefix + name.toString + "\")" + } + + def show(flags: FlagSet): String = { + if (flags == NoFlags) nme.NoFlags.toString + else { + val s_flags = new collection.mutable.ListBuffer[String] + for (i <- 0 to 63 if (flags containsAll (1L << i))) + s_flags += flagToString(1L << i).replace("<", "").replace(">", "").toUpperCase + s_flags mkString " | " + } + } +} diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index cadd76b1ba..6def4d9409 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -28,7 +28,7 @@ abstract class SymbolTable extends makro.Universe with AnnotationInfos with AnnotationCheckers with Trees - with TreePrinters + with Printers with Positions with TypeDebugging with Importers diff --git a/src/reflect/scala/reflect/internal/TreePrinters.scala b/src/reflect/scala/reflect/internal/TreePrinters.scala deleted file mode 100644 index 6d035c8b9d..0000000000 --- a/src/reflect/scala/reflect/internal/TreePrinters.scala +++ /dev/null @@ -1,478 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Martin Odersky - */ - -// [Eugene++ to Martin] we need to unify this prettyprinter with NodePrinters - -package scala.reflect -package internal - -import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } -import Flags._ - -trait TreePrinters extends api.TreePrinters { self: SymbolTable => - - //nsc import treeInfo.{ IsTrue, IsFalse } - - final val showOuterTests = false - - /** Adds backticks if the name is a scala keyword. */ - def quotedName(name: Name, decode: Boolean): String = { - val s = if (decode) name.decode else name.toString - val term = name.toTermName - if (nme.keywords(term) && term != nme.USCOREkw) "`%s`" format s - else s - } - def quotedName(name: Name): String = quotedName(name, false) - def quotedName(name: String): String = quotedName(newTermName(name), false) - - private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { - val sym = tree.symbol - if (sym.name.toString == nme.ERROR.toString) { - "<" + quotedName(name, decoded) + ": error>" - } else if (sym != null && sym != NoSymbol) { - val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" - var suffix = "" - if (settings.uniqid.value) suffix += ("#" + sym.id) - if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) - prefix + quotedName(tree.symbol.decodedName) + suffix - } else { - quotedName(name, decoded) - } - } - - def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) - def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) - - /** Turns a path into a String, introducing backquotes - * as necessary. - */ - def backquotedPath(t: Tree): String = { - t match { - case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) - case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) - case Ident(name) => symName(t, name) - case _ => t.toString - } - } - - class TreePrinter(out: PrintWriter) extends super.TreePrinter { - protected var indentMargin = 0 - protected val indentStep = 2 - protected var indentString = " " // 40 - - typesPrinted = settings.printtypes.value - uniqueIds = settings.uniqid.value - protected def doPrintPositions = settings.Xprintpos.value - - def indent() = indentMargin += indentStep - def undent() = indentMargin -= indentStep - - def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) - - def println() { - out.println() - while (indentMargin > indentString.length()) - indentString += indentString - if (indentMargin > 0) - out.write(indentString, 0, indentMargin) - } - - def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit) { - ls match { - case List() => - case List(x) => printelem(x) - case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) - } - } - - def printColumn(ts: List[Tree], start: String, sep: String, end: String) { - print(start); indent; println() - printSeq(ts){print(_)}{print(sep); println()}; undent; println(); print(end) - } - - def printRow(ts: List[Tree], start: String, sep: String, end: String) { - print(start); printSeq(ts){print(_)}{print(sep)}; print(end) - } - - def printRow(ts: List[Tree], sep: String) { printRow(ts, "", sep, "") } - - def printTypeParams(ts: List[TypeDef]) { - if (!ts.isEmpty) { - print("["); printSeq(ts){ t => - printAnnotations(t) - printParam(t) - }{print(", ")}; print("]") - } - } - - def printLabelParams(ps: List[Ident]) { - print("(") - printSeq(ps){printLabelParam}{print(", ")} - print(")") - } - - def printLabelParam(p: Ident) { - print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe) - } - - def printValueParams(ts: List[ValDef]) { - print("(") - if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "") - printSeq(ts){printParam}{print(", ")} - print(")") - } - - def printParam(tree: Tree) { - tree match { - case ValDef(mods, name, tp, rhs) => - printPosition(tree) - printAnnotations(tree) - print(symName(tree, name)); printOpt(": ", tp); printOpt(" = ", rhs) - case TypeDef(mods, name, tparams, rhs) => - printPosition(tree) - print(symName(tree, name)) - printTypeParams(tparams); print(rhs) - } - } - - def printBlock(tree: Tree) { - tree match { - case Block(_, _) => - print(tree) - case _ => - printColumn(List(tree), "{", ";", "}") - } - } - - private def symFn[T](tree: Tree, f: Symbol => T, orElse: => T): T = tree.symbol match { - case null | NoSymbol => orElse - case sym => f(sym) - } - private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) - - def printOpt(prefix: String, tree: Tree) { - if (!tree.isEmpty) { print(prefix, tree) } - } - - def printModifiers(tree: Tree, mods: Modifiers): Unit = printFlags( - if (tree.symbol == NoSymbol) mods.flags else tree.symbol.flags, "" + ( - if (tree.symbol == NoSymbol) mods.privateWithin - else if (tree.symbol.hasAccessBoundary) tree.symbol.privateWithin.name - else "" - ) - ) - - def printFlags(flags: Long, privateWithin: String) { - var mask: Long = if (settings.debug.value) -1L else PrintableFlags - val s = flagsToString(flags & mask, privateWithin) - if (s != "") print(s + " ") - } - - def printAnnotations(tree: Tree) { - if (!isCompilerUniverse && tree.symbol != null && tree.symbol != NoSymbol) - // [Eugene++] todo. this is not 100% correct, but is necessary for sane printing - // the problem is that getting annotations doesn't automatically initialize the symbol - // so we might easily print something as if it doesn't have annotations, whereas it does - tree.symbol.initialize - - val annots = tree.symbol.annotations match { - case Nil => tree.asInstanceOf[MemberDef].mods.annotations - case anns => anns - } - annots foreach (annot => print("@"+annot+" ")) - } - - private var currentOwner: Symbol = NoSymbol - private var selectorType: Type = NoType - - def printTree(tree: Tree) { - tree match { - case EmptyTree => - print("") - - case ClassDef(mods, name, tparams, impl) => - printAnnotations(tree) - printModifiers(tree, mods) - val word = - if (mods.isTrait) "trait" - else if (ifSym(tree, _.isModuleClass)) "object" - else "class" - - print(word, " ", symName(tree, name)) - printTypeParams(tparams) - print(if (mods.isDeferred) " <: " else " extends ", impl) - - case PackageDef(packaged, stats) => - printAnnotations(tree) - print("package ", packaged); printColumn(stats, " {", ";", "}") - - case ModuleDef(mods, name, impl) => - printAnnotations(tree) - printModifiers(tree, mods); - print("object " + symName(tree, name), " extends ", impl) - - case ValDef(mods, name, tp, rhs) => - printAnnotations(tree) - printModifiers(tree, mods) - print(if (mods.isMutable) "var " else "val ", symName(tree, name)) - printOpt(": ", tp) - if (!mods.isDeferred) - print(" = ", if (rhs.isEmpty) "_" else rhs) - - case DefDef(mods, name, tparams, vparamss, tp, rhs) => - printAnnotations(tree) - printModifiers(tree, mods) - print("def " + symName(tree, name)) - printTypeParams(tparams); vparamss foreach printValueParams - printOpt(": ", tp); printOpt(" = ", rhs) - - case TypeDef(mods, name, tparams, rhs) => - if (mods hasFlag (PARAM | DEFERRED)) { - printAnnotations(tree) - printModifiers(tree, mods); print("type "); printParam(tree) - } else { - printAnnotations(tree) - printModifiers(tree, mods); print("type " + symName(tree, name)) - printTypeParams(tparams); printOpt(" = ", rhs) - } - - case LabelDef(name, params, rhs) => - print(symName(tree, name)); printLabelParams(params); printBlock(rhs) - - case Import(expr, selectors) => - // Is this selector remapping a name (i.e, {name1 => name2}) - def isNotRemap(s: ImportSelector) : Boolean = (s.name == nme.WILDCARD || s.name == s.rename) - def selectorToString(s: ImportSelector): String = { - val from = quotedName(s.name) - if (isNotRemap(s)) from - else from + "=>" + quotedName(s.rename) - } - print("import ", backquotedPath(expr), ".") - selectors match { - case List(s) => - // If there is just one selector and it is not remapping a name, no braces are needed - if (isNotRemap(s)) print(selectorToString(s)) - else print("{", selectorToString(s), "}") - // If there is more than one selector braces are always needed - case many => - print(many.map(selectorToString).mkString("{", ", ", "}")) - } - - case Template(parents, self, body) => - val currentOwner1 = currentOwner - if (tree.symbol != NoSymbol) currentOwner = tree.symbol.owner -// if (parents exists isReferenceToAnyVal) { -// print("AnyVal") -// } -// else { - printRow(parents, " with ") - if (!body.isEmpty) { - if (self.name != nme.WILDCARD) { - print(" { ", self.name); printOpt(": ", self.tpt); print(" => ") - } else if (!self.tpt.isEmpty) { - print(" { _ : ", self.tpt, " => ") - } else { - print(" {") - } - printColumn(body, "", ";", "}") - } -// } - currentOwner = currentOwner1 - - case Block(stats, expr) => - printColumn(stats ::: List(expr), "{", ";", "}") - - case Match(selector, cases) => - val selectorType1 = selectorType - selectorType = selector.tpe - print(selector); printColumn(cases, " match {", "", "}") - selectorType = selectorType1 - - case CaseDef(pat, guard, body) => - print("case ") - def patConstr(pat: Tree): Tree = pat match { - case Apply(fn, args) => patConstr(fn) - case _ => pat - } - if (showOuterTests && - needsOuterTest( - patConstr(pat).tpe.finalResultType, selectorType, currentOwner)) - print("???") - print(pat); printOpt(" if ", guard) - print(" => ", body) - - case Alternative(trees) => - printRow(trees, "(", "| ", ")") - - case Star(elem) => - print("(", elem, ")*") - - case Bind(name, t) => - print("(", symName(tree, name), " @ ", t, ")") - - case UnApply(fun, args) => - print(fun, " "); printRow(args, "(", ", ", ")") - - case ArrayValue(elemtpt, trees) => - print("Array[", elemtpt); printRow(trees, "]{", ", ", "}") - - case Function(vparams, body) => - print("("); printValueParams(vparams); print(" => ", body, ")") - if (uniqueIds && tree.symbol != null) print("#"+tree.symbol.id) - - case Assign(lhs, rhs) => - print(lhs, " = ", rhs) - - case AssignOrNamedArg(lhs, rhs) => - print(lhs, " = ", rhs) - - case If(cond, thenp, elsep) => - print("if (", cond, ")"); indent; println() - print(thenp); undent - if (!elsep.isEmpty) { - println(); print("else"); indent; println(); print(elsep); undent - } - - case Return(expr) => - print("return ", expr) - - case Try(block, catches, finalizer) => - print("try "); printBlock(block) - if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") - printOpt(" finally ", finalizer) - - case Throw(expr) => - print("throw ", expr) - - case New(tpe) => - print("new ", tpe) - - case Typed(expr, tp) => - print("(", expr, ": ", tp, ")") - - case TypeApply(fun, targs) => - print(fun); printRow(targs, "[", ", ", "]") - - case Apply(fun, vargs) => - print(fun); printRow(vargs, "(", ", ", ")") - - case ApplyDynamic(qual, vargs) => - print("(", qual, "#", tree.symbol.nameString) - printRow(vargs, ", (", ", ", "))") - - case Super(This(qual), mix) => - if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + ".") - print("super") - if (!mix.isEmpty) - print("[" + mix + "]") - - case Super(qual, mix) => - print(qual, ".super") - if (!mix.isEmpty) - print("[" + mix + "]") - - case This(qual) => - if (!qual.isEmpty) print(symName(tree, qual) + ".") - print("this") - - case Select(qual @ New(tpe), name) if (!settings.debug.value) => - print(qual) - - case Select(qualifier, name) => - print(backquotedPath(qualifier), ".", symName(tree, name)) - - case id @ Ident(name) => - val str = symName(tree, name) - print( if (id.isBackquoted) "`" + str + "`" else str ) - - case Literal(x) => - print(x.escapedStringValue) - - case tt: TypeTree => - if ((tree.tpe eq null) || (doPrintPositions && tt.original != null)) { - if (tt.original != null) print("") - else print("") - } else if ((tree.tpe.typeSymbol ne null) && tree.tpe.typeSymbol.isAnonymousClass) { - print(tree.tpe.typeSymbol.toString) - } else { - print(tree.tpe.toString) - } - - case Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => - def printAnnot() { - print("@", tpt) - if (!args.isEmpty) - printRow(args, "(", ",", ")") - } - print(tree, if (tree.isType) " " else ": ") - printAnnot() - - case SingletonTypeTree(ref) => - print(ref, ".type") - - case SelectFromTypeTree(qualifier, selector) => - print(qualifier, "#", symName(tree, selector)) - - case CompoundTypeTree(templ) => - print(templ) - - case AppliedTypeTree(tp, args) => - print(tp); printRow(args, "[", ", ", "]") - - case TypeBoundsTree(lo, hi) => - printOpt(" >: ", lo); printOpt(" <: ", hi) - - case ExistentialTypeTree(tpt, whereClauses) => - print(tpt); - printColumn(whereClauses, " forSome { ", ";", "}") - -// SelectFromArray is no longer visible in reflect.internal. -// eliminated until we figure out what we will do with both TreePrinters and -// SelectFromArray. -// case SelectFromArray(qualifier, name, _) => -// print(qualifier); print("."); print(symName(tree, name)) - - case tree => - xprintTree(this, tree) - } - if (typesPrinted && tree.isTerm && !tree.isEmpty) { - print("{", if (tree.tpe eq null) "" else tree.tpe.toString, "}") - } - } - - def print(args: Any*): Unit = args foreach { - case tree: Tree => - printPosition(tree) - printTree(tree) - case name: Name => - print(quotedName(name)) - case arg => - out.print(if (arg == null) "null" else arg.toString) - } - } - - /** Hook for extensions */ - def xprintTree(treePrinter: TreePrinter, tree: Tree) = - treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) - - def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) - def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) - def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) - - /** A writer that writes to the current Console and - * is sensitive to replacement of the Console's - * output stream. - */ - object ConsoleWriter extends Writer { - override def write(str: String) { Console.print(str) } - - def write(cbuf: Array[Char], off: Int, len: Int) { - write(new String(cbuf, off, len)) - } - - def close = { /* do nothing */ } - def flush = { /* do nothing */ } - } -} diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 75bb0e6d49..562a49519f 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -809,7 +809,7 @@ trait Trees extends api.Trees { self: SymbolTable => } } - // Belongs in TreeInfo but then I can't reach it from TreePrinters. + // Belongs in TreeInfo but then I can't reach it from Printers. def isReferenceToScalaMember(t: Tree, Id: Name) = t match { case Ident(Id) => true case Select(Ident(nme.scala_), Id) => true diff --git a/src/reflect/scala/reflect/makro/Universe.scala b/src/reflect/scala/reflect/makro/Universe.scala index ffc4042a0a..98046be555 100644 --- a/src/reflect/scala/reflect/makro/Universe.scala +++ b/src/reflect/scala/reflect/makro/Universe.scala @@ -15,25 +15,6 @@ abstract class Universe extends scala.reflect.api.Universe { // [Eugene++ to Martin] should we also add mutability methods here (similarly to what's done below for trees)? // I'm talking about `setAnnotations` and friends - - /** Can this symbol be loaded by a reflective mirror? - * - * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. - * Such annotations (also called "pickles") are applied on top-level classes and include information - * about all symbols reachable from the annotee. However, local symbols (e.g. classes or definitions local to a block) - * are typically unreachable and information about them gets lost. - * - * This method is useful for macro writers who wish to save certain ASTs to be used at runtime. - * With `isLocatable' it's possible to check whether a tree can be retained as is, or it needs special treatment. - */ - def isLocatable: Boolean - - /** Is this symbol static (i.e. with no outer instance)? - * Q: When exactly is a sym marked as STATIC? - * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. - * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 - */ - def isStatic: Boolean } // Tree extensions --------------------------------------------------------------- diff --git a/test/files/run/showraw_mods.check b/test/files/run/showraw_mods.check new file mode 100644 index 0000000000..83055f2b70 --- /dev/null +++ b/test/files/run/showraw_mods.check @@ -0,0 +1 @@ +Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), newTypeName("C"), List(), Template(List(Ident(java.lang.Object)), emptyValDef, List(DefDef(Modifiers(), newTermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), ValDef(Modifiers(PRIVATE | LOCAL), newTermName("x"), TypeTree(), Literal(Constant(2))), ValDef(Modifiers(MUTABLE), newTermName("y"), TypeTree(), Select(This(newTypeName("C")), newTermName("x"))), ValDef(Modifiers(LAZY), newTermName("z"), TypeTree(), Select(This(newTypeName("C")), newTermName("y"))))))), Literal(Constant(()))) diff --git a/test/files/run/showraw_mods.scala b/test/files/run/showraw_mods.scala new file mode 100644 index 0000000000..a10e4821dc --- /dev/null +++ b/test/files/run/showraw_mods.scala @@ -0,0 +1,6 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree = reify{trait C { private[this] val x = 2; var y = x; lazy val z = y }} + println(showRaw(tree.tree)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree.check b/test/files/run/showraw_tree.check new file mode 100644 index 0000000000..82724cae44 --- /dev/null +++ b/test/files/run/showraw_tree.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree.scala b/test/files/run/showraw_tree.scala new file mode 100644 index 0000000000..3624a24d6a --- /dev/null +++ b/test/files/run/showraw_tree.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree)) + println(showRaw(tree2.tree)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_ids.check b/test/files/run/showraw_tree_ids.check new file mode 100644 index 0000000000..c6dbd6f1ce --- /dev/null +++ b/test/files/run/showraw_tree_ids.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#1903), List(Ident(java.lang.String#129), Ident(java.lang.String#129)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#1908), List(Ident(java.lang.String#129), Ident(java.lang.String#129)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_ids.scala b/test/files/run/showraw_tree_ids.scala new file mode 100644 index 0000000000..b56b8b4476 --- /dev/null +++ b/test/files/run/showraw_tree_ids.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree, printIds = true)) + println(showRaw(tree2.tree, printIds = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_kinds.check b/test/files/run/showraw_tree_kinds.check new file mode 100644 index 0000000000..a12e21c611 --- /dev/null +++ b/test/files/run/showraw_tree_kinds.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#CLS), List(Ident(java.lang.String#CLS), Ident(java.lang.String#CLS)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#CLS), List(Ident(java.lang.String#CLS), Ident(java.lang.String#CLS)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_kinds.scala b/test/files/run/showraw_tree_kinds.scala new file mode 100644 index 0000000000..0ca5a387da --- /dev/null +++ b/test/files/run/showraw_tree_kinds.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree, printKinds = true)) + println(showRaw(tree2.tree, printKinds = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_types_ids.check b/test/files/run/showraw_tree_types_ids.check new file mode 100644 index 0000000000..02e7aeed7c --- /dev/null +++ b/test/files/run/showraw_tree_types_ids.check @@ -0,0 +1,10 @@ +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap#1903), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)))))), nme.CONSTRUCTOR#1913), List()) +[1] TypeRef(ThisType(scala.collection.immutable#1898), scala.collection.immutable.HashMap#1903, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable#1898), scala.collection.immutable.HashMap#1903, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List())))) +[3] TypeRef(ThisType(scala.collection.immutable#1898), scala.collection.immutable.HashMap#1903, List()) +[4] TypeRef(ThisType(java.lang#128), java.lang.String#129, List()) +Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap#1908), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)))))), nme.CONSTRUCTOR#2231), List()) +[4] TypeRef(ThisType(java.lang#128), java.lang.String#129, List()) +[5] TypeRef(ThisType(scala.collection.mutable#1907), scala.collection.mutable.HashMap#1908, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List()))) +[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable#1907), scala.collection.mutable.HashMap#1908, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List())))) +[7] TypeRef(ThisType(scala.collection.mutable#1907), scala.collection.mutable.HashMap#1908, List()) diff --git a/test/files/run/showraw_tree_types_ids.scala b/test/files/run/showraw_tree_types_ids.scala new file mode 100644 index 0000000000..cb2c2bfb0f --- /dev/null +++ b/test/files/run/showraw_tree_types_ids.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tb.typeCheck(tree1.tree), printIds = true, printTypes = true)) + println(showRaw(tb.typeCheck(tree2.tree), printIds = true, printTypes = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_types_typed.check b/test/files/run/showraw_tree_types_typed.check new file mode 100644 index 0000000000..60176c7192 --- /dev/null +++ b/test/files/run/showraw_tree_types_typed.check @@ -0,0 +1,10 @@ +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String)), TypeTree[4]().setOriginal(Ident[4](java.lang.String)))))), nme.CONSTRUCTOR), List()) +[1] TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List())))) +[3] TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List()) +[4] TypeRef(ThisType(java.lang), java.lang.String, List()) +Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String)), TypeTree[4]().setOriginal(Ident[4](java.lang.String)))))), nme.CONSTRUCTOR), List()) +[4] TypeRef(ThisType(java.lang), java.lang.String, List()) +[5] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List()))) +[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List())))) +[7] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List()) diff --git a/test/files/run/showraw_tree_types_typed.scala b/test/files/run/showraw_tree_types_typed.scala new file mode 100644 index 0000000000..d7ccc84ea3 --- /dev/null +++ b/test/files/run/showraw_tree_types_typed.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tb.typeCheck(tree1.tree), printTypes = true)) + println(showRaw(tb.typeCheck(tree2.tree), printTypes = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_types_untyped.check b/test/files/run/showraw_tree_types_untyped.check new file mode 100644 index 0000000000..82724cae44 --- /dev/null +++ b/test/files/run/showraw_tree_types_untyped.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_types_untyped.scala b/test/files/run/showraw_tree_types_untyped.scala new file mode 100644 index 0000000000..4df2eb66b2 --- /dev/null +++ b/test/files/run/showraw_tree_types_untyped.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree, printTypes = true)) + println(showRaw(tree2.tree, printTypes = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_ultimate.check b/test/files/run/showraw_tree_ultimate.check new file mode 100644 index 0000000000..0b409554a0 --- /dev/null +++ b/test/files/run/showraw_tree_ultimate.check @@ -0,0 +1,10 @@ +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap#1903#CLS), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)))))), nme.CONSTRUCTOR#1913#PCTOR), List()) +[1] TypeRef(ThisType(scala.collection.immutable#1898#PK), scala.collection.immutable.HashMap#1903#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable#1898#PK), scala.collection.immutable.HashMap#1903#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List())))) +[3] TypeRef(ThisType(scala.collection.immutable#1898#PK), scala.collection.immutable.HashMap#1903#CLS, List()) +[4] TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()) +Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap#1908#CLS), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)))))), nme.CONSTRUCTOR#2231#CTOR), List()) +[4] TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()) +[5] TypeRef(ThisType(scala.collection.mutable#1907#PK), scala.collection.mutable.HashMap#1908#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()))) +[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable#1907#PK), scala.collection.mutable.HashMap#1908#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List())))) +[7] TypeRef(ThisType(scala.collection.mutable#1907#PK), scala.collection.mutable.HashMap#1908#CLS, List()) diff --git a/test/files/run/showraw_tree_ultimate.scala b/test/files/run/showraw_tree_ultimate.scala new file mode 100644 index 0000000000..dfd7abde52 --- /dev/null +++ b/test/files/run/showraw_tree_ultimate.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tb.typeCheck(tree1.tree), printIds = true, printKinds = true, printTypes = true)) + println(showRaw(tb.typeCheck(tree2.tree), printIds = true, printKinds = true, printTypes = true)) +} \ No newline at end of file -- cgit v1.2.3 From 78ef5406ca01e65432df83a4b6bbd6dfef55376a Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 13 Jun 2012 18:26:36 +0200 Subject: removes pre-M4 compatibility stubs for the IDE --- src/library/scala/reflect/compat.scala | 33 ---------------------- .../scala/reflect/makro/internal/package.scala | 2 +- src/library/scala/reflect/package.scala | 2 +- .../neg/macro-invalidret-nonuniversetree.check | 4 +-- .../macro-invalidret-nonuniversetree/Impls_1.scala | 2 +- .../run/macro-reify-splice-splice/Macros_1.scala | 1 - 6 files changed, 5 insertions(+), 39 deletions(-) delete mode 100644 src/library/scala/reflect/compat.scala (limited to 'test/files/run') diff --git a/src/library/scala/reflect/compat.scala b/src/library/scala/reflect/compat.scala deleted file mode 100644 index fc0e5fbf9c..0000000000 --- a/src/library/scala/reflect/compat.scala +++ /dev/null @@ -1,33 +0,0 @@ -// [Eugene++] delete this once we merge with trunk and have a working IDE - -package scala.reflect { - trait ArrayTag[T] - trait ErasureTag[T] - trait ConcreteTypeTag[T] -} - -package scala.reflect.api { - trait TypeTags { - trait TypeTag[T] - trait ConcreteTypeTag[T] - } -} - -package scala { - import scala.reflect.base.{Universe => BaseUniverse} - - trait reflect_compat { - lazy val mirror: BaseUniverse = ??? - } -} - -package scala.reflect { - import language.experimental.macros - import scala.reflect.base.{Universe => BaseUniverse} - - trait internal_compat { - private[scala] def materializeArrayTag[T](u: BaseUniverse): ArrayTag[T] = ??? - private[scala] def materializeErasureTag[T](u: BaseUniverse): ErasureTag[T] = ??? - private[scala] def materializeConcreteTypeTag[T](u: BaseUniverse): ConcreteTypeTag[T] = ??? - } -} \ No newline at end of file diff --git a/src/library/scala/reflect/makro/internal/package.scala b/src/library/scala/reflect/makro/internal/package.scala index d31a0f0d14..78cb0ffb10 100644 --- a/src/library/scala/reflect/makro/internal/package.scala +++ b/src/library/scala/reflect/makro/internal/package.scala @@ -9,7 +9,7 @@ import scala.reflect.base.{Universe => BaseUniverse} // // todo. once we have implicit macros for tag generation, we can remove these anchors // [Eugene++] how do I hide this from scaladoc? -package object internal extends scala.reflect.internal_compat { +package object internal { private[scala] def materializeClassTag[T](u: BaseUniverse): ClassTag[T] = macro ??? private[scala] def materializeAbsTypeTag[T](u: BaseUniverse): u.AbsTypeTag[T] = macro ??? private[scala] def materializeTypeTag[T](u: BaseUniverse): u.TypeTag[T] = macro ??? diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 0ee58df2cd..2ebc82875e 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -1,6 +1,6 @@ package scala -package object reflect extends reflect_compat { +package object reflect { lazy val basis: base.Universe = new base.Base diff --git a/test/files/neg/macro-invalidret-nonuniversetree.check b/test/files/neg/macro-invalidret-nonuniversetree.check index 4fc06b5ceb..1b9487982f 100644 --- a/test/files/neg/macro-invalidret-nonuniversetree.check +++ b/test/files/neg/macro-invalidret-nonuniversetree.check @@ -1,7 +1,7 @@ Macros_Test_2.scala:2: error: macro implementation has wrong shape: required: (c: scala.reflect.makro.Context): c.Expr[Any] - found : (c: scala.reflect.makro.Context): reflect.mirror.Literal -type mismatch for return type: reflect.mirror.Literal does not conform to c.Expr[Any] + found : (c: scala.reflect.makro.Context): reflect.basis.Literal +type mismatch for return type: reflect.basis.Literal does not conform to c.Expr[Any] def foo = macro Impls.foo ^ one error found diff --git a/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala b/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala index 86b7c8d8d0..da0eb0ac83 100644 --- a/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala +++ b/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala @@ -1,5 +1,5 @@ import scala.reflect.makro.{Context => Ctx} object Impls { - def foo(c: Ctx) = scala.reflect.mirror.Literal(scala.reflect.mirror.Constant(42)) + def foo(c: Ctx) = scala.reflect.basis.Literal(scala.reflect.basis.Constant(42)) } diff --git a/test/files/run/macro-reify-splice-splice/Macros_1.scala b/test/files/run/macro-reify-splice-splice/Macros_1.scala index 030a0a217e..4f1b600f63 100644 --- a/test/files/run/macro-reify-splice-splice/Macros_1.scala +++ b/test/files/run/macro-reify-splice-splice/Macros_1.scala @@ -1,5 +1,4 @@ import scala.reflect.makro.{Context => Ctx} -import scala.reflect.{mirror => mr} object Macros { def foo = macro Impls.foo -- cgit v1.2.3 From b29c01b710b67dd329ad418c7d5ef4e61845c6d1 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Tue, 19 Jun 2012 19:54:02 +0200 Subject: Fix SI-5284. The problem was the false assumption that methods specialized on their type parameter, such as this one: class Foo[@spec(Int) T](val x: T) { def bar[@spec(Int) S >: T](f: S => S) = f(x) } have their normalized versions (`bar$mIc$sp`) never called from the base specialization class `Foo`. This meant that the implementation of `bar$mIc$sp` in `Foo` simply threw an exception. This assumption is not true, however. See this: object Baz { def apply[T]() = new Foo[T] } Calling `Baz.apply[Int]()` will create an instance of the base specialization class `Foo` at `Int`. Calling `bar` on this instance will be rewritten by specialization to calling `bar$mIc$sp`, hence the error. So, we have to emit a valid implementation for `bar`, obviously. Problem is, such an implementation would have conflicting type bounds in the base specialization class `Foo`, since we don't know if `T` is a subtype of `S = Int`. In other words, we cannot emit: def bar$mIc$sp(f: Int => Int) = f(x) // x: T without typechecking errors. However, notice that the bounds are valid if and only if `T = Int`. In the same time, invocations of `bar$mIc$sp` will only be emitted in callsites where the type bounds hold. This means we can cast the expressions in method applications to the required specialized type bound. The following changes have been made: 1) The decision of whether or not to create a normalized version of the specialized method is not done on the `conflicting` relation anymore. Instead, it's done based on the `satisfiable` relation, which is true if there is possibly an instantiation of the type parameters where the bounds hold. 2) The `satisfiable` method has a new variant called `satisfiableConstraints`, which does unification to figure out how the type parameters should be instantiated in order to satisfy the bounds. 3) The `Duplicators` are changed to transform a tree using the `castType` method which just returns the tree by default. In specialization, the `castType` in `Duplicators` is overridden, and uses a map from type parameters to types. This map is obtained by `satisfiableConstraints` from 2). If the type of the expression is not equal to the expected type, and this map contains a mapping to the expected type, then the tree is cast, as discussed above. Additional tests added. Review by @dragos Review by @VladUreche Conflicts: src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala src/compiler/scala/tools/nsc/typechecker/Duplicators.scala --- .../scala/tools/nsc/transform/Constructors.scala | 2 +- .../tools/nsc/transform/SpecializeTypes.scala | 85 ++++++++++++++++++---- .../scala/tools/nsc/typechecker/Duplicators.scala | 31 +++++--- test/files/pos/spec-params-new.scala | 2 +- test/files/run/t5284.check | 1 + test/files/run/t5284.scala | 25 +++++++ test/files/run/t5284b.check | 1 + test/files/run/t5284b.scala | 28 +++++++ test/files/run/t5284c.check | 1 + test/files/run/t5284c.scala | 30 ++++++++ 10 files changed, 180 insertions(+), 26 deletions(-) create mode 100644 test/files/run/t5284.check create mode 100644 test/files/run/t5284.scala create mode 100644 test/files/run/t5284b.check create mode 100644 test/files/run/t5284b.scala create mode 100644 test/files/run/t5284c.check create mode 100644 test/files/run/t5284c.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index bc4483923a..e5119eac71 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -323,7 +323,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { // statements coming from the original class need retyping in the current context debuglog("retyping " + stat2) - val d = new specializeTypes.Duplicator + val d = new specializeTypes.Duplicator(Map[Symbol, Type]()) d.retyped(localTyper.context1.asInstanceOf[d.Context], stat2, genericClazz, diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index c4c769d7cf..1d820afe11 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -450,7 +450,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Type parameters that survive when specializing in the specified environment. */ def survivingParams(params: List[Symbol], env: TypeEnv) = - params.filter(p => !p.isSpecialized || !isPrimitiveValueType(env(p))) + params filter { + p => + !p.isSpecialized || + !env.contains(p) || + !isPrimitiveValueType(env(p)) + } /** Produces the symbols from type parameters `syms` of the original owner, * in the given type environment `env`. The new owner is `nowner`. @@ -1176,7 +1181,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { || specializedTypeVars(t1).nonEmpty || specializedTypeVars(t2).nonEmpty) } - + env forall { case (tvar, tpe) => matches(tvar.info.bounds.lo, tpe) && matches(tpe, tvar.info.bounds.hi) || { if (warnings) @@ -1192,10 +1197,58 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } } + + def satisfiabilityConstraints(env: TypeEnv): Option[TypeEnv] = { + val noconstraints = Some(emptyEnv) + def matches(tpe1: Type, tpe2: Type): Option[TypeEnv] = { + val t1 = subst(env, tpe1) + val t2 = subst(env, tpe2) + // log("---------> " + tpe1 + " matches " + tpe2) + // log(t1 + ", " + specializedTypeVars(t1)) + // log(t2 + ", " + specializedTypeVars(t2)) + // log("unify: " + unify(t1, t2, env, false, false) + " in " + env) + if (t1 <:< t2) noconstraints + else if (specializedTypeVars(t1).nonEmpty) Some(unify(t1, t2, env, false, false) -- env.keys) + else if (specializedTypeVars(t2).nonEmpty) Some(unify(t2, t1, env, false, false) -- env.keys) + else None + } + + env.foldLeft[Option[TypeEnv]](noconstraints) { + case (constraints, (tvar, tpe)) => + val loconstraints = matches(tvar.info.bounds.lo, tpe) + val hiconstraints = matches(tpe, tvar.info.bounds.hi) + val allconstraints = for (c <- constraints; l <- loconstraints; h <- hiconstraints) yield c ++ l ++ h + allconstraints + } + } - class Duplicator extends { + /** This duplicator additionally performs casts of expressions if that is allowed by the `casts` map. */ + class Duplicator(casts: Map[Symbol, Type]) extends { val global: SpecializeTypes.this.global.type = SpecializeTypes.this.global - } with typechecker.Duplicators + } with typechecker.Duplicators { + private val (castfrom, castto) = casts.unzip + private object CastMap extends SubstTypeMap(castfrom.toList, castto.toList) + + class BodyDuplicator(_context: Context) extends super.BodyDuplicator(_context) { + override def castType(tree: Tree, pt: Type): Tree = { + // log(" expected type: " + pt) + // log(" tree type: " + tree.tpe) + tree.tpe = if (tree.tpe != null) fixType(tree.tpe) else null + // log(" tree type: " + tree.tpe) + val ntree = if (tree.tpe != null && !(tree.tpe <:< pt)) { + val casttpe = CastMap(tree.tpe) + if (casttpe <:< pt) gen.mkCast(tree, casttpe) + else if (casttpe <:< CastMap(pt)) gen.mkCast(tree, pt) + else tree + } else tree + ntree.tpe = null + ntree + } + } + + protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context) + + } /** A tree symbol substituter that substitutes on type skolems. * If a type parameter is a skolem, it looks for the original @@ -1475,14 +1528,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { deriveDefDef(tree1)(transform) case NormalizedMember(target) => - debuglog("Normalized member: " + symbol + ", target: " + target) - if (target.isDeferred || conflicting(typeEnv(symbol))) { + val constraints = satisfiabilityConstraints(typeEnv(symbol)) + log("constraints: " + constraints) + if (target.isDeferred || constraints == None) { deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called.")) - } - else { + } else { // we have an rhs, specialize it val tree1 = reportTypeError { - duplicateBody(ddef, target) + duplicateBody(ddef, target, constraints.get) } debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) @@ -1546,7 +1599,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate) debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName) - val d = new Duplicator + val d = new Duplicator(emptyEnv) val newValDef = d.retyped( localTyper.context1.asInstanceOf[d.Context], tree1, @@ -1571,12 +1624,18 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { super.transform(tree) } } - - private def duplicateBody(tree: DefDef, source: Symbol) = { + + /** Duplicate the body of the given method `tree` to the new symbol `source`. + * + * Knowing that the method can be invoked only in the `castmap` type environment, + * this method will insert casts for all the expressions of types mappend in the + * `castmap`. + */ + private def duplicateBody(tree: DefDef, source: Symbol, castmap: TypeEnv = emptyEnv) = { val symbol = tree.symbol val meth = addBody(tree, source) - val d = new Duplicator + val d = new Duplicator(castmap) debuglog("-->d DUPLICATING: " + meth) d.retyped( localTyper.context1.asInstanceOf[d.Context], diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 6386273c9d..63d1bd0e9f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -21,7 +21,7 @@ abstract class Duplicators extends Analyzer { def retyped(context: Context, tree: Tree): Tree = { resetClassOwners - (new BodyDuplicator(context)).typed(tree) + (newBodyDuplicator(context)).typed(tree) } /** Retype the given tree in the given context. Use this method when retyping @@ -37,15 +37,17 @@ abstract class Duplicators extends Analyzer { envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList) debuglog("retyped with env: " + env) - (new BodyDuplicator(context)).typed(tree) + newBodyDuplicator(context).typed(tree) } + protected def newBodyDuplicator(context: Context) = new BodyDuplicator(context) + def retypedMethod(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol): Tree = - (new BodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) + (newBodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) /** Return the special typer for duplicate method bodies. */ override def newTyper(context: Context): Typer = - new BodyDuplicator(context) + newBodyDuplicator(context) private def resetClassOwners() { oldClassOwner = null @@ -209,6 +211,11 @@ abstract class Duplicators extends Analyzer { } } + /** Optionally cast this tree into some other type, if required. + * Unless overridden, just returns the tree. + */ + def castType(tree: Tree, pt: Type): Tree = tree + /** Special typer method for re-type checking trees. It expects a typed tree. * Returns a typed tree that has fresh symbols for all definitions in the original tree. * @@ -319,10 +326,10 @@ abstract class Duplicators extends Analyzer { super.typed(atPos(tree.pos)(tree1), mode, pt) case This(_) => - // log("selection on this, plain: " + tree) + debuglog("selection on this, plain: " + tree) tree.symbol = updateSym(tree.symbol) - tree.tpe = null - val tree1 = super.typed(tree, mode, pt) + val ntree = castType(tree, pt) + val tree1 = super.typed(ntree, mode, pt) // log("plain this typed to: " + tree1) tree1 /* no longer needed, because Super now contains a This(...) @@ -358,16 +365,18 @@ abstract class Duplicators extends Analyzer { case EmptyTree => // no need to do anything, in particular, don't set the type to null, EmptyTree.tpe_= asserts tree - + case _ => - // log("Duplicators default case: " + tree.summaryString + " -> " + tree) + debuglog("Duplicators default case: " + tree.summaryString) + debuglog(" ---> " + tree) if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } - tree.tpe = null - super.typed(tree, mode, pt) + val ntree = castType(tree, pt) + super.typed(ntree, mode, pt) } } + } } diff --git a/test/files/pos/spec-params-new.scala b/test/files/pos/spec-params-new.scala index 661e686f0e..959ce1591c 100644 --- a/test/files/pos/spec-params-new.scala +++ b/test/files/pos/spec-params-new.scala @@ -31,4 +31,4 @@ class Foo[@specialized A: ClassTag] { val xs = new Array[A](1) xs(0) = x } -} \ No newline at end of file +} diff --git a/test/files/run/t5284.check b/test/files/run/t5284.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t5284.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t5284.scala b/test/files/run/t5284.scala new file mode 100644 index 0000000000..ba0845fb8e --- /dev/null +++ b/test/files/run/t5284.scala @@ -0,0 +1,25 @@ + + + + + +/** Here we have a situation where a normalized method parameter `W` + * is used in a position which accepts an instance of type `T` - we know we can + * safely cast `T` to `W` whenever type bounds on `W` hold. + */ +object Test { + def main(args: Array[String]) { + val a = Blarg(Array(1, 2, 3)) + println(a.m((x: Int) => x + 1)) + } +} + + +object Blarg { + def apply[T: Manifest](a: Array[T]) = new Blarg(a) +} + + +class Blarg[@specialized(Int) T: Manifest](val a: Array[T]) { + def m[@specialized(Int) W >: T, @specialized(Int) S](f: W => S) = f(a(0)) +} diff --git a/test/files/run/t5284b.check b/test/files/run/t5284b.check new file mode 100644 index 0000000000..98d9bcb75a --- /dev/null +++ b/test/files/run/t5284b.check @@ -0,0 +1 @@ +17 diff --git a/test/files/run/t5284b.scala b/test/files/run/t5284b.scala new file mode 100644 index 0000000000..a9282a895f --- /dev/null +++ b/test/files/run/t5284b.scala @@ -0,0 +1,28 @@ + + + + + + +/** Here we have a situation where a normalized method parameter `W` + * is used in a position which expects a type `T` - we know we can + * safely cast `W` to `T` whenever typebounds of `W` hold. + */ +object Test { + def main(args: Array[String]) { + val foo = Foo.createUnspecialized[Int] + println(foo.bar(17)) + } +} + + +object Foo { + def createUnspecialized[T] = new Foo[T] +} + + +class Foo[@specialized(Int) T] { + val id: T => T = x => x + + def bar[@specialized(Int) W <: T, @specialized(Int) S](w: W) = id(w) +} diff --git a/test/files/run/t5284c.check b/test/files/run/t5284c.check new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/test/files/run/t5284c.check @@ -0,0 +1 @@ +3 diff --git a/test/files/run/t5284c.scala b/test/files/run/t5284c.scala new file mode 100644 index 0000000000..383b84c2cc --- /dev/null +++ b/test/files/run/t5284c.scala @@ -0,0 +1,30 @@ + + + + + + +/** Here we have a compound type `List[W]` used in + * a position where `List[T]` is expected. The cast + * emitted in the normalized `bar` is safe because the + * normalized `bar` can only be called if the type + * bounds hold. + */ +object Test { + def main(args: Array[String]) { + val foo = Foo.createUnspecialized[Int] + println(foo.bar(List(1, 2, 3))) + } +} + + +object Foo { + def createUnspecialized[T] = new Foo[T] +} + + +class Foo[@specialized(Int) T] { + val len: List[T] => Int = xs => xs.length + + def bar[@specialized(Int) W <: T](ws: List[W]) = len(ws) +} -- cgit v1.2.3 From f84659b46ee25cdc4b63e6d59f5015fc5314a63d Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 21 Jun 2012 14:07:38 +0200 Subject: Fix SI-4809. --- src/library/scala/util/control/Breaks.scala | 8 +++---- test/files/run/t4809.scala | 34 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 test/files/run/t4809.scala (limited to 'test/files/run') diff --git a/src/library/scala/util/control/Breaks.scala b/src/library/scala/util/control/Breaks.scala index d7f5a57f50..accda5b8f7 100644 --- a/src/library/scala/util/control/Breaks.scala +++ b/src/library/scala/util/control/Breaks.scala @@ -41,8 +41,8 @@ class Breaks { } } - trait TryBlock { - def catchBreak(onBreak: => Unit): Unit + sealed trait TryBlock[T] { + def catchBreak(onBreak: =>T): T } /** @@ -57,8 +57,8 @@ class Breaks { * } * }}} */ - def tryBreakable(op: => Unit) = new TryBlock { - def catchBreak(onBreak: => Unit) = try { + def tryBreakable[T](op: =>T) = new TryBlock[T] { + def catchBreak(onBreak: =>T) = try { op } catch { case ex: BreakControl => diff --git a/test/files/run/t4809.scala b/test/files/run/t4809.scala new file mode 100644 index 0000000000..b30d80562f --- /dev/null +++ b/test/files/run/t4809.scala @@ -0,0 +1,34 @@ + + +import scala.util.control.Breaks._ + + + +object Test { + + def main(args: Array[String]) { + val x = tryBreakable { + break + 2 + } catchBreak { + 3 + } + assert(x == 3, x) + + val y = tryBreakable { + 2 + } catchBreak { + 3 + } + assert(y == 2, y) + + val z = tryBreakable { + break + 1.0 + } catchBreak { + 2 + } + assert(z == 2.0, z) + } + +} -- cgit v1.2.3 From 4736897ad2a6425c64cadfe80d5e6d7751ee0fec Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 23 Jun 2012 19:04:50 +0200 Subject: SI-5966 Fix eta expansion for repeated parameters with zero arguments. Reworks part of e33901 / SI-5610, which was inserting an tree as an argument in this case, which turns into a null in icode. --- src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala | 8 +++++--- test/files/run/t5966.check | 3 +++ test/files/run/t5966.scala | 9 +++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t5966.check create mode 100644 test/files/run/t5966.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index e1fb683aa9..177d1ddf19 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -88,9 +88,11 @@ trait EtaExpansion { self: Analyzer => defs ++= stats liftoutPrefix(fun) case Apply(fn, args) => - val byName = fn.tpe.params.map(p => definitions.isByNameParamType(p.tpe)) - // zipAll: with repeated params, there might be more args than params - val newArgs = args.zipAll(byName, EmptyTree, false) map { case (arg, byN) => liftout(arg, byN) } + val byName: Int => Option[Boolean] = fn.tpe.params.map(p => definitions.isByNameParamType(p.tpe)).lift + val newArgs = mapWithIndex(args) { (arg, i) => + // with repeated params, there might be more or fewer args than params + liftout(arg, byName(i).getOrElse(false)) + } treeCopy.Apply(tree, liftoutPrefix(fn), newArgs) setType null case TypeApply(fn, args) => treeCopy.TypeApply(tree, liftoutPrefix(fn), args) setType null diff --git a/test/files/run/t5966.check b/test/files/run/t5966.check new file mode 100644 index 0000000000..bfe8358a77 --- /dev/null +++ b/test/files/run/t5966.check @@ -0,0 +1,3 @@ +(o()_)("") = List() +(o("a1")_)("") = WrappedArray(a1) +(o("a1", "a2")_)("") = WrappedArray(a1, a2) diff --git a/test/files/run/t5966.scala b/test/files/run/t5966.scala new file mode 100644 index 0000000000..bbe1a6e874 --- /dev/null +++ b/test/files/run/t5966.scala @@ -0,0 +1,9 @@ +object o { def apply(i: AnyRef*)(j: String) = i } + +object Test { + def main(args: Array[String]) { + println("(o()_)(\"\") = " + (o()_)("")) + println("(o(\"a1\")_)(\"\") = " + (o("a1")_)("")) + println("(o(\"a1\", \"a2\")_)(\"\") = " + (o("a1", "a2")_)("")) + } +} -- cgit v1.2.3 From dbd31fb845e4731dbe580911b6f44d2d9b1973a9 Mon Sep 17 00:00:00 2001 From: Miguel Garcia Date: Tue, 26 Jun 2012 11:06:50 +0200 Subject: fix for SI-4935 --- .../nsc/backend/opt/DeadCodeElimination.scala | 26 +++++++++++++++++++++- test/files/run/t4935.check | 1 + test/files/run/t4935.flags | 1 + test/files/run/t4935.scala | 9 ++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t4935.check create mode 100644 test/files/run/t4935.flags create mode 100644 test/files/run/t4935.scala (limited to 'test/files/run') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index d4ee9b6b48..5cc6e78e9d 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -100,9 +100,29 @@ abstract class DeadCodeElimination extends SubComponent { var rd = rdef.in(bb); for (Pair(i, idx) <- bb.toList.zipWithIndex) { i match { + case LOAD_LOCAL(l) => defs = defs + Pair(((bb, idx)), rd.vars) -// Console.println(i + ": " + (bb, idx) + " rd: " + rd + " and having: " + defs) + + case STORE_LOCAL(_) => + /* SI-4935 Check whether a module is stack top, if so mark the instruction that loaded it + * (otherwise any side-effects of the module's constructor go lost). + * (a) The other two cases where a module's value is stored (STORE_FIELD and STORE_ARRAY_ITEM) + * are already marked (case clause below). + * (b) A CALL_METHOD targeting a method `m1` where the receiver is potentially a module (case clause below) + * will have the module's load marked provided `isSideEffecting(m1)`. + * TODO check for purity (the ICode?) of the module's constructor (besides m1's purity). + * See also https://github.com/paulp/scala/blob/topic/purity-analysis/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala + */ + val necessary = rdef.findDefs(bb, idx, 1) exists { p => + val (bb1, idx1) = p + bb1(idx1) match { + case LOAD_MODULE(module) => isLoadNeeded(module) + case _ => false + } + } + if (necessary) worklist += ((bb, idx)) + case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx)) @@ -129,6 +149,10 @@ abstract class DeadCodeElimination extends SubComponent { } } + private def isLoadNeeded(module: Symbol): Boolean = { + module.info.member(nme.CONSTRUCTOR).filter(isSideEffecting) != NoSymbol + } + /** Mark useful instructions. Instructions in the worklist are each inspected and their * dependencies are marked useful too, and added to the worklist. */ diff --git a/test/files/run/t4935.check b/test/files/run/t4935.check new file mode 100644 index 0000000000..ef0493b275 --- /dev/null +++ b/test/files/run/t4935.check @@ -0,0 +1 @@ +hello diff --git a/test/files/run/t4935.flags b/test/files/run/t4935.flags new file mode 100644 index 0000000000..ac14fe5dbd --- /dev/null +++ b/test/files/run/t4935.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/run/t4935.scala b/test/files/run/t4935.scala new file mode 100644 index 0000000000..18631e2041 --- /dev/null +++ b/test/files/run/t4935.scala @@ -0,0 +1,9 @@ +object Test extends App { + for (i <- 0 to 1) { + val a = Foo + } +} + +object Foo { + println("hello") +} -- cgit v1.2.3 From 4da95609a92f0c685bb7a48688fe9c485f2bc328 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 25 Jun 2012 17:19:58 +0200 Subject: SI-5914 handle null in classtag extractor --- src/library/scala/reflect/ClassTag.scala | 2 +- test/files/run/t5914.check | 1 + test/files/run/t5914.scala | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t5914.check create mode 100644 test/files/run/t5914.scala (limited to 'test/files/run') diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index f753dfbcbb..860e7bac28 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -52,7 +52,7 @@ trait ClassTag[T] extends Equals with Serializable { * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)` * is uncheckable, but we have an instance of `ClassTag[T]`. */ - def unapply(x: Any): Option[T] = if (runtimeClass.isAssignableFrom(x.getClass)) Some(x.asInstanceOf[T]) else None + def unapply(x: Any): Option[T] = if (x != null && runtimeClass.isAssignableFrom(x.getClass)) Some(x.asInstanceOf[T]) else None /** case class accessories */ override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] diff --git a/test/files/run/t5914.check b/test/files/run/t5914.check new file mode 100644 index 0000000000..818e321255 --- /dev/null +++ b/test/files/run/t5914.check @@ -0,0 +1 @@ +correct diff --git a/test/files/run/t5914.scala b/test/files/run/t5914.scala new file mode 100644 index 0000000000..53cae9be74 --- /dev/null +++ b/test/files/run/t5914.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree: Tree = null + tree match { + case TypeTree() => println("lolwut") + case null => println("correct") + } +} \ No newline at end of file -- cgit v1.2.3 From 22834ee32e8471a8ab33e194d7469686d60ef7d5 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 27 Jun 2012 11:14:24 +0200 Subject: make tests independent of compiler api TODO: t5899 should also be refactored, but I couldn't figure out how I tried the obvious Cake Light pattern with abstract types etc, but that didn't trigger it there must be something with indirection through paths as well --- test/files/neg/t2442.check | 6 +++++- test/files/neg/t2442/MySecondEnum.java | 6 ++++++ test/files/neg/t2442/t2442.scala | 6 ++++++ test/files/run/t5914.scala | 26 ++++++++++++++++++++------ 4 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 test/files/neg/t2442/MySecondEnum.java (limited to 'test/files/run') diff --git a/test/files/neg/t2442.check b/test/files/neg/t2442.check index b45ce3a2f5..714816fd62 100644 --- a/test/files/neg/t2442.check +++ b/test/files/neg/t2442.check @@ -2,4 +2,8 @@ t2442.scala:4: error: match may not be exhaustive. It would fail on the following input: THREE def f(e: MyEnum) = e match { ^ -one error found +t2442.scala:11: error: match may not be exhaustive. +It would fail on the following input: BLUE + def g(e: MySecondEnum) = e match { + ^ +two errors found diff --git a/test/files/neg/t2442/MySecondEnum.java b/test/files/neg/t2442/MySecondEnum.java new file mode 100644 index 0000000000..0f841286de --- /dev/null +++ b/test/files/neg/t2442/MySecondEnum.java @@ -0,0 +1,6 @@ +public enum MySecondEnum { + RED(1), BLUE(2) { public void foo() {} }; + MySecondEnum(int i) {} + + public void foo() {} +} \ No newline at end of file diff --git a/test/files/neg/t2442/t2442.scala b/test/files/neg/t2442/t2442.scala index 4ca0889400..b0a0f3cd41 100644 --- a/test/files/neg/t2442/t2442.scala +++ b/test/files/neg/t2442/t2442.scala @@ -6,4 +6,10 @@ class Test { case TWO => println("two") // missing case --> exhaustivity warning! } + + import MySecondEnum._ + def g(e: MySecondEnum) = e match { + case RED => println("red") + // missing case --> exhaustivity warning! + } } \ No newline at end of file diff --git a/test/files/run/t5914.scala b/test/files/run/t5914.scala index 53cae9be74..45d8815738 100644 --- a/test/files/run/t5914.scala +++ b/test/files/run/t5914.scala @@ -1,9 +1,23 @@ -import scala.reflect.runtime.universe._ +import scala.reflect.ClassTag -object Test extends App { - val tree: Tree = null - tree match { - case TypeTree() => println("lolwut") - case null => println("correct") +trait Trees { + class Tree + implicit val ttTag: ClassTag[TypeTree] + type TypeTree <: Tree + val TypeTree: TypeTreeExtractor + abstract class TypeTreeExtractor { + def unapply(t: TypeTree): Option[String] } + def test(tree: Tree) = + tree match { + case TypeTree(_) => println("lolwut") + case null => println("correct") + } +} + +object Test extends App with Trees { + val ttTag = implicitly[ClassTag[TypeTree]] + case class TypeTree(meh: String) extends Tree + object TypeTree extends TypeTreeExtractor + test(null) // should not crash } \ No newline at end of file -- cgit v1.2.3 From 674f4db1cd9323cb4e637dca6f4641d76c09fc84 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 13:59:20 +0200 Subject: Parallelize convertTo in parallel collection. --- .../collection/parallel/ParIterableLike.scala | 12 ++++++------ test/files/run/collection-conversions.check | 22 ++++++++++++++++++++++ test/files/run/collection-conversions.scala | 6 +++++- 3 files changed, 33 insertions(+), 7 deletions(-) (limited to 'test/files/run') diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index a7ec833193..815253b537 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -841,7 +841,7 @@ self: ParIterableLike[T, Repr, Sequential] => override def toBuffer[U >: T]: collection.mutable.Buffer[U] = seq.toBuffer // have additional, parallel buffers? - override def toTraversable: GenTraversable[T] = this.asInstanceOf[GenTraversable[T]] // TODO add ParTraversable[T] + override def toTraversable: GenTraversable[T] = this.asInstanceOf[GenTraversable[T]] override def toIterable: ParIterable[T] = this.asInstanceOf[ParIterable[T]] @@ -850,13 +850,13 @@ self: ParIterableLike[T, Repr, Sequential] => override def toSet[U >: T]: immutable.ParSet[U] = toParCollection[U, immutable.ParSet[U]](() => immutable.ParSet.newCombiner[U]) override def toMap[K, V](implicit ev: T <:< (K, V)): immutable.ParMap[K, V] = toParMap[K, V, immutable.ParMap[K, V]](() => immutable.ParMap.newCombiner[K, V]) - - // TODO(@alex22): make these better - override def toVector: Vector[T] = seq.toVector - - override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.convertTo[Col] + override def toVector: Vector[T] = convertTo[Vector] + override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = if (cbf().isCombiner) { + toParCollection[T, Col[T]](() => cbf().asCombiner) + } else seq.convertTo(cbf) + /* tasks */ protected trait StrictSplitterCheckTask[R, Tp] extends Task[R, Tp] { diff --git a/test/files/run/collection-conversions.check b/test/files/run/collection-conversions.check index 08d0fa32c5..5e43d25f7e 100644 --- a/test/files/run/collection-conversions.check +++ b/test/files/run/collection-conversions.check @@ -11,6 +11,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing Vector --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -24,6 +25,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing List --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -37,6 +39,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing Buffer --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -50,6 +53,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing ParVector --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -63,6 +67,21 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK +-- Testing ParArray --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing Set --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -76,6 +95,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing SetView --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -89,6 +109,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing BufferView --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -102,3 +123,4 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK \ No newline at end of file diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index b5c4d8e261..6d90caee4b 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -1,6 +1,7 @@ import collection._ import mutable.Buffer import parallel.immutable.ParVector +import parallel.mutable.ParArray import reflect.ClassTag object Test { @@ -29,6 +30,7 @@ object Test { val testStream = Stream(1,2,3) val testArray = Array(1,2,3) val testParVector = ParVector(1,2,3) + val testParArray = ParArray(1,2,3) def testConversion[A: ClassTag](name: String, col: => GenTraversableOnce[A]): Unit = { val tmp = col @@ -45,14 +47,16 @@ object Test { printResult("[Direct] Array ", col.toArray, testArray) printResult("[Copy] Array ", col.convertTo[Array], testArray) printResult("[Copy] ParVector", col.convertTo[ParVector], testParVector) + printResult("[Copy] ParArray ", col.convertTo[ParArray], testParArray) } def main(args: Array[String]): Unit = { - testConversion("iterator", (1 to 3).iterator) + testConversion("iterator", (1 to 3).iterator) testConversion("Vector", Vector(1,2,3)) testConversion("List", List(1,2,3)) testConversion("Buffer", Buffer(1,2,3)) testConversion("ParVector", ParVector(1,2,3)) + testConversion("ParArray", ParArray(1,2,3)) testConversion("Set", Set(1,2,3)) testConversion("SetView", Set(1,2,3).view) testConversion("BufferView", Buffer(1,2,3).view) -- cgit v1.2.3 From ba542ba60836cd89e89ac1e9efbc46be14d766f3 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 14:40:39 +0200 Subject: Fix SI-5971. When using `AbstractTransformed` abstract inner class in views in order to force generating bridges, one must take care to push the corresponding collection trait (such as `Iterable` or `Seq`) as far as possible to the left in the linearization order -- otherwise, overridden methods from these traits can override the already overridden methods in view. This was the case with `takeWhile`. --- .../scala/collection/IterableViewLike.scala | 2 +- src/library/scala/collection/SeqViewLike.scala | 2 +- .../scala/collection/TraversableViewLike.scala | 2 +- test/files/run/t5971.check | 4 ++++ test/files/run/t5971.scala | 23 ++++++++++++++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t5971.check create mode 100644 test/files/run/t5971.scala (limited to 'test/files/run') diff --git a/src/library/scala/collection/IterableViewLike.scala b/src/library/scala/collection/IterableViewLike.scala index c842475590..e0c8b21d09 100644 --- a/src/library/scala/collection/IterableViewLike.scala +++ b/src/library/scala/collection/IterableViewLike.scala @@ -44,7 +44,7 @@ trait IterableViewLike[+A, } /** Explicit instantiation of the `Transformed` trait to reduce class file size in subclasses. */ - private[collection] abstract class AbstractTransformed[+B] extends super[TraversableViewLike].Transformed[B] with Transformed[B] + private[collection] abstract class AbstractTransformed[+B] extends Iterable[B] with super[TraversableViewLike].Transformed[B] with Transformed[B] trait EmptyView extends Transformed[Nothing] with super[TraversableViewLike].EmptyView with super[GenIterableViewLike].EmptyView diff --git a/src/library/scala/collection/SeqViewLike.scala b/src/library/scala/collection/SeqViewLike.scala index f64045c9f6..73f5dda11c 100644 --- a/src/library/scala/collection/SeqViewLike.scala +++ b/src/library/scala/collection/SeqViewLike.scala @@ -40,7 +40,7 @@ trait SeqViewLike[+A, } /** Explicit instantiation of the `Transformed` trait to reduce class file size in subclasses. */ - private[collection] abstract class AbstractTransformed[+B] extends super[IterableViewLike].AbstractTransformed[B] with Transformed[B] + private[collection] abstract class AbstractTransformed[+B] extends Seq[B] with super[IterableViewLike].Transformed[B] with Transformed[B] trait EmptyView extends Transformed[Nothing] with super[IterableViewLike].EmptyView with super[GenSeqViewLike].EmptyView diff --git a/src/library/scala/collection/TraversableViewLike.scala b/src/library/scala/collection/TraversableViewLike.scala index eb2091a5f3..bf4f8205d6 100644 --- a/src/library/scala/collection/TraversableViewLike.scala +++ b/src/library/scala/collection/TraversableViewLike.scala @@ -117,7 +117,7 @@ trait TraversableViewLike[+A, } /** Explicit instantiation of the `Transformed` trait to reduce class file size in subclasses. */ - private[collection] abstract class AbstractTransformed[+B] extends Transformed[B] + private[collection] abstract class AbstractTransformed[+B] extends Traversable[B] with Transformed[B] trait EmptyView extends Transformed[Nothing] with super.EmptyView diff --git a/test/files/run/t5971.check b/test/files/run/t5971.check new file mode 100644 index 0000000000..0c36a1ff02 --- /dev/null +++ b/test/files/run/t5971.check @@ -0,0 +1,4 @@ +r,b +r +a,b +r,a,b \ No newline at end of file diff --git a/test/files/run/t5971.scala b/test/files/run/t5971.scala new file mode 100644 index 0000000000..dbd9beebb3 --- /dev/null +++ b/test/files/run/t5971.scala @@ -0,0 +1,23 @@ + + + + + +/** When using `AbstractTransformed` abstract inner class in views in order + * to force generating bridges, one must take care to push the corresponding + * collection trait (such as `Iterable` or `Seq`) as far as possible to the + * left in the linearization order -- otherwise, overridden methods from these + * traits can override the already overridden methods in view. This was the + * case with `takeWhile`. + * Mind blowing, I know. + */ +object Test { + + def main(args: Array[String]) { + println("bar".view.reverse.filter(_ > 'a').mkString(",")) + println("bar".view.reverse.take(1).mkString(",")) + println("bar".view.reverse.dropWhile(_ > 'a').mkString(",")) + println("bar".view.reverse.takeWhile(_ => true).mkString(",")) + } + +} -- cgit v1.2.3 From 788ac7502154ca1329773ec869242df21015f5f3 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 13:34:49 +0200 Subject: Fix SI-5986. Here we had an issue that RedBlack does not work the same way for sets - which are not supposed to replace an element if it is the same (wrt equals) and maps - which should replace the corresponding values. Adding an overwrite parameter which decides whether to overwrite added keys if they are the same in the ordering. Fix tests. --- .../scala/collection/immutable/RedBlackTree.scala | 33 ++++++++++---------- .../scala/collection/immutable/TreeMap.scala | 4 +-- .../scala/collection/immutable/TreeSet.scala | 4 +-- test/files/run/t5986.check | 15 +++++++++ test/files/run/t5986.scala | 36 ++++++++++++++++++++++ test/files/scalacheck/redblacktree.scala | 4 +-- 6 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 test/files/run/t5986.check create mode 100644 test/files/run/t5986.scala (limited to 'test/files/run') diff --git a/src/library/scala/collection/immutable/RedBlackTree.scala b/src/library/scala/collection/immutable/RedBlackTree.scala index 0f28c4997b..4b573511d1 100644 --- a/src/library/scala/collection/immutable/RedBlackTree.scala +++ b/src/library/scala/collection/immutable/RedBlackTree.scala @@ -43,7 +43,7 @@ object RedBlackTree { } def count(tree: Tree[_, _]) = if (tree eq null) 0 else tree.count - def update[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1)(implicit ordering: Ordering[A]): Tree[A, B1] = blacken(upd(tree, k, v)) + def update[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = blacken(upd(tree, k, v, overwrite)) def delete[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = blacken(del(tree, k)) def rangeImpl[A: Ordering, B](tree: Tree[A, B], from: Option[A], until: Option[A]): Tree[A, B] = (from, until) match { case (Some(from), Some(until)) => this.range(tree, from, until) @@ -122,17 +122,18 @@ object RedBlackTree { else mkTree(isBlack, x, xv, a, r) } - private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { + private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { RedTree(k, v, null, null) } else { val cmp = ordering.compare(k, tree.key) - if (cmp < 0) balanceLeft(isBlackTree(tree), tree.key, tree.value, upd(tree.left, k, v), tree.right) - else if (cmp > 0) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, upd(tree.right, k, v)) - else mkTree(isBlackTree(tree), k, v, tree.left, tree.right) + if (cmp < 0) balanceLeft(isBlackTree(tree), tree.key, tree.value, upd(tree.left, k, v, overwrite), tree.right) + else if (cmp > 0) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, upd(tree.right, k, v, overwrite)) + else if (overwrite || k != tree.key) mkTree(isBlackTree(tree), k, v, tree.left, tree.right) + else tree } - // Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees - // http://www.cse.unsw.edu.au/~dons/data/RedBlackTree.html + /* Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees + * http://www.cse.unsw.edu.au/~dons/data/RedBlackTree.html */ private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { def balance(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tl)) { if (isRedTree(tr)) { @@ -216,7 +217,7 @@ object RedBlackTree { if (ordering.lt(tree.key, from)) return doFrom(tree.right, from) val newLeft = doFrom(tree.left, from) if (newLeft eq tree.left) tree - else if (newLeft eq null) upd(tree.right, tree.key, tree.value) + else if (newLeft eq null) upd(tree.right, tree.key, tree.value, false) else rebalance(tree, newLeft, tree.right) } private[this] def doTo[A, B](tree: Tree[A, B], to: A)(implicit ordering: Ordering[A]): Tree[A, B] = { @@ -224,7 +225,7 @@ object RedBlackTree { if (ordering.lt(to, tree.key)) return doTo(tree.left, to) val newRight = doTo(tree.right, to) if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value) + else if (newRight eq null) upd(tree.left, tree.key, tree.value, false) else rebalance(tree, tree.left, newRight) } private[this] def doUntil[A, B](tree: Tree[A, B], until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { @@ -232,7 +233,7 @@ object RedBlackTree { if (ordering.lteq(until, tree.key)) return doUntil(tree.left, until) val newRight = doUntil(tree.right, until) if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value) + else if (newRight eq null) upd(tree.left, tree.key, tree.value, false) else rebalance(tree, tree.left, newRight) } private[this] def doRange[A, B](tree: Tree[A, B], from: A, until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { @@ -242,8 +243,8 @@ object RedBlackTree { val newLeft = doFrom(tree.left, from) val newRight = doUntil(tree.right, until) if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) upd(newRight, tree.key, tree.value); - else if (newRight eq null) upd(newLeft, tree.key, tree.value); + else if (newLeft eq null) upd(newRight, tree.key, tree.value, false); + else if (newRight eq null) upd(newLeft, tree.key, tree.value, false); else rebalance(tree, newLeft, newRight) } @@ -254,7 +255,7 @@ object RedBlackTree { if (n > count) return doDrop(tree.right, n - count - 1) val newLeft = doDrop(tree.left, n) if (newLeft eq tree.left) tree - else if (newLeft eq null) upd(tree.right, tree.key, tree.value) + else if (newLeft eq null) upd(tree.right, tree.key, tree.value, false) else rebalance(tree, newLeft, tree.right) } private[this] def doTake[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = { @@ -264,7 +265,7 @@ object RedBlackTree { if (n <= count) return doTake(tree.left, n) val newRight = doTake(tree.right, n - count - 1) if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value) + else if (newRight eq null) upd(tree.left, tree.key, tree.value, false) else rebalance(tree, tree.left, newRight) } private[this] def doSlice[A: Ordering, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = { @@ -275,8 +276,8 @@ object RedBlackTree { val newLeft = doDrop(tree.left, from) val newRight = doTake(tree.right, until - count - 1) if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) upd(newRight, tree.key, tree.value) - else if (newRight eq null) upd(newLeft, tree.key, tree.value) + else if (newLeft eq null) upd(newRight, tree.key, tree.value, false) + else if (newRight eq null) upd(newLeft, tree.key, tree.value, false) else rebalance(tree, newLeft, newRight) } diff --git a/src/library/scala/collection/immutable/TreeMap.scala b/src/library/scala/collection/immutable/TreeMap.scala index 4c1a5f2e03..51bc76efc3 100644 --- a/src/library/scala/collection/immutable/TreeMap.scala +++ b/src/library/scala/collection/immutable/TreeMap.scala @@ -131,7 +131,7 @@ class TreeMap[A, +B] private (tree: RB.Tree[A, B])(implicit val ordering: Orderi * @param value the value to be associated with `key` * @return a new $coll with the updated binding */ - override def updated [B1 >: B](key: A, value: B1): TreeMap[A, B1] = new TreeMap(RB.update(tree, key, value)) + override def updated [B1 >: B](key: A, value: B1): TreeMap[A, B1] = new TreeMap(RB.update(tree, key, value, true)) /** Add a key/value pair to this map. * @tparam B1 type of the value of the new binding, a supertype of `B` @@ -171,7 +171,7 @@ class TreeMap[A, +B] private (tree: RB.Tree[A, B])(implicit val ordering: Orderi */ def insert [B1 >: B](key: A, value: B1): TreeMap[A, B1] = { assert(!RB.contains(tree, key)) - new TreeMap(RB.update(tree, key, value)) + new TreeMap(RB.update(tree, key, value, true)) } def - (key:A): TreeMap[A, B] = diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala index 882e828c5b..697da2bc4b 100644 --- a/src/library/scala/collection/immutable/TreeSet.scala +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -112,7 +112,7 @@ class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: Orderin * @param elem a new element to add. * @return a new $coll containing `elem` and all the elements of this $coll. */ - def + (elem: A): TreeSet[A] = newSet(RB.update(tree, elem, ())) + def + (elem: A): TreeSet[A] = newSet(RB.update(tree, elem, (), false)) /** A new `TreeSet` with the entry added is returned, * assuming that elem is not in the TreeSet. @@ -122,7 +122,7 @@ class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: Orderin */ def insert(elem: A): TreeSet[A] = { assert(!RB.contains(tree, elem)) - newSet(RB.update(tree, elem, ())) + newSet(RB.update(tree, elem, (), false)) } /** Creates a new `TreeSet` with the entry removed. diff --git a/test/files/run/t5986.check b/test/files/run/t5986.check new file mode 100644 index 0000000000..4101770c6d --- /dev/null +++ b/test/files/run/t5986.check @@ -0,0 +1,15 @@ +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) \ No newline at end of file diff --git a/test/files/run/t5986.scala b/test/files/run/t5986.scala new file mode 100644 index 0000000000..8cf7086f98 --- /dev/null +++ b/test/files/run/t5986.scala @@ -0,0 +1,36 @@ + + + +import scala.collection._ + + + +/** A sorted set should not replace elements when adding + * and the element already exists in the set. + */ +object Test { + + class Foo(val name: String, val n: Int) { + override def equals(obj: Any): Boolean = obj match { case other: Foo => name == other.name; case _ => false } + override def hashCode = name.## + override def toString = "Foo(" + name + ", " + n + ")" + } + + implicit val ordering: Ordering[Foo] = Ordering.fromLessThan[Foo] { (a, b) => a.name.compareTo(b.name) < 0 } + + def check[S <: Set[Foo]](set: S) { + def output(s: Set[Foo]) = println(s.toList.sorted.mkString(",")) + output(set + new Foo("bar", 2)) + output(set ++ List(new Foo("bar", 2), new Foo("bar", 3), new Foo("bar", 4))) + output(set union Set(new Foo("bar", 2), new Foo("baz", 3), new Foo("bazz", 4))) + } + + def main(args: Array[String]) { + check(Set(new Foo("bar", 1))) + check(immutable.Set(new Foo("bar", 1))) + check(mutable.Set(new Foo("bar", 1))) + check(immutable.SortedSet(new Foo("bar", 1))) + check(mutable.SortedSet(new Foo("bar", 1))) + } + +} diff --git a/test/files/scalacheck/redblacktree.scala b/test/files/scalacheck/redblacktree.scala index e4b356c889..e2609fa200 100644 --- a/test/files/scalacheck/redblacktree.scala +++ b/test/files/scalacheck/redblacktree.scala @@ -121,7 +121,7 @@ package scala.collection.immutable.redblacktree { override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, iterator(tree).size + 1) - override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = update(tree, generateKey(tree, parm), 0) + override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = update(tree, generateKey(tree, parm), 0, true) def generateKey(tree: Tree[String, Int], parm: ModifyParm): String = nodeAt(tree, parm) match { case Some((key, _)) => key.init.mkString + "MN" @@ -144,7 +144,7 @@ package scala.collection.immutable.redblacktree { override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, iterator(tree).size) override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = nodeAt(tree, parm) map { - case (key, _) => update(tree, key, newValue) + case (key, _) => update(tree, key, newValue, true) } getOrElse tree property("update modifies values") = forAll(genInput) { case (tree, parm, newTree) => -- cgit v1.2.3 From 5362f3df48a363308e41434b17fca60a0d4d84da Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 16:30:35 +0200 Subject: Fix SI-3326. The heart of the problem - we want to retain the ordering when using `++` on sorted maps. There are 2 `++` overloads - a generic one in traversables and a map-specific one in `MapLike` - which knows about the ordering. The problem here is that the expected return type for the expression in which `++` appears drives the decision of the overload that needs to be taken. The `collection.SortedMap` does not have `++` overridden to return `SortedMap`, but `immutable.Map` instead. This is why `collection.SortedMap` used to resort to the generic `TraversableLike.++` which knows nothing about the ordering. To avoid `collection.SortedMap`s resort to the more generic `TraverableLike.++`, we override the `MapLike.++` overload in `collection.SortedMap` to return the proper type `SortedMap`. --- src/library/scala/collection/SortedMapLike.scala | 8 +++ test/files/run/t3326.check | 8 +++ test/files/run/t3326.scala | 74 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 test/files/run/t3326.check create mode 100644 test/files/run/t3326.scala (limited to 'test/files/run') diff --git a/src/library/scala/collection/SortedMapLike.scala b/src/library/scala/collection/SortedMapLike.scala index 4dc0820a62..6b25151192 100644 --- a/src/library/scala/collection/SortedMapLike.scala +++ b/src/library/scala/collection/SortedMapLike.scala @@ -72,4 +72,12 @@ self => for (e <- elems) m = m + e m } + + /** Adds a number of elements provided by a traversable object + * and returns a new collection with the added elements. + * + * @param xs the traversable object. + */ + override def ++[B1 >: B](xs: GenTraversableOnce[(A, B1)]): SortedMap[A, B1] = + ((repr: SortedMap[A, B1]) /: xs.seq) (_ + _) } diff --git a/test/files/run/t3326.check b/test/files/run/t3326.check new file mode 100644 index 0000000000..d0e11cebf7 --- /dev/null +++ b/test/files/run/t3326.check @@ -0,0 +1,8 @@ +Map(2 -> Hello, 1 -> World) +Map(5 -> Foo, 4 -> Bar) +Map(5 -> Foo, 4 -> Bar, 2 -> Hello, 1 -> World) +Map(3 -> ?, 2 -> Hello, 1 -> World) +Map(2 -> Hello, 1 -> World) +Map(5 -> Foo, 4 -> Bar) +Map(5 -> Foo, 4 -> Bar, 2 -> Hello, 1 -> World) +Map(3 -> ?, 2 -> Hello, 1 -> World) \ No newline at end of file diff --git a/test/files/run/t3326.scala b/test/files/run/t3326.scala new file mode 100644 index 0000000000..f70cb01504 --- /dev/null +++ b/test/files/run/t3326.scala @@ -0,0 +1,74 @@ + + + +import scala.math.Ordering + + + +/** The heart of the problem - we want to retain the ordering when + * using `++` on sorted maps. + * + * There are 2 `++` overloads - a generic one in traversables and + * a map-specific one in `MapLike` - which knows about the ordering. + * + * The problem here is that the expected return type for the expression + * in which `++` appears drives the decision of the overload that needs + * to be taken. + * The `collection.SortedMap` does not have `++` overridden to return + * `SortedMap`, but `immutable.Map` instead. + * This is why `collection.SortedMap` used to resort to the generic + * `TraversableLike.++` which knows nothing about the ordering. + * + * To avoid `collection.SortedMap`s resort to the more generic `TraverableLike.++`, + * we override the `MapLike.++` overload in `collection.SortedMap` to return + * the proper type `SortedMap`. + */ +object Test { + + def main(args: Array[String]) { + testCollectionSorted() + testImmutableSorted() + } + + def testCollectionSorted() { + import collection._ + val order = implicitly[Ordering[Int]].reverse + var m1: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + var m2: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + + m1 += (1 -> "World") + m1 += (2 -> "Hello") + + m2 += (4 -> "Bar") + m2 += (5 -> "Foo") + + val m3: SortedMap[Int, String] = m1 ++ m2 + + println(m1) + println(m2) + println(m3) + + println(m1 + (3 -> "?")) + } + + def testImmutableSorted() { + import collection.immutable._ + val order = implicitly[Ordering[Int]].reverse + var m1: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + var m2: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + + m1 += (1 -> "World") + m1 += (2 -> "Hello") + + m2 += (4 -> "Bar") + m2 += (5 -> "Foo") + + val m3: SortedMap[Int, String] = m1 ++ m2 + + println(m1) + println(m2) + println(m3) + + println(m1 + (3 -> "?")) + } +} -- cgit v1.2.3