Skip to content

Commit a492ad8

Browse files
committed
Avoid inifinite loop when comparing & types with class types
The problem is that in two cases of subtype checking we form the base type of the lhs, and, if that yields a new type continue with that one. But this is not enough to ensure progress. Concrete test cases were in `run/mixins1` and pos/intersections.scala. In `mixins1` we had an infinite cycle in the situation A[LazyRef(This)] & B <: A[B] where `This <: A[This]` and `B extended A[B]`. We got `A[LazyRef(This)] & A[B]` as a base type, which simplified to `A[_ >: LayzRef(This) | B <: LazyRef(This) & B]`. Then, subsequent base types cycled back and forth until the original type was reached. The change in TypeComparer avoids the simplification (which is really a complication) and keeps instead ``A[LazyRef(This)] & A[B]`. We then need the change in SymDenotations to make sure that the base type of this type wrt `A` is referentially the same type, so that we avoid the loop. I wish there was a more robust measure for determining whether taking a base type represents progress.
1 parent 6010f9b commit a492ad8

File tree

2 files changed

+23
-10
lines changed

2 files changed

+23
-10
lines changed

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,7 +1633,7 @@ object SymDenotations {
16331633
case tp: TypeVar => tp.inst.exists && inCache(tp.inst)
16341634
//case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing
16351635
//case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2)
1636-
case tp: TypeProxy => isCachable(tp.underlying, btrCache)
1636+
case tp: TypeProxy => isCachable(tp.underlying, btrCache)
16371637
case tp: AndOrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache)
16381638
case _ => true
16391639
}
@@ -1678,9 +1678,15 @@ object SymDenotations {
16781678
case tp: TypeProxy =>
16791679
baseTypeOf(tp.superType)
16801680
case AndType(tp1, tp2) =>
1681-
baseTypeOf(tp1) & baseTypeOf(tp2)
1681+
baseTypeOf(tp1) & baseTypeOf(tp2) match {
1682+
case AndType(tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp
1683+
case res => res
1684+
}
16821685
case OrType(tp1, tp2) =>
1683-
baseTypeOf(tp1) | baseTypeOf(tp2)
1686+
baseTypeOf(tp1) | baseTypeOf(tp2) match {
1687+
case OrType(tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp
1688+
case res => res
1689+
}
16841690
case JavaArrayType(_) if symbol == defn.ObjectClass =>
16851691
this.typeRef
16861692
case _ =>

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,10 +1448,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
14481448
val arg2 :: args2Rest = args2
14491449
val v = tparam.paramVariance
14501450
val glbArg =
1451-
if (v > 0) glb(arg1.hiBound, arg2.hiBound)
1451+
if (isSameTypeWhenFrozen(arg1, arg2)) arg1
1452+
else if (v > 0) glb(arg1.hiBound, arg2.hiBound)
14521453
else if (v < 0) lub(arg1.loBound, arg2.loBound)
1453-
else TypeBounds(lub(arg1.loBound, arg2.loBound),
1454-
glb(arg1.hiBound, arg2.hiBound))
1454+
else if (arg1.isInstanceOf[TypeBounds] || arg2.isInstanceOf[TypeBounds])
1455+
TypeBounds(lub(arg1.loBound, arg2.loBound),
1456+
glb(arg1.hiBound, arg2.hiBound))
1457+
else NoType
14551458
glbArg :: glbArgs(args1Rest, args2Rest, tparamsRest)
14561459
case nil =>
14571460
Nil
@@ -1599,7 +1602,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
15991602
case tp1 @ AppliedType(tycon1, args1) =>
16001603
tp2 match {
16011604
case AppliedType(tycon2, args2) if tycon1.typeSymbol == tycon2.typeSymbol =>
1602-
(tycon1 & tycon2).appliedTo(glbArgs(args1, args2, tycon1.typeParams))
1605+
val jointArgs = glbArgs(args1, args2, tycon1.typeParams)
1606+
if (jointArgs.forall(_.exists)) (tycon1 & tycon2).appliedTo(jointArgs)
1607+
else NoType
16031608
case _ =>
16041609
NoType
16051610
}
@@ -1730,15 +1735,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
17301735

17311736
/** Show subtype goal that led to an assertion failure */
17321737
def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = {
1733-
println(ex"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint")
1738+
println(i"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint")
17341739
def explainPoly(tp: Type) = tp match {
17351740
case tp: TypeParamRef => ctx.echo(s"TypeParamRef ${tp.show} found in ${tp.binder.show}")
17361741
case tp: TypeRef if tp.symbol.exists => ctx.echo(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")
17371742
case tp: TypeVar => ctx.echo(s"typevar ${tp.show}, origin = ${tp.origin}")
17381743
case _ => ctx.echo(s"${tp.show} is a ${tp.getClass}")
17391744
}
1740-
explainPoly(tp1)
1741-
explainPoly(tp2)
1745+
if (Config.verboseExplainSubtype) {
1746+
explainPoly(tp1)
1747+
explainPoly(tp2)
1748+
}
17421749
}
17431750

17441751
/** Record statistics about the total number of subtype checks

0 commit comments

Comments
 (0)