Skip to content

Commit 9d158e0

Browse files
committed
Fix #9504: Snapshot type size and covering set in search histories
The problem with #9504 is that types in the search history grow larger over time, since they contain type variables that get instantiated with new types containing new type variables and so on. That's why divergence checking is fooled into believing that the newly searched type is always smaller than the history. To fix this, we need to take a snapshot of type size and covering set at the time the search history is created.
1 parent cfe45b3 commit 9d158e0

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

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

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,17 +1369,15 @@ trait Implicits:
13691369
@tailrec
13701370
def loop(history: SearchHistory, belowByname: Boolean): Boolean =
13711371
history match
1372-
case OpenSearch(cand1, tp, outer) =>
1372+
case prev @ OpenSearch(cand1, tp, outer) =>
13731373
if cand1.ref eq cand.ref then
1374-
val wideTp = tp.widenExpr
1375-
lazy val wildTp = wildApprox(wideTp)
1376-
lazy val tpSize = wideTp.typeSize
1374+
lazy val wildTp = wildApprox(tp.widenExpr)
13771375
if belowByname && (wildTp <:< wildPt) then
13781376
false
1379-
else if tpSize > ptSize || wideTp.coveringSet != ptCoveringSet then
1377+
else if prev.typeSize > ptSize || prev.coveringSet != ptCoveringSet then
13801378
loop(outer, tp.isByName || belowByname)
13811379
else
1382-
tpSize < ptSize
1380+
prev.typeSize < ptSize
13831381
|| wildTp =:= wildPt
13841382
|| loop(outer, tp.isByName || belowByname)
13851383
else loop(outer, tp.isByName || belowByname)
@@ -1434,7 +1432,7 @@ end Implicits
14341432
/**
14351433
* Records the history of currently open implicit searches.
14361434
*
1437-
* A search history maintains a list of open implicit searches (`open`) a shortcut flag
1435+
* A search history maintains a list of open implicit searches (`openSearchPairs`) a shortcut flag
14381436
* indicating whether any of these are by name (`byname`) and a reference to the root
14391437
* search history (`root`) which in turn maintains a possibly empty dictionary of
14401438
* recursive implicit terms constructed during this search.
@@ -1448,7 +1446,7 @@ abstract class SearchHistory:
14481446
val root: SearchRoot
14491447
/** Does this search history contain any by name implicit arguments. */
14501448
val byname: Boolean
1451-
def open: List[(Candidate, Type)]
1449+
def openSearchPairs: List[(Candidate, Type)]
14521450

14531451
/**
14541452
* Create the state for a nested implicit search.
@@ -1469,13 +1467,22 @@ abstract class SearchHistory:
14691467
// This is NOOP unless at the root of this search history.
14701468
def emitDictionary(span: Span, result: SearchResult)(using Context): SearchResult = result
14711469

1472-
override def toString: String = s"SearchHistory(open = $open, byname = $byname)"
1470+
override def toString: String = s"SearchHistory(open = $openSearchPairs, byname = $byname)"
14731471
end SearchHistory
14741472

1475-
case class OpenSearch(cand: Candidate, pt: Type, outer: SearchHistory) extends SearchHistory:
1473+
case class OpenSearch(cand: Candidate, pt: Type, outer: SearchHistory)(using Context) extends SearchHistory:
14761474
val root = outer.root
14771475
val byname = outer.byname || pt.isByName
1478-
def open = (cand, pt) :: outer.open
1476+
def openSearchPairs = (cand, pt) :: outer.openSearchPairs
1477+
1478+
// The typeSize and coveringSet of the current search.
1479+
// Note: It is important to cache size and covering sets since types
1480+
// in search histories can contain type variables that can be instantiated
1481+
// by nested implicit searches, thus leading to types in search histories
1482+
// that grow larger the deeper the search gets. This can mask divergence.
1483+
// An example is in neg/9504.scala
1484+
lazy val typeSize = pt.typeSize
1485+
lazy val coveringSet = pt.coveringSet
14791486
end OpenSearch
14801487

14811488
/**
@@ -1484,7 +1491,7 @@ end OpenSearch
14841491
final class SearchRoot extends SearchHistory:
14851492
val root = this
14861493
val byname = false
1487-
def open = Nil
1494+
def openSearchPairs = Nil
14881495

14891496
/** The dictionary of recursive implicit types and corresponding terms for this search. */
14901497
var myImplicitDictionary: mutable.Map[Type, (TermRef, tpd.Tree)] = null

tests/neg/i9504.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait Monad[F[_]] {
2+
def foo[A](fa: F[A]): Unit = {}
3+
}
4+
5+
class Bla[F[_], A]
6+
7+
object Test {
8+
type Id[A] = A
9+
10+
val bla: Bla[Id, Unit] = ???
11+
implicit def blaMonad[F[_]: Monad, S]: Monad[({type L[X] = Bla[F, X]})#L] = ???
12+
13+
blaMonad.foo(bla) // error: divergence
14+
}

0 commit comments

Comments
 (0)