@@ -231,56 +231,69 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
231231 }
232232 }
233233
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+
234246 def firstTry : Boolean = tp2 match {
235247 case tp2 : NamedType =>
236- def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean = {
248+ def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean =
237249 implicit val ctx : Context = this .ctx
238- tp2.info match {
250+ val info2 = tp2.info
251+ info2 match
239252 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
284297 compareNamed(tp1, tp2)
285298 case tp2 : ProtoType =>
286299 isMatchedByProto(tp2, tp1)
@@ -753,20 +766,16 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
753766 case tp1 @ AppliedType (tycon1, args1) =>
754767 compareAppliedType1(tp1, tycon1, args1)
755768 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
758770 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
765776 }
766- case _ =>
767- false
768- }
769- isNewSubType(tp1.underlying.widenExpr) || comparePaths
777+ case _ => false
778+ comparePaths || isNewSubType(tp1.underlying.widenExpr)
770779 case tp1 : RefinedType =>
771780 isNewSubType(tp1.parent)
772781 case tp1 : RecType =>
@@ -1174,8 +1183,18 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
11741183
11751184 /** If both `tp1` and `tp2` have atoms information, compare the atoms
11761185 * 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.
11771196 */
1178- def compareAtoms (tp1 : Type , tp2 : Type ): Option [Boolean ] =
1197+ def compareAtoms (tp1 : Type , tp2 : Type , knownSingletons : Boolean = false ): Option [Boolean ] =
11791198
11801199 /** Check whether we can compare the given set of atoms with another to determine
11811200 * 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
12091228 case Atoms .Range (lo2, hi2) if canCompareAtoms && canCompare(hi2) =>
12101229 tp1.atoms match
12111230 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
12151237 case _ => Some (verified(recur(tp1, NothingType )))
12161238 case _ => None
12171239
0 commit comments