Skip to content

Commit b350d20

Browse files
committed
Perform variance adaptation only when needed in isSubType
Previously adaptIfHK was performed on every type application. This made t3152 fail. We now do this only on demand, in isSubType. t3152 now passes again. But the change unmasked another error, which makes Iter2 fail to compile.
1 parent 88f24ef commit b350d20

File tree

6 files changed

+48
-29
lines changed

6 files changed

+48
-29
lines changed

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

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ object TypeApplications {
167167
}
168168
}
169169

170-
/** Adapt all arguments to possible higher-kinded type parameters using adaptIfHK
170+
/** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK
171171
*/
172-
def adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] =
172+
def etaExpandIfHK(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] =
173173
if (tparams.isEmpty) args
174-
else args.zipWithConserve(tparams)((arg, tparam) => arg.adaptIfHK(tparam.infoOrCompleter))
174+
else args.zipWithConserve(tparams)((arg, tparam) => arg.etaExpandIfHK(tparam.infoOrCompleter))
175175

176176
def argRefs(rt: RefinedType, n: Int)(implicit ctx: Context) =
177177
List.range(0, n).map(i => RefinedThis(rt).select(tpnme.hkArg(i)))
@@ -340,11 +340,21 @@ class TypeApplications(val self: Type) extends AnyVal {
340340
self.EtaExpand(self.typeParams)
341341
}
342342

343-
/** Adapt argument A to type parameter P in the case P is higher-kinded.
344-
* This means:
345-
* (1) Make sure that A is a type lambda, if necessary by eta-expanding it.
346-
* (2) Make sure the variances of the type lambda
347-
* agrees with variances of corresponding higherkinded type parameters. Example:
343+
/** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */
344+
def etaExpandIfHK(bound: Type)(implicit ctx: Context): Type = {
345+
val boundLambda = bound.LambdaTrait
346+
val hkParams = boundLambda.typeParams
347+
if (hkParams.isEmpty) self
348+
else self match {
349+
case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length =>
350+
EtaExpansion(self)
351+
case _ => self
352+
}
353+
}
354+
355+
/** If argument A and type parameter P are higher-kinded, adapt the variances
356+
* of A to those of P, ensuring that the variances of the type lambda A
357+
* agree with the variances of corresponding higherkinded type parameters of P. Example:
348358
*
349359
* class Companion[+CC[X]]
350360
* Companion[List]
@@ -367,25 +377,26 @@ class TypeApplications(val self: Type) extends AnyVal {
367377
* and the second is not a subtype of the first. So if we have overridding memebrs of the two
368378
* types we get an error.
369379
*/
370-
def adaptIfHK(bound: Type)(implicit ctx: Context): Type = {
380+
def adaptHkVariances(bound: Type)(implicit ctx: Context): Type = {
371381
val boundLambda = bound.LambdaTrait
372382
val hkParams = boundLambda.typeParams
373383
if (hkParams.isEmpty) self
374-
else self match {
375-
case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length =>
376-
EtaExpansion(self).adaptIfHK(bound)
377-
case _ =>
378-
def adaptArg(arg: Type): Type = arg match {
379-
case arg: TypeRef
380-
if arg.symbol.isLambdaTrait &&
381-
!arg.symbol.typeParams.corresponds(boundLambda.typeParams)(_.variance == _.variance) =>
382-
arg.prefix.select(boundLambda)
383-
case arg: RefinedType =>
384-
arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo)
385-
case _ =>
386-
arg
387-
}
388-
adaptArg(self)
384+
else {
385+
def adaptArg(arg: Type): Type = arg match {
386+
case arg: TypeRef if arg.symbol.isLambdaTrait &&
387+
!arg.symbol.typeParams.corresponds(hkParams)(_.variance == _.variance) &&
388+
arg.symbol.typeParams.corresponds(hkParams)(varianceConforms) =>
389+
arg.prefix.select(boundLambda)
390+
case arg: RefinedType =>
391+
arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo)
392+
case arg @ TypeAlias(alias) =>
393+
arg.derivedTypeAlias(adaptArg(alias))
394+
case arg @ TypeBounds(lo, hi) =>
395+
arg.derivedTypeBounds(lo, adaptArg(hi))
396+
case _ =>
397+
arg
398+
}
399+
adaptArg(self)
389400
}
390401
}
391402

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -703,11 +703,19 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
703703
* Further, no refinement refers back to the refined type via a refined this.
704704
* The precondition is established by `skipMatching`.
705705
*/
706-
private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean =
707-
isSubType(tp1.refinedInfo, tp2.refinedInfo) && (
706+
private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean = {
707+
def hasSubRefinement(tp1: RefinedType, refine2: Type): Boolean = {
708+
isSubType(tp1.refinedInfo, refine2) || {
709+
// last effort: try to adapt variances of higher-kinded types if this is sound.
710+
val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info)
711+
adapted2.ne(refine2) && hasSubRefinement(tp1, adapted2)
712+
}
713+
}
714+
hasSubRefinement(tp1, tp2.refinedInfo) && (
708715
(tp2.parent eq limit) ||
709716
isSubRefinements(
710717
tp1.parent.asInstanceOf[RefinedType], tp2.parent.asInstanceOf[RefinedType], limit))
718+
}
711719

712720
/** A type has been covered previously in subtype checking if it
713721
* is some combination of TypeRefs that point to classes, where the

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
715715
else TypeRef(pre, sym.name.asTypeName)
716716
val args = until(end, readTypeRef)
717717
if (sym == defn.ByNameParamClass2x) ExprType(args.head)
718-
else if (args.nonEmpty) tycon.safeAppliedTo(adaptArgs(sym.typeParams, args))
718+
else if (args.nonEmpty) tycon.safeAppliedTo(etaExpandIfHK(sym.typeParams, args))
719719
else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
720720
else tycon
721721
case TYPEBOUNDStpe =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ trait Applications extends Compatibility { self: Typer =>
614614
}
615615

616616
def adaptTypeArg(tree: tpd.Tree, bound: Type)(implicit ctx: Context): tpd.Tree =
617-
tree.withType(tree.tpe.adaptIfHK(bound))
617+
tree.withType(tree.tpe.etaExpandIfHK(bound))
618618

619619
/** Rewrite `new Array[T](....)` trees to calls of newXYZArray methods. */
620620
def convertNewArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ class Namer { typer: Typer =>
853853
val tycon = tp.withoutArgs(args)
854854
val tycon1 = this(tycon)
855855
val tparams = tycon.typeParams
856-
val args1 = if (args.length == tparams.length) adaptArgs(tparams, args) else args
856+
val args1 = if (args.length == tparams.length) etaExpandIfHK(tparams, args) else args
857857
if ((tycon1 eq tycon) && (args1 eq args)) tp else tycon1.appliedTo(args1)
858858
} else mapOver(tp)
859859
case _ => mapOver(tp)
File renamed without changes.

0 commit comments

Comments
 (0)