Skip to content

Commit 88dbba4

Browse files
committed
Try another variant
1 parent 965efd9 commit 88dbba4

File tree

1 file changed

+29
-41
lines changed

1 file changed

+29
-41
lines changed

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

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)