From 6a58cce5a0dcd4db42f6f44ee775ef351ff426b4 Mon Sep 17 00:00:00 2001 From: "Diego E. Alonso-Blas" Date: Wed, 13 Mar 2019 14:27:28 +0000 Subject: [PATCH] Optimisations: avoid List allocations in the RefChecks. We add some small optimisations to the code in the RefChecks - We replace the `map length` calls with the utility methods in the Collections traits. - We replace a combined use of flatten, map, zip, and filter with the use of an iterator and a special iterator function. - We add to the Collections utils a function to create a special iterator, which combines zip, filter, and collect functions. --- .../tools/nsc/typechecker/RefChecks.scala | 16 ++++++++----- .../reflect/internal/util/Collections.scala | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 0ff03b937947..0316cfcbbf4a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -686,16 +686,20 @@ abstract class RefChecks extends Transform { val matchingArity = matchingName filter { m => !m.isDeferred && (m.name == underlying.name) && - (m.paramLists.length == abstractParamLists.length) && - (m.paramLists.map(_.length).sum == abstractParamLists.map(_.length).sum) && - (m.tpe.typeParams.size == underlying.tpe.typeParams.size) + sameLength(m.paramLists, abstractParamLists) && + sumSize(m.paramLists, 0) == sumSize(abstractParamLists, 0) && + sameLength(m.tpe.typeParams, underlying.tpe.typeParams) } matchingArity match { // So far so good: only one candidate method case Scope(concrete) => - val mismatches = abstractParamLists.flatten.map(_.tpe) zip concrete.paramLists.flatten.map(_.tpe) filterNot { case (x, y) => x =:= y } - mismatches match { + val aplIter = abstractParamLists .iterator.flatten + val cplIter = concrete.paramLists.iterator.flatten + def mismatch(apl: Symbol, cpl: Symbol): Option[(Type, Type)] = + if (apl.tpe =:= cpl.tpe) None else Some(apl.tpe -> cpl.tpe) + + mapFilter2(aplIter, cplIter)(mismatch).take(2).toList match { // Only one mismatched parameter: say something useful. case (pa, pc) :: Nil => val abstractSym = pa.typeSymbol @@ -724,7 +728,7 @@ abstract class RefChecks extends Transform { ) undefined("\n(Note that %s does not match %s%s)".format(pa, pc, addendum)) - case xs => + case _ => undefined("") } case _ => diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index c75c44a10873..7adc294112e1 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -16,6 +16,7 @@ package reflect.internal.util import scala.collection.{ mutable, immutable } import scala.annotation.tailrec import mutable.ListBuffer +import java.util.NoSuchElementException /** Profiler driven changes. * TODO - inlining doesn't work from here because of the bug that @@ -308,6 +309,29 @@ trait Collections { true } + final def mapFilter2[A, B, C](itA: Iterator[A], itB: Iterator[B])(f: (A, B) => Option[C]): Iterator[C] = + new Iterator[C] { + private[this] var head: Option[C] = None + private[this] def advanceHead(): Unit = + while (head.isEmpty && itA.hasNext && itB.hasNext) { + val x = itA.next + val y = itB.next + head = f(x, y) + } + + def hasNext: Boolean = { + advanceHead() + ! head.isEmpty + } + + def next(): C = { + advanceHead() + val res = head getOrElse (throw new NoSuchElementException("next on empty Iterator")) + head = None + res + } + } + // "Opt" suffix or traverse clashes with the various traversers' traverses final def sequenceOpt[A](as: List[Option[A]]): Option[List[A]] = traverseOpt(as)(identity) final def traverseOpt[A, B](as: List[A])(f: A => Option[B]): Option[List[B]] =