Skip to content

Commit 89fa365

Browse files
Backport "Delay hard argument comparisons" to LTS (#21020)
Backports #20007 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 5b2d4af + 7cfc669 commit 89fa365

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

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

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,19 +1599,43 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
15991599
* @param tparams2 The type parameters of the type constructor applied to `args2`
16001600
*/
16011601
def isSubArgs(args1: List[Type], args2: List[Type], tp1: Type, tparams2: List[ParamInfo]): Boolean = {
1602+
16021603
/** The bounds of parameter `tparam`, where all references to type paramneters
16031604
* are replaced by corresponding arguments (or their approximations in the case of
16041605
* wildcard arguments).
16051606
*/
16061607
def paramBounds(tparam: Symbol): TypeBounds =
16071608
tparam.info.substApprox(tparams2.asInstanceOf[List[Symbol]], args2).bounds
16081609

1609-
def recurArgs(args1: List[Type], args2: List[Type], tparams2: List[ParamInfo]): Boolean =
1610-
if (args1.isEmpty) args2.isEmpty
1610+
/** Test all arguments. Incomplete argument tests (according to isIncomplete) are deferred in
1611+
* the first run and picked up in the second.
1612+
*/
1613+
def recurArgs(args1: List[Type], args2: List[Type], tparams2: List[ParamInfo],
1614+
canDefer: Boolean,
1615+
deferred1: List[Type], deferred2: List[Type], deferredTparams2: List[ParamInfo]): Boolean =
1616+
if args1.isEmpty then
1617+
args2.isEmpty
1618+
&& (deferred1.isEmpty
1619+
|| recurArgs(
1620+
deferred1.reverse, deferred2.reverse, deferredTparams2.reverse,
1621+
canDefer = false, Nil, Nil, Nil))
16111622
else args2.nonEmpty && tparams2.nonEmpty && {
16121623
val tparam = tparams2.head
16131624
val v = tparam.paramVarianceSign
16141625

1626+
/** An argument test is incomplete if it implies a comparison A <: B where
1627+
* A is an AndType or B is an OrType. In these cases we need to run an
1628+
* either, which can lose solutions if there are type variables involved.
1629+
* So we defer such tests to run last, on the chance that some other argument
1630+
* comparison will instantiate or constrain type variables first.
1631+
*/
1632+
def isIncomplete(arg1: Type, arg2: Type): Boolean =
1633+
val arg1d = arg1.strippedDealias
1634+
val arg2d = arg2.strippedDealias
1635+
(v >= 0) && (arg1d.isInstanceOf[AndType] || arg2d.isInstanceOf[OrType])
1636+
||
1637+
(v <= 0) && (arg1d.isInstanceOf[OrType] || arg2d.isInstanceOf[AndType])
1638+
16151639
/** Try a capture conversion:
16161640
* If the original left-hand type `leftRoot` is a path `p.type`,
16171641
* and the current widened left type is an application with wildcard arguments
@@ -1697,10 +1721,26 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
16971721
else if v > 0 then isSubType(arg1, arg2)
16981722
else isSameType(arg2, arg1)
16991723

1700-
isSubArg(args1.head.boxedUnlessFun(tp1), args2.head.boxedUnlessFun(tp1))
1701-
} && recurArgs(args1.tail, args2.tail, tparams2.tail)
1724+
val arg1 = args1.head.boxedUnlessFun(tp1)
1725+
val arg2 = args2.head.boxedUnlessFun(tp1)
1726+
val rest1 = args1.tail
1727+
if !canDefer
1728+
|| rest1.isEmpty && deferred1.isEmpty
1729+
// skip the incompleteness test if this is the last argument and no previous argument tests were incomplete
1730+
|| !isIncomplete(arg1, arg2)
1731+
then
1732+
isSubArg(arg1, arg2)
1733+
&& recurArgs(
1734+
rest1, args2.tail, tparams2.tail, canDefer,
1735+
deferred1, deferred2, deferredTparams2)
1736+
else
1737+
recurArgs(
1738+
rest1, args2.tail, tparams2.tail, canDefer,
1739+
arg1 :: deferred1, arg2 :: deferred2, tparams2.head :: deferredTparams2)
1740+
}
1741+
1742+
recurArgs(args1, args2, tparams2, canDefer = true, Nil, Nil, Nil)
17021743

1703-
recurArgs(args1, args2, tparams2)
17041744
}
17051745

17061746
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where

tests/pos/i19999.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class InIntersection[I, A]
2+
3+
def derived[A, R0]: InIntersection[A & R0, A] = new InIntersection[A & R0, A]
4+
5+
var x: InIntersection[Int & String, Int] = derived
6+

0 commit comments

Comments
 (0)