@@ -231,17 +231,27 @@ 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)
234+ /** Given an alias type `type A = B` where a recursive comparison with `B` yields
235+ * `false`, can we conclude that the comparison is definitely false?
236+ * This could not be the case if `A` overrides some abstract type. Example:
237+ *
238+ * class C { type A }
239+ * class D { type A = Int }
240+ * val c: C
241+ * val d: D & c.type
242+ * c.A <:< d.A ?
243+ *
244+ * The test should return true, by performing the logic in the bottom half of
245+ * firstTry (where we check the names of types). But just following the alias
246+ * from d.A to Int reduces the problem to `c.A <:< Int`, which returns `false`.
247+ * So we can't drop the alias here, we need to do the backtracking to the name-
248+ * based tests.
249+ */
250+ def canDropAlias (tp : NamedType ): Boolean =
251+ val sym = tp.symbol
252+ ! sym.canMatchInheritedSymbols
253+ || ! tp.prefix.baseClasses.exists(
254+ _.info.nonPrivateDecl(sym.name).symbol.is(Deferred ))
245255
246256 def firstTry : Boolean = tp2 match {
247257 case tp2 : NamedType =>
@@ -258,17 +268,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
258268 tp1.info match {
259269 case info1 : TypeAlias =>
260270 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.
271+ if canDropAlias(tp1) then return false
272272 case _ =>
273273 }
274274 val sym2 = tp2.symbol
0 commit comments