@@ -971,7 +971,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
971
971
*/
972
972
def isMatchingApply (tp1 : Type ): Boolean = tp1 match {
973
973
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.
975
975
// `TypeApplications#appliedTo` already takes care of dealiasing type
976
976
// constructors when this can be done without affecting type
977
977
// inference, doing it here would not only prevent code from compiling
@@ -997,6 +997,25 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
997
997
// ?F := [X] =>> (Int, X)
998
998
//
999
999
// 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.
1000
1019
def loop (tycon1 : Type , args1 : List [Type ]): Boolean = tycon1 match {
1001
1020
case tycon1 : TypeParamRef =>
1002
1021
(tycon1 == tycon2 ||
@@ -1039,9 +1058,35 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1039
1058
(tycon1sym.isClass || tycon2sym.isClass)
1040
1059
&& (! touchedGADTs || gadtIsInstantiated)
1041
1060
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())
1045
1090
}
1046
1091
if (res && touchedGADTs) GADTused = true
1047
1092
res
0 commit comments