@@ -997,7 +997,7 @@ 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.
1000+ // On the other hand, we are not allowed to always stop at the present arguments either.
10011001 // An example is i10129.scala. Here, we have the following situation:
10021002 //
10031003 // type Lifted[A] = Err | A
@@ -1010,12 +1010,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
10101010 // `Lifted` gives `Err | U <: Err | Int`, hence it should be `U <: Err | Int`.
10111011 //
10121012 // 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 .
1013+ // but this could narrow the constraint too much. The solution is to also
1014+ // check the constraint arising from the dealiased subtype test
1015+ // in the case where isSubArgs adds a constraint. If that second constraint
1016+ // is weaker than the first, we keep it in place of the first.
1017+ // Note that if the isSubArgs test fails, we will proceed anyway by
1018+ // dealising by doing a compareLower .
10191019 def loop (tycon1 : Type , args1 : List [Type ]): Boolean = tycon1 match {
10201020 case tycon1 : TypeParamRef =>
10211021 (tycon1 == tycon2 ||
@@ -1058,32 +1058,14 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
10581058 (tycon1sym.isClass || tycon2sym.isClass)
10591059 && (! touchedGADTs || gadtIsInstantiated)
10601060
1061- def checkArgs () : Boolean =
1061+ inFrozenGadtIf( ! tyconIsInjective) {
10621062 if tycon1sym == tycon2sym && tycon1sym.isAliasType then
1063- // Either compare arguments or compare dealiased types.
1064- // Note: This test should be done with an `either`, but this fails
1065- // for hk-alias-unification.scala The problem is not GADT boundschecking;
1066- // that is taken care of by the outer inFrozenGadtIf test. The problem is
1067- // that hk-alias-unification.scala relies on the right isSubArgs being performed
1068- // when constraining the result type of a method. But since #8635 we are only allowed
1069- // to use a necessaryEither, since we might otherwise constrain too much.
1070- // So strictly speaking by going to a sufficientEither we overshoot and narrow
1071- // the constraint unnecessarily. But unlike in the situation of #8365 with test cases
1072- // and-inf.scala and or-inf.scala, we don't cut off solutions in principle by dealiasing.
1073- // We "just" risk missing an opportunity to do a desired hk-type inference. This
1074- // is hopefully a less frequent corner case. Where it goes wrong compared to the status
1075- // before this fix: If isSubArgs succeeds and then the dealised test also succeeds
1076- // while constraining strictly less than the isSubArgs, then the we used to pick the
1077- // constraint after isSubArgs, but we now pick the constraint after the dealiased test.
1078- // There's one test in shapeless/deriving that had to be disabled
1079- // (modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala, value v7 in the functor test).
1080- sufficientEither(
1081- isSubArgs(args1, args2, tp1, tparams),
1082- recur(tp1.superType, tp2.superType))
1063+ val preConstraint = constraint
1064+ isSubArgs(args1, args2, tp1, tparams)
1065+ && tryAlso(preConstraint, recur(tp1.superType, tp2.superType))
10831066 else
10841067 isSubArgs(args1, args2, tp1, tparams)
1085-
1086- inFrozenGadtIf(! tyconIsInjective)(checkArgs())
1068+ }
10871069 }
10881070 if (res && touchedGADTs) GADTused = true
10891071 res
@@ -1594,17 +1576,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
15941576 */
15951577 private def sufficientEither (op1 : => Boolean , op2 : => Boolean ): Boolean =
15961578 val preConstraint = constraint
1597- if op1 then
1598- if constraint ne preConstraint then
1599- // check whether `op2` generates a weaker constraint than `op1`
1600- val leftConstraint = constraint
1601- constraint = preConstraint
1602- if ! (op2 && subsumes(leftConstraint, constraint, preConstraint)) then
1603- if constr != noPrinter && ! subsumes(constraint, leftConstraint, preConstraint) then
1604- constr.println(i " CUT - prefer $leftConstraint over $constraint" )
1605- constraint = leftConstraint
1606- true
1607- else op2
1579+ if op1 then tryAlso(preConstraint, op2) else op2
1580+
1581+ /** Check whether `op` generates a weaker constraint than the
1582+ * current constraint if we run it starting with `preConstraint`.
1583+ * If that's the case, replace the current constraint with the
1584+ * constraint generated by `op`.
1585+ */
1586+ private def tryAlso (preConstraint : Constraint , op : => Boolean ): true =
1587+ if constraint ne preConstraint then
1588+ // check whether `op2` generates a weaker constraint than `op1`
1589+ val leftConstraint = constraint
1590+ constraint = preConstraint
1591+ if ! (op && subsumes(leftConstraint, constraint, preConstraint)) then
1592+ if constr != noPrinter && ! subsumes(constraint, leftConstraint, preConstraint) then
1593+ constr.println(i " CUT - prefer $leftConstraint over $constraint" )
1594+ constraint = leftConstraint
1595+ true
16081596
16091597 /** Returns true iff the result of evaluating either `op1` or `op2` is true, keeping the smaller constraint if any.
16101598 * E.g., if
0 commit comments