@@ -971,7 +971,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
971971 */
972972 def isMatchingApply (tp1 : Type ): Boolean = tp1 match {
973973 case tp1 as AppliedType (tycon1, args1) =>
974- // We intentionally do not dealias `tycon1` or `tycon2` here.
974+ // We intentionally do not automatically dealias `tycon1` or `tycon2` here.
975975 // `TypeApplications#appliedTo` already takes care of dealiasing type
976976 // constructors when this can be done without affecting type
977977 // inference, doing it here would not only prevent code from compiling
@@ -997,6 +997,25 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
997997 // ?F := [X] =>> (Int, X)
998998 //
999999 // Which is not what we wanted!
1000+ // On the other hand, we are not allowed to stop at the present arguments either.
1001+ // An example is i10129.scala. Here, we have the following situation:
1002+ //
1003+ // type Lifted[A] = Err | A
1004+ // def point[O](o: O): Lifted[O] = o
1005+ // extension [O, U](o: Lifted[O]) def map(f: O => U): Lifted[U] = ???
1006+ // point("a").map(_ => if true then 1 else error)
1007+ //
1008+ // This leads to the constraint Lifted[U] <: Lifted[Int]. If we just
1009+ // check the arguments this gives `U <: Int`. But this is wrong. Dealiasing
1010+ // `Lifted` gives `Err | U <: Err | Int`, hence it should be `U <: Err | Int`.
1011+ //
1012+ // So it's a conundrum. We need to check the immediate arguments for hk type inference,
1013+ // but this could narrow the constraint too much. The solution is to do an
1014+ // either with two alternatives when encountering an applied type alis.
1015+ // The first alternative checks the arguments, the second alternative checks the
1016+ // dealiased types. We pick the alternative that constrains least, or if there
1017+ // is no winner, the first one. We are forced to use a `sufficientEither`
1018+ // in the comparison, which is still not quite right. See the comment below.
10001019 def loop (tycon1 : Type , args1 : List [Type ]): Boolean = tycon1 match {
10011020 case tycon1 : TypeParamRef =>
10021021 (tycon1 == tycon2 ||
@@ -1039,9 +1058,35 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
10391058 (tycon1sym.isClass || tycon2sym.isClass)
10401059 && (! touchedGADTs || gadtIsInstantiated)
10411060
1042- inFrozenGadtIf(! tyconIsInjective) {
1043- isSubArgs(args1, args2, tp1, tparams)
1044- }
1061+ def checkArgs (): Boolean =
1062+ if tycon1sym == tycon2sym && tycon1sym.isAliasType then
1063+ // if types are simple forwarding aliases then try the dealiased types
1064+ tp1.superType match
1065+ case tp1a as AppliedType (_, args1a) if args1a.eqElements(args1) =>
1066+ tp2.superType match
1067+ case tp2a as AppliedType (_, args2a) if args2a.eqElements(args2) =>
1068+ return recur(tp1a, tp2a)
1069+ case _ =>
1070+ case _ =>
1071+ // Otherwise either compare arguments or compare dealiased types.
1072+ // Note: This test should be done with an `either`, but this fails
1073+ // for hk-alias-unification.scala The problem is not GADT boundschecking;
1074+ // that is taken care of by the outer inFrozenGadtIf test. The problem is
1075+ // that hk-alias-unification.scala relies on the right isSubArgs being performed
1076+ // when constraining the result type of a method. But there we are only allowed
1077+ // to use a necessaryEither, since it might be that an implicit conversion will
1078+ // be inserted on the result. So strictly speaking by going to a sufficientEither
1079+ // we overshoot and narrow the constraint unnecessarily. On the other hand, this
1080+ // is probably an extreme corner case. If we exclude implicit conversions, the
1081+ // problem goes away since in that case we would have a normal subtype test here,
1082+ // which demands a sufficientEither anyway.
1083+ sufficientEither(
1084+ isSubArgs(args1, args2, tp1, tparams),
1085+ recur(tp1.superType, tp2.superType))
1086+ else
1087+ isSubArgs(args1, args2, tp1, tparams)
1088+
1089+ inFrozenGadtIf(! tyconIsInjective)(checkArgs())
10451090 }
10461091 if (res && touchedGADTs) GADTused = true
10471092 res
0 commit comments