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/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/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]. diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 5d079640fb9a..64862418ad67 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 = { @@ -1211,10 +1211,20 @@ 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 = - 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 testFullyCompleted = + if (classParents.isEmpty) !is(Package) && symbol.eq(defn.AnyClass) + else classParents.forall(isFullyCompletedRef) + flagsUNSAFE.is(FullyCompleted) || + isCompleted && testFullyCompleted && { setFlag(FullyCompleted); true } + } // ------ syncing inheritance-related info ----------------------------- @@ -1300,7 +1310,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 +1334,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 +1349,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 +1359,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 +1392,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 +1407,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/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/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 = { diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 321846de68b4..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 @@ -151,6 +149,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 +766,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..a5fce102044b 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 @@ -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 @@ -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