Skip to content

Commit 68eb84e

Browse files
committed
Avoid looping when inferring types with recursive lower bounds in constraints
i3627.scala caused the typer to loop in several different ways. One was in isSubType, the other in wildApprox. The isSubType loop can be fixed by leaving monitoring on once the limit was reached for the remainder of the top-level subtype check. I am not exactly sure what caused the loop but the observation was a very long or infinite oscillation around the monitoring limit. Leaving monitoring on is certainly not incorrect - the previous trick to turn it off for recursions under the limit was purely for optimization. On the other hand I am not sure whether the new scheme is sufficient, or whether we can still have the situation of infinite recursions that stay under the depth limit. The wildApprox loop was a simple bug (the `seen` value was not propagated correctly to the TypeMap). The fix also removed a case in wildApproxBounds that had no clear purpose.
1 parent 2164c42 commit 68eb84e

File tree

3 files changed

+18
-8
lines changed

3 files changed

+18
-8
lines changed

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
2424

2525
private[this] var pendingSubTypes: mutable.Set[(Type, Type)] = null
2626
private[this] var recCount = 0
27+
private[this] var monitored = false
2728

2829
private[this] var needsGc = false
2930

@@ -101,9 +102,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
101102
if (tp2 eq NoType) return false
102103
if ((tp2 eq tp1) || (tp2 eq WildcardType)) return true
103104
try isSubType(tp1, tp2)
104-
finally
105+
finally {
106+
monitored = false
105107
if (Config.checkConstraintsSatisfiable)
106108
assert(isSatisfiable, constraint.show)
109+
}
107110
}
108111

109112
protected def isSubType(tp1: Type, tp2: Type): Boolean = trace(s"isSubType ${traceInfo(tp1, tp2)}", subtyping) {
@@ -114,9 +117,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
114117
val savedSuccessCount = successCount
115118
try {
116119
recCount = recCount + 1
117-
val result =
118-
if (recCount < Config.LogPendingSubTypesThreshold) firstTry(tp1, tp2)
119-
else monitoredIsSubType(tp1, tp2)
120+
if (recCount >= Config.LogPendingSubTypesThreshold) monitored = true
121+
val result = if (monitored) monitoredIsSubType(tp1, tp2) else firstTry(tp1, tp2)
120122
recCount = recCount - 1
121123
if (!result) state.resetConstraintTo(saved)
122124
else if (recCount == 0 && needsGc) {

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,7 @@ object ProtoTypes {
495495
tp.derivedTypeAlias(wildApprox(tp.alias, theMap, seen))
496496
case tp @ TypeParamRef(poly, pnum) =>
497497
def wildApproxBounds(bounds: TypeBounds) =
498-
if (bounds.lo.isInstanceOf[NamedType] && bounds.hi.isInstanceOf[NamedType])
499-
WildcardType(wildApprox(bounds, theMap, seen).bounds)
500-
else if (seen.contains(tp)) WildcardType
498+
if (seen.contains(tp)) WildcardType
501499
else WildcardType(wildApprox(bounds, theMap, seen + tp).bounds)
502500
def unconstrainedApprox = wildApproxBounds(poly.paramInfos(pnum))
503501
def approxPoly =
@@ -544,7 +542,8 @@ object ProtoTypes {
544542
case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed
545543
tp
546544
case _ =>
547-
(if (theMap != null) theMap else new WildApproxMap(seen)).mapOver(tp)
545+
(if (theMap != null && seen.eq(theMap.seen)) theMap else new WildApproxMap(seen))
546+
.mapOver(tp)
548547
}
549548

550549
@sharable object AssignProto extends UncachedGroundType with MatchAlways

tests/neg/i3627.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
trait Comparinator[T] {
2+
def sort[T](x: Comparinator[_ >: T]) = ()
3+
sort((a: Int) => true) // error
4+
}
5+
6+
trait Comparinator2[T >: U, U] {
7+
def sort[TT](x: Comparinator2[_ >: TT, U]) = ()
8+
sort((a: Int) => true) // error
9+
}

0 commit comments

Comments
 (0)