From da5910c7c622fc248fe525f5c03b1dee6be384b5 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 4 Jun 2010 18:34:02 +0000 Subject: Fix for init-order caused NPE in NumericRange. ran across some tortured logic trying to accomodate the long abandoned idea of having 5 != 5L, so simplified the contains method. Closes #3518, no review. --- .../scala/collection/immutable/NumericRange.scala | 45 ++++++++-------------- test/files/run/bug3518.scala | 16 ++++++++ 2 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 test/files/run/bug3518.scala diff --git a/src/library/scala/collection/immutable/NumericRange.scala b/src/library/scala/collection/immutable/NumericRange.scala index db44e9ffa0..b8bd5bd20e 100644 --- a/src/library/scala/collection/immutable/NumericRange.scala +++ b/src/library/scala/collection/immutable/NumericRange.scala @@ -40,8 +40,8 @@ import generic._ abstract class NumericRange[T] (val start: T, val end: T, val step: T, val isInclusive: Boolean) (implicit num: Integral[T]) -extends IndexedSeq[T] -{ +extends IndexedSeq[T] { + /** Note that NumericRange must be invariant so that constructs * such as * @@ -122,18 +122,6 @@ extends IndexedSeq[T] else start + (fromInt(idx) * step) } - // a well-typed contains method. - def containsTyped(x: T): Boolean = { - def divides(d: T, by: T) = equiv(d % by, zero) - - limitTest(x) || ( - if (step > zero) - (start <= x) && (x < end) && divides(x - start, step) - else - (start >= x) && (x > end) && divides(start - x, step) - ) - } - // Motivated by the desire for Double ranges with BigDecimal precision, // we need some way to map a Range and get another Range. This can't be // done in any fully general way because Ranges are not arbitrary @@ -165,7 +153,7 @@ extends IndexedSeq[T] if (isInclusive) NumericRange.inclusive(start, end, step) else NumericRange(start, end, step) - private val underlyingRange: NumericRange[T] = self + private lazy val underlyingRange: NumericRange[T] = self override def foreach[U](f: A => U) { underlyingRange foreach (x => f(fm(x))) } override def isEmpty = underlyingRange.isEmpty override def apply(idx: Int): A = fm(underlyingRange(idx)) @@ -173,20 +161,21 @@ extends IndexedSeq[T] } } - // The contains situation makes for some interesting code. - // I am not aware of any way to avoid a cast somewhere, because - // contains must take an Any. + // a well-typed contains method. + def containsTyped(x: T): Boolean = { + def divides(d: T, by: T) = equiv(d % by, zero) + + limitTest(x) || ( + if (step > zero) + (start <= x) && (x < end) && divides(x - start, step) + else + (start >= x) && (x > end) && divides(start - x, step) + ) + } + override def contains(x: Any): Boolean = - try { - // if we don't verify that x == typedX, then a range - // of e.g. Longs will appear to contain an Int because - // the cast will perform the conversion. (As of this writing - // it is anticipated that in scala 2.8, 5L != 5 although - // this is not yet implemented.) - val typedX = x.asInstanceOf[T] - containsTyped(typedX) && (x == typedX) - } - catch { case _: ClassCastException => super.contains(x) } + try containsTyped(x.asInstanceOf[T]) + catch { case _: ClassCastException => false } override lazy val hashCode = super.hashCode() override def equals(other: Any) = other match { diff --git a/test/files/run/bug3518.scala b/test/files/run/bug3518.scala new file mode 100644 index 0000000000..033cc19548 --- /dev/null +++ b/test/files/run/bug3518.scala @@ -0,0 +1,16 @@ +object Test { + val r1 = 1.0 to 10.0 by 0.5 + val r2 = 1.0 to 1.0 by 1.0 + val r3 = 10.0 to 1.0 by -0.5 + val r4 = 1.0 until 1.0 by 1.0 + val r5 = 1 to 100 by 2 + + def main(args: Array[String]): Unit = { + assert(r3 forall (r1 contains _)) + assert(r1 forall (r3 contains _)) + assert(r2.size == 1) + assert(r4.isEmpty) + assert(List(1,3,5,97,99) forall (r5 contains _)) + assert(List(2,4,6,98,100) forall (x => !r5.contains(x))) + } +} -- cgit v1.2.3