@@ -1599,19 +1599,43 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1599
1599
* @param tparams2 The type parameters of the type constructor applied to `args2`
1600
1600
*/
1601
1601
def isSubArgs (args1 : List [Type ], args2 : List [Type ], tp1 : Type , tparams2 : List [ParamInfo ]): Boolean = {
1602
+
1602
1603
/** The bounds of parameter `tparam`, where all references to type paramneters
1603
1604
* are replaced by corresponding arguments (or their approximations in the case of
1604
1605
* wildcard arguments).
1605
1606
*/
1606
1607
def paramBounds (tparam : Symbol ): TypeBounds =
1607
1608
tparam.info.substApprox(tparams2.asInstanceOf [List [Symbol ]], args2).bounds
1608
1609
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 ))
1611
1622
else args2.nonEmpty && tparams2.nonEmpty && {
1612
1623
val tparam = tparams2.head
1613
1624
val v = tparam.paramVarianceSign
1614
1625
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
+
1615
1639
/** Try a capture conversion:
1616
1640
* If the original left-hand type `leftRoot` is a path `p.type`,
1617
1641
* and the current widened left type is an application with wildcard arguments
@@ -1697,10 +1721,26 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1697
1721
else if v > 0 then isSubType(arg1, arg2)
1698
1722
else isSameType(arg2, arg1)
1699
1723
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 )
1702
1743
1703
- recurArgs(args1, args2, tparams2)
1704
1744
}
1705
1745
1706
1746
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
0 commit comments