Skip to content

Commit d6df293

Browse files
oderskygzm0
authored andcommitted
Fix of t1236: higher-kinded
(and also of t0625, which reappeared). Several fixes were made. In summary: 1. Naming and representation of KigherKinded traits changed. It's now $HigherKinded$NIP where the letters after the second $ indicate variance (N)egative, (I)nvariant, (P)ositive. The HKtraits themselves are always non-variant in their parameters. 2. When deriving refined types over higher-kinded types, the variance of a type alias is the variance of the new type constructor. 3. isSubTypeHK was changed, as was the position from where it is called. 4. appliedTo also works for PolyTypes.
1 parent 40202ee commit d6df293

File tree

10 files changed

+72
-39
lines changed

10 files changed

+72
-39
lines changed

src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ object Printers {
2222
val unapp: Printer = noPrinter
2323
val completions = noPrinter
2424
val gadts = noPrinter
25+
val hk = noPrinter
2526
val incremental = noPrinter
2627
val config = noPrinter
2728
}

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

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -359,12 +359,6 @@ class Definitions {
359359
*/
360360
def hkTrait(vcs: List[Int]) = {
361361

362-
def varianceSuffix(v: Int) = v match {
363-
case -1 => "N"
364-
case 0 => "I"
365-
case 1 => "P"
366-
}
367-
368362
def varianceFlags(v: Int) = v match {
369363
case -1 => Contravariant
370364
case 0 => Covariant
@@ -375,14 +369,13 @@ class Definitions {
375369
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
376370
val cls = denot.asClass.classSymbol
377371
val paramDecls = newScope
378-
for ((v, i) <- vcs.zipWithIndex)
379-
newTypeParam(cls, tpnme.higherKindedParamName(i), varianceFlags(v), paramDecls)
372+
for (i <- 0 until vcs.length)
373+
newTypeParam(cls, tpnme.higherKindedParamName(i), EmptyFlags, paramDecls)
380374
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, List(ObjectClass.typeRef), paramDecls)
381375
}
382376
}
383377

384-
val traitName =
385-
tpnme.higherKindedTraitName(vcs.length) ++ (vcs map varianceSuffix).mkString
378+
val traitName = tpnme.higherKindedTraitName(vcs)
386379

387380
def createTrait = {
388381
val cls = newClassSymbol(

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,19 @@ object NameOps {
168168
}
169169
}
170170

171+
/** The variances of the higherKinded parameters of the trait named
172+
* by this name.
173+
* @pre The name is a higher-kinded trait name, i.e. it starts with HK_TRAIT_PREFIX
174+
*/
175+
def hkVariances: List[Int] = {
176+
def varianceOfSuffix(suffix: Char): Int = {
177+
val idx = tpnme.varianceSuffixes.indexOf(suffix)
178+
assert(idx >= 0)
179+
idx - 1
180+
}
181+
name.drop(tpnme.HK_TRAIT_PREFIX.length).toList.map(varianceOfSuffix)
182+
}
183+
171184
/** If name length exceeds allowable limit, replace part of it by hash */
172185
def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString))
173186
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ object StdNames {
248248
val SPECIALIZED_INSTANCE: N = "specInstance$"
249249
val THIS: N = "_$this"
250250
val HK_PARAM_PREFIX: N = "_$hk$"
251+
val HK_TRAIT_PREFIX: N = "$HigherKinded$"
251252

252253
final val Nil: N = "Nil"
253254
final val Predef: N = "Predef"
@@ -281,7 +282,6 @@ object StdNames {
281282
val ConstantType: N = "ConstantType"
282283
val ExistentialTypeTree: N = "ExistentialTypeTree"
283284
val Flag : N = "Flag"
284-
val HigherKinded: N = "HigherKinded"
285285
val Ident: N = "Ident"
286286
val Import: N = "Import"
287287
val Literal: N = "Literal"
@@ -645,10 +645,14 @@ object StdNames {
645645
def syntheticTypeParamNames(num: Int): List[TypeName] =
646646
(0 until num).map(syntheticTypeParamName)(breakOut)
647647

648-
def higherKindedTraitName(n: Int) = HigherKinded ++ n.toString
648+
def higherKindedTraitName(vcs: List[Int]): TypeName = HK_TRAIT_PREFIX ++ vcs.map(varianceSuffix).mkString
649649
def higherKindedParamName(n: Int) = HK_PARAM_PREFIX ++ n.toString
650650

651651
final val Conforms = encode("<:<")
652+
653+
def varianceSuffix(v: Int): Char = varianceSuffixes.charAt(v + 1)
654+
655+
val varianceSuffixes = "NIP"
652656
}
653657

654658
abstract class JavaNames[N <: Name] extends DefinedNames[N] {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ class TypeApplications(val self: Type) extends AnyVal {
137137
tp.underlying.appliedTo(args)
138138
case AndType(l, r) =>
139139
l.appliedTo(args) & r
140+
case tp: PolyType =>
141+
tp.instantiate(args)
140142
case ErrorType =>
141143
self
142144
}

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

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,7 @@ class TypeComparer(initctx: Context) extends DotClass {
412412
val base = tp1.baseTypeRef(cls2)
413413
if (base.exists && (base ne tp1)) return isSubType(base, tp2)
414414
if ( cls2 == defn.SingletonClass && tp1.isStable
415-
|| cls2 == defn.NotNullClass && tp1.isNotNull
416-
|| (defn.hkTraits contains cls2) && isSubTypeHK(tp1, tp2)) return true
415+
|| cls2 == defn.NotNullClass && tp1.isNotNull) return true
417416
}
418417
fourthTry(tp1, tp2)
419418
}
@@ -430,7 +429,9 @@ class TypeComparer(initctx: Context) extends DotClass {
430429
}
431430
case _ => tp2
432431
}
433-
def compareRefined: Boolean = tp1.widen match {
432+
def compareRefined: Boolean =
433+
if (defn.hkTraits contains parent2.typeSymbol) isSubTypeHK(tp1, tp2)
434+
else tp1.widen match {
434435
case tp1 @ RefinedType(parent1, name1) if nameMatches(name1, name2, tp1, tp2) =>
435436
// optimized case; all info on tp1.name2 is in refinement tp1.refinedInfo.
436437
isSubType(tp1.refinedInfo, tp2.refinedInfo) && {
@@ -443,7 +444,7 @@ class TypeComparer(initctx: Context) extends DotClass {
443444
case mbr: SingleDenotation => qualifies(mbr)
444445
case _ => mbr hasAltWith qualifies
445446
}
446-
def hasMatchingMember(name: Name): Boolean = /*>|>*/ ctx.traceIndented(s"hasMatchingMember($name) ${tp1.member(name)}", subtyping) /*<|<*/ (
447+
def hasMatchingMember(name: Name): Boolean = /*>|>*/ ctx.traceIndented(s"hasMatchingMember($name) ${tp1.member(name).info.show}", subtyping) /*<|<*/ (
447448
memberMatches(tp1 member name)
448449
||
449450
{ // special case for situations like:
@@ -629,20 +630,33 @@ class TypeComparer(initctx: Context) extends DotClass {
629630
* This is the case if `tp1` and `tp2` have the same number
630631
* of type parameters, the bounds of tp1's paremeters
631632
* are contained in the corresponding bounds of tp2's parameters
632-
* and the variances of correesponding parameters agree.
633+
* and the variances of the parameters agree.
634+
* The variances agree if the supertype parameter is invariant,
635+
* or both parameters have the same variance.
636+
*
637+
* Note: When we get to isSubTypeHK, it might be that tp1 is
638+
* instantiated, or not. If it is instantiated, we compare
639+
* actual argument infos against higher-kinded bounds,
640+
* if it is not instantiated we compare type parameter bounds
641+
* and also compare variances.
633642
*/
634-
def isSubTypeHK(tp1: Type, tp2: Type): Boolean = {
643+
def isSubTypeHK(tp1: Type, tp2: Type): Boolean = ctx.traceIndented(s"isSubTypeHK(${tp1.show}, ${tp2.show}") {
635644
val tparams = tp1.typeParams
645+
val argInfos1 = tp1.argInfos
646+
val args1 =
647+
if (argInfos1.nonEmpty) argInfos1 // tp1 is instantiated, use the argument infos
648+
else { // tp1 is uninstantiated, use the parameter bounds
649+
val base = tp1.narrow
650+
tparams.map(base.memberInfo)
651+
}
636652
val hkArgs = tp2.argInfos
637-
(hkArgs.length == tparams.length) && {
638-
val base = tp1.narrow
639-
(tparams, hkArgs).zipped.forall { (tparam, hkArg) =>
640-
isSubType(base.memberInfo(tparam), hkArg.bounds) // TODO: base.memberInfo needed?
641-
} &&
642-
(tparams, tp2.typeSymbol.typeParams).zipped.forall { (tparam, tparam2) =>
643-
tparam.variance == tparam2.variance
644-
}
645-
}
653+
hk.println(s"isSubTypeHK: args1 = $args1, hkargs = $hkArgs")
654+
val boundsOK = (args1 corresponds hkArgs)(isSubType)
655+
val variancesOK =
656+
argInfos1.nonEmpty || (tparams corresponds tp2.typeSymbol.name.hkVariances) { (tparam, v) =>
657+
v == 0 || tparam.variance == v
658+
}
659+
boundsOK && variancesOK
646660
}
647661

648662
def trySetType(tr: NamedType, bounds: TypeBounds): Boolean =

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ object Types {
123123
final def isLegalPrefix(implicit ctx: Context): Boolean =
124124
isStable || {
125125
val absTypeNames = memberNames(abstractTypeNameFilter)
126-
if (absTypeNames.nonEmpty) typr.println(s"abstract type members of ${this.showWithUnderlying}: $absTypeNames")
126+
if (absTypeNames.nonEmpty) typr.println(s"abstract type members of ${this.showWithUnderlying()}: $absTypeNames")
127127
absTypeNames.isEmpty
128128
}
129129

@@ -815,11 +815,11 @@ object Types {
815815
/** Convert to text */
816816
def toText(printer: Printer): Text = printer.toText(this)
817817

818-
/** Utility method to show the underlying type of a TypeProxy together
818+
/** Utility method to show the underlying type of a TypeProxy chain together
819819
* with the proxy type itself.
820820
*/
821-
def showWithUnderlying(implicit ctx: Context): String = this match {
822-
case tp: TypeProxy => s"$show with underlying ${tp.underlying.show}"
821+
def showWithUnderlying(n: Int = 1)(implicit ctx: Context): String = this match {
822+
case tp: TypeProxy if n > 0 => s"$show with underlying ${tp.underlying.showWithUnderlying(n - 1)}"
823823
case _ => show
824824
}
825825

@@ -1309,12 +1309,13 @@ object Types {
13091309
lazy val underlyingTypeParams = parent.safeUnderlyingTypeParams
13101310
lazy val originalTypeParam = underlyingTypeParams(refinedName.hkParamIndex)
13111311

1312-
/** drop any co/contra variance in refined info if variance disagrees
1313-
* with new type param
1312+
/** Use variance of newly instantiated type parameter rather than the old hk argument
13141313
*/
1315-
def adjustedHKRefinedInfo(hkBounds: TypeBounds) = {
1316-
if (hkBounds.variance == originalTypeParam.info.bounds.variance) hkBounds
1317-
else TypeBounds(hkBounds.lo, hkBounds.hi)
1314+
def adjustedHKRefinedInfo(hkBounds: TypeBounds, underlyingTypeParam: TypeSymbol) = hkBounds match {
1315+
case tp @ TypeBounds(lo, hi) if lo eq hi =>
1316+
tp.derivedTypeBounds(lo, hi, underlyingTypeParam.variance)
1317+
case _ =>
1318+
hkBounds
13181319
}
13191320

13201321
if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo))
@@ -1323,7 +1324,8 @@ object Types {
13231324
// && { println(s"deriving $refinedName $parent $underlyingTypeParams"); true }
13241325
&& refinedName.hkParamIndex < underlyingTypeParams.length
13251326
&& originalTypeParam.name != refinedName)
1326-
derivedRefinedType(parent, originalTypeParam.name, adjustedHKRefinedInfo(refinedInfo.bounds))
1327+
derivedRefinedType(parent, originalTypeParam.name,
1328+
adjustedHKRefinedInfo(refinedInfo.bounds, underlyingTypeParams(refinedName.hkParamIndex)))
13271329
else
13281330
RefinedType(parent, refinedName, rt => refinedInfo.substThis(this, RefinedThis(rt)))
13291331
}

src/dotty/tools/dotc/core/transform/Erasure.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ object Erasure {
126126
sigName(tp1)
127127
case OrType(tp1, tp2) =>
128128
lubClass(tp1, tp2).name
129-
case ErrorType | WildcardType =>
129+
case tp: WildcardType =>
130+
tpnme.WILDCARD
131+
case ErrorType =>
130132
tpnme.WILDCARD
131133
}
132134

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ class Namer { typer: Typer =>
549549
else NoType
550550
}
551551
val iResType = iInstInfo.finalResultType.asSeenFrom(site, cls)
552+
if (iResType.exists)
553+
typr.println(s"using inherited type; raw: $iRawInfo, inst: $iInstInfo, inherited: $iResType")
552554
tp & iResType
553555
}
554556
}

tests/pending/pos/t1236.scala renamed to tests/pos/t1236.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ trait Empty[E[_]] {
44

55
object T {
66
val ListEmpty = new Empty[List] {
7-
def e[A]/*: List*/ = Nil // uncomment to get crash
7+
def e[B] = Nil
88
}
99

1010
def foo[F[_]](q:(String,String)) = "hello"

0 commit comments

Comments
 (0)