Skip to content

Commit e2399f1

Browse files
committed
A better criterion for avoiding backtracking after aliasing
A better criterion for avoiding backtracking after comparing a dealiased type.
1 parent 83d6fda commit e2399f1

File tree

1 file changed

+22
-22
lines changed

1 file changed

+22
-22
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

+22-22
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)