diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 29a31375b0c9..14a36f39848c 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -626,7 +626,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { loop(from.owner, from :: froms, to :: tos) else { //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") - new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.withMode(Mode.FutureDefsOK)).apply(tree) + new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.addMode(Mode.FutureDefsOK)).apply(tree) } } loop(from, Nil, to :: Nil) @@ -652,7 +652,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { traverseChildren(tree) } } - traverser.traverse(tree)(ctx.withMode(Mode.FutureDefsOK)) + traverser.traverse(tree)(ctx.addMode(Mode.FutureDefsOK)) tree } diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala index 544304e8a18d..577b958c848e 100644 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -23,6 +23,7 @@ trait ConstraintHandling { implicit val ctx: Context protected def isSubType(tp1: Type, tp2: Type): Boolean + protected def isSameType(tp1: Type, tp2: Type): Boolean val state: TyperState import state.constraint @@ -104,13 +105,20 @@ trait ConstraintHandling { up.forall(addOneBound(_, lo, isUpper = false)) } - protected final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { + final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { val saved = frozenConstraint frozenConstraint = true try isSubType(tp1, tp2) finally frozenConstraint = saved } + final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { + val saved = frozenConstraint + frozenConstraint = true + try isSameType(tp1, tp2) + finally frozenConstraint = saved + } + /** Test whether the lower bounds of all parameters in this * constraint are a solution to the constraint. */ diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 206ef9d8b9d4..f9d64b2cc56f 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -439,12 +439,12 @@ object Contexts { } implicit class ModeChanges(val c: Context) extends AnyVal { - final def withMode(mode: Mode): Context = + final def withModeBits(mode: Mode): Context = if (mode != c.mode) c.fresh.setMode(mode) else c - final def addMode(mode: Mode): Context = withMode(c.mode | mode) - final def maskMode(mode: Mode): Context = withMode(c.mode & mode) - final def retractMode(mode: Mode): Context = withMode(c.mode &~ mode) + final def addMode(mode: Mode): Context = withModeBits(c.mode | mode) + final def maskMode(mode: Mode): Context = withModeBits(c.mode & mode) + final def retractMode(mode: Mode): Context = withModeBits(c.mode &~ mode) } implicit class FreshModeChanges(val c: FreshContext) extends AnyVal { diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index fff4478037a4..b223a80865e3 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -339,8 +339,8 @@ object Denotations { val info1 = denot1.info val info2 = denot2.info val sameSym = sym1 eq sym2 - if (sameSym && info1 <:< info2) denot2 - else if (sameSym && info2 <:< info1) denot1 + if (sameSym && (info1 frozen_<:< info2)) denot2 + else if (sameSym && (info2 frozen_<:< info1)) denot1 else { val jointSym = if (sameSym) sym1 diff --git a/src/dotty/tools/dotc/core/OrderingConstraint.scala b/src/dotty/tools/dotc/core/OrderingConstraint.scala index 7e27ee628174..e818862cbbad 100644 --- a/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -129,7 +129,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds, type This = OrderingConstraint - // ----------- Basic indices -------------------------------------------------- /** The number of type parameters in the given entry array */ @@ -576,4 +575,22 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } Text.lines(List(header, uninstVarsText, constrainedText, boundsText, orderingText, ")")) } + + override def toString: String = { + def entryText(tp: Type): String = tp match { + case tp: TypeBounds => tp.toString + case _ =>" := " + tp + } + val constrainedText = + " constrained types = " + domainPolys.mkString("\n") + val boundsText = + " bounds = " + { + val assocs = + for (param <- domainParams) + yield + param.binder.paramNames(param.paramNum) + ": " + entryText(entry(param)) + assocs.mkString("\n") + } + constrainedText + "\n" + boundsText + } } diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 2ce2b8d20db3..d5ab09930011 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1587,6 +1587,7 @@ object SymDenotations { if (isCachable(tp)) baseTypeRefCache.put(tp, basetp) else baseTypeRefCache.remove(tp) } else if (basetp == NoPrefix) { + baseTypeRefCache.put(tp, null) throw CyclicReference(this) } basetp diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 927c4fcc5522..47935f79e190 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -497,7 +497,7 @@ class TypeApplications(val self: Type) extends AnyVal { val boundss = new mutable.ListBuffer[TypeBounds] for (sym <- boundSyms) { val bounds = sym.info.bounds - if (!(TypeBounds.empty <:< bounds)) { + if (!(TypeBounds.empty frozen_<:< bounds)) { boundNames += sym.name boundss += bounds } @@ -574,7 +574,7 @@ class TypeApplications(val self: Type) extends AnyVal { // we have a binding T = Lambda$XYZ{...}.this.hk$i where hk$i names the current `tparam`. val pcore = etaCore(tp.parent, otherParams) val hkBounds = self.member(rname).info.bounds - if (TypeBounds.empty <:< hkBounds) pcore + if (TypeBounds.empty frozen_<:< hkBounds) pcore else tp.derivedRefinedType(pcore, tp.refinedName, hkBounds) case _ => val pcore = etaCore(tp.parent, tparams) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index e3e5f3960eec..5fbffe6e95ab 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -154,24 +154,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: NamedType => val sym1 = tp1.symbol (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) - ctx.erasedTypes || sym1.isStaticOwner || - { // Implements: A # X <: B # X - // if either A =:= B (i.e. A <: B and B <: A), or the following three conditions hold: - // 1. X is a class type, - // 2. B is a class type without abstract type members. - // 3. A <: B. - // Dealiasing is taken care of elsewhere. - val pre1 = tp1.prefix - val pre2 = tp2.prefix - isSameType(pre1, pre2) || - sym1.isClass && - pre2.classSymbol.exists && - pre2.abstractTypeMembers.isEmpty && - isSubType(pre1, pre2) - } + ctx.erasedTypes || sym1.isStaticOwner || isSubType(tp1.prefix, tp2.prefix) else (tp1.name eq tp2.name) && - isSameType(tp1.prefix, tp2.prefix) && + isSubType(tp1.prefix, tp2.prefix) && (tp1.signature == tp2.signature) && !tp1.isInstanceOf[WithFixedSym] && !tp2.isInstanceOf[WithFixedSym] @@ -229,6 +215,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { compareSuper case AndType(tp21, tp22) => isSubType(tp1, tp21) && isSubType(tp1, tp22) + case OrType(tp21, tp22) => + if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21) + else secondTry(tp1, tp2) case TypeErasure.ErasedValueType(cls2, underlying2) => def compareErasedValueType = tp1 match { case TypeErasure.ErasedValueType(cls1, underlying1) => @@ -291,6 +280,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isSubType(tp1.ref, tp2) case tp1: AnnotatedType => isSubType(tp1.tpe, tp2) + case AndType(tp11, tp12) => + if (tp11.stripTypeVar eq tp12.stripTypeVar) isSubType(tp11, tp2) + else thirdTry(tp1, tp2) case OrType(tp11, tp12) => isSubType(tp11, tp2) && isSubType(tp12, tp2) case ErrorType => @@ -353,6 +345,21 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } compareRefined case OrType(tp21, tp22) => + // Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22) + // and analogously for T1 <: T21 | (T221 & T222) + // `|' types to the right of <: are problematic, because + // we have to choose one constraint set or another, which might cut off + // solutions. The rewriting delays the point where we have to choose. + tp21 match { + case AndType(tp211, tp212) => + return isSubType(tp1, OrType(tp211, tp22)) && isSubType(tp1, OrType(tp212, tp22)) + case _ => + } + tp22 match { + case AndType(tp221, tp222) => + return isSubType(tp1, OrType(tp21, tp221)) && isSubType(tp1, OrType(tp21, tp222)) + case _ => + } eitherIsSubType(tp1, tp21, tp1, tp22) || fourthTry(tp1, tp2) case tp2 @ MethodType(_, formals2) => def compareMethod = tp1 match { @@ -454,6 +461,21 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isNewSubType(tp1.parent, tp2) || needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil) case AndType(tp11, tp12) => + // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 + // and analogously for T11 & (T121 | T122) & T12 <: T2 + // `&' types to the left of <: are problematic, because + // we have to choose one constraint set or another, which might cut off + // solutions. The rewriting delays the point where we have to choose. + tp11 match { + case OrType(tp111, tp112) => + return isSubType(AndType(tp111, tp12), tp2) && isSubType(AndType(tp112, tp12), tp2) + case _ => + } + tp12 match { + case OrType(tp121, tp122) => + return isSubType(AndType(tp11, tp121), tp2) && isSubType(AndType(tp11, tp122), tp2) + case _ => + } eitherIsSubType(tp11, tp2, tp12, tp2) case JavaArrayType(elem1) => def compareJavaArray = tp2 match { @@ -693,7 +715,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case formal1 :: rest1 => formals2 match { case formal2 :: rest2 => - (isSameType(formal1, formal2) + (isSameTypeWhenFrozen(formal1, formal2) || isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) || isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && matchingParams(rest1, rest2, isJava1, isJava2) @@ -1067,7 +1089,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: ClassInfo => tp2 match { case tp2: ClassInfo => - isSubType(tp1.prefix, tp2.prefix) || (tp1.cls.owner derivesFrom tp2.cls.owner) + isSubTypeWhenFrozen(tp1.prefix, tp2.prefix) || (tp1.cls.owner derivesFrom tp2.cls.owner) case _ => false } @@ -1083,7 +1105,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2 match { case tp2: MethodType => def asGoodParams(formals1: List[Type], formals2: List[Type]) = - (formals2 corresponds formals1)(isSubType) + (formals2 corresponds formals1)(isSubTypeWhenFrozen) asGoodParams(tp1.paramTypes, tp2.paramTypes) && (!asGoodParams(tp2.paramTypes, tp1.paramTypes) || isAsGood(tp1.resultType, tp2.resultType)) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index bce57ae23757..e4f228915a58 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -595,6 +595,11 @@ object Types { ctx.typeComparer.topLevelSubType(this, that) } + /** Is this type a subtype of that type? */ + final def frozen_<:<(that: Type)(implicit ctx: Context): Boolean = track("frozen_<:<") { + ctx.typeComparer.isSubTypeWhenFrozen(this, that) + } + /** Is this type the same as that type? * This is the case iff `this <:< that` and `that <:< this`. */ @@ -625,9 +630,9 @@ object Types { case ExprType(_) | MethodType(Nil, _) => tp.resultType case _ => tp } - this <:< that || { + (this frozen_<:< that) || { val rthat = result(that) - (rthat ne that) && result(this) <:< rthat + (rthat ne that) && (result(this) frozen_<:< rthat) } } @@ -801,12 +806,18 @@ object Types { case _ => NoType } - /** The chain of underlying types as long as type is a TypeProxy. + /** The iterator of underlying types as long as type is a TypeProxy. * Useful for diagnostics */ - def underlyingChain(implicit ctx: Context): List[Type] = this match { - case tp: TypeProxy => tp :: tp.underlying.underlyingChain - case _ => Nil + def underlyingIterator(implicit ctx: Context): Iterator[Type] = new Iterator[Type] { + var current = Type.this + var hasNext = true + def next = { + val res = current + hasNext = current.isInstanceOf[TypeProxy] + if (hasNext) current = current.asInstanceOf[TypeProxy].underlying + res + } } /** A prefix-less refined this or a termRef to a new skolem symbol @@ -2652,13 +2663,13 @@ object Types { } def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = - if (this.lo <:< that.lo && that.hi <:< this.hi) that - else if (that.lo <:< this.lo && this.hi <:< that.hi) this + if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) that + else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) this else TypeBounds(this.lo | that.lo, this.hi & that.hi) def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = - if (this.lo <:< that.lo && that.hi <:< this.hi) this - else if (that.lo <:< this.lo && this.hi <:< that.hi) that + if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) this + else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) that else TypeBounds(this.lo & that.lo, this.hi | that.hi) override def & (that: Type)(implicit ctx: Context) = that match { diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 45928af4b7bd..5daef9fb8657 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -166,7 +166,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else { val constr = ctx.typerState.constraint val bounds = - if (constr.contains(tp)) constr.fullBounds(tp.origin) + if (constr.contains(tp)) constr.fullBounds(tp.origin)(ctx.addMode(Mode.Printing)) else TypeBounds.empty "(" ~ toText(tp.origin) ~ "?" ~ toText(bounds) ~ ")" } diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index 7adeeac1cb7c..086575fb4da0 100644 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/src/dotty/tools/dotc/reporting/Reporter.scala @@ -149,16 +149,18 @@ trait Reporting { this: Context => else traceIndented[T](s"==> $question?", (res: Any) => s"<== $question = ${resStr(res)}")(op) } - def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = { - var finalized = false - var logctx = this - while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer - def finalize(result: Any, note: String) = - if (!finalized) { - base.indent -= 1 - logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note") - finalized = true - } + def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = + if (ctx.mode.is(Mode.Printing)) op + else { + var finalized = false + var logctx = this + while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer + def finalize(result: Any, note: String) = + if (!finalized) { + base.indent -= 1 + logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note") + finalized = true + } try { logctx.log(s"${base.indentTab * base.indent}$leading") base.indent += 1 diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index f046226d10b8..2296ae65881b 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -66,7 +66,7 @@ class TreeChecker extends Phase with SymTransformer { val cur = symd.linkedClass val prev = ctx.atPhase(ctx.phase.prev) { ct => { - implicit val ctx: Context = ct.withMode(Mode.FutureDefsOK) + implicit val ctx: Context = ct.addMode(Mode.FutureDefsOK) symd.symbol.linkedClass } } diff --git a/test/test/DeSugarTest.scala b/test/test/DeSugarTest.scala index 2c74abae73ad..44360d657cdc 100644 --- a/test/test/DeSugarTest.scala +++ b/test/test/DeSugarTest.scala @@ -35,7 +35,7 @@ class DeSugarTest extends ParserTest { def transform(trees: List[Tree], mode: Mode)(implicit ctx: Context): List[Tree] = withMode(mode) { transform(trees) } override def transform(tree: Tree)(implicit ctx: Context): Tree = { - val tree1 = desugar(tree)(ctx.withMode(curMode)) + val tree1 = desugar(tree)(ctx.withModeBits(curMode)) tree1 match { case TypedSplice(t) => tree1 diff --git a/tests/pickling/i94-nada.scala b/tests/pending/pickling/i94-nada.scala similarity index 100% rename from tests/pickling/i94-nada.scala rename to tests/pending/pickling/i94-nada.scala diff --git a/tests/pos/Iter2.scala b/tests/pos/Iter2.scala new file mode 100644 index 000000000000..b9dc5d625611 --- /dev/null +++ b/tests/pos/Iter2.scala @@ -0,0 +1,245 @@ +package dotty1.collections +package immutable + +import annotation.unchecked.uncheckedVariance + +object Iter2 { + + trait Iterator[+A] extends IterableOnce[A] { + def hasNext: Boolean + def next: A + def iterator = this + def foreach(f: A => Unit): Unit = ??? + def map[B](f: A => B): Iterator[B] = ??? + def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = ??? + def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = ??? + def drop(n: Int): Iterator[A] = ??? + def indexWhere(p: A => Boolean): Int = { + var i = 0 + while (hasNext) { + if (p(next)) return i + i += 1 + } + -1 + } + def zip[B](that: Iterator[B]): Iterator[(A, B)] = ??? + def copy: Iterator[A] = ??? + } +/* + trait View[+A] { self => + def iterator: Iterator[A] + def foreach(f: A => Unit): Unit = iterator.foreach(f) + def map[B](f: A => B): View[B] = + new View[B] { def iterator = self.iterator.map(f) } + def flatMap[B](f: A => IterableOnce[B]): View[B] = + new View[B] { def iterator = self.iterator.flatMap(f) } + def ++[B >: A](xs: IterableOnce[B]): View[B] = + new View[B] { def iteratpr = self.iterator ++ xs } + def drop(n: Int): View[A] = + new View[A] { def iteratpr = self.iterator.drop(n) } + def indexWhere(p: A => Boolean): Int = { + var i = 0 + while (hasNext) { + if (p(next)) return i + i += 1 + } + -1 + } + def zip[B](that: Iterator[B]): Iterator[(A, B)] = ??? + } +*/ + + trait IterableOnce[+A] { + def iterator: Iterator[A] + def buildIterator: Iterator[A] = iterator + } + + trait FromIterator[+C[X] <: Iterable[X]] { + def fromIterator[B](it: Iterator[B]): C[B] + } + + trait Iterable[+IA] extends IterableOnce[IA] with FromIterator[Iterable] { + def view: View[IA] = new View(iterator) + } + + trait Seq[+AA] extends Iterable[AA] with FromIterator[Seq] { + def apply(i: Int): AA + def length: Int + } + + sealed trait List[+A] extends Seq[A] with FromIterator[List] { + def isEmpty: Boolean + def head: A + def tail: List[A] + def iterator = new ListIterator[A](this) + def fromIterator[B](it: Iterator[B]): List[B] = it match { + case ListIterator(xs) => xs + case _ => if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil + } + def apply(i: Int): A = { + require(!isEmpty) + if (i == 0) head else tail.apply(i - 1) + } + def length: Int = + if (isEmpty) 0 else 1 + tail.length + } + + class View[+A](it: Iterator[A]) extends Iterable[A] with FromIterator[View] { + def iterator: Iterator[A] = it.copy + def fromIterator[B](it: Iterator[B]): View[B] = new View(it) + } + + case class Cons[+A](x: A, xs: List[A]) extends List[A] { + def isEmpty = false + def head = x + def tail = xs + } + + case object Nil extends List[Nothing] { + def isEmpty = true + def head = ??? + def tail = ??? + } + + class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] { + def this() = this(new Array[AnyRef](16), 0) + def this(it: ArrayIterator[A]) = this(it.elems, it.len) + private var elems: Array[AnyRef] = initElems + private var len = 0 + def iterator = + elems.iterator.take(len).asInstanceOf[Iterator[A]] + override def buildIterator = + new ArrayIterator(elems, len).asInstanceOf[Iterator[A]] + def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = + new ArrayBuffer(ArrayIterator.fromIterator(it)) + def apply(i: Int) = elems(i).asInstanceOf[A] + def length = len + } +/* + class SeqView[A](itf: () => Iterator) extends Seq[A] with FromIterator[SeqView] { + def iterator = it + def buildIterator = it + def fromIterator[B](it: Iterator[B]) = it match { + case ViewIterator(itf) => SeqView(itf) + } + } +*/ + implicit class IterableTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterator[C]) extends AnyVal { + def map[B](f: A => B): C[B] = c.fromIterator(c.buildIterator.map(f)) + def flatMap[B](f: A => IterableOnce[B]): C[B] = c.fromIterator(c.buildIterator.flatMap(f(_).buildIterator)) + def ++[B >: A](xs: IterableOnce[B]): C[B] = c.fromIterator(c.buildIterator ++ xs.buildIterator) + def drop(n: Int): C[A] = c.fromIterator(c.buildIterator.drop(n)) + def head: A = c.iterator.next + def zip[B](xs: IterableOnce[B]): C[(A, B)] = c.fromIterator(c.iterator.zip(xs.iterator)) + } + + implicit class SeqTransforms[SA, C[X] <: Seq[X]](val c: Seq[SA] with FromIterator[C]) extends AnyVal { + def reverse: C[SA] = { + val elems = new Array[AnyRef](c.length) + var i = elems.length + val it = c.iterator + while (it.hasNext) { + i -= 1 + elems(i) = it.next.asInstanceOf[AnyRef] + } + val xzz = c.fromIterator(ArrayIterator[SA](elems, c.length)) + xzz + } + def indexWhere(p: SA => Boolean): Int = c.iterator.indexWhere(p) + } +/* + case class ViewIterator[+A](itf: () => Iterator) extends Iterator[A] { + + def hasNext = it.hasNext + def next + def map(f: A => B): ViewIterator[B] = ViewIterator(() => it().map(f)) + def + } +*/ + case class ListIterator[+A](xs: List[A]) extends Iterator[A] { + private[this] var current: List[A] = xs + def hasNext = !current.isEmpty + def next = { val res = current.head; current = current.tail; res } + } + + case class ArrayIterator[+A](elems: Array[AnyRef], len: Int) extends Iterator[A] { + import ArrayIterator._ + + private def elem(i: Int) = elems(i).asInstanceOf[A] + + private var cur = 0 + + def hasNext = cur < len + def next = { val res = elem(cur); cur += 1; res } + + override def foreach(f: A => Unit): Unit = + for (i <- 0 until len) f(elem(i)) + + override def map[B](f: A => B): ArrayIterator[B] = { + var mapped = elems + for (i <- 0 until len) { + val x = elem(i) + val y = widen(f(x)) + if (widen(x) ne y) { + if (mapped eq elems) mapped = new Array[AnyRef](len) + mapped(i) = y + } + } + if (mapped eq elems) this.asInstanceOf[ArrayIterator[B]] + else new ArrayIterator(mapped, len) + } + + override def flatMap[B](f: A => IterableOnce[B]): ArrayIterator[B] = + flatten(map(f(_).buildIterator)) + + override def ++[B >: A](that: IterableOnce[B]): ArrayIterator[B] = { + val thatIterator @ ArrayIterator(elems2, len2) = fromIterator(that.iterator) + if (len == 0) thatIterator + else if (len2 == 0) this + else { + val resLen = len + len2 + val resElems = new Array[AnyRef](resLen) + Array.copy(elems, 0, resElems, 0, len) + Array.copy(elems2, 0, resElems, len, len2) + new ArrayIterator(resElems, resLen) + } + } + } + + object ArrayIterator { + private def widen(x: Any): AnyRef = x.asInstanceOf[AnyRef] + + def fromIterator[A](it: Iterator[A]): ArrayIterator[A] = it match { + case it: ArrayIterator[A] => it + case _ => + var elems = new Array[AnyRef](32) + var len = 0 + def ensureCapacity() = { + while (len > elems.length) { + val newElems = new Array[AnyRef](elems.length * 2) + Array.copy(elems, 0, newElems, 0, elems.length) + elems = newElems + } + } + while (it.hasNext) { + len += 1 + ensureCapacity() + elems(len - 1) = widen(it.next) + } + ArrayIterator(elems, len) + } + + def flatten[A](its: ArrayIterator[Iterator[A]]): ArrayIterator[A] = { + var arrayIts = its.map(fromIterator) + var totalLen = 0 + arrayIts.foreach(totalLen += _.len) + val allElems = new Array[AnyRef](totalLen) + var j = 0 + arrayIts.foreach { it => + Array.copy(it.elems, 0, allElems, j, it.len) + j += it.len + } + new ArrayIterator(allElems, totalLen) + } + } +}