From 0c3e52010a7692f6e24a1d6332afcf63a0c946cb Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 20 Mar 2018 14:11:14 +0100 Subject: [PATCH 1/4] Improve performance of ErasedDecls phase --- .../src/dotty/tools/dotc/transform/UnusedDecls.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala b/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala index 0cc5060e3125..a9c66dee43d4 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala @@ -40,10 +40,11 @@ class ErasedDecls extends MiniPhase with InfoTransformer { /* Info transform */ - override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match { - case tp: ClassInfo => - if (tp.classSymbol.is(JavaDefined) || !tp.decls.iterator.exists(_.is(Erased))) tp - else tp.derivedClassInfo(decls = tp.decls.filteredScope(!_.is(Erased))) - case _ => tp + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { + if (sym.is(JavaDefined) || sym.is(Scala2x)) tp + else tp match { + case tp: ClassInfo => tp.derivedClassInfo(decls = tp.decls.filteredScope(!_.is(Erased))) + case _ => tp + } } } From 69a98aa4bbac30f34af30ee789065d714903cd88 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 20 Mar 2018 15:36:45 +0100 Subject: [PATCH 2/4] Use mayChange --- bench/scripts/collection-vector.sh | 2 + bench/tests/Vector.scala | 1231 +++++++++++++++++ .../tools/dotc/transform/UnusedDecls.scala | 12 +- 3 files changed, 1239 insertions(+), 6 deletions(-) create mode 100755 bench/scripts/collection-vector.sh create mode 100644 bench/tests/Vector.scala diff --git a/bench/scripts/collection-vector.sh b/bench/scripts/collection-vector.sh new file mode 100755 index 000000000000..fb23a4a709db --- /dev/null +++ b/bench/scripts/collection-vector.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +sbt "dotty-bench-bootstrapped/jmh:run 40 40 3 bench/tests/Vector.scala" diff --git a/bench/tests/Vector.scala b/bench/tests/Vector.scala new file mode 100644 index 000000000000..1572e12a7165 --- /dev/null +++ b/bench/tests/Vector.scala @@ -0,0 +1,1231 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package collection +package immutable + +import scala.annotation.unchecked.uncheckedVariance +import scala.compat.Platform +import scala.collection.generic._ +import scala.collection.mutable.Builder + +/** Companion object to the Vector class + */ +object Vector extends IndexedSeqFactory[Vector] { + def newBuilder[A]: Builder[A, Vector[A]] = new VectorBuilder[A] + implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Vector[A]] = + ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] + private[immutable] val NIL = new Vector[Nothing](0, 0, 0) + override def empty[A]: Vector[A] = NIL + + // Constants governing concat strategy for performance + private final val Log2ConcatFaster = 5 + private final val TinyAppendFaster = 2 +} + +// in principle, most members should be private. however, access privileges must +// be carefully chosen to not prevent method inlining + +/** Vector is a general-purpose, immutable data structure. It provides random access and updates + * in effectively constant time, as well as very fast append and prepend. Because vectors strike + * a good balance between fast random selections and fast random functional updates, they are + * currently the default implementation of immutable indexed sequences. It is backed by a little + * endian bit-mapped vector trie with a branching factor of 32. Locality is very good, but not + * contiguous, which is good for very large sequences. + * + * @see [[http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#vectors "Scala's Collection Library overview"]] + * section on `Vectors` for more information. + * + * @tparam A the element type + * + * @define Coll `Vector` + * @define coll vector + * @define thatinfo the class of the returned collection. In the standard library configuration, + * `That` is always `Vector[B]` because an implicit of type `CanBuildFrom[Vector, B, That]` + * is defined in object `Vector`. + * @define bfinfo an implicit value of class `CanBuildFrom` which determines the + * result class `That` from the current representation type `Repr` + * and the new element type `B`. This is usually the `canBuildFrom` value + * defined in object `Vector`. + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +final class Vector[+A] private[immutable] (private[collection] val startIndex: Int, private[collection] val endIndex: Int, focus: Int) + extends AbstractSeq[A] + with IndexedSeq[A] + with GenericTraversableTemplate[A, Vector] + with IndexedSeqLike[A, Vector[A]] + with VectorPointer[A @uncheckedVariance] + with Serializable +{ self => + + override def companion: GenericCompanion[Vector] = Vector + + //assert(startIndex >= 0, startIndex+"<0") + //assert(startIndex <= endIndex, startIndex+">"+endIndex) + //assert(focus >= 0, focus+"<0") + //assert(focus <= endIndex, focus+">"+endIndex) + + private[immutable] var dirty = false + + def length = endIndex - startIndex + + override def toVector: Vector[A] = this + + override def lengthCompare(len: Int): Int = length - len + + private[collection] final def initIterator[B >: A](s: VectorIterator[B]) = { + s.initFrom(this) + if (dirty) s.stabilize(focus) + if (s.depth > 1) s.gotoPos(startIndex, startIndex ^ focus) + } + + override def iterator: VectorIterator[A] = { + val s = new VectorIterator[A](startIndex, endIndex) + initIterator(s) + s + } + + + // can still be improved + override /*SeqLike*/ + def reverseIterator: Iterator[A] = new AbstractIterator[A] { + private var i = self.length + def hasNext: Boolean = 0 < i + def next(): A = + if (0 < i) { + i -= 1 + self(i) + } else Iterator.empty.next() + } + + // TODO: reverse + + // TODO: check performance of foreach/map etc. should override or not? + // Ideally, clients will inline calls to map all the way down, including the iterator/builder methods. + // In principle, escape analysis could even remove the iterator/builder allocations and do it + // with local variables exclusively. But we're not quite there yet ... + + def apply(index: Int): A = { + val idx = checkRangeConvert(index) + //println("get elem: "+index + "/"+idx + "(focus:" +focus+" xor:"+(idx^focus)+" depth:"+depth+")") + getElem(idx, idx ^ focus) + } + + private def checkRangeConvert(index: Int) = { + val idx = index + startIndex + if (0 <= index && idx < endIndex) + idx + else + throw new IndexOutOfBoundsException(index.toString) + } + + + // SeqLike api + + override def updated[B >: A, That](index: Int, elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = + if (bf eq IndexedSeq.ReusableCBF) updateAt(index, elem).asInstanceOf[That] // just ignore bf + else super.updated(index, elem)(bf) + + override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = + if (bf eq IndexedSeq.ReusableCBF) appendFront(elem).asInstanceOf[That] // just ignore bf + else super.+:(elem)(bf) + + override def :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = + if (bf eq IndexedSeq.ReusableCBF) appendBack(elem).asInstanceOf[That] // just ignore bf + else super.:+(elem)(bf) + + override def take(n: Int): Vector[A] = { + if (n <= 0) + Vector.empty + else if (startIndex + n < endIndex) + dropBack0(startIndex + n) + else + this + } + + override def drop(n: Int): Vector[A] = { + if (n <= 0) + this + else if (startIndex + n < endIndex) + dropFront0(startIndex + n) + else + Vector.empty + } + + override def takeRight(n: Int): Vector[A] = { + if (n <= 0) + Vector.empty + else if (endIndex - n > startIndex) + dropFront0(endIndex - n) + else + this + } + + override def dropRight(n: Int): Vector[A] = { + if (n <= 0) + this + else if (endIndex - n > startIndex) + dropBack0(endIndex - n) + else + Vector.empty + } + + override /*IterableLike*/ def head: A = { + if (isEmpty) throw new UnsupportedOperationException("empty.head") + apply(0) + } + + override /*TraversableLike*/ def tail: Vector[A] = { + if (isEmpty) throw new UnsupportedOperationException("empty.tail") + drop(1) + } + + override /*TraversableLike*/ def last: A = { + if (isEmpty) throw new UnsupportedOperationException("empty.last") + apply(length-1) + } + + override /*TraversableLike*/ def init: Vector[A] = { + if (isEmpty) throw new UnsupportedOperationException("empty.init") + dropRight(1) + } + + override /*IterableLike*/ def slice(from: Int, until: Int): Vector[A] = + take(until).drop(from) + + override /*IterableLike*/ def splitAt(n: Int): (Vector[A], Vector[A]) = (take(n), drop(n)) + + + // concat (suboptimal but avoids worst performance gotchas) + override def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Vector[A], B, That]): That = { + if (bf eq IndexedSeq.ReusableCBF) { + import Vector.{Log2ConcatFaster, TinyAppendFaster} + if (that.isEmpty) this.asInstanceOf[That] + else { + val again = if (!that.isTraversableAgain) that.toVector else that.seq + again.size match { + // Often it's better to append small numbers of elements (or prepend if RHS is a vector) + case n if n <= TinyAppendFaster || n < (this.size >> Log2ConcatFaster) => + var v: Vector[B] = this + for (x <- again) v = v :+ x + v.asInstanceOf[That] + case n if this.size < (n >> Log2ConcatFaster) && again.isInstanceOf[Vector[_]] => + var v = again.asInstanceOf[Vector[B]] + val ri = this.reverseIterator + while (ri.hasNext) v = ri.next +: v + v.asInstanceOf[That] + case _ => super.++(again) + } + } + } + else super.++(that.seq) + } + + + + // semi-private api + + private[immutable] def updateAt[B >: A](index: Int, elem: B): Vector[B] = { + val idx = checkRangeConvert(index) + val s = new Vector[B](startIndex, endIndex, idx) + s.initFrom(this) + s.dirty = dirty + s.gotoPosWritable(focus, idx, focus ^ idx) // if dirty commit changes; go to new pos and prepare for writing + s.display0(idx & 0x1f) = elem.asInstanceOf[AnyRef] + s + } + + + private def gotoPosWritable(oldIndex: Int, newIndex: Int, xor: Int) = if (dirty) { + gotoPosWritable1(oldIndex, newIndex, xor) + } else { + gotoPosWritable0(newIndex, xor) + dirty = true + } + + private def gotoFreshPosWritable(oldIndex: Int, newIndex: Int, xor: Int) = if (dirty) { + gotoFreshPosWritable1(oldIndex, newIndex, xor) + } else { + gotoFreshPosWritable0(oldIndex, newIndex, xor) + dirty = true + } + + private[immutable] def appendFront[B>:A](value: B): Vector[B] = { + if (endIndex != startIndex) { + val blockIndex = (startIndex - 1) & ~31 + val lo = (startIndex - 1) & 31 + + if (startIndex != blockIndex + 32) { + val s = new Vector(startIndex - 1, endIndex, blockIndex) + s.initFrom(this) + s.dirty = dirty + s.gotoPosWritable(focus, blockIndex, focus ^ blockIndex) + s.display0(lo) = value.asInstanceOf[AnyRef] + s + } else { + + val freeSpace = ((1<<5*(depth)) - endIndex) // free space at the right given the current tree-structure depth + val shift = freeSpace & ~((1<<5*(depth-1))-1) // number of elements by which we'll shift right (only move at top level) + val shiftBlocks = freeSpace >>> 5*(depth-1) // number of top-level blocks + + //println("----- appendFront " + value + " at " + (startIndex - 1) + " reached block start") + if (shift != 0) { + // case A: we can shift right on the top level + debug() + //println("shifting right by " + shiftBlocks + " at level " + (depth-1) + " (had "+freeSpace+" free space)") + + if (depth > 1) { + val newBlockIndex = blockIndex + shift + val newFocus = focus + shift + val s = new Vector(startIndex - 1 + shift, endIndex + shift, newBlockIndex) + s.initFrom(this) + s.dirty = dirty + s.shiftTopLevel(0, shiftBlocks) // shift right by n blocks + s.debug() + s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) // maybe create pos; prepare for writing + s.display0(lo) = value.asInstanceOf[AnyRef] + //assert(depth == s.depth) + s + } else { + val newBlockIndex = blockIndex + 32 + val newFocus = focus + + //assert(newBlockIndex == 0) + //assert(newFocus == 0) + + val s = new Vector(startIndex - 1 + shift, endIndex + shift, newBlockIndex) + s.initFrom(this) + s.dirty = dirty + s.shiftTopLevel(0, shiftBlocks) // shift right by n elements + s.gotoPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) // prepare for writing + s.display0(shift-1) = value.asInstanceOf[AnyRef] + s.debug() + s + } + } else if (blockIndex < 0) { + // case B: we need to move the whole structure + val move = (1 << 5*(depth+1)) - (1 << 5*(depth)) + //println("moving right by " + move + " at level " + (depth-1) + " (had "+freeSpace+" free space)") + + val newBlockIndex = blockIndex + move + val newFocus = focus + move + + + val s = new Vector(startIndex - 1 + move, endIndex + move, newBlockIndex) + s.initFrom(this) + s.dirty = dirty + s.debug() + s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) // could optimize: we know it will create a whole branch + s.display0(lo) = value.asInstanceOf[AnyRef] + s.debug() + //assert(s.depth == depth+1) + s + } else { + val newBlockIndex = blockIndex + val newFocus = focus + + val s = new Vector(startIndex - 1, endIndex, newBlockIndex) + s.initFrom(this) + s.dirty = dirty + s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) + s.display0(lo) = value.asInstanceOf[AnyRef] + //assert(s.depth == depth) + s + } + + } + } else { + // empty vector, just insert single element at the back + val elems = new Array[AnyRef](32) + elems(31) = value.asInstanceOf[AnyRef] + val s = new Vector(31,32,0) + s.depth = 1 + s.display0 = elems + s + } + } + + private[immutable] def appendBack[B>:A](value: B): Vector[B] = { + // //println("------- append " + value) + // debug() + if (endIndex != startIndex) { + val blockIndex = endIndex & ~31 + val lo = endIndex & 31 + + if (endIndex != blockIndex) { + //println("will make writable block (from "+focus+") at: " + blockIndex) + val s = new Vector(startIndex, endIndex + 1, blockIndex) + s.initFrom(this) + s.dirty = dirty + s.gotoPosWritable(focus, blockIndex, focus ^ blockIndex) + s.display0(lo) = value.asInstanceOf[AnyRef] + s + } else { + val shift = startIndex & ~((1<<5*(depth-1))-1) + val shiftBlocks = startIndex >>> 5*(depth-1) + + //println("----- appendBack " + value + " at " + endIndex + " reached block end") + + if (shift != 0) { + debug() + //println("shifting left by " + shiftBlocks + " at level " + (depth-1) + " (had "+startIndex+" free space)") + if (depth > 1) { + val newBlockIndex = blockIndex - shift + val newFocus = focus - shift + val s = new Vector(startIndex - shift, endIndex + 1 - shift, newBlockIndex) + s.initFrom(this) + s.dirty = dirty + s.shiftTopLevel(shiftBlocks, 0) // shift left by n blocks + s.debug() + s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) + s.display0(lo) = value.asInstanceOf[AnyRef] + s.debug() + //assert(depth == s.depth) + s + } else { + val newBlockIndex = blockIndex - 32 + val newFocus = focus + + //assert(newBlockIndex == 0) + //assert(newFocus == 0) + + val s = new Vector(startIndex - shift, endIndex + 1 - shift, newBlockIndex) + s.initFrom(this) + s.dirty = dirty + s.shiftTopLevel(shiftBlocks, 0) // shift right by n elements + s.gotoPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) + s.display0(32 - shift) = value.asInstanceOf[AnyRef] + s.debug() + s + } + } else { + val newBlockIndex = blockIndex + val newFocus = focus + + val s = new Vector(startIndex, endIndex + 1, newBlockIndex) + s.initFrom(this) + s.dirty = dirty + s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) + s.display0(lo) = value.asInstanceOf[AnyRef] + //assert(s.depth == depth+1) might or might not create new level! + if (s.depth == depth+1) { + //println("creating new level " + s.depth + " (had "+0+" free space)") + s.debug() + } + s + } + } + } else { + val elems = new Array[AnyRef](32) + elems(0) = value.asInstanceOf[AnyRef] + val s = new Vector(0,1,0) + s.depth = 1 + s.display0 = elems + s + } + } + + + // low-level implementation (needs cleanup, maybe move to util class) + + private def shiftTopLevel(oldLeft: Int, newLeft: Int) = (depth - 1) match { + case 0 => + display0 = copyRange(display0, oldLeft, newLeft) + case 1 => + display1 = copyRange(display1, oldLeft, newLeft) + case 2 => + display2 = copyRange(display2, oldLeft, newLeft) + case 3 => + display3 = copyRange(display3, oldLeft, newLeft) + case 4 => + display4 = copyRange(display4, oldLeft, newLeft) + case 5 => + display5 = copyRange(display5, oldLeft, newLeft) + } + + private def zeroLeft(array: Array[AnyRef], index: Int): Unit = { + var i = 0; while (i < index) { array(i) = null; i+=1 } + } + + private def zeroRight(array: Array[AnyRef], index: Int): Unit = { + var i = index; while (i < array.length) { array(i) = null; i+=1 } + } + + private def copyLeft(array: Array[AnyRef], right: Int): Array[AnyRef] = { + // if (array eq null) + // println("OUCH!!! " + right + "/" + depth + "/"+startIndex + "/" + endIndex + "/" + focus) + val a2 = new Array[AnyRef](array.length) + Platform.arraycopy(array, 0, a2, 0, right) + a2 + } + private def copyRight(array: Array[AnyRef], left: Int): Array[AnyRef] = { + val a2 = new Array[AnyRef](array.length) + Platform.arraycopy(array, left, a2, left, a2.length - left) + a2 + } + + private def preClean(depth: Int) = { + this.depth = depth + (depth - 1) match { + case 0 => + display1 = null + display2 = null + display3 = null + display4 = null + display5 = null + case 1 => + display2 = null + display3 = null + display4 = null + display5 = null + case 2 => + display3 = null + display4 = null + display5 = null + case 3 => + display4 = null + display5 = null + case 4 => + display5 = null + case 5 => + } + } + + // requires structure is at index cutIndex and writable at level 0 + private def cleanLeftEdge(cutIndex: Int) = { + if (cutIndex < (1 << 5)) { + zeroLeft(display0, cutIndex) + } else + if (cutIndex < (1 << 10)) { + zeroLeft(display0, cutIndex & 0x1f) + display1 = copyRight(display1, (cutIndex >>> 5)) + } else + if (cutIndex < (1 << 15)) { + zeroLeft(display0, cutIndex & 0x1f) + display1 = copyRight(display1, (cutIndex >>> 5) & 0x1f) + display2 = copyRight(display2, (cutIndex >>> 10)) + } else + if (cutIndex < (1 << 20)) { + zeroLeft(display0, cutIndex & 0x1f) + display1 = copyRight(display1, (cutIndex >>> 5) & 0x1f) + display2 = copyRight(display2, (cutIndex >>> 10) & 0x1f) + display3 = copyRight(display3, (cutIndex >>> 15)) + } else + if (cutIndex < (1 << 25)) { + zeroLeft(display0, cutIndex & 0x1f) + display1 = copyRight(display1, (cutIndex >>> 5) & 0x1f) + display2 = copyRight(display2, (cutIndex >>> 10) & 0x1f) + display3 = copyRight(display3, (cutIndex >>> 15) & 0x1f) + display4 = copyRight(display4, (cutIndex >>> 20)) + } else + if (cutIndex < (1 << 30)) { + zeroLeft(display0, cutIndex & 0x1f) + display1 = copyRight(display1, (cutIndex >>> 5) & 0x1f) + display2 = copyRight(display2, (cutIndex >>> 10) & 0x1f) + display3 = copyRight(display3, (cutIndex >>> 15) & 0x1f) + display4 = copyRight(display4, (cutIndex >>> 20) & 0x1f) + display5 = copyRight(display5, (cutIndex >>> 25)) + } else { + throw new IllegalArgumentException() + } + } + + // requires structure is writable and at index cutIndex + private def cleanRightEdge(cutIndex: Int) = { + + // we're actually sitting one block left if cutIndex lies on a block boundary + // this means that we'll end up erasing the whole block!! + + if (cutIndex <= (1 << 5)) { + zeroRight(display0, cutIndex) + } else + if (cutIndex <= (1 << 10)) { + zeroRight(display0, ((cutIndex-1) & 0x1f) + 1) + display1 = copyLeft(display1, (cutIndex >>> 5)) + } else + if (cutIndex <= (1 << 15)) { + zeroRight(display0, ((cutIndex-1) & 0x1f) + 1) + display1 = copyLeft(display1, (((cutIndex-1) >>> 5) & 0x1f) + 1) + display2 = copyLeft(display2, (cutIndex >>> 10)) + } else + if (cutIndex <= (1 << 20)) { + zeroRight(display0, ((cutIndex-1) & 0x1f) + 1) + display1 = copyLeft(display1, (((cutIndex-1) >>> 5) & 0x1f) + 1) + display2 = copyLeft(display2, (((cutIndex-1) >>> 10) & 0x1f) + 1) + display3 = copyLeft(display3, (cutIndex >>> 15)) + } else + if (cutIndex <= (1 << 25)) { + zeroRight(display0, ((cutIndex-1) & 0x1f) + 1) + display1 = copyLeft(display1, (((cutIndex-1) >>> 5) & 0x1f) + 1) + display2 = copyLeft(display2, (((cutIndex-1) >>> 10) & 0x1f) + 1) + display3 = copyLeft(display3, (((cutIndex-1) >>> 15) & 0x1f) + 1) + display4 = copyLeft(display4, (cutIndex >>> 20)) + } else + if (cutIndex <= (1 << 30)) { + zeroRight(display0, ((cutIndex-1) & 0x1f) + 1) + display1 = copyLeft(display1, (((cutIndex-1) >>> 5) & 0x1f) + 1) + display2 = copyLeft(display2, (((cutIndex-1) >>> 10) & 0x1f) + 1) + display3 = copyLeft(display3, (((cutIndex-1) >>> 15) & 0x1f) + 1) + display4 = copyLeft(display4, (((cutIndex-1) >>> 20) & 0x1f) + 1) + display5 = copyLeft(display5, (cutIndex >>> 25)) + } else { + throw new IllegalArgumentException() + } + } + + private def requiredDepth(xor: Int) = { + if (xor < (1 << 5)) 1 + else if (xor < (1 << 10)) 2 + else if (xor < (1 << 15)) 3 + else if (xor < (1 << 20)) 4 + else if (xor < (1 << 25)) 5 + else if (xor < (1 << 30)) 6 + else throw new IllegalArgumentException() + } + + private def dropFront0(cutIndex: Int): Vector[A] = { + val blockIndex = cutIndex & ~31 + val xor = cutIndex ^ (endIndex - 1) + val d = requiredDepth(xor) + val shift = (cutIndex & ~((1 << (5*d))-1)) + + //println("cut front at " + cutIndex + ".." + endIndex + " (xor: "+xor+" shift: " + shift + " d: " + d +")") + + /* + val s = new Vector(cutIndex-shift, endIndex-shift, blockIndex-shift) + s.initFrom(this) + if (s.depth > 1) + s.gotoPos(blockIndex, focus ^ blockIndex) + s.depth = d + s.stabilize(blockIndex-shift) + s.cleanLeftEdge(cutIndex-shift) + s + */ + + // need to init with full display iff going to cutIndex requires swapping block at level >= d + + val s = new Vector(cutIndex-shift, endIndex-shift, blockIndex-shift) + s.initFrom(this) + s.dirty = dirty + s.gotoPosWritable(focus, blockIndex, focus ^ blockIndex) + s.preClean(d) + s.cleanLeftEdge(cutIndex - shift) + s + } + + private def dropBack0(cutIndex: Int): Vector[A] = { + val blockIndex = (cutIndex - 1) & ~31 + val xor = startIndex ^ (cutIndex - 1) + val d = requiredDepth(xor) + val shift = (startIndex & ~((1 << (5*d))-1)) + + /* + println("cut back at " + startIndex + ".." + cutIndex + " (xor: "+xor+" d: " + d +")") + if (cutIndex == blockIndex + 32) + println("OUCH!!!") + */ + val s = new Vector(startIndex-shift, cutIndex-shift, blockIndex-shift) + s.initFrom(this) + s.dirty = dirty + s.gotoPosWritable(focus, blockIndex, focus ^ blockIndex) + s.preClean(d) + s.cleanRightEdge(cutIndex-shift) + s + } + +} + + +class VectorIterator[+A](_startIndex: Int, endIndex: Int) + extends AbstractIterator[A] + with Iterator[A] + with VectorPointer[A @uncheckedVariance] { + + private var blockIndex: Int = _startIndex & ~31 + private var lo: Int = _startIndex & 31 + + private var endLo = math.min(endIndex - blockIndex, 32) + + def hasNext = _hasNext + + private var _hasNext = blockIndex + lo < endIndex + + def next(): A = { + if (!_hasNext) throw new NoSuchElementException("reached iterator end") + + val res = display0(lo).asInstanceOf[A] + lo += 1 + + if (lo == endLo) { + if (blockIndex + lo < endIndex) { + val newBlockIndex = blockIndex+32 + gotoNextBlockStart(newBlockIndex, blockIndex ^ newBlockIndex) + + blockIndex = newBlockIndex + endLo = math.min(endIndex - blockIndex, 32) + lo = 0 + } else { + _hasNext = false + } + } + + res + } + + private[collection] def remainingElementCount: Int = (endIndex - (blockIndex + lo)) max 0 + + /** Creates a new vector which consists of elements remaining in this iterator. + * Such a vector can then be split into several vectors using methods like `take` and `drop`. + */ + private[collection] def remainingVector: Vector[A] = { + val v = new Vector(blockIndex + lo, endIndex, blockIndex + lo) + v.initFrom(this) + v + } +} + + +final class VectorBuilder[A]() extends Builder[A,Vector[A]] with VectorPointer[A @uncheckedVariance] { + + // possible alternative: start with display0 = null, blockIndex = -32, lo = 32 + // to avoid allocating initial array if the result will be empty anyways + + display0 = new Array[AnyRef](32) + depth = 1 + + private var blockIndex = 0 + private var lo = 0 + + def += (elem: A): this.type = { + if (lo >= display0.length) { + val newBlockIndex = blockIndex+32 + gotoNextBlockStartWritable(newBlockIndex, blockIndex ^ newBlockIndex) + blockIndex = newBlockIndex + lo = 0 + } + display0(lo) = elem.asInstanceOf[AnyRef] + lo += 1 + this + } + + override def ++=(xs: TraversableOnce[A]): this.type = + super.++=(xs) + + def result: Vector[A] = { + val size = blockIndex + lo + if (size == 0) + return Vector.empty + val s = new Vector[A](0, size, 0) // should focus front or back? + s.initFrom(this) + if (depth > 1) s.gotoPos(0, size - 1) // we're currently focused to size - 1, not size! + s + } + + def clear(): Unit = { + display0 = new Array[AnyRef](32) + depth = 1 + blockIndex = 0 + lo = 0 + } +} + + + +private[immutable] trait VectorPointer[T] { + private[immutable] var depth: Int = _ + private[immutable] var display0: Array[AnyRef] = _ + private[immutable] var display1: Array[AnyRef] = _ + private[immutable] var display2: Array[AnyRef] = _ + private[immutable] var display3: Array[AnyRef] = _ + private[immutable] var display4: Array[AnyRef] = _ + private[immutable] var display5: Array[AnyRef] = _ + + // used + private[immutable] final def initFrom[U](that: VectorPointer[U]): Unit = initFrom(that, that.depth) + + private[immutable] final def initFrom[U](that: VectorPointer[U], depth: Int) = { + this.depth = depth + (depth - 1) match { + case -1 => + case 0 => + display0 = that.display0 + case 1 => + display1 = that.display1 + display0 = that.display0 + case 2 => + display2 = that.display2 + display1 = that.display1 + display0 = that.display0 + case 3 => + display3 = that.display3 + display2 = that.display2 + display1 = that.display1 + display0 = that.display0 + case 4 => + display4 = that.display4 + display3 = that.display3 + display2 = that.display2 + display1 = that.display1 + display0 = that.display0 + case 5 => + display5 = that.display5 + display4 = that.display4 + display3 = that.display3 + display2 = that.display2 + display1 = that.display1 + display0 = that.display0 + } + } + + + // requires structure is at pos oldIndex = xor ^ index + private[immutable] final def getElem(index: Int, xor: Int): T = { + if (xor < (1 << 5)) { // level = 0 + display0(index & 31).asInstanceOf[T] + } else + if (xor < (1 << 10)) { // level = 1 + display1((index >> 5) & 31).asInstanceOf[Array[AnyRef]](index & 31).asInstanceOf[T] + } else + if (xor < (1 << 15)) { // level = 2 + display2((index >> 10) & 31).asInstanceOf[Array[AnyRef]]((index >> 5) & 31).asInstanceOf[Array[AnyRef]](index & 31).asInstanceOf[T] + } else + if (xor < (1 << 20)) { // level = 3 + display3((index >> 15) & 31).asInstanceOf[Array[AnyRef]]((index >> 10) & 31).asInstanceOf[Array[AnyRef]]((index >> 5) & 31).asInstanceOf[Array[AnyRef]](index & 31).asInstanceOf[T] + } else + if (xor < (1 << 25)) { // level = 4 + display4((index >> 20) & 31).asInstanceOf[Array[AnyRef]]((index >> 15) & 31).asInstanceOf[Array[AnyRef]]((index >> 10) & 31).asInstanceOf[Array[AnyRef]]((index >> 5) & 31).asInstanceOf[Array[AnyRef]](index & 31).asInstanceOf[T] + } else + if (xor < (1 << 30)) { // level = 5 + display5((index >> 25) & 31).asInstanceOf[Array[AnyRef]]((index >> 20) & 31).asInstanceOf[Array[AnyRef]]((index >> 15) & 31).asInstanceOf[Array[AnyRef]]((index >> 10) & 31).asInstanceOf[Array[AnyRef]]((index >> 5) & 31).asInstanceOf[Array[AnyRef]](index & 31).asInstanceOf[T] + } else { // level = 6 + throw new IllegalArgumentException() + } + } + + + // go to specific position + // requires structure is at pos oldIndex = xor ^ index, + // ensures structure is at pos index + private[immutable] final def gotoPos(index: Int, xor: Int): Unit = { + if (xor < (1 << 5)) { // level = 0 (could maybe removed) + } else + if (xor < (1 << 10)) { // level = 1 + display0 = display1((index >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 15)) { // level = 2 + display1 = display2((index >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = display1((index >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 20)) { // level = 3 + display2 = display3((index >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = display2((index >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = display1((index >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 25)) { // level = 4 + display3 = display4((index >> 20) & 31).asInstanceOf[Array[AnyRef]] + display2 = display3((index >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = display2((index >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = display1((index >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 30)) { // level = 5 + display4 = display5((index >> 25) & 31).asInstanceOf[Array[AnyRef]] + display3 = display4((index >> 20) & 31).asInstanceOf[Array[AnyRef]] + display2 = display3((index >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = display2((index >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = display1((index >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else { // level = 6 + throw new IllegalArgumentException() + } + } + + + + // USED BY ITERATOR + + // xor: oldIndex ^ index + private[immutable] final def gotoNextBlockStart(index: Int, xor: Int): Unit = { // goto block start pos + if (xor < (1 << 10)) { // level = 1 + display0 = display1((index >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 15)) { // level = 2 + display1 = display2((index >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = display1(0).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 20)) { // level = 3 + display2 = display3((index >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = display2(0).asInstanceOf[Array[AnyRef]] + display0 = display1(0).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 25)) { // level = 4 + display3 = display4((index >> 20) & 31).asInstanceOf[Array[AnyRef]] + display2 = display3(0).asInstanceOf[Array[AnyRef]] + display1 = display2(0).asInstanceOf[Array[AnyRef]] + display0 = display1(0).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 30)) { // level = 5 + display4 = display5((index >> 25) & 31).asInstanceOf[Array[AnyRef]] + display3 = display4(0).asInstanceOf[Array[AnyRef]] + display2 = display3(0).asInstanceOf[Array[AnyRef]] + display1 = display2(0).asInstanceOf[Array[AnyRef]] + display0 = display1(0).asInstanceOf[Array[AnyRef]] + } else { // level = 6 + throw new IllegalArgumentException() + } + } + + // USED BY BUILDER + + // xor: oldIndex ^ index + private[immutable] final def gotoNextBlockStartWritable(index: Int, xor: Int): Unit = { // goto block start pos + if (xor < (1 << 10)) { // level = 1 + if (depth == 1) { display1 = new Array(32); display1(0) = display0; depth+=1} + display0 = new Array(32) + display1((index >> 5) & 31) = display0 + } else + if (xor < (1 << 15)) { // level = 2 + if (depth == 2) { display2 = new Array(32); display2(0) = display1; depth+=1} + display0 = new Array(32) + display1 = new Array(32) + display1((index >> 5) & 31) = display0 + display2((index >> 10) & 31) = display1 + } else + if (xor < (1 << 20)) { // level = 3 + if (depth == 3) { display3 = new Array(32); display3(0) = display2; depth+=1} + display0 = new Array(32) + display1 = new Array(32) + display2 = new Array(32) + display1((index >> 5) & 31) = display0 + display2((index >> 10) & 31) = display1 + display3((index >> 15) & 31) = display2 + } else + if (xor < (1 << 25)) { // level = 4 + if (depth == 4) { display4 = new Array(32); display4(0) = display3; depth+=1} + display0 = new Array(32) + display1 = new Array(32) + display2 = new Array(32) + display3 = new Array(32) + display1((index >> 5) & 31) = display0 + display2((index >> 10) & 31) = display1 + display3((index >> 15) & 31) = display2 + display4((index >> 20) & 31) = display3 + } else + if (xor < (1 << 30)) { // level = 5 + if (depth == 5) { display5 = new Array(32); display5(0) = display4; depth+=1} + display0 = new Array(32) + display1 = new Array(32) + display2 = new Array(32) + display3 = new Array(32) + display4 = new Array(32) + display1((index >> 5) & 31) = display0 + display2((index >> 10) & 31) = display1 + display3((index >> 15) & 31) = display2 + display4((index >> 20) & 31) = display3 + display5((index >> 25) & 31) = display4 + } else { // level = 6 + throw new IllegalArgumentException() + } + } + + + + // STUFF BELOW USED BY APPEND / UPDATE + + private[immutable] final def copyOf(a: Array[AnyRef]) = { + //println("copy") + if (a eq null) println ("NULL") + val b = new Array[AnyRef](a.length) + Platform.arraycopy(a, 0, b, 0, a.length) + b + } + + private[immutable] final def nullSlotAndCopy(array: Array[AnyRef], index: Int) = { + //println("copy and null") + val x = array(index) + array(index) = null + copyOf(x.asInstanceOf[Array[AnyRef]]) + } + + + // make sure there is no aliasing + // requires structure is at pos index + // ensures structure is clean and at pos index and writable at all levels except 0 + + private[immutable] final def stabilize(index: Int) = (depth - 1) match { + case 5 => + display5 = copyOf(display5) + display4 = copyOf(display4) + display3 = copyOf(display3) + display2 = copyOf(display2) + display1 = copyOf(display1) + display5((index >> 25) & 31) = display4 + display4((index >> 20) & 31) = display3 + display3((index >> 15) & 31) = display2 + display2((index >> 10) & 31) = display1 + display1((index >> 5) & 31) = display0 + case 4 => + display4 = copyOf(display4) + display3 = copyOf(display3) + display2 = copyOf(display2) + display1 = copyOf(display1) + display4((index >> 20) & 31) = display3 + display3((index >> 15) & 31) = display2 + display2((index >> 10) & 31) = display1 + display1((index >> 5) & 31) = display0 + case 3 => + display3 = copyOf(display3) + display2 = copyOf(display2) + display1 = copyOf(display1) + display3((index >> 15) & 31) = display2 + display2((index >> 10) & 31) = display1 + display1((index >> 5) & 31) = display0 + case 2 => + display2 = copyOf(display2) + display1 = copyOf(display1) + display2((index >> 10) & 31) = display1 + display1((index >> 5) & 31) = display0 + case 1 => + display1 = copyOf(display1) + display1((index >> 5) & 31) = display0 + case 0 => + } + + + + /// USED IN UPDATE AND APPEND BACK + + // prepare for writing at an existing position + + // requires structure is clean and at pos oldIndex = xor ^ newIndex, + // ensures structure is dirty and at pos newIndex and writable at level 0 + private[immutable] final def gotoPosWritable0(newIndex: Int, xor: Int): Unit = (depth - 1) match { + case 5 => + display5 = copyOf(display5) + display4 = nullSlotAndCopy(display5, (newIndex >> 25) & 31).asInstanceOf[Array[AnyRef]] + display3 = nullSlotAndCopy(display4, (newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + display2 = nullSlotAndCopy(display3, (newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + case 4 => + display4 = copyOf(display4) + display3 = nullSlotAndCopy(display4, (newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + display2 = nullSlotAndCopy(display3, (newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + case 3 => + display3 = copyOf(display3) + display2 = nullSlotAndCopy(display3, (newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + case 2 => + display2 = copyOf(display2) + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + case 1 => + display1 = copyOf(display1) + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + case 0 => + display0 = copyOf(display0) + } + + + // requires structure is dirty and at pos oldIndex, + // ensures structure is dirty and at pos newIndex and writable at level 0 + private[immutable] final def gotoPosWritable1(oldIndex: Int, newIndex: Int, xor: Int): Unit = { + if (xor < (1 << 5)) { // level = 0 + display0 = copyOf(display0) + } else + if (xor < (1 << 10)) { // level = 1 + display1 = copyOf(display1) + display1((oldIndex >> 5) & 31) = display0 + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31) + } else + if (xor < (1 << 15)) { // level = 2 + display1 = copyOf(display1) + display2 = copyOf(display2) + display1((oldIndex >> 5) & 31) = display0 + display2((oldIndex >> 10) & 31) = display1 + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 20)) { // level = 3 + display1 = copyOf(display1) + display2 = copyOf(display2) + display3 = copyOf(display3) + display1((oldIndex >> 5) & 31) = display0 + display2((oldIndex >> 10) & 31) = display1 + display3((oldIndex >> 15) & 31) = display2 + display2 = nullSlotAndCopy(display3, (newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 25)) { // level = 4 + display1 = copyOf(display1) + display2 = copyOf(display2) + display3 = copyOf(display3) + display4 = copyOf(display4) + display1((oldIndex >> 5) & 31) = display0 + display2((oldIndex >> 10) & 31) = display1 + display3((oldIndex >> 15) & 31) = display2 + display4((oldIndex >> 20) & 31) = display3 + display3 = nullSlotAndCopy(display4, (newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + display2 = nullSlotAndCopy(display3, (newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else + if (xor < (1 << 30)) { // level = 5 + display1 = copyOf(display1) + display2 = copyOf(display2) + display3 = copyOf(display3) + display4 = copyOf(display4) + display5 = copyOf(display5) + display1((oldIndex >> 5) & 31) = display0 + display2((oldIndex >> 10) & 31) = display1 + display3((oldIndex >> 15) & 31) = display2 + display4((oldIndex >> 20) & 31) = display3 + display5((oldIndex >> 25) & 31) = display4 + display4 = nullSlotAndCopy(display5, (newIndex >> 25) & 31).asInstanceOf[Array[AnyRef]] + display3 = nullSlotAndCopy(display4, (newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + display2 = nullSlotAndCopy(display3, (newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + display1 = nullSlotAndCopy(display2, (newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + display0 = nullSlotAndCopy(display1, (newIndex >> 5) & 31).asInstanceOf[Array[AnyRef]] + } else { // level = 6 + throw new IllegalArgumentException() + } + } + + + // USED IN DROP + + private[immutable] final def copyRange(array: Array[AnyRef], oldLeft: Int, newLeft: Int) = { + val elems = new Array[AnyRef](32) + Platform.arraycopy(array, oldLeft, elems, newLeft, 32 - math.max(newLeft,oldLeft)) + elems + } + + + + + // USED IN APPEND + // create a new block at the bottom level (and possibly nodes on its path) and prepares for writing + + // requires structure is clean and at pos oldIndex, + // ensures structure is dirty and at pos newIndex and writable at level 0 + private[immutable] final def gotoFreshPosWritable0(oldIndex: Int, newIndex: Int, xor: Int): Unit = { // goto block start pos + if (xor < (1 << 5)) { // level = 0 + //println("XXX clean with low xor") + } else + if (xor < (1 << 10)) { // level = 1 + if (depth == 1) { + display1 = new Array(32) + display1((oldIndex >> 5) & 31) = display0 + depth +=1 + } + display0 = new Array(32) + } else + if (xor < (1 << 15)) { // level = 2 + if (depth == 2) { + display2 = new Array(32) + display2((oldIndex >> 10) & 31) = display1 + depth +=1 + } + display1 = display2((newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + if (display1 == null) display1 = new Array(32) + display0 = new Array(32) + } else + if (xor < (1 << 20)) { // level = 3 + if (depth == 3) { + display3 = new Array(32) + display3((oldIndex >> 15) & 31) = display2 + display2 = new Array(32) + display1 = new Array(32) + depth +=1 + } + display2 = display3((newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + if (display2 == null) display2 = new Array(32) + display1 = display2((newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + if (display1 == null) display1 = new Array(32) + display0 = new Array(32) + } else + if (xor < (1 << 25)) { // level = 4 + if (depth == 4) { + display4 = new Array(32) + display4((oldIndex >> 20) & 31) = display3 + display3 = new Array(32) + display2 = new Array(32) + display1 = new Array(32) + depth +=1 + } + display3 = display4((newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + if (display3 == null) display3 = new Array(32) + display2 = display3((newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + if (display2 == null) display2 = new Array(32) + display1 = display2((newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + if (display1 == null) display1 = new Array(32) + display0 = new Array(32) + } else + if (xor < (1 << 30)) { // level = 5 + if (depth == 5) { + display5 = new Array(32) + display5((oldIndex >> 25) & 31) = display4 + display4 = new Array(32) + display3 = new Array(32) + display2 = new Array(32) + display1 = new Array(32) + depth +=1 + } + display4 = display5((newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + if (display4 == null) display4 = new Array(32) + display3 = display4((newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + if (display3 == null) display3 = new Array(32) + display2 = display3((newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] + if (display2 == null) display2 = new Array(32) + display1 = display2((newIndex >> 10) & 31).asInstanceOf[Array[AnyRef]] + if (display1 == null) display1 = new Array(32) + display0 = new Array(32) + } else { // level = 6 + throw new IllegalArgumentException() + } + } + + + // requires structure is dirty and at pos oldIndex, + // ensures structure is dirty and at pos newIndex and writable at level 0 + private[immutable] final def gotoFreshPosWritable1(oldIndex: Int, newIndex: Int, xor: Int): Unit = { + stabilize(oldIndex) + gotoFreshPosWritable0(oldIndex, newIndex, xor) + } + + + + + // DEBUG STUFF + + private[immutable] def debug(): Unit = { + return + /* + //println("DISPLAY 5: " + display5 + " ---> " + (if (display5 ne null) display5.map(x=> if (x eq null) "." else x + "->" +x.asInstanceOf[Array[AnyRef]].mkString("")).mkString(" ") else "null")) + //println("DISPLAY 4: " + display4 + " ---> " + (if (display4 ne null) display4.map(x=> if (x eq null) "." else x + "->" +x.asInstanceOf[Array[AnyRef]].mkString("")).mkString(" ") else "null")) + //println("DISPLAY 3: " + display3 + " ---> " + (if (display3 ne null) display3.map(x=> if (x eq null) "." else x + "->" +x.asInstanceOf[Array[AnyRef]].mkString("")).mkString(" ") else "null")) + //println("DISPLAY 2: " + display2 + " ---> " + (if (display2 ne null) display2.map(x=> if (x eq null) "." else x + "->" +x.asInstanceOf[Array[AnyRef]].mkString("")).mkString(" ") else "null")) + //println("DISPLAY 1: " + display1 + " ---> " + (if (display1 ne null) display1.map(x=> if (x eq null) "." else x + "->" +x.asInstanceOf[Array[AnyRef]].mkString("")).mkString(" ") else "null")) + //println("DISPLAY 0: " + display0 + " ---> " + (if (display0 ne null) display0.map(x=> if (x eq null) "." else x.toString).mkString(" ") else "null")) + */ + //println("DISPLAY 5: " + (if (display5 ne null) display5.map(x=> if (x eq null) "." else x.asInstanceOf[Array[AnyRef]].deepMkString("[","","]")).mkString(" ") else "null")) + //println("DISPLAY 4: " + (if (display4 ne null) display4.map(x=> if (x eq null) "." else x.asInstanceOf[Array[AnyRef]].deepMkString("[","","]")).mkString(" ") else "null")) + //println("DISPLAY 3: " + (if (display3 ne null) display3.map(x=> if (x eq null) "." else x.asInstanceOf[Array[AnyRef]].deepMkString("[","","]")).mkString(" ") else "null")) + //println("DISPLAY 2: " + (if (display2 ne null) display2.map(x=> if (x eq null) "." else x.asInstanceOf[Array[AnyRef]].deepMkString("[","","]")).mkString(" ") else "null")) + //println("DISPLAY 1: " + (if (display1 ne null) display1.map(x=> if (x eq null) "." else x.asInstanceOf[Array[AnyRef]].deepMkString("[","","]")).mkString(" ") else "null")) + //println("DISPLAY 0: " + (if (display0 ne null) display0.map(x=> if (x eq null) "." else x.toString).mkString(" ") else "null")) + } + + +} diff --git a/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala b/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala index a9c66dee43d4..ca20a9c768e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnusedDecls.scala @@ -40,11 +40,11 @@ class ErasedDecls extends MiniPhase with InfoTransformer { /* Info transform */ - override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { - if (sym.is(JavaDefined) || sym.is(Scala2x)) tp - else tp match { - case tp: ClassInfo => tp.derivedClassInfo(decls = tp.decls.filteredScope(!_.is(Erased))) - case _ => tp - } + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match { + case tp: ClassInfo => tp.derivedClassInfo(decls = tp.decls.filteredScope(!_.is(Erased))) + case _ => tp } + + override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = + sym.isClass && !sym.is(JavaDefined) && !sym.is(Scala2x) } From dd9210bc3af87a39ed1fc1a5b11fb85909190144 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 20 Mar 2018 16:03:38 +0100 Subject: [PATCH 3/4] Add mayChange to FirstTransform --- compiler/src/dotty/tools/dotc/transform/FirstTransform.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index f988e519862d..bdff59d1f11e 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -61,6 +61,8 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => tp } + override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isClass + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { tree match { case Select(qual, name) if !name.is(OuterSelectName) && tree.symbol.exists => From 702b641ea605634348a02adc18d2c8e53b5e8756 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 20 Mar 2018 17:31:40 +0100 Subject: [PATCH 4/4] Move checks to mayChange --- compiler/src/dotty/tools/dotc/transform/ElimByName.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 36c22f2e2068..b37aad0ddfb3 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -73,9 +73,9 @@ class ElimByName extends TransformByNameApply with InfoTransformer { } def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match { - case ExprType(rt) if exprBecomesFunction(sym) => defn.FunctionOf(Nil, rt) + case ExprType(rt) => defn.FunctionOf(Nil, rt) case _ => tp } - override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm + override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm && exprBecomesFunction(sym) } diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 39fabb732d5b..6e1f19c2992c 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -48,7 +48,7 @@ class ExplicitOuter extends MiniPhase with InfoTransformer { thisPhase => /** Add outer accessors if a class always needs an outer pointer */ override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { - case tp @ ClassInfo(_, cls, _, decls, _) if needsOuterAlways(cls) && !sym.is(JavaDefined) => + case tp @ ClassInfo(_, cls, _, decls, _) if needsOuterAlways(cls) => val newDecls = decls.cloneScope newOuterAccessors(cls).foreach(newDecls.enter) tp.derivedClassInfo(decls = newDecls) @@ -56,7 +56,7 @@ class ExplicitOuter extends MiniPhase with InfoTransformer { thisPhase => tp } - override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isClass + override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isClass && !sym.is(JavaDefined) /** First, add outer accessors if a class does not have them yet and it references an outer this. * If the class has outer accessors, implement them.