@@ -42,6 +42,8 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
42
42
43
43
private var needsGc = false
44
44
45
+ private var canCompareAtoms : Boolean = true // used for internal consistency checking
46
+
45
47
/** Is a subtype check in progress? In that case we may not
46
48
* permanently instantiate type variables, because the corresponding
47
49
* constraint might still be retracted and the instantiation should
@@ -418,6 +420,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
418
420
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
419
421
else thirdTry
420
422
case tp1 @ OrType (tp11, tp12) =>
423
+ compareAtoms(tp1, tp2) match
424
+ case Some (b) => return b
425
+ case None =>
421
426
422
427
def joinOK = tp2.dealiasKeepRefiningAnnots match {
423
428
case tp2 : AppliedType if ! tp2.tycon.typeSymbol.isClass =>
@@ -440,19 +445,14 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
440
445
(tp1.widenSingletons ne tp1) &&
441
446
recur(tp1.widenSingletons, tp2)
442
447
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.
456
456
case tp1 : MatchType =>
457
457
val reduced = tp1.reduced
458
458
if (reduced.exists) recur(reduced, tp2) else thirdTry
@@ -613,9 +613,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
613
613
}
614
614
compareTypeLambda
615
615
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 _ =>
619
619
620
620
// The next clause handles a situation like the one encountered in i2745.scala.
621
621
// We have:
@@ -1172,22 +1172,48 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
1172
1172
else None
1173
1173
}
1174
1174
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.
1183
1177
*/
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 || {
1186
1190
val hasSkolems = new ExistsAccumulator (_.isInstanceOf [SkolemType ]) {
1187
1191
override def stopAtStatic = true
1188
1192
}
1189
- ! atoms .exists(hasSkolems(false , _))
1193
+ ! ts .exists(hasSkolems(false , _))
1190
1194
}
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
1191
1217
1192
1218
/** Subtype test for corresponding arguments in `args1`, `args2` according to
1193
1219
* variances in type parameters `tparams2`.
@@ -1787,15 +1813,15 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
1787
1813
else if tp2.isAny && ! tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass ) then tp2
1788
1814
else
1789
1815
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 =>
1799
1825
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
1800
1826
if (t1.exists) return t1
1801
1827
0 commit comments