@@ -997,7 +997,7 @@ 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.
1000
+ // On the other hand, we are not allowed to always stop at the present arguments either.
1001
1001
// An example is i10129.scala. Here, we have the following situation:
1002
1002
//
1003
1003
// type Lifted[A] = Err | A
@@ -1010,12 +1010,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1010
1010
// `Lifted` gives `Err | U <: Err | Int`, hence it should be `U <: Err | Int`.
1011
1011
//
1012
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 .
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 .
1019
1019
def loop (tycon1 : Type , args1 : List [Type ]): Boolean = tycon1 match {
1020
1020
case tycon1 : TypeParamRef =>
1021
1021
(tycon1 == tycon2 ||
@@ -1058,32 +1058,14 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1058
1058
(tycon1sym.isClass || tycon2sym.isClass)
1059
1059
&& (! touchedGADTs || gadtIsInstantiated)
1060
1060
1061
- def checkArgs () : Boolean =
1061
+ inFrozenGadtIf( ! tyconIsInjective) {
1062
1062
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))
1083
1066
else
1084
1067
isSubArgs(args1, args2, tp1, tparams)
1085
-
1086
- inFrozenGadtIf(! tyconIsInjective)(checkArgs())
1068
+ }
1087
1069
}
1088
1070
if (res && touchedGADTs) GADTused = true
1089
1071
res
@@ -1594,17 +1576,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1594
1576
*/
1595
1577
private def sufficientEither (op1 : => Boolean , op2 : => Boolean ): Boolean =
1596
1578
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
1608
1596
1609
1597
/** Returns true iff the result of evaluating either `op1` or `op2` is true, keeping the smaller constraint if any.
1610
1598
* E.g., if
0 commit comments