@@ -42,6 +42,8 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
4242
4343 private var needsGc = false
4444
45+ private var canCompareAtoms : Boolean = true // used for internal consistency checking
46+
4547 /** Is a subtype check in progress? In that case we may not
4648 * permanently instantiate type variables, because the corresponding
4749 * constraint might still be retracted and the instantiation should
@@ -418,6 +420,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
418420 if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
419421 else thirdTry
420422 case tp1 @ OrType (tp11, tp12) =>
423+ compareAtoms(tp1, tp2) match
424+ case Some (b) => return b
425+ case None =>
421426
422427 def joinOK = tp2.dealiasKeepRefiningAnnots match {
423428 case tp2 : AppliedType if ! tp2.tycon.typeSymbol.isClass =>
@@ -440,19 +445,14 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
440445 (tp1.widenSingletons ne tp1) &&
441446 recur(tp1.widenSingletons, tp2)
442447
443- if (tp2.atoms().nonEmpty && canCompare(tp2.atoms()))
444- val atoms1 = tp1.atoms(widenOK = true )
445- atoms1.nonEmpty && atoms1.subsetOf(tp2.atoms())
446- else
447- widenOK
448- || joinOK
449- || recur(tp11, tp2) && recur(tp12, tp2)
450- || containsAnd(tp1) && recur(tp1.join, tp2)
451- // An & on the left side loses information. Compensate by also trying the join.
452- // This is less ad-hoc than it looks since we produce joins in type inference,
453- // and then need to check that they are indeed supertypes of the original types
454- // under -Ycheck. Test case is i7965.scala.
455-
448+ widenOK
449+ || joinOK
450+ || recur(tp11, tp2) && recur(tp12, tp2)
451+ || containsAnd(tp1) && recur(tp1.join, tp2)
452+ // An & on the left side loses information. Compensate by also trying the join.
453+ // This is less ad-hoc than it looks since we produce joins in type inference,
454+ // and then need to check that they are indeed supertypes of the original types
455+ // under -Ycheck. Test case is i7965.scala.
456456 case tp1 : MatchType =>
457457 val reduced = tp1.reduced
458458 if (reduced.exists) recur(reduced, tp2) else thirdTry
@@ -613,9 +613,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
613613 }
614614 compareTypeLambda
615615 case OrType (tp21, tp22) =>
616- if (tp2.atoms().nonEmpty && canCompare( tp2.atoms()))
617- val atoms1 = tp1.atoms(widenOK = true )
618- return atoms1.nonEmpty && atoms1.subsetOf(tp2.atoms()) || isSubType(tp1, NothingType )
616+ compareAtoms(tp1, tp2) match
617+ case Some (b) => return b
618+ case _ =>
619619
620620 // The next clause handles a situation like the one encountered in i2745.scala.
621621 // We have:
@@ -1172,22 +1172,48 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
11721172 else None
11731173 }
11741174
1175- /** Check whether we can compare the given set of atoms with another to determine
1176- * a subtype test between OrTypes. There is one situation where this is not
1177- * the case, which has to do with SkolemTypes. TreeChecker sometimes expects two
1178- * types to be equal that have different skolems. To account for this, we identify
1179- * two different skolems in all phases `p`, where `p.isTyper` is false.
1180- * But in that case comparing two sets of atoms that contain skolems
1181- * for equality would give the wrong result, so we should not use the sets
1182- * for comparisons.
1175+ /** If both `tp1` and `tp2` have atoms information, compare the atoms
1176+ * in a Some, otherwise None.
11831177 */
1184- def canCompare (atoms : Set [Type ]): Boolean =
1185- ctx.phase.isTyper || {
1178+ def compareAtoms (tp1 : Type , tp2 : Type ): Option [Boolean ] =
1179+
1180+ /** Check whether we can compare the given set of atoms with another to determine
1181+ * a subtype test between OrTypes. There is one situation where this is not
1182+ * the case, which has to do with SkolemTypes. TreeChecker sometimes expects two
1183+ * types to be equal that have different skolems. To account for this, we identify
1184+ * two different skolems in all phases `p`, where `p.isTyper` is false.
1185+ * But in that case comparing two sets of atoms that contain skolems
1186+ * for equality would give the wrong result, so we should not use the sets
1187+ * for comparisons.
1188+ */
1189+ def canCompare (ts : Set [Type ]) = ctx.phase.isTyper || {
11861190 val hasSkolems = new ExistsAccumulator (_.isInstanceOf [SkolemType ]) {
11871191 override def stopAtStatic = true
11881192 }
1189- ! atoms .exists(hasSkolems(false , _))
1193+ ! ts .exists(hasSkolems(false , _))
11901194 }
1195+ def verified (result : Boolean ): Boolean =
1196+ if Config .checkAtomsComparisons then
1197+ try
1198+ canCompareAtoms = false
1199+ val regular = recur(tp1, tp2)
1200+ assert(result == regular,
1201+ i """ Atoms inconsistency for $tp1 <:< $tp2
1202+ |atoms predicted $result
1203+ |atoms1 = ${tp1.atoms}
1204+ |atoms2 = ${tp2.atoms}""" )
1205+ finally canCompareAtoms = true
1206+ result
1207+
1208+ tp2.atoms match
1209+ case Atoms .Range (lo2, hi2) if canCompareAtoms && canCompare(hi2) =>
1210+ tp1.atoms match
1211+ case Atoms .Range (lo1, hi1) =>
1212+ if hi1.subsetOf(lo2) then Some (verified(true ))
1213+ else if ! lo1.subsetOf(hi2) then Some (verified(false ))
1214+ else None
1215+ case _ => Some (verified(recur(tp1, NothingType )))
1216+ case _ => None
11911217
11921218 /** Subtype test for corresponding arguments in `args1`, `args2` according to
11931219 * variances in type parameters `tparams2`.
@@ -1787,15 +1813,15 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17871813 else if tp2.isAny && ! tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass ) then tp2
17881814 else
17891815 def mergedLub (tp1 : Type , tp2 : Type ): Type = {
1790- val atoms1 = tp1.atoms(widenOK = true )
1791- if (atoms1.nonEmpty && ! widenInUnions) {
1792- val atoms2 = tp2.atoms(widenOK = true )
1793- if (atoms2.nonEmpty) {
1794- if (atoms1 .subsetOf(atoms2)) return tp2
1795- if (atoms2 .subsetOf(atoms1)) return tp1
1796- if ((atoms1 & atoms2 ).isEmpty) return orType(tp1, tp2)
1797- }
1798- }
1816+ tp1.atoms match
1817+ case Atoms . Range (lo1, hi1) if ! widenInUnions =>
1818+ tp2.atoms match
1819+ case Atoms . Range (lo2, hi2) =>
1820+ if hi1 .subsetOf(lo2) then return tp2
1821+ if hi2 .subsetOf(lo1) then return tp1
1822+ if (hi1 & hi2 ).isEmpty then return orType(tp1, tp2)
1823+ case none =>
1824+ case none =>
17991825 val t1 = mergeIfSuper(tp1, tp2, canConstrain)
18001826 if (t1.exists) return t1
18011827
0 commit comments