Skip to content

Commit eefa611

Browse files
committed
Merge pull request #950 from dotty-staging/fix-#916
Adapt type parameters of typed eta expansion according to expected variances
2 parents f9c8675 + b37b9e3 commit eefa611

File tree

8 files changed

+151
-85
lines changed

8 files changed

+151
-85
lines changed

src/dotty/tools/dotc/core/Flags.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ object Flags {
348348
/** A bridge method. Set by Erasure */
349349
final val Bridge = termFlag(34, "<bridge>")
350350

351+
/** All class attributes are fully defined */
352+
final val FullyCompleted = typeFlag(34, "<fully-completed>")
353+
351354
/** Symbol is a Java varargs bridge */ // (needed?)
352355
final val VBridge = termFlag(35, "<vbridge>") // TODO remove
353356

src/dotty/tools/dotc/core/Hashable.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ object Hashable {
2828
trait Hashable {
2929
import Hashable._
3030

31-
protected def hashSeed: Int = getClass.getSimpleName.hashCode
31+
protected def hashSeed: Int = getClass.hashCode
3232

3333
protected final def finishHash(hashCode: Int, arity: Int): Int =
3434
avoidNotCached(hashing.finalizeHash(hashCode, arity))

src/dotty/tools/dotc/core/Names.scala

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -240,64 +240,61 @@ object Names {
240240
/** Create a term name from the characters in cs[offset..offset+len-1].
241241
* Assume they are already encoded.
242242
*/
243-
def termName(cs: Array[Char], offset: Int, len: Int): TermName = {
243+
def termName(cs: Array[Char], offset: Int, len: Int): TermName = synchronized {
244244
util.Stats.record("termName")
245245
val h = hashValue(cs, offset, len) & (table.size - 1)
246246

247-
synchronized {
248-
249-
/** Make sure the capacity of the character array is at least `n` */
250-
def ensureCapacity(n: Int) =
251-
if (n > chrs.length) {
252-
val newchrs = new Array[Char](chrs.length * 2)
253-
chrs.copyToArray(newchrs)
254-
chrs = newchrs
255-
}
256-
257-
/** Enter characters into chrs array. */
258-
def enterChars(): Unit = {
259-
ensureCapacity(nc + len)
260-
var i = 0
261-
while (i < len) {
262-
chrs(nc + i) = cs(offset + i)
263-
i += 1
264-
}
265-
nc += len
247+
/** Make sure the capacity of the character array is at least `n` */
248+
def ensureCapacity(n: Int) =
249+
if (n > chrs.length) {
250+
val newchrs = new Array[Char](chrs.length * 2)
251+
chrs.copyToArray(newchrs)
252+
chrs = newchrs
266253
}
267254

268-
/** Rehash chain of names */
269-
def rehash(name: TermName): Unit =
270-
if (name != null) {
271-
val oldNext = name.next
272-
val h = hashValue(chrs, name.start, name.length) & (table.size - 1)
273-
name.next = table(h)
274-
table(h) = name
275-
rehash(oldNext)
276-
}
255+
/** Enter characters into chrs array. */
256+
def enterChars(): Unit = {
257+
ensureCapacity(nc + len)
258+
var i = 0
259+
while (i < len) {
260+
chrs(nc + i) = cs(offset + i)
261+
i += 1
262+
}
263+
nc += len
264+
}
277265

278-
/** Make sure the hash table is large enough for the given load factor */
279-
def incTableSize() = {
280-
size += 1
281-
if (size.toDouble / table.size > fillFactor) {
282-
val oldTable = table
283-
table = new Array[TermName](table.size * 2)
284-
for (i <- 0 until oldTable.size) rehash(oldTable(i))
285-
}
266+
/** Rehash chain of names */
267+
def rehash(name: TermName): Unit =
268+
if (name != null) {
269+
val oldNext = name.next
270+
val h = hashValue(chrs, name.start, name.length) & (table.size - 1)
271+
name.next = table(h)
272+
table(h) = name
273+
rehash(oldNext)
286274
}
287275

288-
val next = table(h)
289-
var name = next
290-
while (name ne null) {
291-
if (name.length == len && equals(name.start, cs, offset, len))
292-
return name
293-
name = name.next
276+
/** Make sure the hash table is large enough for the given load factor */
277+
def incTableSize() = {
278+
size += 1
279+
if (size.toDouble / table.size > fillFactor) {
280+
val oldTable = table
281+
table = new Array[TermName](table.size * 2)
282+
for (i <- 0 until oldTable.size) rehash(oldTable(i))
294283
}
295-
name = new TermName(nc, len, next)
296-
enterChars()
297-
table(h) = name
298-
incTableSize()
299-
name
300284
}
285+
286+
val next = table(h)
287+
var name = next
288+
while (name ne null) {
289+
if (name.length == len && equals(name.start, cs, offset, len))
290+
return name
291+
name = name.next
292+
}
293+
name = new TermName(nc, len, next)
294+
enterChars()
295+
table(h) = name
296+
incTableSize()
297+
name
301298
}
302299

303300
/** Create a type name from the characters in cs[offset..offset+len-1].

src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ object SymDenotations {
446446

447447
/** is this symbol a trait representing a type lambda? */
448448
final def isLambdaTrait(implicit ctx: Context): Boolean =
449-
isClass && name.startsWith(tpnme.LambdaPrefix)
449+
isClass && name.startsWith(tpnme.LambdaPrefix) && owner == defn.ScalaPackageClass
450450

451451
/** Is this symbol a package object or its module class? */
452452
def isPackageObject(implicit ctx: Context): Boolean = {
@@ -1211,10 +1211,20 @@ object SymDenotations {
12111211

12121212
/** The denotation is fully completed: all attributes are fully defined.
12131213
* ClassDenotations compiled from source are first completed, then fully completed.
1214+
* Packages are never fully completed since members can be added at any time.
12141215
* @see Namer#ClassCompleter
12151216
*/
1216-
private def isFullyCompleted(implicit ctx: Context): Boolean =
1217-
isCompleted && classParents.nonEmpty
1217+
private def isFullyCompleted(implicit ctx: Context): Boolean = {
1218+
def isFullyCompletedRef(tp: TypeRef) = tp.denot match {
1219+
case d: ClassDenotation => d.isFullyCompleted
1220+
case _ => false
1221+
}
1222+
def testFullyCompleted =
1223+
if (classParents.isEmpty) !is(Package) && symbol.eq(defn.AnyClass)
1224+
else classParents.forall(isFullyCompletedRef)
1225+
flagsUNSAFE.is(FullyCompleted) ||
1226+
isCompleted && testFullyCompleted && { setFlag(FullyCompleted); true }
1227+
}
12181228

12191229
// ------ syncing inheritance-related info -----------------------------
12201230

@@ -1300,7 +1310,7 @@ object SymDenotations {
13001310
baseTypeRefValid = ctx.runId
13011311
}
13021312

1303-
private def computeBases(implicit ctx: Context): Unit = {
1313+
private def computeBases(implicit ctx: Context): (List[ClassSymbol], BitSet) = {
13041314
if (myBaseClasses eq Nil) throw CyclicReference(this)
13051315
myBaseClasses = Nil
13061316
val seen = new mutable.BitSet
@@ -1324,17 +1334,22 @@ object SymDenotations {
13241334
case nil =>
13251335
to
13261336
}
1327-
myBaseClasses = classSymbol :: addParentBaseClasses(classParents, Nil)
1328-
mySuperClassBits = seen.toImmutable
1337+
val bcs = classSymbol :: addParentBaseClasses(classParents, Nil)
1338+
val scbits = seen.toImmutable
1339+
if (isFullyCompleted) {
1340+
myBaseClasses = bcs
1341+
mySuperClassBits = scbits
1342+
}
1343+
else myBaseClasses = null
1344+
(bcs, scbits)
13291345
}
13301346

13311347
/** A bitset that contains the superId's of all base classes */
13321348
private def superClassBits(implicit ctx: Context): BitSet =
13331349
if (classParents.isEmpty) BitSet() // can happen when called too early in Namers
13341350
else {
13351351
checkBasesUpToDate()
1336-
if (mySuperClassBits == null) computeBases
1337-
mySuperClassBits
1352+
if (mySuperClassBits != null) mySuperClassBits else computeBases._2
13381353
}
13391354

13401355
/** The base classes of this class in linearization order,
@@ -1344,8 +1359,7 @@ object SymDenotations {
13441359
if (classParents.isEmpty) classSymbol :: Nil // can happen when called too early in Namers
13451360
else {
13461361
checkBasesUpToDate()
1347-
if (myBaseClasses == null) computeBases
1348-
myBaseClasses
1362+
if (myBaseClasses != null) myBaseClasses else computeBases._1
13491363
}
13501364

13511365
final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean =
@@ -1378,9 +1392,9 @@ object SymDenotations {
13781392
while (ps.nonEmpty) {
13791393
val parent = ps.head.typeSymbol
13801394
parent.denot match {
1381-
case classd: ClassDenotation =>
1382-
fp.include(classd.memberFingerPrint)
1383-
parent.denot.setFlag(Frozen)
1395+
case parentDenot: ClassDenotation =>
1396+
fp.include(parentDenot.memberFingerPrint)
1397+
if (parentDenot.isFullyCompleted) parentDenot.setFlag(Frozen)
13841398
case _ =>
13851399
}
13861400
ps = ps.tail
@@ -1393,10 +1407,13 @@ object SymDenotations {
13931407
* not be used for package classes because cache never
13941408
* gets invalidated.
13951409
*/
1396-
def memberFingerPrint(implicit ctx: Context): FingerPrint = {
1397-
if (myMemberFingerPrint == FingerPrint.unknown) myMemberFingerPrint = computeMemberFingerPrint
1398-
myMemberFingerPrint
1399-
}
1410+
def memberFingerPrint(implicit ctx: Context): FingerPrint =
1411+
if (myMemberFingerPrint != FingerPrint.unknown) myMemberFingerPrint
1412+
else {
1413+
val fp = computeMemberFingerPrint
1414+
if (isFullyCompleted) myMemberFingerPrint = fp
1415+
fp
1416+
}
14001417

14011418
private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null
14021419
private[this] var myMemberCachePeriod: Period = Nowhere

src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -524,29 +524,36 @@ class TypeApplications(val self: Type) extends AnyVal {
524524
}
525525
}
526526

527-
/** Convert a type constructor `TC` with type parameters `T1, ..., Tn` to
527+
/** Convert a type constructor `TC` which has type parameters `T1, ..., Tn`
528+
* in a context where type parameters `U1,...,Un` are expected to
528529
*
529530
* LambdaXYZ { Apply = TC[hk$0, ..., hk$n] }
530531
*
531-
* where XYZ is a corresponds to the variances of the type parameters.
532+
* Here, XYZ corresponds to the variances of
533+
* - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`,
534+
* - `T1,...,Tn` otherwise.
535+
* v1 is compatible with v2, if v1 = v2 or v2 is non-variant.
532536
*/
533-
def EtaExpand(implicit ctx: Context): Type = {
534-
val tparams = typeParams
535-
self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams)
537+
def EtaExpand(tparams: List[Symbol])(implicit ctx: Context): Type = {
538+
def varianceCompatible(actual: Symbol, formal: Symbol) =
539+
formal.variance == 0 || actual.variance == formal.variance
540+
val tparamsToUse =
541+
if (typeParams.corresponds(tparams)(varianceCompatible)) tparams else typeParams
542+
self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse)
536543
//.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}")
537544
}
538545

539546
/** Eta expand if `bound` is a higher-kinded type */
540547
def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type =
541-
if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand
548+
if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand(bound.typeParams)
542549
else self
543550

544551
/** Eta expand the prefix in front of any refinements. */
545552
def EtaExpandCore(implicit ctx: Context): Type = self.stripTypeVar match {
546553
case self: RefinedType =>
547554
self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo)
548555
case _ =>
549-
self.EtaExpand
556+
self.EtaExpand(self.typeParams)
550557
}
551558

552559
/** 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 {
645652
param2.variance == param2.variance || param2.variance == 0
646653
if (classBounds.exists(tycon.derivesFrom(_)) &&
647654
tycon.typeParams.corresponds(tparams)(variancesMatch)) {
648-
val expanded = tycon.EtaExpand
655+
val expanded = tycon.EtaExpand(tparams)
649656
val lifted = (expanded /: targs) { (partialInst, targ) =>
650657
val tparam = partialInst.typeParams.head
651658
RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance))
@@ -659,7 +666,7 @@ class TypeApplications(val self: Type) extends AnyVal {
659666
false
660667
}
661668
tparams.nonEmpty &&
662-
(typeParams.nonEmpty && p(EtaExpand) ||
669+
(typeParams.hasSameLengthAs(tparams) && p(EtaExpand(tparams)) ||
663670
classBounds.nonEmpty && tryLift(self.baseClasses))
664671
}
665672
}

src/dotty/tools/dotc/core/Uniques.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ object Uniques {
9292
}
9393

9494
final class RefinedUniques extends HashSet[RefinedType](Config.initialUniquesCapacity) with Hashable {
95-
override val hashSeed = "CachedRefinedType".hashCode // some types start life as CachedRefinedTypes, need to have same hash seed
95+
override val hashSeed = classOf[CachedRefinedType].hashCode // some types start life as CachedRefinedTypes, need to have same hash seed
9696
override def hash(x: RefinedType): Int = x.hash
9797

9898
private def findPrevious(h: Int, parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = {

src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,6 @@ object Scala2Unpickler {
112112
if (tsym.exists) tsym.setFlag(TypeParam)
113113
else denot.enter(tparam, decls)
114114
}
115-
denot.info = ClassInfo(
116-
denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost) // more refined infowith parents
117115
if (!(denot.flagsUNSAFE is JavaModule)) ensureConstructor(denot.symbol.asClass, decls)
118116

119117
val scalacCompanion = denot.classSymbol.scalacLinkedClass
@@ -151,6 +149,51 @@ object Scala2Unpickler {
151149
denot.info = ClassInfo( // final info
152150
denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost)
153151
}
152+
153+
/** Adapt arguments to type parameters so that variance of type lambda arguments
154+
* agrees with variance of corresponding higherkinded type parameters. Example:
155+
*
156+
* class Companion[+CC[X]]
157+
* Companion[List]
158+
*
159+
* with adaptArgs, this will expand to
160+
*
161+
* Companion[[X] => List[X]]
162+
*
163+
* instead of
164+
*
165+
* Companion[[+X] => List[X]]
166+
*
167+
* even though `List` is covariant. This adaptation is necessary to ignore conflicting
168+
* variances in overriding members that have types of hk-type parameters such as `Companion[GenTraversable]`
169+
* or `Companion[ListBuffer]`. Without the adaptation we would end up with
170+
*
171+
* Companion[[+X] => GenTraversable[X]]
172+
* Companion[[X] => List[X]]
173+
*
174+
* and the second is not a subtype of the first. So if we have overridding memebrs of the two
175+
* types we get an error.
176+
*/
177+
def adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] = tparams match {
178+
case tparam :: tparams1 =>
179+
val boundLambda = tparam.infoOrCompleter match {
180+
case TypeBounds(_, hi) => hi.LambdaClass(forcing = false)
181+
case _ => NoSymbol
182+
}
183+
def adaptArg(arg: Type): Type = arg match {
184+
case arg: TypeRef if arg.symbol.isLambdaTrait =>
185+
assert(arg.symbol.typeParams.length == boundLambda.typeParams.length)
186+
arg.prefix.select(boundLambda)
187+
case arg: RefinedType =>
188+
arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo)
189+
case _ =>
190+
arg
191+
}
192+
val arg = args.head
193+
val adapted = if (boundLambda.exists) adaptArg(arg) else arg
194+
adapted :: adaptArgs(tparams1, args.tail)
195+
case nil => args
196+
}
154197
}
155198

156199
/** Unpickle symbol table information descending from a class and/or module root
@@ -723,8 +766,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
723766
else TypeRef(pre, sym.name.asTypeName)
724767
val args = until(end, readTypeRef)
725768
if (sym == defn.ByNameParamClass2x) ExprType(args.head)
726-
else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand
727-
else tycon.appliedTo(args)
769+
else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
770+
else tycon.appliedTo(adaptArgs(sym.typeParams, args))
728771
case TYPEBOUNDStpe =>
729772
TypeBounds(readTypeRef(), readTypeRef())
730773
case REFINEDtpe =>

0 commit comments

Comments
 (0)