diff options
22 files changed, 299 insertions, 38 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 153e32f3a3..2e1ed0863a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4728,7 +4728,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper UnstableTreeError(qualTyped) ) val tree1 = name match { - case nme.withFilter => tryWithFilterAndFilter(tree, qualStableOrError) + case nme.withFilter if !settings.future => tryWithFilterAndFilter(tree, qualStableOrError) case _ => typedSelect(tree, qualStableOrError, name) } def sym = tree1.symbol diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index c3728fa02a..930e13a9d3 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -203,17 +203,19 @@ sealed abstract class List[+A] extends AbstractSeq[A] override def toList: List[A] = this - override def take(n: Int): List[A] = { - val b = new ListBuffer[A] - var i = 0 - var these = this - while (!these.isEmpty && i < n) { + override def take(n: Int): List[A] = if (isEmpty || n <= 0) Nil else { + val h = new ::(head, Nil) + var t = h + var rest = tail + var i = 1 + while ({if (rest.isEmpty) return this; i < n}) { i += 1 - b += these.head - these = these.tail + val nx = new ::(rest.head, Nil) + t.tl = nx + t = nx + rest = rest.tail } - if (these.isEmpty) this - else b.toList + h } override def drop(n: Int): List[A] = { @@ -264,6 +266,85 @@ sealed abstract class List[+A] extends AbstractSeq[A] } (b.toList, these) } + + @noinline // TODO - fix optimizer bug that requires noinline (see SI-8334) + final override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That = { + if (bf eq List.ReusableCBF) { + if (this eq Nil) Nil.asInstanceOf[That] else { + val h = new ::[B](f(head), Nil) + var t: ::[B] = h + var rest = tail + while (rest ne Nil) { + val nx = new ::(f(rest.head), Nil) + t.tl = nx + t = nx + rest = rest.tail + } + h.asInstanceOf[That] + } + } + else super.map(f) + } + + @noinline // TODO - fix optimizer bug that requires noinline for map; applied here to be safe (see SI-8334) + final override def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[List[A], B, That]): That = { + if (bf eq List.ReusableCBF) { + if (this eq Nil) Nil.asInstanceOf[That] else { + var rest = this + var h: ::[B] = null + var x: A = null.asInstanceOf[A] + // Special case for first element + do { + val x: Any = pf.applyOrElse(rest.head, List.partialNotApplied) + if (x.asInstanceOf[AnyRef] ne List.partialNotApplied) h = new ::(x.asInstanceOf[B], Nil) + rest = rest.tail + if (rest eq Nil) return (if (h eq null ) Nil else h).asInstanceOf[That] + } while (h eq null) + var t = h + // Remaining elements + do { + val x: Any = pf.applyOrElse(rest.head, List.partialNotApplied) + if (x.asInstanceOf[AnyRef] ne List.partialNotApplied) { + val nx = new ::(x.asInstanceOf[B], Nil) + t.tl = nx + t = nx + } + rest = rest.tail + } while (rest ne Nil) + h.asInstanceOf[That] + } + } + else super.collect(pf) + } + + @noinline // TODO - fix optimizer bug that requires noinline for map; applied here to be safe (see SI-8334) + final override def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That = { + if (bf eq List.ReusableCBF) { + if (this eq Nil) Nil.asInstanceOf[That] else { + var rest = this + var found = false + var h: ::[B] = null + var t: ::[B] = null + while (rest ne Nil) { + f(rest.head).foreach{ b => + if (!found) { + h = new ::(b, Nil) + t = h + found = true + } + else { + val nx = new ::(b, Nil) + t.tl = nx + t = nx + } + } + rest = rest.tail + } + (if (!found) Nil else h).asInstanceOf[That] + } + } + else super.flatMap(f) + } @inline final override def takeWhile(p: A => Boolean): List[A] = { val b = new ListBuffer[A] @@ -375,6 +456,8 @@ object List extends SeqFactory[List] { override def empty[A]: List[A] = Nil override def apply[A](xs: A*): List[A] = xs.toList + + private[collection] val partialNotApplied = new Function1[Any, Any] { def apply(x: Any): Any = this } @SerialVersionUID(1L) private class SerializationProxy[A](@transient private var orig: List[A]) extends Serializable { diff --git a/src/library/scala/collection/mutable/BufferLike.scala b/src/library/scala/collection/mutable/BufferLike.scala index 7ba0b27ce5..3c57387c03 100644 --- a/src/library/scala/collection/mutable/BufferLike.scala +++ b/src/library/scala/collection/mutable/BufferLike.scala @@ -211,9 +211,11 @@ trait BufferLike[A, +This <: BufferLike[A, This] with Buffer[A]] */ override def stringPrefix: String = "Buffer" - /** Provide a read-only view of this buffer as a sequence - * @return A sequence which refers to this buffer for all its operations. + /** Returns the current evolving(!) state of this buffer as a read-only sequence. + * + * @return A sequence that forwards to this buffer for all its operations. */ + @deprecated("The returned sequence changes as this buffer is mutated. For an immutable copy, use, e.g., toList.", "2.11.0") def readOnly: scala.collection.Seq[A] = toSeq /** Creates a new collection containing both the elements of this collection and the provided diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index e76825cea9..5e838d0d88 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -13,6 +13,7 @@ package mutable import generic._ import immutable.{List, Nil, ::} import java.io._ +import scala.annotation.migration /** A `Buffer` implementation back up by a list. It provides constant time * prepend and append. Most other operations are linear. @@ -262,7 +263,7 @@ final class ListBuffer[A] * @param n the index which refers to the first element to remove. * @param count the number of elements to remove. */ - @scala.annotation.migration("Invalid input values will be rejected in future releases.", "2.11") + @migration("Invalid input values will be rejected in future releases.", "2.11") override def remove(n: Int, count: Int) { if (n >= len) return @@ -407,7 +408,7 @@ final class ListBuffer[A] } } - /** expose the underlying list but do not mark it as exported */ + @deprecated("The result of this method will change along with this buffer, which is often not what's expected.", "2.11.0") override def readOnly: List[A] = start // Private methods diff --git a/src/library/scala/collection/mutable/Map.scala b/src/library/scala/collection/mutable/Map.scala index fe29ce4221..2ac3cb65b5 100644 --- a/src/library/scala/collection/mutable/Map.scala +++ b/src/library/scala/collection/mutable/Map.scala @@ -47,18 +47,6 @@ trait Map[A, B] * @return a wrapper of the map with a default value */ def withDefaultValue(d: B): mutable.Map[A, B] = new Map.WithDefault[A, B](this, x => d) - - /* Return a read-only projection of this map. !!! or just use an (immutable) MapProxy? - def readOnly : scala.collection.Map[A, B] = new scala.collection.Map[A, B] { - override def size = self.size - override def update(key: A, value: B) = self.update(key, value) - override def - (elem: A) = self - elem - override def iterator = self.iterator - override def foreach[U](f: ((A, B)) => U) = self.foreach(f) - override def empty[C] = self.empty[C] - def get(key: A) = self.get(key) - } - */ } /** $factoryInfo diff --git a/src/library/scala/math/package.scala b/src/library/scala/math/package.scala index af5dad5c38..58ece8a05b 100644 --- a/src/library/scala/math/package.scala +++ b/src/library/scala/math/package.scala @@ -93,12 +93,22 @@ package object math { */ def pow(x: Double, y: Double): Double = java.lang.Math.pow(x, y) - /** Returns the closest `long` to the argument. + /** There is no reason to round a `Long`, but this method prevents unintended conversion to `Float` followed by rounding to `Int`. */ + @deprecated("This is an integer type; there is no reason to round it. Perhaps you meant to call this with a floating-point value?", "2.11.0") + def round(x: Long): Long = x + + /** Returns the closest `Int` to the argument. * - * @param x a floating-point value to be rounded to a `long`. - * @return the value of the argument rounded to the nearest`long` value. + * @param x a floating-point value to be rounded to a `Int`. + * @return the value of the argument rounded to the nearest `Int` value. */ def round(x: Float): Int = java.lang.Math.round(x) + + /** Returns the closest `Long` to the argument. + * + * @param x a floating-point value to be rounded to a `Long`. + * @return the value of the argument rounded to the nearest`long` value. + */ def round(x: Double): Long = java.lang.Math.round(x) def abs(x: Int): Int = java.lang.Math.abs(x) diff --git a/src/library/scala/runtime/RichInt.scala b/src/library/scala/runtime/RichInt.scala index cc4e92dfa3..cda9d2907a 100644 --- a/src/library/scala/runtime/RichInt.scala +++ b/src/library/scala/runtime/RichInt.scala @@ -36,6 +36,10 @@ final class RichInt(val self: Int) extends AnyVal with ScalaNumberProxy[Int] wit override def max(that: Int): Int = math.max(self, that) override def min(that: Int): Int = math.min(self, that) override def signum: Int = math.signum(self) + + /** There is no reason to round an `Int`, but this method is provided to avoid accidental loss of precision from a detour through `Float`. */ + @deprecated("This is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value?", "2.11.0") + def round: Int = self def toBinaryString: String = java.lang.Integer.toBinaryString(self) def toHexString: String = java.lang.Integer.toHexString(self) diff --git a/src/library/scala/runtime/RichLong.scala b/src/library/scala/runtime/RichLong.scala index df0bbec047..b405fcda3d 100644 --- a/src/library/scala/runtime/RichLong.scala +++ b/src/library/scala/runtime/RichLong.scala @@ -32,6 +32,10 @@ final class RichLong(val self: Long) extends AnyVal with IntegralProxy[Long] { override def max(that: Long): Long = math.max(self, that) override def min(that: Long): Long = math.min(self, that) override def signum: Int = math.signum(self).toInt + + /** There is no reason to round a `Long`, but this method is provided to avoid accidental conversion to `Int` through `Float`. */ + @deprecated("This is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value?", "2.11.0") + def round: Long = self def toBinaryString: String = java.lang.Long.toBinaryString(self) def toHexString: String = java.lang.Long.toHexString(self) diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index 89db57a55e..b0cf122f2a 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -111,6 +111,35 @@ sealed abstract class Try[+T] { */ def filter(p: T => Boolean): Try[T] + /** Creates a non-strict filter, which eventually converts this to a `Failure` + * if the predicate is not satisfied. + * + * Note: unlike filter, withFilter does not create a new Try. + * Instead, it restricts the domain of subsequent + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * + * As Try is a one-element collection, this may be a bit overkill, + * but it's consistent with withFilter on Option and the other collections. + * + * @param p the predicate used to test elements. + * @return an object of class `WithFilter`, which supports + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * All these operations apply to those elements of this Try + * which satisfy the predicate `p`. + */ + @inline final def withFilter(p: T => Boolean): WithFilter = new WithFilter(p) + + /** We need a whole WithFilter class to honor the "doesn't create a new + * collection" contract even though it seems unlikely to matter much in a + * collection with max size 1. + */ + class WithFilter(p: T => Boolean) { + def map[U](f: T => U): Try[U] = Try.this filter p map f + def flatMap[U](f: T => Try[U]): Try[U] = Try.this filter p flatMap f + def foreach[U](f: T => U): Unit = Try.this filter p foreach f + def withFilter(q: T => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) + } + /** * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. * This is like `flatMap` for the exception. diff --git a/src/reflect/scala/reflect/api/Liftables.scala b/src/reflect/scala/reflect/api/Liftables.scala index 6ac5557caa..ec9d85b69e 100644 --- a/src/reflect/scala/reflect/api/Liftables.scala +++ b/src/reflect/scala/reflect/api/Liftables.scala @@ -2,27 +2,72 @@ package scala package reflect package api -// TODO: needs a Scaladoc trait Liftables { self: Universe => - // TODO: needs a Scaladoc + /** A type class that defines a representation of `T` as a `Tree`. + * + * @see [[http://docs.scala-lang.org/overviews/macros/quasiquotes.html#lifting]] + */ trait Liftable[T] { def apply(value: T): Tree } - // TODO: needs a Scaladoc + /** Companion to `Liftable` type class that contains standard instances + * and provides a helper `apply` method to simplify creation of new ones. + */ object Liftable extends StandardLiftableInstances { + /** A helper method that simplifies creation of `Liftable` instances. + * Takes a type and a function that maps that type to a tree representation. + * + * For example to write Liftable for object one might use it like: + * + * {{{ + * scala> object O + * + * scala> val Oref = symbolOf[O.type].asClass.module + * + * scala> implicit val liftO = Liftable[O.type] { _ => q"$Oref" } + * + * scala> val lifted = q"$O" + * lifted: universe.Tree = O + * }}} + * + * @see [[http://docs.scala-lang.org/overviews/macros/quasiquotes.html#lifting]] + */ def apply[T](f: T => Tree): Liftable[T] = new Liftable[T] { def apply(value: T): Tree = f(value) } } - // TODO: needs a Scaladoc + /** A type class that defines a way to extract instance of `T` from a `Tree`. + * + * @see [[http://docs.scala-lang.org/overviews/macros/quasiquotes.html#unlifting]] + */ trait Unliftable[T] { def unapply(tree: Tree): Option[T] } - // TODO: needs a Scaladoc + /** Companion to `Unliftable` type class that contains standard instances + * and provides a helper `apply` method to simplify creation of new ones. + */ object Unliftable extends StandardUnliftableInstances { + /** A helper method that simplifies creation of `Unliftable` instances. + * Takes a partial function which is defined on correct representations of `T` + * and returns corresponing instances. + * + * For example to extract a reference to an object as object itself: + * + * {{{ + * scala> object O + * + * scala> val Oref = symbolOf[O.type].asClass.module + * + * scala> implicit val unliftO = Unliftable[O.type] { case t if t.symbol == Oref => O } + * + * scala> val q"${_: O.type}" = q"$Oref" + * }}} + * + * @see [[http://docs.scala-lang.org/overviews/macros/quasiquotes.html#unlifting]] + */ def apply[T](pf: PartialFunction[Tree, T]): Unliftable[T] = new Unliftable[T] { def unapply(value: Tree): Option[T] = pf.lift(value) } diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala index c939eee164..0065926e3b 100644 --- a/src/reflect/scala/reflect/api/Quasiquotes.scala +++ b/src/reflect/scala/reflect/api/Quasiquotes.scala @@ -3,10 +3,16 @@ package api trait Quasiquotes { self: Universe => - // implementation is hardwired to `dispatch` method of `scala.tools.reflect.quasiquotes.Quasiquotes` - // using the mechanism implemented in `scala.tools.reflect.FastTrack` + /** Implicit class that introduces `q`, `tq`, `cq,` `p` and `fq` string interpolators + * that are also known as quasiquotes. With their help you can easily manipulate + * Scala reflection ASTs. + * + * @see [[http://docs.scala-lang.org/overviews/macros/quasiquotes.html]] + */ implicit class Quasiquote(ctx: StringContext) { protected trait api { + // implementation is hardwired to `dispatch` method of `scala.tools.reflect.quasiquotes.Quasiquotes` + // using the mechanism implemented in `scala.tools.reflect.FastTrack` def apply[T](args: T*): Tree = macro ??? def unapply(scrutinee: Any): Any = macro ??? } diff --git a/test/files/neg/t6455.check b/test/files/neg/t6455.check new file mode 100644 index 0000000000..8f2aad0b9e --- /dev/null +++ b/test/files/neg/t6455.check @@ -0,0 +1,4 @@ +t6455.scala:5: error: value withFilter is not a member of object O + O.withFilter(f => true) + ^ +one error found diff --git a/test/files/neg/t6455.flags b/test/files/neg/t6455.flags new file mode 100644 index 0000000000..112fc720a0 --- /dev/null +++ b/test/files/neg/t6455.flags @@ -0,0 +1 @@ +-Xfuture
\ No newline at end of file diff --git a/test/files/neg/t6455.scala b/test/files/neg/t6455.scala new file mode 100644 index 0000000000..ebbb37f1cd --- /dev/null +++ b/test/files/neg/t6455.scala @@ -0,0 +1,6 @@ +object O { def filter(p: Int => Boolean): O.type = this } + +class Test { + // should not compile because we no longer rewrite withFilter => filter under -Xfuture + O.withFilter(f => true) +}
\ No newline at end of file diff --git a/test/files/pos/list-optim-check.flags b/test/files/pos/list-optim-check.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/pos/list-optim-check.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/pos/list-optim-check.scala b/test/files/pos/list-optim-check.scala new file mode 100644 index 0000000000..f6e6ddec77 --- /dev/null +++ b/test/files/pos/list-optim-check.scala @@ -0,0 +1,21 @@ +// Tests a map known to crash in optimizer with faster List map in SI-8240. +// Equivalent tests for collect and flatmap do not crash, but are provided +// anyway. +// See ticket SI-8334 for optimizer bug. +// TODO - Remove this test once SI-8334 is fixed and has its own test. +class A { + def f: Boolean = { + val xs = Nil map (_ => return false) + true + } + + def g: Boolean = { + val xs = Nil collect { case _ => return false } + true + } + + def h: Boolean = { + val xs = Nil flatMap { _ => return false } + true + } +} diff --git a/test/files/run/t3235-minimal.check b/test/files/run/t3235-minimal.check new file mode 100644 index 0000000000..d7f716002f --- /dev/null +++ b/test/files/run/t3235-minimal.check @@ -0,0 +1,12 @@ +t3235-minimal.scala:3: warning: method round in class RichInt is deprecated: This is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + assert(123456789.round == 123456789) + ^ +t3235-minimal.scala:4: warning: method round in package math is deprecated: This is an integer type; there is no reason to round it. Perhaps you meant to call this with a floating-point value? + assert(math.round(123456789) == 123456789) + ^ +t3235-minimal.scala:5: warning: method round in class RichLong is deprecated: This is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + assert(1234567890123456789L.round == 1234567890123456789L) + ^ +t3235-minimal.scala:6: warning: method round in package math is deprecated: This is an integer type; there is no reason to round it. Perhaps you meant to call this with a floating-point value? + assert(math.round(1234567890123456789L) == 1234567890123456789L) + ^ diff --git a/test/files/run/t3235-minimal.flags b/test/files/run/t3235-minimal.flags new file mode 100644 index 0000000000..dcc59ebe32 --- /dev/null +++ b/test/files/run/t3235-minimal.flags @@ -0,0 +1 @@ +-deprecation diff --git a/test/files/run/t3235-minimal.scala b/test/files/run/t3235-minimal.scala new file mode 100644 index 0000000000..dc9907b63b --- /dev/null +++ b/test/files/run/t3235-minimal.scala @@ -0,0 +1,8 @@ +object Test { + def main(args: Array[String]) { + assert(123456789.round == 123456789) + assert(math.round(123456789) == 123456789) + assert(1234567890123456789L.round == 1234567890123456789L) + assert(math.round(1234567890123456789L) == 1234567890123456789L) + } +} diff --git a/test/files/run/t4288.scala b/test/files/run/t4288.scala index 4e7b366f60..23319d1c27 100644 --- a/test/files/run/t4288.scala +++ b/test/files/run/t4288.scala @@ -1,6 +1,6 @@ object Test { def f1 = scala.collection.mutable.ListBuffer(1 to 9: _*).slice(-5, -1) - def f2 = scala.collection.mutable.ListBuffer(1 to 9: _*).readOnly.slice(-5, -1) + def f2 = List(1 to 9: _*).slice(-5, -1) def f3 = Vector(1 to 9: _*).slice(-5, -1) def f4 = Traversable(1 to 9: _*).slice(-5, -1) def f5 = (1 to 9).toArray.slice(-5, -1) diff --git a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala index 432c0959c9..7c4cb0306e 100644 --- a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala +++ b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala @@ -44,7 +44,7 @@ object TypecheckedProps extends QuasiquoteProperties("typechecked") { val enums = fq"foo <- new Foo" :: fq"if foo != null" :: Nil val body = q"foo" val q"$_; for(..$enums1) yield $body1" = typecheck(q""" - class Foo { def map(f: Any => Any) = this; def filter(cond: Any => Boolean) = this } + class Foo { def map(f: Any => Any) = this; def withFilter(cond: Any => Boolean) = this } for(..$enums) yield $body """) assert(enums1 ≈ enums) diff --git a/test/junit/scala/util/TryTest.scala b/test/junit/scala/util/TryTest.scala new file mode 100644 index 0000000000..03604a8065 --- /dev/null +++ b/test/junit/scala/util/TryTest.scala @@ -0,0 +1,35 @@ +package scala.util + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import org.junit.Assert._ + +/* Test Try's withFilter method, which was added along with the -Xfuture fix for SI-6455 */ +@RunWith(classOf[JUnit4]) +class TryTest { + @Test + def withFilterFail(): Unit = { + val fail = for (x <- util.Try(1) if x > 1) yield x + assert(fail.isFailure) + } + + @Test + def withFilterSuccess(): Unit = { + val success1 = for (x <- util.Try(1) if x >= 1) yield x + assertEquals(success1, util.Success(1)) + } + + @Test + def withFilterFlatMap(): Unit = { + val successFlatMap = for (x <- util.Try(1) if x >= 1; y <- util.Try(2) if x < y) yield x + assertEquals(successFlatMap, util.Success(1)) + } + + @Test + def withFilterForeach(): Unit = { + var ok = false + for (x <- util.Try(1) if x == 1) ok = x == 1 + assert(ok) + } +}
\ No newline at end of file |