Skip to content

Commit 80f5018

Browse files
committed
Use adaptHkVariances when comparing type arguments
Used to be done only for refinements. We should see whether we can move the whole logic to eta expansion instead. With the change we can undo a previous change that suppressed variance checking when in Scala 2 mode.
1 parent 75477bc commit 80f5018

File tree

2 files changed

+14
-29
lines changed

2 files changed

+14
-29
lines changed

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -290,15 +290,8 @@ class TypeApplications(val self: Type) extends AnyVal {
290290
self
291291
}
292292

293-
/** Convert a type constructor `TC` which has type parameters `T1, ..., Tn`
294-
* in a context where type parameters `U1,...,Un` are expected to
295-
*
296-
* LambdaXYZ { Apply = TC[hk$0, ..., hk$n] }
297-
*
298-
* Here, XYZ corresponds to the variances of
299-
* - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`,
300-
* - `T1,...,Tn` otherwise.
301-
* v1 is compatible with v2, if v1 = v2 or v2 is non-variant.
293+
/** Convert a type constructor `TC` which has type parameters `X1, ..., Xn`
294+
* to `[X1, ..., Xn] -> TC[X1, ..., Xn]`.
302295
*/
303296
def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = {
304297
val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols

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

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
446446
case tp2: HKTypeLambda =>
447447
def compareTypeLambda: Boolean = tp1.stripTypeVar match {
448448
case tp1: HKTypeLambda =>
449-
/* Don't compare bounds or variances of lambdas under language:Scala2.
450-
* (1) If we compare bounds, t2994 will fail.
449+
/* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail.
451450
* The issue is that, logically, bounds should compare contravariantly,
452451
* but that would invalidate a pattern exploited in t2994:
453452
*
@@ -459,32 +458,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
459458
*
460459
* Note: it would be nice if this could trigger a migration warning, but I
461460
* am not sure how, since the code is buried so deep in subtyping logic.
462-
*
463-
* (2) If we compare variances, compilation of scala.collection.mutable.Set wil fail.
464-
* The issue is the following:
465-
*
466-
* Error overriding method companion in trait Iterable of type
467-
* => scala.collection.generic.GenericCompanion[[+A] => scala.collection.Iterable[A]];
468-
* method companion of type
469-
* => scala.collection.generic.GenericCompanion[[A] => scala.collection.mutable.Set[A]]
470-
* has incompatible type.
471-
*
472-
* Indeed, a non-variant Set is not a legal substitute for a covariant Iterable.
473-
* Every instantiated Set is an Iterable, but the type constructor Iterable can be
474-
* passed to a covariant type constructor CC[+X] whereas a non-variant Set cannot.
475461
*/
476462
def boundsOK =
477463
ctx.scala2Mode ||
478464
tp1.typeParams.corresponds(tp2.typeParams)((tparam1, tparam2) =>
479465
isSubType(tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo))
480-
def variancesOK =
481-
ctx.scala2Mode ||
482-
variancesConform(tp1.typeParams, tp2.typeParams)
483466
val saved = comparedTypeLambdas
484467
comparedTypeLambdas += tp1
485468
comparedTypeLambdas += tp2
486469
try
487-
variancesOK && boundsOK &&
470+
variancesConform(tp1.typeParams, tp2.typeParams) &&
471+
boundsOK &&
488472
isSubType(tp1.resType, tp2.resType.subst(tp2, tp1))
489473
finally comparedTypeLambdas = saved
490474
case _ =>
@@ -1014,7 +998,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1014998
}
1015999
}
10161000

1017-
isSubArg(args1.head, args2.head)
1001+
val arg1 = args1.head
1002+
val arg2 = args2.head
1003+
isSubArg(arg1, arg2) || {
1004+
// last effort: try to adapt variances of higher-kinded types if this is sound.
1005+
// TODO: Move this to eta-expansion?
1006+
val adapted2 = arg2.adaptHkVariances(tparam.paramInfo)
1007+
adapted2.ne(arg2) && isSubArg(arg1, adapted2)
1008+
}
10181009
} && isSubArgs(args1.tail, args2.tail, tp1, tparams.tail)
10191010

10201011
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
@@ -1186,6 +1177,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
11861177
def hasSubRefinement(tp1: RefinedType, refine2: Type): Boolean = {
11871178
isSubType(tp1.refinedInfo, refine2) || {
11881179
// last effort: try to adapt variances of higher-kinded types if this is sound.
1180+
// TODO: Move this to eta-expansion?
11891181
val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info)
11901182
adapted2.ne(refine2) && hasSubRefinement(tp1, adapted2)
11911183
}

0 commit comments

Comments
 (0)