diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index 0de32b5bb6d1..23ac69d57c71 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -47,7 +47,8 @@ abstract class AccessProxies { if (passReceiverAsArg(accessor.name)) (argss.head.head.select(accessed), tps.takeRight(numTypeParams), argss.tail) else - (ref(TermRef(cls.thisType, accessed)), tps, argss) + (if (accessed.isStatic) ref(accessed) else ref(TermRef(cls.thisType, accessed)), + tps, argss) val rhs = if (accessor.name.isSetterName && forwardedArgss.nonEmpty && forwardedArgss.head.nonEmpty) // defensive conditions @@ -70,6 +71,11 @@ abstract class AccessProxies { def accessorNameKind: ClassifiedNameKind def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean + def ifNoHost(reference: RefTree)(implicit ctx: Context): Tree = { + assert(false, "no host found for $reference with ${reference.symbol.showLocated} from ${ctx.owner}") + reference + } + /** A fresh accessor symbol */ private def newAccessorSymbol(owner: Symbol, name: TermName, info: Type, pos: Position)(implicit ctx: Context): TermSymbol = { val sym = ctx.newSymbol(owner, name, Synthetic | Method, info, coord = pos).entered @@ -125,18 +131,14 @@ abstract class AccessProxies { def useAccessor(reference: RefTree)(implicit ctx: Context): Tree = { val accessed = reference.symbol.asTerm var accessorClass = hostForAccessorOf(accessed: Symbol) - if (!accessorClass.exists) { - val curCls = ctx.owner.enclosingClass - transforms.println(i"${curCls.ownersIterator.toList}%, %") - ctx.error(i"illegal access to protected ${accessed.showLocated} from $curCls", - reference.pos) - accessorClass = curCls + if (accessorClass.exists) { + val accessorName = accessorNameKind(accessed.name) + val accessorInfo = + accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner) + val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed) + rewire(reference, accessor) } - val accessorName = accessorNameKind(accessed.name) - val accessorInfo = - accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner) - val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed) - rewire(reference, accessor) + else ifNoHost(reference) } /** Replace tree with a reference to an accessor if needed */ @@ -156,6 +158,12 @@ object AccessProxies { /** Where an accessor for the `accessed` symbol should be placed. * This is the closest enclosing class that has `accessed` as a member. */ - def hostForAccessorOf(accessed: Symbol)(implicit ctx: Context): Symbol = - ctx.owner.ownersIterator.findSymbol(_.derivesFrom(accessed.owner)) + def hostForAccessorOf(accessed: Symbol)(implicit ctx: Context): Symbol = { + def recur(cls: Symbol): Symbol = + if (!cls.exists) NoSymbol + else if (cls.derivesFrom(accessed.owner)) cls + else if (cls.companionModule.moduleClass == accessed.owner) accessed.owner + else recur(cls.owner) + recur(ctx.owner) + } } diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index 401aa6f900a3..44a12fda4fa2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -8,6 +8,7 @@ import core.Flags._ import core.Decorators._ import MegaPhase.MiniPhase import ast.Trees._ +import config.Printers.transforms /** Add accessors for all protected accesses. An accessor is needed if * according to the rules of the JVM a protected class member is not accesissible @@ -56,6 +57,14 @@ class ProtectedAccessors extends MiniPhase { val insert = new Insert { def accessorNameKind = ProtectedAccessorName def needsAccessor(sym: Symbol)(implicit ctx: Context) = ProtectedAccessors.needsAccessor(sym) + + override def ifNoHost(reference: RefTree)(implicit ctx: Context): Tree = { + val curCls = ctx.owner.enclosingClass + transforms.println(i"${curCls.ownersIterator.toList}%, %") + ctx.error(i"illegal access to protected ${reference.symbol.showLocated} from $curCls", + reference.pos) + reference + } } } diff --git a/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala b/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala index 03f91b090d59..2ba909733ba3 100644 --- a/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala +++ b/compiler/src/dotty/tools/dotc/transform/VCElideAllocations.scala @@ -12,7 +12,7 @@ import ExtensionMethods._, TreeExtractors._, ValueClasses._ * For a value class V defined as: * class V(val underlying: U) extends AnyVal * we avoid unnecessary allocations: - * new V(u1) == new V(u2) => u1 == u2 + * new V(u1) == new V(u2) => u1 == u2 provided V does not redefine `equals` * (new V(u)).underlying() => u */ class VCElideAllocations extends MiniPhase with IdentityDenotTransformer { @@ -27,7 +27,9 @@ class VCElideAllocations extends MiniPhase with IdentityDenotTransformer { // new V(u1) == new V(u2) => u1 == u2 // (We don't handle != because it has been eliminated by InterceptedMethods) case BinaryOp(NewWithArgs(tp1, List(u1)), op, NewWithArgs(tp2, List(u2))) - if (tp1 eq tp2) && (op eq defn.Any_==) && isDerivedValueClass(tp1.typeSymbol) => + if (tp1 eq tp2) && (op eq defn.Any_==) && + isDerivedValueClass(tp1.typeSymbol) && + !defn.Any_equals.overridingSymbol(tp1.typeSymbol.asClass).exists => // == is overloaded in primitive classes u1.equal(u2) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index ca87c1f58c42..cd55764631a6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -81,11 +81,11 @@ object Inliner { ctx.error("Implementation restriction: cannot use private constructors in inline methods", tree.pos) tree // TODO: create a proper accessor for the private constructor } - else if (AccessProxies.hostForAccessorOf(tree.symbol).exists) useAccessor(tree) - else tree + else useAccessor(tree) case _ => tree } + override def ifNoHost(reference: RefTree)(implicit ctx: Context): Tree = reference } /** Fallback approach if the direct approach does not work: Place the accessor method @@ -124,7 +124,7 @@ object Inliner { if needsAccessor(tree.symbol) && tree.isTerm && !tree.symbol.isConstructor => val (refPart, targs, argss) = decomposeCall(tree) val qual = qualifier(refPart) - inlining.println(i"adding receiver passing inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))") + println(i"adding receiver passing inline accessor for $tree/$refPart -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))") // Need to dealias in order to cagtch all possible references to abstracted over types in // substitutions @@ -142,7 +142,7 @@ object Inliner { def addQualType(tp: Type): Type = tp match { case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType)) case tp: ExprType => addQualType(tp.resultType) - case tp => MethodType(qualType :: Nil, tp) + case tp => MethodType(qualType.simplified :: Nil, tp) } // Abstract accessed type over local refs diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index ee98321dcc78..0bef02453441 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -6,6 +6,7 @@ import core._ import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._, NameOps._ import Decorators._ import Variances._ +import NameKinds._ import util.Positions._ import rewrite.Rewrites.patch import config.Printers.variances @@ -134,6 +135,7 @@ class VarianceChecker()(implicit ctx: Context) { def skip = !sym.exists || sym.is(PrivateLocal) || + sym.name.is(InlineAccessorName) || // TODO: should we exclude all synthetic members? sym.is(TypeParam) && sym.owner.isClass // already taken care of in primary constructor of class tree match { case defn: MemberDef if skip => diff --git a/tests/run/lst/Lst.scala b/tests/run/lst/Lst.scala index 34c579b22b2c..3cdd4a076119 100644 --- a/tests/run/lst/Lst.scala +++ b/tests/run/lst/Lst.scala @@ -4,16 +4,18 @@ package util import printing.{Printer, Texts} import Texts.Text import collection.mutable.{ListBuffer, StringBuilder} - +import collection.immutable.Map +import reflect.ClassTag /** A lightweight class for lists, optimized for short and medium lengths. * A list is represented at runtime as * - * If it is empty: the value `Lst.Empty` - * If it contains one element: the element itself - * If it contains more elements: an Array[Any] containing the elements + * If it is empty: the value `Lst.Empty` + * If it contains one element, + * and the element is not an array: the element itself + * Otherwise: an Array[Any] containing the elements */ -class Lst[+T](val elems: Any) extends AnyVal { +class Lst[+T](val elems: Any) extends AnyVal { self => import Lst._ def length: Int = elems match { @@ -36,6 +38,17 @@ class Lst[+T](val elems: Any) extends AnyVal { } } + inline def foreachReversed(inline op: T => Unit): Unit = { + def sharedOp(x: T) = op(x) + elems match { + case null => + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = elems.length + while (i > 0) { i -= 1; sharedOp(elem(i)) } + case elem: T @ unchecked => sharedOp(elem) + } + } + /** Like `foreach`, but completely inlines `op`, at the price of generating the code twice. * Should be used only of `op` is small */ @@ -59,6 +72,34 @@ class Lst[+T](val elems: Any) extends AnyVal { case elem: T @ unchecked => target(from) = elem } + def toArray[U >: T : ClassTag]: Array[U] = { + val result = new Array[U](length) + copyToArray(result, 0) + result + } + + def toSet[U >: T]: collection.immutable.Set[U] = { + var xs = Set[U]() + foreach(xs += _) + xs + } + + def toList: List[T] = { + val buf = new collection.mutable.ListBuffer[T] + foreach(buf += _) + buf.toList + } + + def toListReversed: List[T] = { + var result: List[T] = Nil + foreach(x => result = x :: result) + result + } + + def toIterable: Iterable[T] = new Iterable[T] { + def iterator = self.iterator() + } + /** `f` is pulled out, not duplicated */ inline def map[U](f: => T => U): Lst[U] = { def op(x: T) = f(x) @@ -68,8 +109,8 @@ class Lst[+T](val elems: Any) extends AnyVal { val newElems = new Arr(elems.length) var i = 0 while (i < elems.length) { newElems(i) = op(elem(i)); i += 1 } - new Lst[U](newElems) - case elem: T @ unchecked => new Lst[U](op(elem)) + multi[U](newElems) + case elem: T @ unchecked => single[U](op(elem)) } } @@ -89,10 +130,12 @@ class Lst[+T](val elems: Any) extends AnyVal { } i += 1 } - if (newElems == null) this.asInstanceOf[Lst[U]] else new Lst[U](newElems) - case elem: T @ unchecked => new Lst[U](f(elem)) + if (newElems == null) this.asInstanceOf[Lst[U]] else multi[U](newElems) + case elem: T @ unchecked => single[U](f(elem)) } + def flatMapIterable[U](f: T => Iterable[U]): Lst[U] = flatMap(x => fromIterable(f(x))) + def flatMap[U](f: T => Lst[U]): Lst[U] = elems match { case null => Empty case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] @@ -121,9 +164,9 @@ class Lst[+T](val elems: Any) extends AnyVal { j += ys.length i += 1 } - new Lst[U](newElems) + multi[U](newElems) } - case elem: T @ unchecked => new Lst[U](f(elem).elems) + case elem: T @ unchecked => f(elem) } def filter(p: T => Boolean): Lst[T] = elems match { @@ -144,6 +187,12 @@ class Lst[+T](val elems: Any) extends AnyVal { } def filterNot(p: T => Boolean): Lst[T] = filter(!p(_)) + def collect[U](pf: PartialFunction[T, U]): Lst[U] = { + val buf = new Lst.Buffer[U] + foreach(x => if (pf.isDefinedAt(x)) buf += pf(x)) + buf.toLst + } + inline def exists(p: => T => Boolean): Boolean = { def op(x: T) = p(x) elems match { @@ -197,6 +246,8 @@ class Lst[+T](val elems: Any) extends AnyVal { inline def /: [U](z: U)(op: => (U, T) => U) = foldLeft(z)(op) def reduceLeft[U >: T](op: (U, U) => U) = elems match { + case null => + throw new UnsupportedOperationException("Lst.Empty.reduceLeft") case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] var i = 1 var acc: U = elem(0) @@ -214,7 +265,7 @@ class Lst[+T](val elems: Any) extends AnyVal { newElems(elems.length - 1 - i) = elem(i) i += 1 } - new Lst[T](newElems) + multi[T](newElems) case _ => this } @@ -236,13 +287,43 @@ class Lst[+T](val elems: Any) extends AnyVal { else elems match { case null => this case elems: Arr => _fromArray(elems, start, end `min` elems.length) - case elem: T @ unchecked => if (end == 0) Empty else this + case elem: T @ unchecked => if (start == 0 && end > 0) this else Empty } def drop(n: Int): Lst[T] = slice(n, length) def tail = drop(1) def take(n: Int): Lst[T] = slice(0, n) + def dropRight(n: Int): Lst[T] = take(length - n) + def takeRight(n: Int): Lst[T] = drop(length - n) + def init = dropRight(1) + + def firstIndexOf[U >: T](x: U, from: Int = 0): Int = elems match { + case null => 0 + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = from + while (i < length && elem(i) != x) i += 1 + i + case elem: T @ unchecked => if (from == 0 && elem == x) 0 else 1 + } + + def firstIndexWhere(p: T => Boolean, from: Int = 0): Int = elems match { + case null => 0 + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = from + while (i < length && !p(elem(i))) i += 1 + i + case elem: T @ unchecked => if (from == 0 && p(elem)) 0 else 1 + } + + def find(p: T => Boolean): Option[T] = { + val idx = firstIndexWhere(p) + if (idx < length) Some(apply(idx)) else None + } + + def takeWhile(p: T => Boolean): Lst[T] = take(firstIndexWhere(!p(_))) + def dropWhile(p: T => Boolean): Lst[T] = drop(firstIndexWhere(!p(_))) + def ++ [U >: T](that: Lst[U]): Lst[U] = if (elems == null) that else if (that.elems == null) this @@ -252,9 +333,34 @@ class Lst[+T](val elems: Any) extends AnyVal { val newElems = new Arr(len1 + len2) this.copyToArray(newElems, 0) that.copyToArray(newElems, len1) - new Lst[U](newElems) + multi[U](newElems) } + def :: [U >: T](x: U): Lst[U] = elems match { + case null => Lst(x) + case elems: Arr => + val newElems = new Arr(1 + elems.length) + newElems(0) = x + copyToArray(newElems, 1) + multi[U](newElems) + case elem: T @unchecked => + Lst(x, elem) + } + + def :+ [U >: T](x: U): Lst[U] = elems match { + case null => Lst(x) + case elems: Arr => + val newElems = new Arr(elems.length + 1) + copyToArray(newElems, 0) + newElems(elems.length) = x + multi[U](newElems) + case elem: T @unchecked => + Lst(elem, x) + } + + def union [U >: T](that: Lst[U]): Lst[U] = this ++ that.filterNot(this.contains) + def intersect [U >: T](that: Lst[U]): Lst[U] = this.filter(that.contains) + def zipWith[U, V](that: Lst[U])(op: (T, U) => V): Lst[V] = this.elems match { case null => Empty @@ -264,7 +370,7 @@ class Lst[+T](val elems: Any) extends AnyVal { case elems2: Arr => def elem2(i: Int) = elems2(i).asInstanceOf[U] val len = elems1.length min elems2.length if (len == 0) Empty - else if (len == 1) new Lst[V](op(elem1(0), elem2(0))) + else if (len == 1) single[V](op(elem1(0), elem2(0))) else { var newElems: Arr = null var i = 0 @@ -279,17 +385,17 @@ class Lst[+T](val elems: Any) extends AnyVal { } i += 1 } - new Lst[V](newElems) + multi[V](newElems) } case elem2: U @unchecked => - new Lst[V](op(elem1(0), elem2)) + single[V](op(elem1(0), elem2)) } case elem1: T @unchecked => that.elems match { case null => Empty case elems2: Arr => def elem2(i: Int) = elems2(i).asInstanceOf[U] - new Lst[V](op(elem1, elem2(0))) - case elem2: U @unchecked => new Lst[V](op(elem1, elem2)) + single[V](op(elem1, elem2(0))) + case elem2: U @unchecked => single[V](op(elem1, elem2)) } } @@ -301,9 +407,20 @@ class Lst[+T](val elems: Any) extends AnyVal { val newElems = new Arr(elems.length) var i = 0 while (i < elems.length) { newElems(i) = (elem(i), i); i += 1 } - new Lst[(T, Int)](newElems) + multi[(T, Int)](newElems) case elem: T @unchecked => - new Lst[(T, Int)]((elem, 0)) + single[(T, Int)]((elem, 0)) + } + + def indices: Lst[Int] = elems match { + case null => Empty + case elems: Arr => + val newElems: Arr = new Arr(elems.length) + var i = 0 + while (i < elems.length) { newElems(i) = i; i += 1 } + multi[Int](newElems) + case elem => + single[Int](0) } def corresponds[U](that: Lst[U])(p: (T, U) => Boolean): Boolean = @@ -331,23 +448,27 @@ class Lst[+T](val elems: Any) extends AnyVal { } } - def === [U](that: Lst[U]) = - (this `eqLst` that) || { - elems match { - case elems1: Arr => - that.elems match { - case elems2: Arr => - val len = elems1.length - len == elems2.length && { - var i = 0 - while (i < len && elems1(i).equals(elems2(i))) i += 1 - i == len - } - case _ => false - } - case elem => elem == that.elems + override def equals(that: Any) = that match { + case that: Lst[_] => + (this `eqLst` that) || { + elems match { + case elems1: Arr => + that.elems match { + case elems2: Arr => + val len = elems1.length + len == elems2.length && { + var i = 0 + while (i < len && elems1(i).equals(elems2(i))) i += 1 + i == len + } + case _ => false + } + case elem => elem == that.elems + } } - } + case _ => + false + } def eqLst[U](that: Lst[U]) = eq(elems, that.elems) @@ -381,7 +502,7 @@ object Lst { def apply[T](): Lst[T] = Empty - def apply[T](x0: T): Lst[T] = new Lst[T](x0) + def apply[T](x0: T): Lst[T] = single[T](x0) def apply[T](x0: T, x1: T): Lst[T] = { val elems = new Arr(2) @@ -425,14 +546,28 @@ object Lst { new Lst[T](elems) } + def tabulate[T](n: Int)(elem: Int => T) = { + val elems = new Arr(n) + var i = 0 + while (i < n) { elems(i) = elem(i); i += 1} + new Lst[T](elems) + } + + def range(from: Int, until: Int): Lst[Int] = { + val elems = new Arr(until - from) + var i = from + while (i < until) { elems(i) = i; i += 1} + new Lst[Int](elems) + } + class Buffer[T] { private var len = 0 - private var elem: T = _ + private var elem: Any = _ private var elems: Arr = _ def size = len - /** pre: len > 0, n > 1 */ + /** pre: len > 0, n >= 1 */ private def ensureSize(n: Int) = if (len == 1) { elems = new Arr(n `max` 16) @@ -445,6 +580,8 @@ object Lst { elems = newElems } + def isEmpty = size == 0 + def += (x: T): this.type = { if (len == 0) elem = x else { @@ -459,7 +596,9 @@ object Lst { xs.elems match { case null => this case elems2: Arr => - if (len == 0) elems = elems2 + if (len == 0 && elems2.length != 1) + // if elems2.length == 1, elems2 is a wrapped single element list, which has to be unpacked + elems = elems2 else { ensureSize(len + elems2.length) System.arraycopy(elems2, 0, elems, len, elems2.length) @@ -470,19 +609,51 @@ object Lst { this } + def exists(p: T => Boolean): Boolean = + if (len == 0) false + else if (len == 1) p(elem.asInstanceOf[T]) + else { + var i = 0 + while (i < len && !p(elems(i).asInstanceOf[T])) i += 1 + i < len + } + def forall(p: T => Boolean): Boolean = !exists(!p(_)) + + def contains(x: T): Boolean = + if (len == 0) false + else if (len == 1) elem == x + else { + var i = 0 + while (i < len && elems(i) != x) i += 1 + i < len + } + def toLst: Lst[T] = if (len == 0) Empty - else if (len == 1) new Lst[T](elem) + else if (len == 1) single(elem) else _fromArray(elems, 0, len) def clear() = len = 0 } + private def single[T](elem: Any): Lst[T] = elem match { + case null | _: Arr => + val wrapped = new Arr(1) + wrapped(0) = elem + new Lst[T](wrapped) + case _ => + new Lst[T](elem) + } + + private def multi[T](elems: Array[Any]) = + if (elems.length == 1) single(elems(0)) + else new Lst[T](elems) + private def _fromArray[T](elems: Arr, start: Int, end: Int): Lst[T] = { val len = end - start if (len <= 0) Empty - else if (len == 1) new Lst[T](elems(start)) + else if (len == 1) single[T](elems(start)) else if (start == 0 && end == elems.length) new Lst[T](elems) else { val newElems = new Arr(len) @@ -493,4 +664,22 @@ object Lst { def fromArray[T](elems: Array[T], start: Int, end: Int): Lst[T] = _fromArray(elems.asInstanceOf[Arr], start, end) + + def fromIterator[T](it: Iterator[T]): Lst[T] = { + val buf = new Buffer[T] + it.foreach(buf += _) + buf.toLst + } + + def fromIterable[T](xs: Iterable[T]): Lst[T] = fromIterator(xs.iterator) + + object :: { + def unapply[T](xs: Lst[T]): Option[(T, Lst[T])] = xs match { + case null => None + case elems: Arr => + Some((elems(0).asInstanceOf[T], _fromArray[T](elems, 1, elems.length))) + case elem: T @unchecked => + Some((elem, Empty)) + } + } } diff --git a/tests/run/lst/LstTest.check b/tests/run/lst/LstTest.check new file mode 100644 index 000000000000..ac3bf9813758 --- /dev/null +++ b/tests/run/lst/LstTest.check @@ -0,0 +1,4 @@ +Lst(Lst()) +Lst(Lst(a)) +Lst(Lst(a), Lst(b), Lst(c), Lst(d), Lst(e)) +Lst(Lst(a), Lst(b), Lst(c), Lst(d), Lst(e), Lst(a), Lst(b), Lst(c), Lst(d), Lst(e)) diff --git a/tests/run/lst/LstTest.scala b/tests/run/lst/LstTest.scala index 87f2541fb62a..9e3caad751a0 100644 --- a/tests/run/lst/LstTest.scala +++ b/tests/run/lst/LstTest.scala @@ -17,6 +17,19 @@ object Test extends App { val is5 = Lst(1, 2, 3, 4, 5) val is10 = is5 ++ is5 + val xss0: Lst[Lst[String]] = Lst(Lst()) + val xss1 = Lst(Lst("a")) + val xss2 = Lst(Lst("a"), Lst("b")) + val xss3 = Lst(Lst("a"), Lst("b"), Lst("c")) + val xss4 = Lst(Lst("a"), Lst("b"), Lst("c"), Lst("d")) + val xss5 = Lst(Lst("a"), Lst("b"), Lst("c"), Lst("d"), Lst("e")) + val xss10 = xss5 ++ xss5 + + println(xss0) + println(xss1) + println(xss5) + println(xss10) + def lengthTest() = { assert(xs0.length == 0) assert(xs1.length == 1) @@ -27,6 +40,11 @@ object Test extends App { assert(is1.length == 1) assert(is2.length == 2) assert(is10.length == 10) + + assert(xss0.length == 1) + assert(xss1.length == 1) + assert(xss2.length == 2) + assert(xss10.length == 10) } def concatTest() = { @@ -39,6 +57,11 @@ object Test extends App { assert(is0 ++ is1 == is1) assert(is3 ++ is0 == is3) assert(is0 ++ is4 == is4) + + assert(xss1 ++ xs0 == xss1) + assert(xs0 ++ xss1 == xss1) + assert(xss3 ++ xs0 == xss3) + assert(xs0 ++ xss4 == xss4) } def foreachTest() = { @@ -57,6 +80,9 @@ object Test extends App { is0.foreach(i => assert(i == 1)) is3.foreach(i => assert(i <= 3)) + + xss0.foreach(s => assert(s.length == 0)) + xss3.foreach(s => assert(s.length == 1)) } def mapTest() = { @@ -71,6 +97,13 @@ object Test extends App { assert(js1.mkString == "2") val js5 = is5.map(s => s + s) assert(js5.mkString == "2, 4, 6, 8, 10") + + val yss0 = xss0.map(_.reverse) + assert(yss0.mkString == "Lst()", yss0.mkString) + val yss1 = xss1.map(s => s ++ s) + assert(yss1.mkString == "Lst(a, a)", yss1.mkString) + val yss5 = xss5.map(s => s ++ s) + assert(yss5.mkString == "Lst(a, a), Lst(b, b), Lst(c, c), Lst(d, d), Lst(e, e)") } def mapConserveTest() = { @@ -93,6 +126,13 @@ object Test extends App { assert(js5.mkString == "2, 4, 6, 8, 10") val js4 = is4.mapConserve(s => if (s == 3) -3 else s) assert(js4.mkString == "1, 2, -3, 4") + + val yss0 = xss0.mapConserve(_.reverse) + assert(yss0.mkString == "Lst()", yss0.mkString) + val yss1 = xss1.mapConserve(s => s ++ s) + assert(yss1.mkString == "Lst(a, a)", yss1.mkString) + val yss5 = xss5.mapConserve(s => s ++ s) + assert(yss5.mkString == "Lst(a, a), Lst(b, b), Lst(c, c), Lst(d, d), Lst(e, e)") } def flatMapTest() = { @@ -117,6 +157,17 @@ object Test extends App { assert(js4.mkString == "2, 3, 3, 4, 4, 4", js4) val js5 = is5.flatMap(s => if s == 3 then Lst(-3) else Lst()) assert(js5 == Lst(-3)) + + val yss0 = xss0.flatMap(s => Lst(s, s)) + assert(yss0.length == 2 && yss0.forall(_.isEmpty)) + val yss2 = xss2.flatMap(s => Lst(s, s)) + assert(yss2.mkString == "Lst(a), Lst(a), Lst(b), Lst(b)") + val yss2a = xss2.flatMap(_ => Lst.Empty) + assert(yss2a.isEmpty) + val yss4 = xss4.flatMap(s => Lst.fill(s.head.head - 'a')(s)) + assert(yss4.mkString == "Lst(b), Lst(c), Lst(c), Lst(d), Lst(d), Lst(d)", yss4.mkString) + val yss5 = xss5.flatMap(s => if s.head == "c" then Lst(s) else Lst()) + assert(yss5 == Lst(Lst("c"))) } def filterTest() = { @@ -127,7 +178,7 @@ object Test extends App { val ys1a = xs1.filterNot(_.head >= 'c') assert(ys1a `eqLst` xs1) val ys5 = xs5.filter(_.head % 2 != 0) - assert(ys5 === Lst("a", "c", "e"), ys5) + assert(ys5 == Lst("a", "c", "e"), ys5) val js0 = is0.filter(_ > 3) assert(js0.isEmpty) @@ -136,7 +187,18 @@ object Test extends App { val js1a = is1.filterNot(_ > 3) assert(js1a `eqLst` is1) val js5 = is5.filter(_ % 2 != 0) - assert(js5 === Lst(1, 3, 5), js5) + assert(js5 == Lst(1, 3, 5), js5) + + val yss0 = xss0.filter(_.nonEmpty) + assert(yss0.isEmpty) + val yss1 = xss1.filter(_.head.head >= 'c') + assert(yss1.isEmpty) + val yss1a = xss1.filterNot(_.head.head >= 'c') + assert(yss1a `eqLst` xss1) + val yss5 = xss5.filter(_.head.head % 2 != 0) + assert(yss5 == Lst(Lst("a"), Lst("c"), Lst("e")), yss5) + val yss5a = xss5.map(_.filter(_.head % 2 != 0)) + assert(yss5a == Lst(Lst("a"), Lst(), Lst("c"), Lst(), Lst("e")), yss5) } def existsTest() = { @@ -149,6 +211,13 @@ object Test extends App { assert(is1.exists(_ == 1)) assert(is5.exists(_ == 3)) assert(!is5.exists(_ > 5)) + + assert(!xss0.exists(_.exists(_ => true))) + assert(xss1.exists(_.head == "a")) + assert(xss1.exists(_.exists(_ == "a"))) + assert(xss5.exists(_.head == "c")) + assert(xss5.exists(_.exists(_ == "c"))) + assert(!xss5.exists(_.head.head > 'e')) } def forallTest() = { @@ -168,6 +237,11 @@ object Test extends App { assert(is1.contains(1)) assert(is10.contains(5)) assert(!is10.contains(6)) + + assert(!xss0.contains(List(""))) + assert(xss1.contains(Lst("a"))) + assert(xss10.contains(Lst("e")), xss10) + assert(!xss10.contains(Lst("f"))) } def foldTest() = { @@ -192,13 +266,24 @@ object Test extends App { assert((3 /: is3)(_ + _) == 9) assert(is1.reduceLeft(_ + _) == 1) assert(is3.reduceLeft(_ + _) == 6) + + assert(xss0.foldLeft(Lst("x"))(_ ++ _) == Lst("x")) + assert(xss1.foldLeft(Lst("x"))(_ ++ _) == Lst("x", "a")) + assert(xss2.foldLeft(Lst(): Lst[String])(_ ++ _) == Lst("a", "b")) + assert(xss3.foldLeft(Lst.Empty: Lst[String])(_ ++ _) == Lst("a", "b", "c")) + assert((Lst("x") /: xss0)(_ ++ _) == Lst("x")) + assert((Lst("x") /: xss1)(_ ++ _) == Lst("x", "a")) + assert((Lst("x") /: xss2)(_ ++ _) == Lst("x", "a", "b")) + assert((Lst("x") /: xss3)(_ ++ _) == Lst("x", "a", "b", "c")) + assert(xss1.reduceLeft(_ ++ _) == Lst("a")) + assert(xss3.reduceLeft(_ ++ _) == Lst("a", "b", "c")) } def reverseTest() = { - assert(xs0.reverse === xs0) - assert(xs1.reverse === xs1) + assert(xs0.reverse == xs0) + assert(xs1.reverse == xs1) assert(xs3.reverse.mkString == "c, b, a", xs3.reverse.mkString) - assert(xs4.reverse.reverse === xs4, xs4.reverse.reverse) + assert(xs4.reverse.reverse == xs4, xs4.reverse.reverse) } def applyTest() = { @@ -211,18 +296,23 @@ object Test extends App { assert(is5.last == 5) assert(is5(3) == 4) assert(is1(0) == 1) + + assert(xss5.head.head == "a") + assert(xss5.last.head == "e") + assert(xss5(3).head == "d") + assert(xss1(0).head == "a") } def sliceTest() = { - assert(xs5.slice(2, 4) === Lst("c", "d")) - assert(xs5.drop(4) === Lst("e")) + assert(xs5.slice(2, 4) == Lst("c", "d")) + assert(xs5.drop(4) == Lst("e")) assert(xs5.take(4).mkString("") == "abcd") assert(xs5.drop(-1) `eqLst` xs5) assert(xs1.take(1) `eqLst` xs1) assert(xs0.take(10).length == 0) - assert(is5.slice(2, 4) === Lst(3, 4)) - assert(is5.drop(4) === Lst(5)) + assert(is5.slice(2, 4) == Lst(3, 4)) + assert(is5.drop(4) == Lst(5)) assert(is5.take(4).mkString("") == "1234") assert(is5.drop(-1) `eqLst` is5) assert(is1.take(1) `eqLst` is1) @@ -233,34 +323,34 @@ object Test extends App { val ys4a = xs4.zipWith(xs5)(_ + _) val ys4b = xs5.zipWith(xs4)(_ + _) assert(ys4a.mkString("") == "aabbccdd", ys4a) - assert(ys4a === ys4b) + assert(ys4a == ys4b) val ys1a = xs1.zipWith(xs1)(_ + _) - assert(ys1a === Lst("aa")) + assert(ys1a == Lst("aa")) val ys1b = xs1.zipWith(xs2)(_ + _) - assert(ys1b === Lst("aa")) + assert(ys1b == Lst("aa")) val ys1c = xs2.zipWith(xs1)(_ + _) - assert(ys1c === Lst("aa")) + assert(ys1c == Lst("aa")) val ys0a = xs1.zipWith(xs0)(_ + _) val ys0b = xs0.zipWith(xs1)(_ + _) assert((ys0a ++ ys0b).isEmpty) val ys3i = xs3.zipWithIndex.map((x, y) => (x, y + 1)) - assert(ys3i === Lst(("a", 1), ("b", 2), ("c", 3)), ys3i) + assert(ys3i == Lst(("a", 1), ("b", 2), ("c", 3)), ys3i) val js4a = is4.zipWith(is5)(_ + _) val js4b = is5.zipWith(is4)(_ + _) assert(js4a.mkString("") == "2468", js4a) - assert(js4a === js4b) + assert(js4a == js4b) val js1a = is1.zipWith(is1)(_ + _) - assert(js1a === Lst(2)) + assert(js1a == Lst(2)) val js1b = is1.zipWith(is2)(_ + _) - assert(js1b === Lst(2)) + assert(js1b == Lst(2)) val js1c = is2.zipWith(is1)(_ + _) - assert(js1c === Lst(2)) + assert(js1c == Lst(2)) val js0a = is1.zipWith(is0)(_ + _) val js0b = is0.zipWith(is1)(_ + _) assert((js0a ++ js0b).isEmpty) val js3i = is3.zipWithIndex.map((x, y) => (x, y + 1)) - assert(js3i === Lst((1, 1), (2, 2), (3, 3)), js3i) + assert(js3i == Lst((1, 1), (2, 2), (3, 3)), js3i) assert(js3i.forall(_ == _)) } @@ -290,7 +380,7 @@ object Test extends App { { val b = new Lst.Buffer[String] b += "a" assert(b.size == 1) - assert(b.toLst === Lst("a")) + assert(b.toLst == Lst("a")) b += "aa" b ++= Lst.fill(20)("a") assert(b.toLst.mkString("") == "a" * 23) @@ -300,15 +390,24 @@ object Test extends App { { val b = new Lst.Buffer[Int] b += 1 assert(b.size == 1) - assert(b.toLst === Lst(1)) + assert(b.toLst == Lst(1)) b += 11 b ++= Lst.fill(20)(1) assert(b.toLst.mkString("") == "1" * 23) assert(b.size == 22) } + + { val b = new Lst.Buffer[Lst[String]] + b += Lst("a") + assert(b.size == 1) + assert(b.toLst == Lst(Lst("a"))) + b += Lst("aa") + b ++= Lst.fill(20)(Lst("a")) + assert(b.toLst.map(_.mkString("")).mkString("") == "a" * 23) + assert(b.size == 22) + } } - println("testing") lengthTest() concatTest() foreachTest() @@ -326,4 +425,4 @@ object Test extends App { zipWithTest() correspondsTest() bufferTest() -} \ No newline at end of file +} diff --git a/tests/run/t6534.scala b/tests/run/t6534.scala index b5789f52c4fa..db8d0f652a87 100644 --- a/tests/run/t6534.scala +++ b/tests/run/t6534.scala @@ -8,7 +8,7 @@ object Test { def main(args: Array[String]): Unit = { val b1 = new Bippy1(71) val b2 = new Bippy2(71) - assert(b1 == b1) + assert(b1 != b1) assert(b1.## == b1.x.##, "hash differs1 " + ((b1, b1.##))) assert(b2 == b2) // assert(b2.## == b2.x.##, "hash differs2 " + ((b2, b2.##, b2.x.##))) diff --git a/tests/run/vc-equals.scala b/tests/run/vc-equals.scala new file mode 100644 index 000000000000..15d9e73e5f47 --- /dev/null +++ b/tests/run/vc-equals.scala @@ -0,0 +1,49 @@ +object Test extends App { + + class C(val s: Array[Int]) extends AnyVal { + override def equals(that: Any) = that match { + case that: C => s.deep == that.s.deep + case _ => false + } + } + + def test1() = { + val c = new C(Array(1, 2,3)) + + assert(c `equals` new C(Array(1, 2, 3))) + assert(c == (new C(Array(1, 2, 3)): Any)) + assert(c == new C(Array(1, 2, 3))) + + assert(new C(Array(1, 2, 3)) == c) + assert((new C(Array(1, 2, 3)): Any) == c) + assert(new C(Array(1, 2, 3)) == c) + } + + trait Eql extends Any { + def deep: Any + override def equals(that: Any) = that match { + case that: D => deep == that.s.deep + case _ => false + } + } + + class D(val s: Array[Int]) extends AnyVal with Eql { + def deep = s.deep + } + + def test2() = { + val c = new D(Array(1, 2,3)) + + assert(c `equals` new D(Array(1, 2, 3))) + assert(c == (new D(Array(1, 2, 3)): Any)) + assert(c == new D(Array(1, 2, 3))) + + assert(new D(Array(1, 2, 3)) == c) + assert((new D(Array(1, 2, 3)): Any) == c) + assert(new D(Array(1, 2, 3)) == c) + } + + test1() + test2() + +}