@@ -233,54 +233,45 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
233
233
234
234
def firstTry : Boolean = tp2 match {
235
235
case tp2 : NamedType =>
236
- def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean = {
236
+ def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean =
237
237
implicit val ctx : Context = this .ctx
238
- tp2.info match {
238
+ val info2 = tp2.info
239
+ info2 match
239
240
case info2 : TypeAlias =>
240
- recur(tp1, info2.alias)
241
- case _ => tp1 match {
242
- case tp1 : NamedType =>
243
- tp1.info match {
244
- case info1 : TypeAlias =>
245
- if (recur(info1.alias, tp2)) return true
246
- if (tp1.prefix.isStable) return false
247
- // If tp1.prefix is stable, the alias does contain all information about the original ref, so
248
- // there's no need to try something else. (This is important for performance).
249
- // To see why we cannot in general stop here, consider:
250
- //
251
- // trait C { type A }
252
- // trait D { type A = String }
253
- // (C & D)#A <: C#A
254
- //
255
- // Following the alias leads to the judgment `String <: C#A` which is false.
256
- // However the original judgment should be true.
257
- case _ =>
258
- }
259
- val sym2 = tp2.symbol
260
- var sym1 = tp1.symbol
261
- if (sym1.is(ModuleClass ) && sym2.is(ModuleVal ))
262
- // For convenience we want X$ <:< X.type
263
- // This is safe because X$ self-type is X.type
264
- sym1 = sym1.companionModule
265
- if ((sym1 ne NoSymbol ) && (sym1 eq sym2))
266
- ctx.erasedTypes ||
267
- sym1.isStaticOwner ||
268
- isSubType(tp1.prefix, tp2.prefix) ||
269
- thirdTryNamed(tp2)
270
- else
271
- ( (tp1.name eq tp2.name)
272
- && tp1.isMemberRef
273
- && tp2.isMemberRef
274
- && isSubType(tp1.prefix, tp2.prefix)
275
- && tp1.signature == tp2.signature
276
- && ! (sym1.isClass && sym2.isClass) // class types don't subtype each other
277
- ) ||
278
- thirdTryNamed(tp2)
279
- case _ =>
280
- secondTry
281
- }
282
- }
283
- }
241
+ if recur(tp1, info2.alias) then return true
242
+ if tp2.asInstanceOf [TypeRef ].canDropAlias then return false
243
+ case _ =>
244
+ tp1 match
245
+ case tp1 : NamedType =>
246
+ tp1.info match {
247
+ case info1 : TypeAlias =>
248
+ if recur(info1.alias, tp2) then return true
249
+ if tp1.asInstanceOf [TypeRef ].canDropAlias then return false
250
+ case _ =>
251
+ }
252
+ val sym2 = tp2.symbol
253
+ var sym1 = tp1.symbol
254
+ if (sym1.is(ModuleClass ) && sym2.is(ModuleVal ))
255
+ // For convenience we want X$ <:< X.type
256
+ // This is safe because X$ self-type is X.type
257
+ sym1 = sym1.companionModule
258
+ if ((sym1 ne NoSymbol ) && (sym1 eq sym2))
259
+ ctx.erasedTypes ||
260
+ sym1.isStaticOwner ||
261
+ isSubType(tp1.prefix, tp2.prefix) ||
262
+ thirdTryNamed(tp2)
263
+ else
264
+ ( (tp1.name eq tp2.name)
265
+ && tp1.isMemberRef
266
+ && tp2.isMemberRef
267
+ && isSubType(tp1.prefix, tp2.prefix)
268
+ && tp1.signature == tp2.signature
269
+ && ! (sym1.isClass && sym2.isClass) // class types don't subtype each other
270
+ ) ||
271
+ thirdTryNamed(tp2)
272
+ case _ =>
273
+ secondTry
274
+ end compareNamed
284
275
compareNamed(tp1, tp2)
285
276
case tp2 : ProtoType =>
286
277
isMatchedByProto(tp2, tp1)
@@ -753,20 +744,16 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
753
744
case tp1 @ AppliedType (tycon1, args1) =>
754
745
compareAppliedType1(tp1, tycon1, args1)
755
746
case tp1 : SingletonType =>
756
- /** if `tp2 == p.type` and `p: q.type` then try `tp1 <:< q.type` as a last effort.*/
757
- def comparePaths = tp2 match {
747
+ def comparePaths = tp2 match
758
748
case tp2 : TermRef =>
759
- tp2.info.widenExpr.dealias match {
760
- case tp2i : SingletonType =>
761
- recur(tp1, tp2i)
762
- // see z1720.scala for a case where this can arise even in typer.
763
- // Also, i1753.scala, to show why the dealias above is necessary.
764
- case _ => false
749
+ compareAtoms(tp1, tp2, knownSingletons = true ).getOrElse(false )
750
+ || { // needed to make from-tasty work. test cases: pos/i1753.scala, pos/t839.scala
751
+ tp2.info.widenExpr.dealias match
752
+ case tp2i : SingletonType => recur(tp1, tp2i)
753
+ case _ => false
765
754
}
766
- case _ =>
767
- false
768
- }
769
- isNewSubType(tp1.underlying.widenExpr) || comparePaths
755
+ case _ => false
756
+ comparePaths || isNewSubType(tp1.underlying.widenExpr)
770
757
case tp1 : RefinedType =>
771
758
isNewSubType(tp1.parent)
772
759
case tp1 : RecType =>
@@ -1177,8 +1164,18 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
1177
1164
1178
1165
/** If both `tp1` and `tp2` have atoms information, compare the atoms
1179
1166
* in a Some, otherwise None.
1167
+ * @param knownSingletons If true, we are coming from a comparison of two singleton types
1168
+ * This influences the comparison as shown below:
1169
+ *
1170
+ * Say you have singleton types p.type and q.type the atoms of p.type are `{p.type}..{p.type}`,
1171
+ * and the atoms of `q.type` are `{}..{p.type}`. Normally the atom comparison between p's
1172
+ * atoms and q's atoms gives false. But in this case we know that `q.type` is an alias of `p.type`
1173
+ * so we are still allowed to conclude that `p.type <:< q.type`. A situation where this happens
1174
+ * is in i6635.scala. Here,
1175
+ *
1176
+ * p: A, q: B & p.type and we want to conclude that p.type <: q.type.
1180
1177
*/
1181
- def compareAtoms (tp1 : Type , tp2 : Type ): Option [Boolean ] =
1178
+ def compareAtoms (tp1 : Type , tp2 : Type , knownSingletons : Boolean = false ): Option [Boolean ] =
1182
1179
1183
1180
/** Check whether we can compare the given set of atoms with another to determine
1184
1181
* a subtype test between OrTypes. There is one situation where this is not
@@ -1212,9 +1209,12 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
1212
1209
case Atoms .Range (lo2, hi2) if canCompareAtoms && canCompare(hi2) =>
1213
1210
tp1.atoms match
1214
1211
case Atoms .Range (lo1, hi1) =>
1215
- if hi1.subsetOf(lo2) then Some (verified(true ))
1216
- else if ! lo1.subsetOf(hi2) then Some (verified(false ))
1217
- else None
1212
+ if hi1.subsetOf(lo2) || knownSingletons && hi2.size == 1 && hi1 == hi2 then
1213
+ Some (verified(true ))
1214
+ else if ! lo1.subsetOf(hi2) then
1215
+ Some (verified(false ))
1216
+ else
1217
+ None
1218
1218
case _ => Some (verified(recur(tp1, NothingType )))
1219
1219
case _ => None
1220
1220
0 commit comments