From d56de583c418923c6e574e98c65497b0a3a44b10 Mon Sep 17 00:00:00 2001 From: Joel Wilsson Date: Fri, 30 Aug 2024 21:51:36 +0200 Subject: [PATCH] Eliminate LazyRefs before comparing seen types in collectCompanions Closes #21521 --- .../dotty/tools/dotc/core/TypeComparer.scala | 36 ++++++++++--------- .../dotty/tools/dotc/typer/Implicits.scala | 7 ++-- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 8fc6307c426c..742c2ed52007 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -270,23 +270,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling report.log(explained(_.isSubType(tp1, tp2, approx), short = false)) } // Eliminate LazyRefs before checking whether we have seen a type before - val normalize = new TypeMap with CaptureSet.IdempotentCaptRefMap { - val DerefLimit = 10 - var derefCount = 0 - def apply(t: Type) = t match { - case t: LazyRef => - // Dereference a lazyref to detect underlying matching types, but - // be careful not to get into an infinite recursion. If recursion count - // exceeds `DerefLimit`, approximate with `t` instead. - derefCount += 1 - if t.evaluating || derefCount >= DerefLimit then t - else try mapOver(t.ref) finally derefCount -= 1 - case tp: TypeVar => - tp - case _ => - mapOver(t) - } - } + val normalize = eliminateLazyRefs val p = (normalize(tp1), normalize(tp2)) !pendingSubTypes.nn.contains(p) && { try { @@ -3338,6 +3322,24 @@ object TypeComparer { end CoveredStatus type CoveredStatus = CoveredStatus.Repr + def eliminateLazyRefs(using Context) = new TypeMap with CaptureSet.IdempotentCaptRefMap { + val DerefLimit = 10 + var derefCount = 0 + def apply(t: Type) = t match { + case t: LazyRef => + // Dereference a lazyref to detect underlying matching types, but + // be careful not to get into an infinite recursion. If recursion count + // exceeds `DerefLimit`, approximate with `t` instead. + derefCount += 1 + if t.evaluating || derefCount >= DerefLimit then t + else try mapOver(t.ref) finally derefCount -= 1 + case tp: TypeVar => + tp + case _ => + mapOver(t) + } + } + def topLevelSubType(tp1: Type, tp2: Type)(using Context): Boolean = comparing(_.topLevelSubType(tp1, tp2)) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 51e468153d1f..ba59dc6df779 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -688,6 +688,7 @@ trait ImplicitRunInfo: end collectParts val seen = util.HashSet[Type]() + val normalize = TypeComparer.eliminateLazyRefs val incomplete = util.HashSet[Type]() def collectCompanions(tp: Type, parts: collection.Set[Type]): TermRefSet = @@ -698,11 +699,13 @@ trait ImplicitRunInfo: case is: OfTypeImplicits => is.companionRefs case null => - if seen.contains(t) then + // Eliminate LazyRefs before checking whether we have seen a type before + val nt = normalize(t) + if seen.contains(nt) then incomplete += tp // all references for `t` will be accounted for in `seen` so we return `EmptySet`. TermRefSet.empty // on the other hand, the refs of `tp` are now inaccurate, so `tp` is marked incomplete. else - seen += t + seen += nt val is = recur(t) if !implicitScopeCache.contains(t) then incomplete += tp is.companionRefs