@@ -1683,19 +1683,43 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1683
1683
* @param tparams2 The type parameters of the type constructor applied to `args2`
1684
1684
*/
1685
1685
def isSubArgs (args1 : List [Type ], args2 : List [Type ], tp1 : Type , tparams2 : List [ParamInfo ]): Boolean = {
1686
+
1686
1687
/** The bounds of parameter `tparam`, where all references to type paramneters
1687
1688
* are replaced by corresponding arguments (or their approximations in the case of
1688
1689
* wildcard arguments).
1689
1690
*/
1690
1691
def paramBounds (tparam : Symbol ): TypeBounds =
1691
1692
tparam.info.substApprox(tparams2.asInstanceOf [List [Symbol ]], args2).bounds
1692
1693
1693
- def recurArgs (args1 : List [Type ], args2 : List [Type ], tparams2 : List [ParamInfo ]): Boolean =
1694
- if (args1.isEmpty) args2.isEmpty
1694
+ /** Test all arguments. Hard argument tests (according to isHard) are deferred in
1695
+ * the first run and picked up in the second.
1696
+ */
1697
+ def recurArgs (args1 : List [Type ], args2 : List [Type ], tparams2 : List [ParamInfo ],
1698
+ canDefer : Boolean ,
1699
+ deferred1 : List [Type ], deferred2 : List [Type ], deferredTparams2 : List [ParamInfo ]): Boolean =
1700
+ if args1.isEmpty then
1701
+ args2.isEmpty
1702
+ && (deferred1.isEmpty
1703
+ || recurArgs(
1704
+ deferred1.reverse, deferred2.reverse, deferredTparams2.reverse,
1705
+ canDefer = false , Nil , Nil , Nil ))
1695
1706
else args2.nonEmpty && tparams2.nonEmpty && {
1696
1707
val tparam = tparams2.head
1697
1708
val v = tparam.paramVarianceSign
1698
1709
1710
+ /** An argument test is hard if it implies a comparison A <: B where
1711
+ * A is an AndType or B is an OrType. In these cases we need to run an
1712
+ * either, which can lose solutions if there are type variables involved.
1713
+ * So we defer such tests to run last, on the chance that some other argument
1714
+ * comparison will instantiate or constrain type variables first.
1715
+ */
1716
+ def isHard (arg1 : Type , arg2 : Type ): Boolean =
1717
+ val arg1d = arg1.stripped
1718
+ val arg2d = arg2.stripped
1719
+ (v >= 0 ) && (arg1d.isInstanceOf [AndType ] || arg2d.isInstanceOf [OrType ])
1720
+ ||
1721
+ (v <= 0 ) && (arg1d.isInstanceOf [OrType ] || arg2d.isInstanceOf [AndType ])
1722
+
1699
1723
/** Try a capture conversion:
1700
1724
* If the original left-hand type `leftRoot` is a path `p.type`,
1701
1725
* and the current widened left type is an application with wildcard arguments
@@ -1781,10 +1805,26 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
1781
1805
else if v > 0 then isSubType(arg1, arg2)
1782
1806
else isSameType(arg2, arg1)
1783
1807
1784
- isSubArg(args1.head, args2.head)
1785
- } && recurArgs(args1.tail, args2.tail, tparams2.tail)
1808
+ val arg1 = args1.head
1809
+ val arg2 = args2.head
1810
+ val rest1 = args1.tail
1811
+ if ! canDefer
1812
+ || rest1.isEmpty && deferred1.isEmpty
1813
+ // skip the hardness test if this is the last argument and no previous arguments were hard
1814
+ || ! isHard(arg1, arg2)
1815
+ then
1816
+ isSubArg(arg1, arg2)
1817
+ && recurArgs(
1818
+ rest1, args2.tail, tparams2.tail, canDefer,
1819
+ deferred1, deferred2, deferredTparams2)
1820
+ else
1821
+ recurArgs(
1822
+ rest1, args2.tail, tparams2.tail, canDefer,
1823
+ arg1 :: deferred1, arg2 :: deferred2, tparams2.head :: deferredTparams2)
1824
+ }
1825
+
1826
+ recurArgs(args1, args2, tparams2, canDefer = true , Nil , Nil , Nil )
1786
1827
1787
- recurArgs(args1, args2, tparams2)
1788
1828
}
1789
1829
1790
1830
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
0 commit comments