From d53da9502fe0cb98ed5f8dfe64fbc6e62c0e3368 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 11 Nov 2015 18:19:28 +0100 Subject: [PATCH 1/7] Eta expand according to expected type parameter variance When eta expanding a type `C` to `[vX] => C[X]` the variance `v` is now the variance of the expected type, not the variance of the type constructor `C`, as long as the variance of the expected type is compatible with the variance of `C`. --- .../tools/dotc/core/TypeApplications.scala | 25 ++++++---- .../core/unpickleScala2/Scala2Unpickler.scala | 49 ++++++++++++++++++- test/dotc/scala-collections.whitelist | 7 ++- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 2631e99644f7..be070dace030 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -524,21 +524,28 @@ class TypeApplications(val self: Type) extends AnyVal { } } - /** Convert a type constructor `TC` with type parameters `T1, ..., Tn` to + /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` + * in a context where type parameters `U1,...,Un` are expected to * * LambdaXYZ { Apply = TC[hk$0, ..., hk$n] } * - * where XYZ is a corresponds to the variances of the type parameters. + * Here, XYZ corresponds to the variances of + * - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`, + * - `T1,...,Tn` otherwise. + * v1 is compatible with v2, if v1 = v2 or v2 is non-variant. */ - def EtaExpand(implicit ctx: Context): Type = { - val tparams = typeParams - self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams) + def EtaExpand(tparams: List[Symbol])(implicit ctx: Context): Type = { + def varianceCompatible(actual: Symbol, formal: Symbol) = + formal.variance == 0 || actual.variance == formal.variance + val tparamsToUse = + if (typeParams.corresponds(tparams)(varianceCompatible)) tparams else typeParams + self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } /** Eta expand if `bound` is a higher-kinded type */ def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type = - if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand + if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand(bound.typeParams) else self /** Eta expand the prefix in front of any refinements. */ @@ -546,7 +553,7 @@ class TypeApplications(val self: Type) extends AnyVal { case self: RefinedType => self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo) case _ => - self.EtaExpand + self.EtaExpand(self.typeParams) } /** If `self` is a (potentially partially instantiated) eta expansion of type T, return T, @@ -645,7 +652,7 @@ class TypeApplications(val self: Type) extends AnyVal { param2.variance == param2.variance || param2.variance == 0 if (classBounds.exists(tycon.derivesFrom(_)) && tycon.typeParams.corresponds(tparams)(variancesMatch)) { - val expanded = tycon.EtaExpand + val expanded = tycon.EtaExpand(tparams) val lifted = (expanded /: targs) { (partialInst, targ) => val tparam = partialInst.typeParams.head RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance)) @@ -659,7 +666,7 @@ class TypeApplications(val self: Type) extends AnyVal { false } tparams.nonEmpty && - (typeParams.nonEmpty && p(EtaExpand) || + (typeParams.hasSameLengthAs(tparams) && p(EtaExpand(tparams)) || classBounds.nonEmpty && tryLift(self.baseClasses)) } } diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 321846de68b4..8f4b47058a06 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -151,6 +151,51 @@ object Scala2Unpickler { denot.info = ClassInfo( // final info denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost) } + + /** Adapt arguments to type parameters so that variance of type lambda arguments + * agrees with variance of corresponding higherkinded type parameters. Example: + * + * class Companion[+CC[X]] + * Companion[List] + * + * with adaptArgs, this will expand to + * + * Companion[[X] => List[X]] + * + * instead of + * + * Companion[[+X] => List[X]] + * + * even though `List` is covariant. This adaptation is necessary to ignore conflicting + * variances in overriding members that have types of hk-type parameters such as `Companion[GenTraversable]` + * or `Companion[ListBuffer]`. Without the adaptation we would end up with + * + * Companion[[+X] => GenTraversable[X]] + * Companion[[X] => List[X]] + * + * and the second is not a subtype of the first. So if we have overridding memebrs of the two + * types we get an error. + */ + def adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] = tparams match { + case tparam :: tparams1 => + val boundLambda = tparam.infoOrCompleter match { + case TypeBounds(_, hi) => hi.LambdaClass(forcing = false) + case _ => NoSymbol + } + def adaptArg(arg: Type): Type = arg match { + case arg: TypeRef if arg.symbol.isLambdaTrait => + assert(arg.symbol.typeParams.length == boundLambda.typeParams.length) + arg.prefix.select(boundLambda) + case arg: RefinedType => + arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo) + case _ => + arg + } + val arg = args.head + val adapted = if (boundLambda.exists) adaptArg(arg) else arg + adapted :: adaptArgs(tparams1, args.tail) + case nil => args + } } /** Unpickle symbol table information descending from a class and/or module root @@ -723,8 +768,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas else TypeRef(pre, sym.name.asTypeName) val args = until(end, readTypeRef) if (sym == defn.ByNameParamClass2x) ExprType(args.head) - else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand - else tycon.appliedTo(args) + else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams) + else tycon.appliedTo(adaptArgs(sym.typeParams, args)) case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef()) case REFINEDtpe => diff --git a/test/dotc/scala-collections.whitelist b/test/dotc/scala-collections.whitelist index 6c4a34f0e359..4f3eeeed5ce9 100644 --- a/test/dotc/scala-collections.whitelist +++ b/test/dotc/scala-collections.whitelist @@ -82,9 +82,9 @@ ./scala-scala/src/library/scala/collection/immutable/Queue.scala # https://github.com/lampepfl/dotty/issues/916 -#./scala-scala/src/library/scala/collection/immutable/Seq.scala -#./scala-scala/src/library/scala/collection/mutable/IndexedSeq.scala -#./scala-scala/src/library/scala/collection/mutable/ListBuffer.scala +./scala-scala/src/library/scala/collection/immutable/Seq.scala +./scala-scala/src/library/scala/collection/mutable/IndexedSeq.scala +./scala-scala/src/library/scala/collection/mutable/ListBuffer.scala ./scala-scala/src/library/scala/collection/immutable/Stack.scala ./scala-scala/src/library/scala/collection/immutable/StringLike.scala @@ -224,7 +224,6 @@ ./scala-scala/src/library/scala/collection/immutable/NumericRange.scala ./scala-scala/src/library/scala/collection/immutable/Range.scala ./scala-scala/src/library/scala/collection/immutable/RedBlackTree.scala -./scala-scala/src/library/scala/collection/immutable/Seq.scala # uses refinements that dotty does not support #./scala-scala/src/library/scala/collection/immutable/Set.scala From 199dca76a3a6771272a04b1cb717a4b8f04dd9a7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 12 Nov 2015 13:46:10 +0100 Subject: [PATCH 2/7] Harden definition of isScalaTrait Exclude false positives such as `Lambda|` be requiring that lambda traits are defined in the Scala package. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 5d079640fb9a..2f27033974a8 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -446,7 +446,7 @@ object SymDenotations { /** is this symbol a trait representing a type lambda? */ final def isLambdaTrait(implicit ctx: Context): Boolean = - isClass && name.startsWith(tpnme.LambdaPrefix) + isClass && name.startsWith(tpnme.LambdaPrefix) && owner == defn.ScalaPackageClass /** Is this symbol a package object or its module class? */ def isPackageObject(implicit ctx: Context): Boolean = { From 7a61c8693fd7f3431d5a80829a6a051151969122 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 17 Nov 2015 17:52:56 +0100 Subject: [PATCH 3/7] Revert: Make hash codes on type more predictable. getSimpleName crashes on some module names created by scalac. May help finding the partest issue. (reverted from commit c11646c40042404550eb983577c9e7096a40502a) --- src/dotty/tools/dotc/core/Hashable.scala | 2 +- src/dotty/tools/dotc/core/Uniques.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Hashable.scala b/src/dotty/tools/dotc/core/Hashable.scala index 08309f5c8a06..6a64f865558b 100644 --- a/src/dotty/tools/dotc/core/Hashable.scala +++ b/src/dotty/tools/dotc/core/Hashable.scala @@ -28,7 +28,7 @@ object Hashable { trait Hashable { import Hashable._ - protected def hashSeed: Int = getClass.getSimpleName.hashCode + protected def hashSeed: Int = getClass.hashCode protected final def finishHash(hashCode: Int, arity: Int): Int = avoidNotCached(hashing.finalizeHash(hashCode, arity)) diff --git a/src/dotty/tools/dotc/core/Uniques.scala b/src/dotty/tools/dotc/core/Uniques.scala index 0571100a309a..b00508d6003c 100644 --- a/src/dotty/tools/dotc/core/Uniques.scala +++ b/src/dotty/tools/dotc/core/Uniques.scala @@ -92,7 +92,7 @@ object Uniques { } final class RefinedUniques extends HashSet[RefinedType](Config.initialUniquesCapacity) with Hashable { - override val hashSeed = "CachedRefinedType".hashCode // some types start life as CachedRefinedTypes, need to have same hash seed + override val hashSeed = classOf[CachedRefinedType].hashCode // some types start life as CachedRefinedTypes, need to have same hash seed override def hash(x: RefinedType): Int = x.hash private def findPrevious(h: Int, parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = { From c435a8a2e3df50f3ca41f2f9f9f9d8d22a037785 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 13 Nov 2015 19:06:49 +0100 Subject: [PATCH 4/7] Avoid setting info with parents too early when unpickling Adding parents signals (via SymDenotation.fullyDefined) that the class can now be frozen. So this should be done only after all members are entered. --- src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 8f4b47058a06..d58122f2d6cf 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -112,8 +112,6 @@ object Scala2Unpickler { if (tsym.exists) tsym.setFlag(TypeParam) else denot.enter(tparam, decls) } - denot.info = ClassInfo( - denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost) // more refined infowith parents if (!(denot.flagsUNSAFE is JavaModule)) ensureConstructor(denot.symbol.asClass, decls) val scalacCompanion = denot.classSymbol.scalacLinkedClass From e5fcf57b1e62e420c611ed58d475d081d95f9048 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 15 Nov 2015 11:48:37 +0100 Subject: [PATCH 5/7] Fix problems arising when hierarchies of classes are under completion Fix problems arising when hierarchies of classes are under completion at the same time. In this case it can happen that we see a subclass (e.g. Arrays.scala) which depends on a superclass (e.g. GenTraversableLike.scala) that itself does not have its parents defined yet. Previously, several things went wrong here - One of the base classes would be marked as frozen, even though it dod not have all members entered yet. This led to an error in finger printing. - The baseclasses and super class bits of the subclass would be computed before the parents of the middle class were known. The baseclasses would then be chached, leading to false results for isDerivedFrom. We need to refine the logic for computing base classes, super class bits, and fingerprints to account for that issue. --- src/dotty/tools/dotc/core/Flags.scala | 3 ++ .../tools/dotc/core/SymDenotations.scala | 48 ++++++++++++------- test/dotc/scala-collections.whitelist | 2 +- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 1271d7f6a8ec..c8a9dc13bab4 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -348,6 +348,9 @@ object Flags { /** A bridge method. Set by Erasure */ final val Bridge = termFlag(34, "") + /** All class attributes are fully defined */ + final val FullyCompleted = typeFlag(34, "") + /** Symbol is a Java varargs bridge */ // (needed?) final val VBridge = termFlag(35, "") // TODO remove diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 2f27033974a8..00ca99da5763 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1213,8 +1213,17 @@ object SymDenotations { * ClassDenotations compiled from source are first completed, then fully completed. * @see Namer#ClassCompleter */ - private def isFullyCompleted(implicit ctx: Context): Boolean = - isCompleted && classParents.nonEmpty + private def isFullyCompleted(implicit ctx: Context): Boolean = { + def isFullyCompletedRef(tp: TypeRef) = tp.denot match { + case d: ClassDenotation => d.isFullyCompleted + case _ => false + } + def isLocallyFullyCompleted = + if (classParents.isEmpty) is(Package) || symbol.eq(defn.AnyClass) + else classParents.forall(isFullyCompletedRef) + flagsUNSAFE.is(FullyCompleted) || + isCompleted && isLocallyFullyCompleted && { setFlag(FullyCompleted); true } + } // ------ syncing inheritance-related info ----------------------------- @@ -1300,7 +1309,7 @@ object SymDenotations { baseTypeRefValid = ctx.runId } - private def computeBases(implicit ctx: Context): Unit = { + private def computeBases(implicit ctx: Context): (List[ClassSymbol], BitSet) = { if (myBaseClasses eq Nil) throw CyclicReference(this) myBaseClasses = Nil val seen = new mutable.BitSet @@ -1324,8 +1333,14 @@ object SymDenotations { case nil => to } - myBaseClasses = classSymbol :: addParentBaseClasses(classParents, Nil) - mySuperClassBits = seen.toImmutable + val bcs = classSymbol :: addParentBaseClasses(classParents, Nil) + val scbits = seen.toImmutable + if (isFullyCompleted) { + myBaseClasses = bcs + mySuperClassBits = scbits + } + else myBaseClasses = null + (bcs, scbits) } /** A bitset that contains the superId's of all base classes */ @@ -1333,8 +1348,7 @@ object SymDenotations { if (classParents.isEmpty) BitSet() // can happen when called too early in Namers else { checkBasesUpToDate() - if (mySuperClassBits == null) computeBases - mySuperClassBits + if (mySuperClassBits != null) mySuperClassBits else computeBases._2 } /** The base classes of this class in linearization order, @@ -1344,8 +1358,7 @@ object SymDenotations { if (classParents.isEmpty) classSymbol :: Nil // can happen when called too early in Namers else { checkBasesUpToDate() - if (myBaseClasses == null) computeBases - myBaseClasses + if (myBaseClasses != null) myBaseClasses else computeBases._1 } final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean = @@ -1378,9 +1391,9 @@ object SymDenotations { while (ps.nonEmpty) { val parent = ps.head.typeSymbol parent.denot match { - case classd: ClassDenotation => - fp.include(classd.memberFingerPrint) - parent.denot.setFlag(Frozen) + case parentDenot: ClassDenotation => + fp.include(parentDenot.memberFingerPrint) + if (parentDenot.isFullyCompleted) parentDenot.setFlag(Frozen) case _ => } ps = ps.tail @@ -1393,10 +1406,13 @@ object SymDenotations { * not be used for package classes because cache never * gets invalidated. */ - def memberFingerPrint(implicit ctx: Context): FingerPrint = { - if (myMemberFingerPrint == FingerPrint.unknown) myMemberFingerPrint = computeMemberFingerPrint - myMemberFingerPrint - } + def memberFingerPrint(implicit ctx: Context): FingerPrint = + if (myMemberFingerPrint != FingerPrint.unknown) myMemberFingerPrint + else { + val fp = computeMemberFingerPrint + if (isFullyCompleted) myMemberFingerPrint = fp + fp + } private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null private[this] var myMemberCachePeriod: Period = Nowhere diff --git a/test/dotc/scala-collections.whitelist b/test/dotc/scala-collections.whitelist index 4f3eeeed5ce9..a5fce102044b 100644 --- a/test/dotc/scala-collections.whitelist +++ b/test/dotc/scala-collections.whitelist @@ -140,7 +140,7 @@ # breaks genMapLike https://github.com/lampepfl/dotty/issues/937 #./scala-scala/src/library/scala/collection/GenTraversable.scala -#./scala-scala/src/library/scala/collection/GenTraversableLike.scala +./scala-scala/src/library/scala/collection/GenTraversableLike.scala ./scala-scala/src/library/scala/collection/GenTraversableOnce.scala ./scala-scala/src/library/scala/collection/IndexedSeq.scala From 17a5066c0692a0ad0cdc5a38e3b549ace8fc5e06 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 15 Nov 2015 11:56:55 +0100 Subject: [PATCH 6/7] Packages are never fully completed The previous commit made packages always fully completed. This is wrong - since new members can be added to packages at any time, packages are never fully completed. --- src/dotty/tools/dotc/core/SymDenotations.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 00ca99da5763..64862418ad67 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1211,6 +1211,7 @@ object SymDenotations { /** The denotation is fully completed: all attributes are fully defined. * ClassDenotations compiled from source are first completed, then fully completed. + * Packages are never fully completed since members can be added at any time. * @see Namer#ClassCompleter */ private def isFullyCompleted(implicit ctx: Context): Boolean = { @@ -1218,11 +1219,11 @@ object SymDenotations { case d: ClassDenotation => d.isFullyCompleted case _ => false } - def isLocallyFullyCompleted = - if (classParents.isEmpty) is(Package) || symbol.eq(defn.AnyClass) + def testFullyCompleted = + if (classParents.isEmpty) !is(Package) && symbol.eq(defn.AnyClass) else classParents.forall(isFullyCompletedRef) flagsUNSAFE.is(FullyCompleted) || - isCompleted && isLocallyFullyCompleted && { setFlag(FullyCompleted); true } + isCompleted && testFullyCompleted && { setFlag(FullyCompleted); true } } // ------ syncing inheritance-related info ----------------------------- From b37b9e3b9f675c5b312f85511d20aaa50200eff1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 17 Nov 2015 17:26:19 +0100 Subject: [PATCH 7/7] Extend synchronized scope in newTermName Just noted that the previous scope might have been too small. We compute the bucket index with the table size before going into the synchronized. But that might mean we see a stale table size. Let's see what this gives us. --- src/dotty/tools/dotc/core/Names.scala | 93 +++++++++++++-------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala index 12def107626f..f1e6f7606921 100644 --- a/src/dotty/tools/dotc/core/Names.scala +++ b/src/dotty/tools/dotc/core/Names.scala @@ -240,64 +240,61 @@ object Names { /** Create a term name from the characters in cs[offset..offset+len-1]. * Assume they are already encoded. */ - def termName(cs: Array[Char], offset: Int, len: Int): TermName = { + def termName(cs: Array[Char], offset: Int, len: Int): TermName = synchronized { util.Stats.record("termName") val h = hashValue(cs, offset, len) & (table.size - 1) - synchronized { - - /** Make sure the capacity of the character array is at least `n` */ - def ensureCapacity(n: Int) = - if (n > chrs.length) { - val newchrs = new Array[Char](chrs.length * 2) - chrs.copyToArray(newchrs) - chrs = newchrs - } - - /** Enter characters into chrs array. */ - def enterChars(): Unit = { - ensureCapacity(nc + len) - var i = 0 - while (i < len) { - chrs(nc + i) = cs(offset + i) - i += 1 - } - nc += len + /** Make sure the capacity of the character array is at least `n` */ + def ensureCapacity(n: Int) = + if (n > chrs.length) { + val newchrs = new Array[Char](chrs.length * 2) + chrs.copyToArray(newchrs) + chrs = newchrs } - /** Rehash chain of names */ - def rehash(name: TermName): Unit = - if (name != null) { - val oldNext = name.next - val h = hashValue(chrs, name.start, name.length) & (table.size - 1) - name.next = table(h) - table(h) = name - rehash(oldNext) - } + /** Enter characters into chrs array. */ + def enterChars(): Unit = { + ensureCapacity(nc + len) + var i = 0 + while (i < len) { + chrs(nc + i) = cs(offset + i) + i += 1 + } + nc += len + } - /** Make sure the hash table is large enough for the given load factor */ - def incTableSize() = { - size += 1 - if (size.toDouble / table.size > fillFactor) { - val oldTable = table - table = new Array[TermName](table.size * 2) - for (i <- 0 until oldTable.size) rehash(oldTable(i)) - } + /** Rehash chain of names */ + def rehash(name: TermName): Unit = + if (name != null) { + val oldNext = name.next + val h = hashValue(chrs, name.start, name.length) & (table.size - 1) + name.next = table(h) + table(h) = name + rehash(oldNext) } - val next = table(h) - var name = next - while (name ne null) { - if (name.length == len && equals(name.start, cs, offset, len)) - return name - name = name.next + /** Make sure the hash table is large enough for the given load factor */ + def incTableSize() = { + size += 1 + if (size.toDouble / table.size > fillFactor) { + val oldTable = table + table = new Array[TermName](table.size * 2) + for (i <- 0 until oldTable.size) rehash(oldTable(i)) } - name = new TermName(nc, len, next) - enterChars() - table(h) = name - incTableSize() - name } + + val next = table(h) + var name = next + while (name ne null) { + if (name.length == len && equals(name.start, cs, offset, len)) + return name + name = name.next + } + name = new TermName(nc, len, next) + enterChars() + table(h) = name + incTableSize() + name } /** Create a type name from the characters in cs[offset..offset+len-1].