@@ -231,56 +231,69 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
231
231
}
232
232
}
233
233
234
+ def singleBounds (tp : Type ): List [Type ] = tp.widenExpr.dealias match
235
+ case tp1 : SingletonType => tp1 :: Nil
236
+ case AndType (tp11, tp12) => singleBounds(tp1) ::: singleBounds(tp2)
237
+ case _ => Nil
238
+
239
+ def isSingletonAlias (tp : Type ): Boolean = tp match
240
+ case tp : TermRef => singleBounds(tp).nonEmpty || isSingletonAlias(tp.prefix)
241
+ case _ => false
242
+
243
+ // THIS IS STILL PROVISIONAL
244
+ def canDropAlias (tp : NamedType ): Boolean = ! isSingletonAlias(tp.prefix)
245
+
234
246
def firstTry : Boolean = tp2 match {
235
247
case tp2 : NamedType =>
236
- def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean = {
248
+ def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean =
237
249
implicit val ctx : Context = this .ctx
238
- tp2.info match {
250
+ val info2 = tp2.info
251
+ info2 match
239
252
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
- }
253
+ if recur(tp1, info2.alias) then return true
254
+ if canDropAlias(tp2) then return false
255
+ case _ =>
256
+ tp1 match
257
+ case tp1 : NamedType =>
258
+ tp1.info match {
259
+ case info1 : TypeAlias =>
260
+ if recur(info1.alias, tp2) then return true
261
+ if tp1.prefix.isStable && canDropAlias(tp1) then return false
262
+ // If tp1.prefix is stable, the alias does contain all information about the original ref, so
263
+ // there's no need to try something else. (This is important for performance).
264
+ // To see why we cannot in general stop here, consider:
265
+ //
266
+ // trait C { type A }
267
+ // trait D { type A = String }
268
+ // (C & D)#A <: C#A
269
+ //
270
+ // Following the alias leads to the judgment `String <: C#A` which is false.
271
+ // However the original judgment should be true.
272
+ case _ =>
273
+ }
274
+ val sym2 = tp2.symbol
275
+ var sym1 = tp1.symbol
276
+ if (sym1.is( ModuleClass ) && sym2.is( ModuleVal ))
277
+ // For convenience we want X$ <:< X.type
278
+ // This is safe because X$ self-type is X.type
279
+ sym1 = sym1.companionModule
280
+ if ((sym1 ne NoSymbol ) && ( sym1 eq sym2))
281
+ ctx.erasedTypes ||
282
+ sym1.isStaticOwner ||
283
+ isSubType(tp1.prefix, tp2.prefix) ||
284
+ thirdTryNamed( tp2)
285
+ else
286
+ ( (tp1.name eq tp2.name)
287
+ && tp1.isMemberRef
288
+ && tp2.isMemberRef
289
+ && isSubType(tp1.prefix, tp2.prefix)
290
+ && tp1.signature == tp2.signature
291
+ && ! (sym1.isClass && sym2.isClass) // class types don't subtype each other
292
+ ) ||
293
+ thirdTryNamed(tp2)
294
+ case _ =>
295
+ secondTry
296
+ end compareNamed
284
297
compareNamed(tp1, tp2)
285
298
case tp2 : ProtoType =>
286
299
isMatchedByProto(tp2, tp1)
@@ -753,20 +766,16 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
753
766
case tp1 @ AppliedType (tycon1, args1) =>
754
767
compareAppliedType1(tp1, tycon1, args1)
755
768
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 {
769
+ def comparePaths = tp2 match
758
770
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
771
+ compareAtoms(tp1, tp2, knownSingletons = true ).getOrElse(false )
772
+ || { // needed to make from-tasty work. test cases: pos/i1753.scala, pos/t839.scala
773
+ tp2.info.widenExpr.dealias match
774
+ case tp2i : SingletonType => recur(tp1, tp2i)
775
+ case _ => false
765
776
}
766
- case _ =>
767
- false
768
- }
769
- isNewSubType(tp1.underlying.widenExpr) || comparePaths
777
+ case _ => false
778
+ comparePaths || isNewSubType(tp1.underlying.widenExpr)
770
779
case tp1 : RefinedType =>
771
780
isNewSubType(tp1.parent)
772
781
case tp1 : RecType =>
@@ -1174,8 +1183,18 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
1174
1183
1175
1184
/** If both `tp1` and `tp2` have atoms information, compare the atoms
1176
1185
* in a Some, otherwise None.
1186
+ * @param knownSingletons If true, we are coming from a comparison of two singleton types
1187
+ * This influences the comparison as shown below:
1188
+ *
1189
+ * Say you have singleton types p.type and q.type the atoms of p.type are `{p.type}..{p.type}`,
1190
+ * and the atoms of `q.type` are `{}..{p.type}`. Normally the atom comparison between p's
1191
+ * atoms and q's atoms gives false. But in this case we know that `q.type` is an alias of `p.type`
1192
+ * so we are still allowed to conclude that `p.type <:< q.type`. A situation where this happens
1193
+ * is in i6635.scala. Here,
1194
+ *
1195
+ * p: A, q: B & p.type and we want to conclude that p.type <: q.type.
1177
1196
*/
1178
- def compareAtoms (tp1 : Type , tp2 : Type ): Option [Boolean ] =
1197
+ def compareAtoms (tp1 : Type , tp2 : Type , knownSingletons : Boolean = false ): Option [Boolean ] =
1179
1198
1180
1199
/** Check whether we can compare the given set of atoms with another to determine
1181
1200
* a subtype test between OrTypes. There is one situation where this is not
@@ -1209,9 +1228,12 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
1209
1228
case Atoms .Range (lo2, hi2) if canCompareAtoms && canCompare(hi2) =>
1210
1229
tp1.atoms match
1211
1230
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
1231
+ if hi1.subsetOf(lo2) || knownSingletons && hi2.size == 1 && hi1 == hi2 then
1232
+ Some (verified(true ))
1233
+ else if ! lo1.subsetOf(hi2) then
1234
+ Some (verified(false ))
1235
+ else
1236
+ None
1215
1237
case _ => Some (verified(recur(tp1, NothingType )))
1216
1238
case _ => None
1217
1239
0 commit comments