@@ -699,7 +699,7 @@ class TypeComparer(initctx: Context) extends DotClass {
699
699
}
700
700
compareRefined
701
701
case OrType (tp21, tp22) =>
702
- isSubType (tp1, tp21) || isSubType( tp1, tp22) || fourthTry(tp1, tp2)
702
+ eitherIsSubType (tp1, tp21, tp1, tp22) || fourthTry(tp1, tp2)
703
703
case tp2 @ MethodType (_, formals2) =>
704
704
def compareMethod = tp1 match {
705
705
case tp1 @ MethodType (_, formals1) =>
@@ -799,13 +799,52 @@ class TypeComparer(initctx: Context) extends DotClass {
799
799
finally pendingRefinedBases = saved
800
800
} || needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _))
801
801
case AndType (tp11, tp12) =>
802
- isNewSubType (tp11, tp2) || isNewSubType( tp12, tp2)
802
+ eitherIsSubType (tp11, tp2, tp12, tp2)
803
803
case JavaArrayType (elem1) =>
804
804
tp2 isRef ObjectClass
805
805
case _ =>
806
806
false
807
807
}
808
808
809
+ /** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
810
+ * to keep the constraint as wide as possible. Specifically, if
811
+ *
812
+ * tp11 <:< tp12 = true with post-constraint c1
813
+ * tp12 <:< tp22 = true with post-constraint c2
814
+ *
815
+ * and c1 subsumes c2, then c2 is kept as the post-constraint of the result,
816
+ * otherwise c1 is kept.
817
+ *
818
+ * This method is used to approximate a solution in one of the following cases
819
+ *
820
+ * T1 & T2 <:< T3
821
+ * T1 <:< T2 | T3
822
+ *
823
+ * In the first case (the second one is analogous), we have a choice whether we
824
+ * want to establish the subtyping judgement using
825
+ *
826
+ * T1 <:< T3 or T2 <:< T3
827
+ *
828
+ * as a precondition. Either precondition might constrain type variables.
829
+ * The purpose of this method is to pick the precondition that constrains less.
830
+ * The method is not complete, because sometimes there is no best solution. Example:
831
+ *
832
+ * A? & B? <: T
833
+ *
834
+ * Here, each precondition leads to a different constraint, and neither of
835
+ * the two post-constraints subsumes the other.
836
+ */
837
+ def eitherIsSubType (tp11 : Type , tp21 : Type , tp12 : Type , tp22 : Type ) = {
838
+ val preConstraint = constraint
839
+ isSubType(tp11, tp21) && {
840
+ val leftConstraint = constraint
841
+ constraint = preConstraint
842
+ if (isSubType(tp12, tp22) && ! subsumes(leftConstraint, constraint, preConstraint))
843
+ constraint = leftConstraint
844
+ true
845
+ } || isSubType(tp12, tp22)
846
+ }
847
+
809
848
/** Like tp1 <:< tp2, but returns false immediately if we know that
810
849
* the case was covered previously during subtyping.
811
850
*/
@@ -1343,6 +1382,26 @@ class TypeComparer(initctx: Context) extends DotClass {
1343
1382
false
1344
1383
}
1345
1384
1385
+ /** Constraint `c1` subsumes constraint `c2`, if under `c2` as constraint we have
1386
+ * for all poly params `p` defined in `c2` as `p >: L2 <: U2`:
1387
+ *
1388
+ * c1 defines p with bounds p >: L1 <: U1, and
1389
+ * L2 <: L1, and
1390
+ * U1 <: U2
1391
+ *
1392
+ * Both `c1` and `c2` are required to derive from constraint `pre`, possibly
1393
+ * narrowing it with further bounds.
1394
+ */
1395
+ def subsumes (c1 : Constraint , c2 : Constraint , pre : Constraint ): Boolean =
1396
+ if (c2 eq pre) true
1397
+ else if (c1 eq pre) false
1398
+ else {
1399
+ val saved = constraint
1400
+ try
1401
+ c2.forallParams(p => c1.contains(p) && isSubType(c1.bounds(p), c2.bounds(p)))
1402
+ finally constraint = saved
1403
+ }
1404
+
1346
1405
/** A new type comparer of the same type as this one, using the given context. */
1347
1406
def copyIn (ctx : Context ) = new TypeComparer (ctx)
1348
1407
0 commit comments